Merge into feat/code-generator

This commit is contained in:
春希 2020-07-01 10:14:51 +08:00
commit 397ad5b93d
2307 changed files with 568612 additions and 13703 deletions

3
.gitignore vendored
View File

@ -100,3 +100,6 @@ typings/
# mac config files # mac config files
.DS_Store .DS_Store
# codealike
codealike.json

View File

@ -6,7 +6,7 @@
#### 创建新包: #### 创建新包:
- `./create.sh <package-name>` - `./scripts/create.sh <package-name>`
#### 跑起来: #### 跑起来:

View File

@ -1,29 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1" />
<meta name="viewport" content="width=device-width" />
<title>LowCodeEngine Editor DEMO</title>
<link rel="shortcut icon" href="./favicon.png" />
<script src="https://g.alicdn.com/code/lib/react/16.9.0/umd/react.development.js"></script>
<script src="https://g.alicdn.com/code/lib/react-dom/16.9.0/umd/react-dom.development.js"></script>
<script src="https://g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js"></script>
<script> React.PropTypes = PropTypes; </script>
<script src="https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"></script>
<link rel="stylesheet" href="https://alifd.alicdn.com/npm/@alifd/next/1.11.6/next.min.css" />
<script src="https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.js"></script>
<!-- lowcode engine globals -->
<link rel="stylesheet" href="./globals.css" />
<!-- lowcode engine app -->
<link rel="stylesheet" href="./lowcode-editor.css" />
</head>
<body>
<div id="lce-container"></div>
<!-- lowcode engine globals -->
<script src="./globals.js"></script>
<!-- lowcode engine app -->
<script src="./lowcode-editor.js"></script>
</body>
</html>

View File

@ -1,23 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1" />
<meta name="viewport" content="width=device-width" />
<title>LowCodeEngine Preview DEMO</title>
<link rel="shortcut icon" href="./favicon.png" />
<script src="https://g.alicdn.com/code/lib/react/16.9.0/umd/react.development.js"></script>
<script src="https://g.alicdn.com/code/lib/react-dom/16.9.0/umd/react-dom.development.js"></script>
<script src="https://g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js"></script>
<script> React.PropTypes = PropTypes; </script>
<script src="https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"></script>
<link rel="stylesheet" href="https://alifd.alicdn.com/npm/@alifd/next/1.11.6/next.min.css">
<script src="https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.js"></script>
<link rel="stylesheet" href="./lowcode-preview.css" />
</head>
<body>
<script src="./lowcode-preview.js"></script>
</body>
</html>

View File

@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1" />
<meta name="viewport" content="width=device-width" />
<title>LowCodeEngine Editor DEMO</title>
<link rel="shortcut icon" href="./favicon.png" />
<script src="https://g.alicdn.com/code/lib/react/16.9.0/umd/react.development.js"></script>
<script src="https://g.alicdn.com/code/lib/react-dom/16.9.0/umd/react-dom.development.js"></script>
<script src="https://g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js"></script>
<script> React.PropTypes = PropTypes; </script>
<script src="https://g.alicdn.com/platform/c/??react15-polyfill/0.0.1/dist/index.js,lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js,natty-storage/2.0.2/dist/natty-storage.min.js,natty-fetch/2.6.0/dist/natty-fetch.pc.min.js,tinymce/4.2.5/tinymce-full.js"></script>
<script src="https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"></script>
<link rel="stylesheet" href="https://alifd.alicdn.com/npm/@alifd/next/1.11.6/next.min.css" />
<script src="https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.js"></script>
<!-- lowcode engine globals -->
<link rel="stylesheet" href="./editor-preset-vision.css" />
<!-- lowcode engine app -->
<link rel="stylesheet" href="./lowcode-editor.css" />
<script>
window.pageConfig = {
env: 'release',
locale: 'zh_CN',
pageType: 'single',
deviceType: 'web',
appName: '基础包管理后台',
appType: '',
templateType: '',
pageId: 'FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V',
slug: 'test',
appMode: 'back',
isAppAdmin: 'y',
isSuperAdmin: 'n',
isBetaDeveloper: 'n',
formType: 'display',
title: { en_US: '测试', type: 'i18n', zh_CN: '测试' },
urlPrefix: 'https://go.alibaba-inc.com',
APIUrlPrefix: 'https://mocks.alibaba-inc.com/mock/lowCodeEngine',
devVersion: '0.1.0', // 这个是子应用的变更 id
subAppType: '0.1.0',
appKey: '',
RE_VERSION: '7.1.1',
appSource: '',
isDomainDefault: 'n',
useReleaseBundle: 'n',
isDomainPkg: 'n',
medusaAppName: '',
domainCode: 'kS6SyH',
aecp: {
mdcDomain: '',
projectId: '',
appCode: '',
},
designerConfigs: {},
navConfig:
'{"appName":{"en_US":"基础包管理后台","key":"","type":"i18n","zh_CN":"基础包管理后台"},"bgColor":"white","data":[{"children":[],"hidden":false,"icon":"","inner":true,"navUuid":"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V","relateUuid":"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V","slug":"test","targetNew":false,"title":{"en_US":"测试","type":"i18n","zh_CN":"测试"}}],"isFixed":"y","isFold":"y","isFoldHorizontal":"n","languageChangeUrl":{"en_US":"/common/account/changeAccountLanguage.json","type":"i18n","zh_CN":"/common/account/changeAccountLanguage.json"},"layout":"auto","navStyle":"orange","navTheme":"light","openSubMode":false,"showAppTitle":true,"showCrumb":true,"showIcon":false,"showLanguageChange":true,"showNav":true,"showSearch":"n","singletons":{"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V":{"isFixed":"n","isFold":"n","isFoldHorizontal":"n","showAppTitle":false,"showCrumb":false,"showLanguageChange":false,"showNav":false,"showSearch":"n","singleton":false},"test":{"$ref":"$.singletons.FORM\\-3KYJN7RV\\-DIOD8LLK1WGQ89S7NHA92\\-QJVH497K\\-V"}},"type":"top_fold"}',
historyType: 'HASH',
isSinglePage: 'n',
rhino: 'n',
isMiniApp: '',
taskId: '',
appSchema: 'V5',
openSubMode: 'n',
};
window.g_config = {};
</script>
</head>
<body>
<div id="lce-container"></div>
<!-- lowcode engine globals -->
<script src="./editor-preset-vision.js"></script>
<script src="https://dev.g.alicdn.com/vision/visualengine-utils/5.0.0/engine-utils.js"></script>
<link rel="stylesheet" type="text/css" href="//g.alicdn.com/??platform/common/s/1.1/global/global.css,uxcore/uxcore-kuma/2.2.1/orange.min.css">
<!-- lowcode engine app -->
<script src="./lowcode-editor.js"></script>
</body>
</html>

View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1" />
<meta name="viewport" content="width=device-width" />
<title>LowCodeEngine Editor DEMO</title>
<link rel="shortcut icon" href="./favicon.png" />
<script src="https://g.alicdn.com/code/lib/react/16.9.0/umd/react.development.js"></script>
<script src="https://g.alicdn.com/code/lib/react-dom/16.9.0/umd/react-dom.development.js"></script>
<script src="https://g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js"></script>
<script> React.PropTypes = PropTypes; </script>
<script src="https://g.alicdn.com/platform/c/??react15-polyfill/0.0.1/dist/index.js,lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js,natty-storage/2.0.2/dist/natty-storage.min.js,natty-fetch/2.6.0/dist/natty-fetch.pc.min.js,tinymce/4.2.5/tinymce-full.js"></script>
<script src="https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"></script>
<link rel="stylesheet" href="https://alifd.alicdn.com/npm/@alifd/next/1.11.6/next.min.css" />
<script src="https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.js"></script>
<!-- lowcode engine globals -->
<link rel="stylesheet" href="./core.css" />
<!-- lowcode engine globals -->
<link rel="stylesheet" href="./vision-preset.css" />
<!-- lowcode engine app -->
<link rel="stylesheet" href="./lowcode-editor.css" />
<script>
window.pageConfig = {
env: 'release',
locale: 'zh_CN',
pageType: 'single',
deviceType: 'web',
appName: '基础包管理后台',
appType: '',
templateType: '',
pageId: 'FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V',
slug: 'test',
appMode: 'back',
isAppAdmin: 'y',
isSuperAdmin: 'n',
isBetaDeveloper: 'n',
formType: 'display',
title: { en_US: '测试', type: 'i18n', zh_CN: '测试' },
urlPrefix: 'https://go.alibaba-inc.com',
APIUrlPrefix: 'https://mocks.alibaba-inc.com/mock/lowCodeEngine',
devVersion: '0.1.0', // 这个是子应用的变更 id
subAppType: '0.1.0',
appKey: '',
RE_VERSION: '7.1.1',
appSource: '',
isDomainDefault: 'n',
useReleaseBundle: 'n',
isDomainPkg: 'n',
medusaAppName: '',
domainCode: 'kS6SyH',
aecp: {
mdcDomain: '',
projectId: '',
appCode: '',
},
designerConfigs: {},
navConfig:
'{"appName":{"en_US":"基础包管理后台","key":"","type":"i18n","zh_CN":"基础包管理后台"},"bgColor":"white","data":[{"children":[],"hidden":false,"icon":"","inner":true,"navUuid":"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V","relateUuid":"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V","slug":"test","targetNew":false,"title":{"en_US":"测试","type":"i18n","zh_CN":"测试"}}],"isFixed":"y","isFold":"y","isFoldHorizontal":"n","languageChangeUrl":{"en_US":"/common/account/changeAccountLanguage.json","type":"i18n","zh_CN":"/common/account/changeAccountLanguage.json"},"layout":"auto","navStyle":"orange","navTheme":"light","openSubMode":false,"showAppTitle":true,"showCrumb":true,"showIcon":false,"showLanguageChange":true,"showNav":true,"showSearch":"n","singletons":{"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V":{"isFixed":"n","isFold":"n","isFoldHorizontal":"n","showAppTitle":false,"showCrumb":false,"showLanguageChange":false,"showNav":false,"showSearch":"n","singleton":false},"test":{"$ref":"$.singletons.FORM\\-3KYJN7RV\\-DIOD8LLK1WGQ89S7NHA92\\-QJVH497K\\-V"}},"type":"top_fold"}',
historyType: 'HASH',
isSinglePage: 'n',
rhino: 'n',
isMiniApp: '',
taskId: '',
appSchema: 'V5',
openSubMode: 'n',
};
window.g_config = {};
</script>
</head>
<body>
<div id="lce-container"></div>
<!-- lowcode engine globals -->
<script src="./core.js"></script>
<!-- lowcode engine globals -->
<script src="./vision-preset.js"></script>
<script src="https://dev.g.alicdn.com/vision/visualengine-utils/5.0.0/engine-utils.js"></script>
<link rel="stylesheet" type="text/css" href="//g.alicdn.com/??platform/common/s/1.1/global/global.css,uxcore/uxcore-kuma/2.2.1/orange.min.css">
<!-- lowcode engine app -->
<script src="./lowcode-editor.js"></script>
</body>
</html>

View File

@ -10,10 +10,10 @@
] ]
}, },
"scripts": { "scripts": {
"build": "lerna run build --stream", "build": "./scripts/build.sh",
"clean": "rm -rf ./packages/*/lib ./packages/*/es ./packages/*/dist ./packages/*/build", "clean": "rm -rf ./packages/*/lib ./packages/*/es ./packages/*/dist ./packages/*/build",
"commit": "git-cz", "commit": "git-cz",
"pub": "lerna publish", "pub": "lerna publish --cd-version patch",
"setup": "./scripts/setup.sh", "setup": "./scripts/setup.sh",
"start": "./scripts/start.sh", "start": "./scripts/start.sh",
"test": "lerna run test --stream", "test": "lerna run test --stream",
@ -48,5 +48,9 @@
}, },
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
},
"tnpm": {
"mode": "yarn",
"lockfile": "enable"
} }
} }

View File

@ -3,6 +3,38 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
<a name="0.8.6"></a>
## [0.8.6](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.8.5...@ali/lowcode-code-generator@0.8.6) (2020-06-23)
### Bug Fixes
* 更改生成 id 的规则, 否则命中 recore 解析 id 的一个限制 ([5adff44](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/5adff44))
<a name="0.8.5"></a>
## [0.8.5](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.8.4...@ali/lowcode-code-generator@0.8.5) (2020-04-15)
### Bug Fixes
* enhance compile config ([2899149](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2899149))
* path resolve problem ([b12c0f8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b12c0f8))
* post process file error ([389eaf7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/389eaf7))
* rm demo in lib ([55630d6](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/55630d6))
* use webpack for package ([b350a88](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b350a88))
### Features
* add prettier post processor ([49ac9a3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/49ac9a3))
* export publisher ([4a53faa](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/4a53faa))
<a name="0.8.4"></a> <a name="0.8.4"></a>
## [0.8.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.8.3...@ali/lowcode-code-generator@0.8.4) (2020-03-30) ## [0.8.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.8.3...@ali/lowcode-code-generator@0.8.4) (2020-03-30)

View File

@ -1,6 +1,6 @@
{ {
"name": "@ali/lowcode-code-generator", "name": "@ali/lowcode-code-generator",
"version": "0.8.4", "version": "0.8.6",
"description": "出码引擎 for LowCode Engine", "description": "出码引擎 for LowCode Engine",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [

View File

@ -3,6 +3,219 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
<a name="0.8.33"></a>
## [0.8.33](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.32...@ali/lowcode-demo@0.8.33) (2020-06-24)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.32"></a>
## [0.8.32](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.31...@ali/lowcode-demo@0.8.32) (2020-06-23)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.31"></a>
## [0.8.31](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.30...@ali/lowcode-demo@0.8.31) (2020-06-23)
### Bug Fixes
* 更改生成 id 的规则, 否则命中 recore 解析 id 的一个限制 ([5adff44](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/5adff44))
<a name="0.8.30"></a>
## [0.8.30](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.29...@ali/lowcode-demo@0.8.30) (2020-06-16)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.29"></a>
## [0.8.29](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.28...@ali/lowcode-demo@0.8.29) (2020-06-15)
### Bug Fixes
* style ([4694331](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/4694331))
<a name="0.8.28"></a>
## [0.8.28](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.27...@ali/lowcode-demo@0.8.28) (2020-05-20)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.27"></a>
## [0.8.27](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.26...@ali/lowcode-demo@0.8.27) (2020-05-19)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.26"></a>
## [0.8.26](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.25...@ali/lowcode-demo@0.8.26) (2020-05-18)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.25"></a>
## [0.8.25](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.24...@ali/lowcode-demo@0.8.25) (2020-05-18)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.24"></a>
## [0.8.24](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.23...@ali/lowcode-demo@0.8.24) (2020-05-16)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.23"></a>
## [0.8.23](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.22...@ali/lowcode-demo@0.8.23) (2020-05-16)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.22"></a>
## [0.8.22](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.21...@ali/lowcode-demo@0.8.22) (2020-05-16)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.21"></a>
## [0.8.21](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.20...@ali/lowcode-demo@0.8.21) (2020-05-15)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.20"></a>
## [0.8.20](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.19...@ali/lowcode-demo@0.8.20) (2020-05-15)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.19"></a>
## [0.8.19](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.18...@ali/lowcode-demo@0.8.19) (2020-05-15)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.18"></a>
## [0.8.18](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.17...@ali/lowcode-demo@0.8.18) (2020-05-13)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.17"></a>
## [0.8.17](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.16...@ali/lowcode-demo@0.8.17) (2020-05-13)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.16"></a>
## [0.8.16](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.15...@ali/lowcode-demo@0.8.16) (2020-05-08)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.15"></a>
## [0.8.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.14...@ali/lowcode-demo@0.8.15) (2020-05-08)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.14"></a>
## [0.8.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.13...@ali/lowcode-demo@0.8.14) (2020-05-07)
### Bug Fixes
* 🐛 add history pane for vision demo ([3ce7079](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3ce7079))
* 🐛 清理无用代码 ([015b58a](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/015b58a))
<a name="0.8.13"></a>
## [0.8.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.12...@ali/lowcode-demo@0.8.13) (2020-04-27)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.12"></a>
## [0.8.12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.11...@ali/lowcode-demo@0.8.12) (2020-04-27)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.11"></a>
## [0.8.11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.10...@ali/lowcode-demo@0.8.11) (2020-04-27)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.10"></a>
## [0.8.10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.9...@ali/lowcode-demo@0.8.10) (2020-04-16)
**Note:** Version bump only for package @ali/lowcode-demo
<a name="0.8.9"></a>
## [0.8.9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.8...@ali/lowcode-demo@0.8.9) (2020-04-15)
### Features
* mixin-setter get all setter ([a5eb62d](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a5eb62d))
<a name="0.8.8"></a> <a name="0.8.8"></a>
## [0.8.8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.7...@ali/lowcode-demo@0.8.8) (2020-03-31) ## [0.8.8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.7...@ali/lowcode-demo@0.8.8) (2020-03-31)

View File

@ -1,8 +1,8 @@
{ {
"entry": { "entry": {
"index": "src/index.ts", "index": "src/index",
"react-simulator-renderer": "../react-simulator-renderer/src/index.ts", "editor-preset-vision": "../editor-preset-vision/src/index.ts",
"preview": "src/preview.ts" "react-simulator-renderer": "../react-simulator-renderer/src/index.ts"
}, },
"vendor": false, "vendor": false,
"devServer": { "devServer": {
@ -10,10 +10,12 @@
}, },
"publicPath": "/", "publicPath": "/",
"externals": { "externals": {
"react": "window.React", "react": "var window.React",
"react-dom": "window.ReactDOM", "react-dom": "var window.ReactDOM",
"prop-types": "window.PropTypes", "prop-types": "var window.PropTypes",
"@alifd/next": "window.Next" "@alifd/next": "var window.Next",
"@ali/visualengine": "var window.VisualEngine",
"@ali/visualengine-utils": "var window.VisualEngineUtils"
}, },
"plugins": [ "plugins": [
[ [

View File

@ -1,12 +1,22 @@
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = ({ onGetWebpackConfig }) => { module.exports = ({ onGetWebpackConfig }) => {
onGetWebpackConfig((config) => { onGetWebpackConfig((config) => {
config.resolve config.resolve.plugin('tsconfigpaths').use(TsconfigPathsPlugin, [
.plugin('tsconfigpaths') {
.use(TsconfigPathsPlugin, [{ configFile: './tsconfig.json',
configFile: "./tsconfig.json" },
}]); ]);
config
// 定义插件名称
.plugin('MonacoWebpackPlugin')
// 第一项为具体插件,第二项为插件参数
.use(new MonacoWebpackPlugin({
languages:["typescript","css","json"]
}), []);
config.plugins.delete('hot'); config.plugins.delete('hot');
config.devServer.hot(false); config.devServer.hot(false);
}); });

View File

@ -1,14 +1,18 @@
{ {
"entry": { "entry": {
"lowcode-editor": "src/index.ts", "lowcode-editor": "src/vision/index.ts"
"lowcode-preview": "src/preview.ts"
}, },
"vendor": false, "vendor": false,
"externals": { "externals": {
"react": "window.React", "react": "window.React",
"react-dom": "window.ReactDOM", "react-dom": "window.ReactDOM",
"prop-types": "window.PropTypes", "prop-types": "window.PropTypes",
"@ali/lowcode-globals": "window.LCEGlobals" "@ali/visualengine": "window.VisualEngine",
"@ali/visualengine-utils": "window.VisualEngineUtils",
"@ali/lowcode-editor-preset-general": "window.LowcodeEditor",
"@ali/lowcode-editor-core": "window.LowcodeEditor",
"@ali/lowcode-editor-skeleton": "window.LowcodeEditor",
"@ali/lowcode-designer": "window.LowcodeEditor"
}, },
"minify": false, "minify": false,
"sourcemap": true, "sourcemap": true,

View File

@ -1,39 +1,47 @@
{ {
"name": "@ali/lowcode-demo", "name": "@ali/lowcode-demo",
"version": "0.8.8", "version": "0.8.33",
"private": true, "private": true,
"description": "低代码引擎 DEMO", "description": "低代码引擎 DEMO",
"scripts": { "scripts": {
"cloud-build": "build-scripts build --config cloud-build.json", "cloud-build": "build-scripts build --config cloud-build.json",
"gen": "npm run genSkeleton && tyarn",
"genSkeleton": "iceluna gen lowcode -c ./skeleton.config.js -t ./src/editor/config",
"start": "build-scripts start" "start": "build-scripts start"
}, },
"config": {}, "config": {},
"dependencies": { "dependencies": {
"@ali/lowcode-editor-core": "^0.8.4", "@ali/lowcode-editor-core": "^0.8.19",
"@ali/lowcode-editor-skeleton": "^0.8.0", "@ali/lowcode-editor-skeleton": "^0.8.29",
"@ali/lowcode-plugin-components-pane": "^0.8.0", "@ali/lowcode-plugin-components-pane": "^0.8.25",
"@ali/lowcode-plugin-designer": "^0.9.1", "@ali/lowcode-plugin-designer": "^0.9.23",
"@ali/lowcode-plugin-event-bind-dialog": "^0.8.0", "@ali/lowcode-plugin-event-bind-dialog": "^0.8.18",
"@ali/lowcode-plugin-outline-pane": "^0.8.7", "@ali/lowcode-plugin-outline-pane": "^0.8.29",
"@ali/lowcode-plugin-sample-logo": "^0.8.0", "@ali/lowcode-plugin-sample-logo": "^0.8.17",
"@ali/lowcode-plugin-sample-preview": "^0.8.6", "@ali/lowcode-plugin-sample-preview": "^0.8.27",
"@ali/lowcode-plugin-settings-pane": "^0.8.8", "@ali/lowcode-plugin-settings-pane": "^0.8.8",
"@ali/lowcode-plugin-undo-redo": "^0.8.0", "@ali/lowcode-plugin-undo-redo": "^0.8.27",
"@ali/lowcode-plugin-variable-bind-dialog": "^0.8.2", "@ali/lowcode-plugin-variable-bind-dialog": "^0.8.16",
"@ali/lowcode-plugin-zh-en": "^0.8.6", "@ali/lowcode-plugin-zh-en": "^0.8.20",
"@ali/lowcode-react-renderer": "^0.8.4", "@ali/lowcode-react-renderer": "^0.8.12",
"@ali/lowcode-runtime": "^0.8.7", "@ali/lowcode-runtime": "^0.8.16",
"@ali/lowcode-setters": "^0.8.6", "@ali/lowcode-utils": "^0.8.10",
"@ali/ve-action-pane": "^4.7.0-beta.0",
"@ali/ve-datapool-pane": "^6.4.3",
"@ali/ve-history-pane": "4.0.0",
"@ali/ve-i18n-manage-pane": "^4.3.0",
"@ali/ve-i18n-pane": "^4.0.0-beta.0",
"@ali/ve-page-history": "1.2.0",
"@ali/ve-page-history-pane": "^5.0.0-beta.0",
"@ali/ve-trunk-pane": "^5.1.0-beta.14",
"@ali/vs-variable-setter": "^3.1.0",
"@ali/vu-function-parser": "^2.5.0-beta.0",
"@ali/vu-legao-design-fetch-context": "^1.0.3",
"@alifd/next": "^1.19.12", "@alifd/next": "^1.19.12",
"@alife/theme-lowcode-dark": "^0.1.0",
"@alife/theme-lowcode-light": "^0.1.0", "@alife/theme-lowcode-light": "^0.1.0",
"compare-versions": "^3.0.1",
"react": "^16.8.1", "react": "^16.8.1",
"react-dom": "^16.8.1" "react-dom": "^16.8.1"
}, },
"devDependencies": { "devDependencies": {
"@ali/iceluna-cli": "^0.0.16",
"@alib/build-scripts": "^0.1.18", "@alib/build-scripts": "^0.1.18",
"@types/events": "^3.0.0", "@types/events": "^3.0.0",
"@types/react": "^16.8.3", "@types/react": "^16.8.3",
@ -41,6 +49,7 @@
"build-plugin-fusion": "^0.1.0", "build-plugin-fusion": "^0.1.0",
"build-plugin-moment-locales": "^0.1.0", "build-plugin-moment-locales": "^0.1.0",
"build-plugin-react-app": "^1.1.2", "build-plugin-react-app": "^1.1.2",
"monaco-editor-webpack-plugin": "^1.9.0",
"tsconfig-paths-webpack-plugin": "^3.2.0" "tsconfig-paths-webpack-plugin": "^3.2.0"
} }
} }

View File

@ -1,21 +1,21 @@
{ {
"version": "1.0.0", "version": "1.0.0",
"packages": { "packages": [
"moment": { {
"package": "moment", "package": "moment",
"urls": ["https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"], "urls": ["https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"],
"library": "moment" "library": "moment"
}, },
"@alifd/next": { {
"title": "fusion组件库", "title": "fusion组件库",
"package": "@alifd/next", "package": "@alifd/next",
"version": "1.19.18", "version": "1.19.18",
"urls": ["https://unpkg.antfin-inc.com/@alife/next@1.19.18/dist/next.js", "https://unpkg.antfin-inc.com/@alife/next@1.19.18/dist/next.css"], "urls": ["https://unpkg.antfin-inc.com/@alife/next@1.19.18/dist/next.js", "https://unpkg.antfin-inc.com/@alife/next@1.19.18/dist/next.css"],
"library": "Next" "library": "Next"
} }
}, ],
"components": { "components": [
"Page": { {
"componentName": "Page", "componentName": "Page",
"title": "页面", "title": "页面",
"configure": { "configure": {
@ -39,8 +39,14 @@
} }
} }
}, },
"Div": { {
"componentName": "Div", "componentName": "Card",
"npm": {
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Card"
},
"title": "容器", "title": "容器",
"configure": { "configure": {
"component": { "component": {
@ -48,7 +54,7 @@
} }
} }
}, },
"Button": { {
"componentName": "Button", "componentName": "Button",
"title": "按钮", "title": "按钮",
"devMode": "proCode", "devMode": "proCode",
@ -147,7 +153,7 @@
"propType": "node" "propType": "node"
}] }]
}, },
"Button.Group": { {
"componentName": "Button.Group", "componentName": "Button.Group",
"title": "按钮组", "title": "按钮组",
"devMode": "proCode", "devMode": "proCode",
@ -186,7 +192,7 @@
} }
} }
}, },
"Input": { {
"componentName": "Input", "componentName": "Input",
"title": "输入框", "title": "输入框",
"devMode": "proCode", "devMode": "proCode",
@ -197,6 +203,10 @@
"exportName": "Input" "exportName": "Input"
}, },
"props": [{ "props": [{
"name": "value",
"propType": "string",
"description": ""
},{
"name": "label", "name": "label",
"propType": "node", "propType": "node",
"description": "label" "description": "label"
@ -300,7 +310,7 @@
"description": "预览态模式下渲染的内容\n@param {number} value 评分值" "description": "预览态模式下渲染的内容\n@param {number} value 评分值"
}] }]
}, },
"Form": { {
"componentName": "Form", "componentName": "Form",
"title": "表单容器", "title": "表单容器",
"devMode": "proCode", "devMode": "proCode",
@ -423,7 +433,7 @@
} }
} }
}, },
"Form.Item": { {
"componentName": "Form.Item", "componentName": "Form.Item",
"title": "表单项", "title": "表单项",
"devMode": "proCode", "devMode": "proCode",
@ -677,7 +687,7 @@
} }
} }
}, },
"NumberPicker": { {
"componentName": "NumberPicker", "componentName": "NumberPicker",
"title": "数字输入", "title": "数字输入",
"devMode": "proCode", "devMode": "proCode",
@ -829,7 +839,7 @@
"description": "预设屏幕宽度" "description": "预设屏幕宽度"
}] }]
}, },
"Select": { {
"componentName": "Select", "componentName": "Select",
"title": "下拉", "title": "下拉",
"devMode": "proCode", "devMode": "proCode",
@ -1053,6 +1063,14 @@
"name": "maxTagCount", "name": "maxTagCount",
"title": "最多显示多少个 tag", "title": "最多显示多少个 tag",
"setter": "ExpressionSetter" "setter": "ExpressionSetter"
}, {
"name": "color",
"title": "颜色选择",
"setter": "ColorSetter"
}, {
"name": "json",
"title": "JSON设置",
"setter": "JsonSetter"
}, { }, {
"name": "MixinSetter", "name": "MixinSetter",
"placeholder": "混合", "placeholder": "混合",
@ -1081,11 +1099,14 @@
}, { }, {
"label": "左", "label": "左",
"value": "l" "value": "l"
}] }],
"defaultValue": "l"
} }
}, { }, {
"name": "NumberSetter", "name": "NumberSetter",
"props": {} "props": {
"defaultValue": 5
}
}, { }, {
"name": "BoolSetter", "name": "BoolSetter",
"props": {} "props": {}
@ -1450,7 +1471,7 @@
}] }]
} }
}, },
"Select.Option": { {
"componentName": "Select.Option", "componentName": "Select.Option",
"title": "选择项", "title": "选择项",
"devMode": "proCode", "devMode": "proCode",
@ -1485,14 +1506,14 @@
} }
} }
} }
}, ],
"componentList": [{ "componentList": [{
"title": "基础", "title": "基础",
"icon": "", "icon": "",
"children": [{ "children": [{
"componentName": "Button", "componentName": "Button",
"title": "按钮", "title": "按钮",
"icon": "", "icon": "add",
"package": "@alife/next", "package": "@alife/next",
"library": "Next", "library": "Next",
"snippets": [{ "snippets": [{
@ -1521,7 +1542,8 @@
"schema": { "schema": {
"componentName": "Button", "componentName": "Button",
"props": { "props": {
"type": "normal" "type": "normal",
"value": "normal"
}, },
"children": "normal" "children": "normal"
} }
@ -1577,7 +1599,7 @@
"title": "其他", "title": "其他",
"icon": "", "icon": "",
"children": [{ "children": [{
"componentName": "Div", "componentName": "Card",
"library": "Next", "library": "Next",
"title": "容器", "title": "容器",
"icon": "", "icon": "",
@ -1585,7 +1607,7 @@
"title": "默认", "title": "默认",
"screenshot": "", "screenshot": "",
"schema": { "schema": {
"componentName": "Div", "componentName": "Card",
"props": {} "props": {}
} }
}] }]

View File

@ -4,18 +4,71 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1" /> <meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<title>LowCodeEngine DEMO</title> <title>LowCodeEngine Editor DEMO</title>
<link rel="shortcut icon" href="./favicon.png" />
<script src="https://g.alicdn.com/code/lib/react/16.9.0/umd/react.development.js"></script> <script src="https://g.alicdn.com/code/lib/react/16.9.0/umd/react.development.js"></script>
<script src="https://g.alicdn.com/code/lib/react-dom/16.9.0/umd/react-dom.development.js"></script> <script src="https://g.alicdn.com/code/lib/react-dom/16.9.0/umd/react-dom.development.js"></script>
<script src="https://g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js"></script> <script src="https://g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js"></script>
<script> React.PropTypes = PropTypes; </script> <script>
React.PropTypes = PropTypes;
</script>
<script src="https://g.alicdn.com/platform/c/??react15-polyfill/0.0.1/dist/index.js,lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js,natty-storage/2.0.2/dist/natty-storage.min.js,natty-fetch/2.6.0/dist/natty-fetch.pc.min.js,tinymce/4.2.5/tinymce-full.js"></script>
<script src="https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"></script> <script src="https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"></script>
<link rel="stylesheet" href="https://alifd.alicdn.com/npm/@alifd/next/1.11.6/next.min.css" />
<link rel="stylesheet" href="https://alifd.alicdn.com/npm/@alifd/next/1.11.6/next.min.css">
<script src="https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.js"></script> <script src="https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.js"></script>
<link rel="stylesheet" href="/css/editor-preset-vision.css" />
<script>
window.pageConfig = {
env: 'release',
locale: 'zh_CN',
pageType: 'single',
deviceType: 'web',
appName: '基础包管理后台',
appType: 'legao_base_packages',
templateType: '',
pageId: 'FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V',
slug: 'test',
appMode: 'back',
isAppAdmin: 'y',
isSuperAdmin: 'n',
isBetaDeveloper: 'n',
formType: 'display',
title: { en_US: '测试', type: 'i18n', zh_CN: '测试' },
urlPrefix: 'https://go.alibaba-inc.com',
APIUrlPrefix: 'https://go.alibaba-inc.com',
devVersion: '0.1.0', // 这个是子应用的变更 id
subAppType: '0.1.0',
appKey: 'legao_base_packages',
RE_VERSION: '7.1.1',
appSource: '',
isDomainDefault: 'n',
useReleaseBundle: 'n',
isDomainPkg: 'n',
medusaAppName: '',
domainCode: 'kS6SyH',
aecp: {
mdcDomain: '',
projectId: '',
appCode: '',
},
designerConfigs: {},
navConfig:
'{"appName":{"en_US":"基础包管理后台","key":"","type":"i18n","zh_CN":"基础包管理后台"},"bgColor":"white","data":[{"children":[],"hidden":false,"icon":"","inner":true,"navUuid":"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V","relateUuid":"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V","slug":"test","targetNew":false,"title":{"en_US":"测试","type":"i18n","zh_CN":"测试"}}],"isFixed":"y","isFold":"y","isFoldHorizontal":"n","languageChangeUrl":{"en_US":"/common/account/changeAccountLanguage.json","type":"i18n","zh_CN":"/common/account/changeAccountLanguage.json"},"layout":"auto","navStyle":"orange","navTheme":"light","openSubMode":false,"showAppTitle":true,"showCrumb":true,"showIcon":false,"showLanguageChange":true,"showNav":true,"showSearch":"n","singletons":{"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V":{"isFixed":"n","isFold":"n","isFoldHorizontal":"n","showAppTitle":false,"showCrumb":false,"showLanguageChange":false,"showNav":false,"showSearch":"n","singleton":false},"test":{"$ref":"$.singletons.FORM\\-3KYJN7RV\\-DIOD8LLK1WGQ89S7NHA92\\-QJVH497K\\-V"}},"type":"top_fold"}',
historyType: 'HASH',
isSinglePage: 'n',
rhino: 'n',
isMiniApp: '',
taskId: '',
appSchema: 'V5',
openSubMode: 'n',
};
window.g_config = {};
</script>
</head> </head>
<body> <body>
<div id="lce-container"></div> <!-- lowcode engine globals -->
<script src="/js/editor-preset-vision.js"></script>
<script src="https://dev.g.alicdn.com/vision/visualengine-utils/5.0.0/engine-utils.js"></script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,504 @@
{
"externals": [
{
"urls": ["//g.alicdn.com/platform/c/react/16.5.2/react.min.js"],
"library": "React",
"name": "react",
"version": "16.5.2"
},
{
"urls": ["//g.alicdn.com/platform/c/react-dom/16.5.2/react-dom.min.js"],
"library": "ReactDOM",
"name": "react-dom",
"version": "16.12.0"
},
{
"urls": ["//g.alicdn.com/platform/c/prop-types/15.6.2/prop-types.js"],
"library": "PropTypes",
"name": "prop-types",
"version": "15.6.2"
},
{ "library": "ReactRouter", "name": "react-router" },
{ "library": "ReactRouterDOM", "name": "react-router-dom" },
{ "library": "Babel", "name": "babel-standalone" },
{ "library": "Recore", "name": "@ali/recore" },
{
"urls": ["https://g.alicdn.com/code/lib/moment.js/2.24.0/moment-with-locales.min.js"],
"library": "moment",
"name": "moment",
"version": "2.24.0"
},
{
"urls": ["https://g.alicdn.com/platform/c/??react15-polyfill/0.0.1/dist/index.js,lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js,natty-storage/2.0.2/dist/natty-storage.min.js,natty-fetch/2.6.0/dist/natty-fetch.pc.min.js,tinymce/4.2.5/tinymce-full.js"],
"library": "nattyFetch",
"name": "natty-fetch",
"version": "2.24.0"
},
{ "library": "VisualEngine", "name": "engine" },
{ "library": "VisualEngine", "name": "visualengine" },
{ "library": "VisualEngine", "name": "@ali/visualengine" },
{ "library": "VisualEngineUtils", "name": "engine-utils" },
{ "library": "VisualEngineUtils", "name": "@ali/visualengine-utils" },
{ "library": "VisualEngine.ui.Popup", "name": "@ali/ve-popups" },
{ "library": "VisualEngineUtils.FieldControl", "name": "@ali/ve-field" },
{ "library": "VisualEngineUtils.BoolControl", "name": "@ali/ve-bool-control" },
{ "library": "VisualEngineUtils.ChoiceControl", "name": "@ali/ve-choice-control" },
{ "library": "VisualEngineUtils.ColorControl", "name": "@ali/ve-color-control" },
{ "library": "VisualEngineUtils.DateControl", "name": "@ali/ve-date-control" },
{ "library": "VisualEngineUtils.I18nControl", "name": "@ali/ve-i18n-control" },
{ "library": "VisualEngineUtils.NumberControl", "name": "@ali/ve-number-control" },
{ "library": "VisualEngineUtils.SelectControl", "name": "@ali/ve-select-control" },
{ "library": "VisualEngineUtils.SortableControl", "name": "@ali/ve-sortable" },
{ "library": "VisualEngineUtils.TextControl", "name": "@ali/ve-text-control" },
{ "library": "VisualEngineUtils.ImageControl", "name": "@ali/ve-image-control" },
{ "library": "VisualEngineUtils.SearchControl", "name": "@ali/ve-search-control" },
{ "library": "VisualEngineUtils.BoolSetter", "name": "@ali/vs-bool" },
{ "library": "VisualEngineUtils.ChoiceSetter", "name": "@ali/vs-choice" },
{ "library": "VisualEngineUtils.CodeSetter", "name": "@ali/vs-code" },
{ "library": "VisualEngineUtils.ColorSetter", "name": "@ali/vs-color" },
{ "library": "VisualEngineUtils.DateSetter", "name": "@ali/vs-date" },
{ "library": "VisualEngineUtils.I18nSetter", "name": "@ali/vs-i18n" },
{ "library": "VisualEngineUtils.JsonSetter", "name": "@ali/vs-json" },
{ "library": "VisualEngineUtils.ListSetter", "name": "@ali/vs-list" },
{ "library": "VisualEngineUtils.NumberSetter", "name": "@ali/vs-number" },
{ "library": "VisualEngineUtils.OptionsSetter", "name": "@ali/vs-options" },
{ "library": "VisualEngineUtils.SelectSetter", "name": "@ali/vs-select" },
{ "library": "VisualEngineUtils.TextSetter", "name": "@ali/vs-text" },
{ "library": "VisualEngineUtils.ValidationSetter", "name": "@ali/vs-validation" },
{ "library": "VisualEngineUtils.ImageSetter", "name": "@ali/vs-image" },
{ "library": "VisualEngineUtils.StyleSetter", "name": "@ali/vs-style" },
{ "library": "VisualEngineUtils.EventSetter", "name": "@ali/vs-event" },
{ "library": "RenderEngine", "name": "@ali/render-engine" },
{ "library": "Highcharts", "name": "highcharts" },
{ "library": "Object", "name": "highcharts-more" },
{ "library": "Highcharts && window.Highcharts.map", "name": "highcharts/highmaps" },
{ "library": "jQuery", "name": "$" },
{ "library": "jQuery", "name": "jquery" },
{ "library": "_", "name": "lodash" },
{ "library": "nattyFetch", "name": "natty-fetch" },
{ "library": "nattyFetch", "name": "natty-fetch/dist/natty-fetch.pc" },
{ "library": "nattyFetch", "name": "natty-fetch/dist/natty-fetch" },
{ "library": "nattyStorage", "name": "natty-storage" },
{ "library": "Rax", "name": "rax" }
],
"systemType": "",
"appKey": "legao_base_packages",
"componentDependencies": [
{
"prototypeConfigsUrl": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-div/1.0.1/proto.c54985f.js"],
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVcDiv",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-div/1.0.1/view.f31fe6d.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-div/1.0.1/view.d3df802.js"
],
"components": null,
"packageName": "@ali/vc-div",
"version": "1.0.1"
},
{
"prototypeConfigsUrl": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.1.12/proto.611ab53.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.1.12/proto.5b7b3d3.js"
],
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVcDeep",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/view.03e2bef.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/view.e380202.js",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/view.mobile.03e2bef.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/view.mobile.4c4a443.js"
],
"components": null,
"packageName": "@ali/vc-deep",
"version": "2.0.11"
},
{
"prototypeConfigsUrl": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-shell/1.5.3/proto.81cf560.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-shell/1.5.3/proto.5de342b.js"
],
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVcShell",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-shell/1.5.3/view.81cf560.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-shell/1.5.3/view.3ef1990.js"
],
"components": null,
"packageName": "@ali/vc-shell",
"version": "1.5.3"
},
{
"prototypeConfigsUrl": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-slot/2.0.1/proto.0e43387.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-slot/2.0.1/proto.0bda625.js"
],
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVcSlot",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-slot/2.0.1/view.0e43387.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-slot/2.0.1/view.890474e.js"
],
"components": null,
"packageName": "@ali/vc-slot",
"version": "2.0.1"
},
{
"prototypeConfigsUrl": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.0/proto.595bd91.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.0/proto.7afa924.js"
],
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVcText",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.0/view.764bd38.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.0/view.0dcac71.js",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.0/view.mobile.8a20311.js"
],
"components": null,
"packageName": "@ali/vc-text",
"version": "4.0.0"
},
{
"prototypeConfigsUrl": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/proto.4828821.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/proto.dd97364.js"
],
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVcLink",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/view.387943c.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/view.ec70dd9.js",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/view.mobile.387943c.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/view.mobile.bc72f32.js"
],
"components": null,
"packageName": "@ali/vc-link",
"version": "5.1.1"
},
{
"prototypeConfigsUrl": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/proto.b486b90.js"
],
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVcLinkBlock",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/view.82ef4b0.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/view.51d10d6.js",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/view.mobile.82ef4b0.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/view.mobile.d608862.js"
],
"components": null,
"packageName": "@ali/vc-link-block",
"version": "5.1.0"
},
{
"prototypeConfigsUrl": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-html/2.0.0/proto.8b5fefc.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-html/2.0.0/proto.0d2716f.js"
],
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVcHtml",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-html/2.0.0/view.0e43387.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-html/2.0.0/view.d3ef287.js"
],
"components": null,
"packageName": "@ali/vc-html",
"version": "2.0.0"
},
{
"prototypeConfigsUrl": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-iframe/3.0.2/proto.6c9d90a.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-iframe/3.0.2/proto.c03e93e.js"
],
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVcIframe",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-iframe/3.0.2/view.6c9d90a.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-iframe/3.0.2/view.5bdba85.js"
],
"components": null,
"packageName": "@ali/vc-iframe",
"version": "3.0.2"
},
{
"prototypeConfigsUrl": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-jsx/2.0.0/proto.9419927.js"],
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVcJsx",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-jsx/2.0.0/view.133d01c.js"],
"components": null,
"packageName": "@ali/vc-jsx",
"version": "2.0.0"
},
{
"prototypeConfigsUrl": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-markdown/2.0.0/proto.3f91095.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-markdown/2.0.0/proto.fa33aff.js"
],
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVcMarkdown",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-markdown/2.0.0/view.3f91095.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-markdown/2.0.0/view.288bb26.js"
],
"components": null,
"packageName": "@ali/vc-markdown",
"version": "2.0.0"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliDeepLegaoComponentImport",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/deep-legao-component-import/0.3.0/main.47a28d7.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/deep-legao-component-import/0.3.0/main.0751f12.js"
],
"components": [
{
"snippets": [
{
"schema": {
"componentName": "LegaoComponentImport",
"props": { "urlPrefix": "https://go.alibaba-inc.com/", "scene": "editConfig" }
},
"code": "<LegaoComponentImport scene=\"editConfig\" urlPrefix=\"https://go.alibaba-inc.com/\" />",
"screenshot": "https://tianshu.alicdn.com/a6115d32-ec32-4d32-bf94-f24aace23ffa.png"
}
],
"componentName": "LegaoComponentImport",
"screenshot": "https://tianshu.alicdn.com/a6115d32-ec32-4d32-bf94-f24aace23ffa.png",
"configure": {
"component": {},
"props": [
{ "name": "onFinish", "title": "onFinish", "setter": "Function" },
{ "name": "scene", "title": "scene", "setter": "Input" },
{ "name": "componentInfo", "title": "componentInfo", "setter": "Object" },
{ "name": "urlPrefix", "title": "urlPrefix", "setter": "Input" },
{ "name": "getTicket", "title": "getTicket", "setter": "Function" },
{ "name": "mode", "title": "mode", "setter": "Input" },
{ "name": "height", "title": "height", "setter": "Number" },
{ "name": "configListType", "title": "configListType", "setter": "Input" }
]
},
"category": "自定义"
}
],
"packageName": "@ali/deep-legao-component-import",
"version": "0.3.0"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliReactCodeToImage",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/react-code-to-image/0.0.13/main.5ea90fb.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/react-code-to-image/0.0.13/main.08ab636.js"
],
"components": [
{
"snippets": [
{
"schema": { "componentName": "AliReactCodeToImage", "props": {} },
"code": "<AliReactCodeToImage/>",
"label": "react code to image",
"screenshot": "https://tianshu.alicdn.com/958192c1-1d1a-4186-8438-63c80cb04fb3.png"
}
],
"componentName": "AliReactCodeToImage",
"screenshot": "https://tianshu.alicdn.com/958192c1-1d1a-4186-8438-63c80cb04fb3.png",
"configure": {
"component": {},
"props": [
{ "name": "library", "title": "library", "setter": "Input" },
{ "name": "assets", "title": "assets", "setter": "Object" },
{ "name": "initCode", "title": "initCode", "setter": "Input" },
{ "name": "showEditor", "title": "showEditor", "setter": "Switch" },
{ "name": "onRender", "title": "onRender", "setter": "Function" },
{ "name": "onUploaded", "title": "onUploaded", "setter": "Function" },
{ "name": "upload", "title": "upload", "setter": "Input" },
{ "name": "showPreview", "title": "showPreview", "setter": "Switch" },
{ "name": "builtInReact", "title": "builtInReact", "setter": "Switch" },
{ "name": "builtInBabel", "title": "builtInBabel", "setter": "Switch" },
{ "name": "style", "title": "style", "setter": "Input" },
{ "name": "autoRender", "title": "autoRender", "setter": "Switch" },
{ "name": "delay", "title": "delay", "setter": "Number" },
{ "name": "autoUpload", "title": "autoUpload", "setter": "Switch" },
{ "name": "noUI", "title": "noUI", "setter": "Switch" }
]
},
"title": "react code to image",
"category": "自定义"
}
],
"packageName": "@ali/react-code-to-image",
"version": "0.0.13"
}
],
"utilsDependencies": [
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVuFormatter",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-formatter/2.0.0/main.e75ff20.js"],
"components": null,
"packageName": "@ali/vu-formatter",
"version": "2.0.0"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVuFusion",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-fusion/2.0.0/main.01f2f51.js"],
"components": null,
"packageName": "@ali/vu-fusion",
"version": "2.0.0"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVuToolkit",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-toolkit/1.0.4/main.96fb938.js"],
"components": null,
"packageName": "@ali/vu-toolkit",
"version": "1.0.4"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVuLegaoBuiltin",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-legao-builtin/1.2.2/main.2178271.js"],
"components": null,
"packageName": "@ali/vu-legao-builtin",
"version": "1.2.2"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVuDataSource",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-dataSource/1.0.3/main.c153335.js"],
"components": null,
"packageName": "@ali/vu-dataSource",
"version": "1.0.3"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVuRouter",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-router/1.2.1/main.5a856a4.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-router/1.2.1/main.6f0b6b1.js"
],
"components": null,
"packageName": "@ali/vu-router",
"version": "1.2.1"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVuRouterSpa",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-router-spa/1.3.7/main.c231469.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-router-spa/1.3.7/main.756e5a0.js"
],
"components": null,
"packageName": "@ali/vu-router-spa",
"version": "1.3.7"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVuSpm",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-spm/1.1.11/main.0495881.js"],
"components": null,
"packageName": "@ali/vu-spm",
"version": "1.1.11"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVuSwitchSchema",
"urls": [
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-switch-schema/1.0.7/main.1d9ec8c.css",
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-switch-schema/1.0.7/main.70ec614.js"
],
"components": null,
"packageName": "@ali/vu-switch-schema",
"version": "1.0.7"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVuLegaoBuiltin",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-legao-builtin/1.0.5/main.0ebe6a1.js"],
"components": null,
"packageName": "@ali/vu-legao-builtin",
"version": "1.0.5"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVuDataSource",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vu-dataSource/1.0.2/main.62e0289.js"],
"components": null,
"packageName": "@ali/vu-dataSource",
"version": "1.0.2"
}
],
"otherDependencies": [
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "MyBabel",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/my-babel/0.9.6/main.8bd0181.js"],
"components": null,
"packageName": "my-babel",
"version": "0.9.6"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVmRouter",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vm-router/1.0.8/main.115bf4f.js"],
"components": null,
"packageName": "@ali/vm-router",
"version": "1.0.8"
},
{
"prototypeConfigsUrl": null,
"prototypeViewsUrl": null,
"alias": "",
"library": "AliVmSchemaNav",
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vm-schema-nav/1.0.0/main.711344e.js"],
"components": null,
"packageName": "@ali/vm-schema-nav",
"version": "1.0.0"
}
]
}

View File

@ -1,12 +1,6 @@
{ {
"componentName": "Page", "componentName": "Page",
"fileName": "test", "id": "node_1",
"dataSource": {
"list": []
},
"state": {
"text": "outter"
},
"props": { "props": {
"ref": "outterView", "ref": "outterView",
"autoLoading": true, "autoLoading": true,
@ -14,97 +8,884 @@
"padding": 20 "padding": 20
} }
}, },
"children": [{ "fileName": "test",
"componentName": "Form", "dataSource": {
"props": { "list": []
"labelCol": 3,
"style": {},
"ref": "testForm"
}, },
"children": [{ "state": {
"componentName": "Form.Item", "text": "outter",
"props": { "abc": [
"label": "姓名:", 1,
"name": "name", 2,
"initValue": "李雷" 3
]
}, },
"children": [{ "children": [
"componentName": "Input", {
"componentName": "Steps",
"id": "node_1i",
"props": { "props": {
"placeholder": "请输入", "dataSource": [
"size": "medium", {
"style": { "title": {
"width": 320 "type": "i18n",
} "use": "zh_CN",
} "en_US": null,
}] "zh_CN": "报名"
}, {
"componentName": "Form.Item",
"props": {
"label": "年龄:",
"name": "age",
"initValue": "22"
}, },
"children": [{ "status": "",
"componentName": "NumberPicker", "content": {
"props": { "type": "i18n",
"size": "medium", "use": "zh_CN",
"type": "normal" "en_US": null,
} "zh_CN": "Open the refrigerator door"
}]
}, {
"componentName": "Form.Item",
"props": {
"label": "职业:",
"name": "profession"
}, },
"children": [{ "customSwitcher": false
"componentName": "Select", },
"props": { {
"dataSource": [{ "title": {
"label": "教师", "type": "i18n",
"value": "t" "use": "zh_CN",
}, { "en_US": null,
"label": "医生", "zh_CN": "信息收集"
"value": "d" },
}, { "status": "",
"label": "歌手", "content": {
"value": "s" "type": "i18n",
}] "use": "zh_CN",
"en_US": null,
"zh_CN": "Put the elephant in the refrigerator"
},
"customSwitcher": false
},
{
"title": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": "审核"
},
"status": "",
"content": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": "Close the refrigerator door"
},
"customSwitcher": false
} }
}] ],
}, { "current": 1,
"componentName": "Div", "shape": "circle",
"props": { "direction": "horizontal",
"style": { "labelPlacement": "vertical",
"textAlign": "center" "readOnly": false,
"animation": true,
"__style__": {},
"fieldId": "steps_kadcb0ov"
} }
}, },
"children": [{ {
"componentName": "Button.Group", "componentName": "Title",
"props": {}, "id": "node_b",
"children": [{
"componentName": "Button",
"props": { "props": {
"text": "请填写以下人员信息表单",
"type": "primary", "type": "primary",
"style": { "noDecoration": false,
"margin": "0 5px 0 5px" "__style__": {},
"fieldId": "title_kadcb0nw"
}
}, },
"htmlType": "submit" {
}, "componentName": "Paragraph",
"children": "提交" "id": "node_e",
}, {
"componentName": "Button",
"props": { "props": {
"type": "normal", "content": "人最宝贵的是生命。它给予我们只有一次。人的一生应当这样度过:当他回首往事时不因虚度年华而悔恨,也不因碌碌无为而羞耻。这样在他临死的时侯就能够说:我已把我整个的生命和全部精力都献给最壮丽的事业——为人类的解放而斗争。",
"style": { "size": "medium",
"margin": "0 5px 0 5px" "type": "long",
"__style__": {},
"fieldId": "paragraph_kadcb0nz"
}
}, },
"htmlType": "reset" {
"componentName": "ColumnsLayout",
"id": "node_r",
"props": {
"layout": "6:6",
"columnGap": "16px",
"rowGap": "16px",
"__style__": {
"marginTop": "24px"
}, },
"children": "重置" "fieldId": "columnsLayout_kadcb0ob"
}] },
}] "children": [
}] {
}] "componentName": "Column",
"id": "node_s",
"props": {
"fieldId": "column_kadcb0o9",
"__style__": {}
},
"children": [
{
"componentName": "Card",
"id": "node_n",
"props": {
"title": "基本信息",
"subTitle": {
"type": "i18n",
"zh_CN": "",
"en_US": ""
},
"extra": {
"type": "JSSlot",
"params": null,
"value": [
{
"componentName": "Icon",
"id": "node_q",
"props": {
"type": {
"useType": true,
"baseType": "smile",
"otherType": "smile"
},
"size": "medium",
"__style__": {},
"fieldId": "icon_kadcb0o8"
}
}
]
},
"showTitleBullet": true,
"showHeadDivider": true,
"dividerNoInset": false,
"contentHeight": "",
"__style__": {},
"fieldId": "card_kadcb0o7"
},
"children": [
{
"componentName": "CardContent",
"id": "node_o",
"props": {},
"children": [
{
"componentName": "Form",
"id": "node_f",
"props": {
"labelAlign": "top",
"size": "medium",
"behavior": "NORMAL",
"autoValidate": true,
"scrollToFirstError": true,
"autoUnmount": true,
"fieldOptions": {},
"__style__": {
"marginTop": "24px"
},
"fieldId": "form_kadcb0o5"
},
"children": [
{
"componentName": "TextField",
"id": "node_g",
"props": {
"__category__": "form",
"__useMediator": "value",
"label": "姓名",
"value": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"labelAlign": "top",
"labelColSpan": 4,
"labelColOffset": 0,
"wrapperColSpan": 0,
"wrapperColOffset": 0,
"labelTextAlign": "right",
"placeholder": {
"type": "i18n",
"use": "zh_CN",
"en_US": "Please enter",
"zh_CN": "请输入"
},
"tips": {
"zh_CN": "",
"en_US": "",
"type": "i18n"
},
"size": "medium",
"behavior": "NORMAL",
"labelTipsTypes": "none",
"labelTipsIcon": "",
"labelTipsText": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"htmlType": "input",
"state": "",
"rows": 4,
"autoHeight": false,
"hasClear": false,
"trim": false,
"autoFocus": false,
"addonBefore": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"addonAfter": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"validation": [],
"hasLimitHint": false,
"cutString": false,
"__style__": {},
"fieldId": "textField_kadcb0o0"
}
},
{
"componentName": "RadioField",
"id": "node_14",
"props": {
"__category__": "form",
"__useMediator": "value",
"label": "性别",
"value": "",
"labelAlign": "top",
"labelColSpan": 4,
"labelColOffset": 0,
"wrapperColSpan": 0,
"wrapperColOffset": 0,
"labelTextAlign": "right",
"tips": {
"zh_CN": "",
"en_US": "",
"type": "i18n"
},
"size": "medium",
"behavior": "NORMAL",
"labelTipsTypes": "none",
"labelTipsIcon": "",
"labelTipsText": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"shape": "default",
"itemDirection": "hoz",
"dataSource": [
{
"text": {
"zh_CN": "选项一",
"en_US": "Option 1",
"type": "i18n",
"use": "zh_CN"
},
"value": "1",
"disable": false
},
{
"text": {
"zh_CN": "选项二",
"en_US": "Option 2",
"type": "i18n",
"use": "zh_CN"
},
"value": "2",
"disable": false
},
{
"text": {
"zh_CN": "选项三",
"en_US": "Option 3",
"type": "i18n",
"use": "zh_CN"
},
"value": "3",
"disable": true
}
],
"validation": [],
"__style__": {},
"fieldId": "radioField_kadcb0ok"
}
},
{
"componentName": "SelectField",
"id": "node_h",
"props": {
"__category__": "form",
"__useMediator": "value",
"label": "学校",
"value": "",
"labelAlign": "top",
"labelColSpan": 4,
"labelColOffset": 0,
"wrapperColSpan": 0,
"wrapperColOffset": 0,
"labelTextAlign": "right",
"placeholder": {
"type": "i18n",
"use": "zh_CN",
"en_US": "please select",
"zh_CN": "请选择"
},
"tips": {
"zh_CN": "",
"en_US": "",
"type": "i18n"
},
"size": "medium",
"behavior": "NORMAL",
"labelTipsTypes": "none",
"labelTipsIcon": "",
"labelTipsText": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"mode": "single",
"hasClear": false,
"hasSelectAll": false,
"showSearch": false,
"filterLocal": true,
"dataSource": [
{
"text": {
"zh_CN": "选项一",
"en_US": "Option 1",
"type": "i18n"
},
"value": "1"
},
{
"text": {
"zh_CN": "选项二",
"en_US": "Option 2",
"type": "i18n"
},
"value": "2"
},
{
"text": {
"zh_CN": "选项三",
"en_US": "Option 3",
"type": "i18n"
},
"value": "3"
}
],
"validation": [],
"notFoundContent": {
"type": "i18n",
"use": "zh_CN",
"en_US": null
},
"hasArrow": true,
"hasBorder": true,
"autoWidth": true,
"searchDelay": 300,
"__style__": {},
"fieldId": "select_kadcb0o1"
}
},
{
"componentName": "Div",
"id": "node_i",
"props": {
"behavior": "NORMAL",
"__style__": {},
"fieldId": "div_kadcb0o4"
},
"children": [
{
"componentName": "Button",
"id": "node_j",
"props": {
"content": {
"type": "i18n",
"en_US": "Ok",
"zh_CN": "提交"
},
"type": "primary",
"size": "medium",
"behavior": "NORMAL",
"baseIcon": "",
"otherIcon": "",
"loading": false,
"triggerEventsWhenLoading": false,
"__style__": ":root {\n margin-right: 15px;\n}",
"fieldId": "button_kadcb0o2",
"onClick": {
"type": "JSExpression",
"value": "function onSubmit(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').submit(function(data, error) {\n if (data) {\n console.log(data);\n // 往后端提交数据,一般写法如下\n // this.dataSourceMap['xxx'].load(data).then(() => {\n // this.utils.toast({\n // type: 'success',\n // title: '提交成功'\n // });\n // });\n }\n });\n}",
"extType": "function",
"events": [
{
"name": "onClick",
"params": {},
"func": {
"type": "js",
"source": "function onSubmit(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').submit(function(data, error) {\n if (data) {\n console.log(data);\n // 往后端提交数据,一般写法如下\n // this.dataSourceMap['xxx'].load(data).then(() => {\n // this.utils.toast({\n // type: 'success',\n // title: '提交成功'\n // });\n // });\n }\n });\n}",
"compiled": "function onSubmit(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').submit(function(data, error) {\n if (data) {\n console.log(data);\n // 往后端提交数据,一般写法如下\n // this.dataSourceMap['xxx'].load(data).then(() => {\n // this.utils.toast({\n // type: 'success',\n // title: '提交成功'\n // });\n // });\n }\n });\n}"
}
}
]
}
}
},
{
"componentName": "Button",
"id": "node_k",
"props": {
"content": {
"type": "i18n",
"en_US": "Reset",
"zh_CN": "重置"
},
"type": "secondary",
"size": "medium",
"behavior": "NORMAL",
"baseIcon": "",
"otherIcon": "",
"loading": false,
"triggerEventsWhenLoading": false,
"__style__": {},
"fieldId": "button_kadcb0o3",
"onClick": {
"type": "JSExpression",
"value": "function onReset(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').reset();\n}",
"extType": "function",
"events": [
{
"name": "onClick",
"params": {},
"func": {
"type": "js",
"source": "function onReset(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').reset();\n}",
"compiled": "function onReset(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').reset();\n}"
}
}
]
}
}
}
]
}
]
}
]
}
]
}
]
},
{
"componentName": "Column",
"id": "node_t",
"props": {
"fieldId": "column_kadcb0oa",
"__style__": {}
},
"children": [
{
"componentName": "Card",
"id": "node_u",
"props": {
"title": {
"type": "JSSlot",
"params": null,
"value": [
{
"componentName": "Icon",
"id": "node_18",
"props": {
"type": {
"useType": true,
"baseType": "smile",
"otherType": "smile"
},
"size": "medium",
"__style__": {},
"fieldId": "icon_kadcb0on"
}
},
{
"componentName": "Link",
"id": "node_19",
"props": {
"content": {
"type": "i18n",
"use": "zh_CN",
"en_US": "link text",
"zh_CN": "扩展信息"
},
"textOverflow": false,
"link": {
"type": "page",
"page": null,
"router": {
"type": "JSExpression",
"value": "this.utils.router"
}
},
"__style__": {},
"fieldId": "link_kadcb0oo"
}
}
]
},
"subTitle": {
"type": "i18n",
"zh_CN": "",
"en_US": ""
},
"extra": {
"type": "JSSlot",
"params": null,
"value": [
{
"componentName": "Icon",
"id": "node_1a",
"props": {
"type": {
"useType": true,
"baseType": "smile",
"otherType": "smile"
},
"size": "xl",
"__style__": {},
"fieldId": "icon_kadcb0op"
}
}
]
},
"showTitleBullet": true,
"showHeadDivider": true,
"dividerNoInset": false,
"contentHeight": "",
"__style__": {},
"fieldId": "card_kadcb0oc"
},
"children": [
{
"componentName": "CardContent",
"id": "node_v",
"props": {},
"children": [
{
"componentName": "Form",
"id": "node_x",
"props": {
"labelAlign": "top",
"size": "medium",
"behavior": "NORMAL",
"autoValidate": true,
"scrollToFirstError": true,
"autoUnmount": true,
"fieldOptions": {},
"__style__": {
"marginTop": "24px"
},
"fieldId": "form_kadcb0oi"
},
"children": [
{
"componentName": "TextField",
"id": "node_y",
"props": {
"__category__": "form",
"__useMediator": "value",
"label": "职位",
"value": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"labelAlign": "top",
"labelColSpan": 4,
"labelColOffset": 0,
"wrapperColSpan": 0,
"wrapperColOffset": 0,
"labelTextAlign": "right",
"placeholder": {
"type": "i18n",
"use": "zh_CN",
"en_US": "Please enter",
"zh_CN": "请输入"
},
"tips": {
"zh_CN": "",
"en_US": "",
"type": "i18n"
},
"size": "medium",
"behavior": "NORMAL",
"labelTipsTypes": "none",
"labelTipsIcon": "",
"labelTipsText": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"htmlType": "input",
"state": "",
"rows": 4,
"autoHeight": false,
"hasClear": false,
"trim": false,
"autoFocus": false,
"addonBefore": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"addonAfter": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"validation": [],
"hasLimitHint": false,
"cutString": false,
"__style__": {},
"fieldId": "textField_kadcb0od"
}
},
{
"componentName": "DateField",
"id": "node_15",
"props": {
"__category__": "form",
"__useMediator": "value",
"label": "入职时间",
"value": "",
"labelAlign": "top",
"labelColSpan": 4,
"labelColOffset": 0,
"wrapperColSpan": 0,
"wrapperColOffset": 0,
"labelTextAlign": "right",
"placeholder": {
"type": "i18n",
"use": "zh_CN",
"en_US": "please select",
"zh_CN": "请选择"
},
"tips": {
"zh_CN": "",
"en_US": "",
"type": "i18n"
},
"size": "medium",
"behavior": "NORMAL",
"format": "YYYY-MM-DD",
"returnType": "timestamp",
"labelTipsTypes": "none",
"labelTipsIcon": "",
"labelTipsText": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"hasClear": true,
"resetTime": false,
"disabledDate": false,
"validation": [],
"__style__": {},
"fieldId": "dateField_kadcb0ol"
}
},
{
"componentName": "SelectField",
"id": "node_z",
"props": {
"__category__": "form",
"__useMediator": "value",
"label": "下拉选择",
"value": "",
"labelAlign": "top",
"labelColSpan": 4,
"labelColOffset": 0,
"wrapperColSpan": 0,
"wrapperColOffset": 0,
"labelTextAlign": "right",
"placeholder": {
"type": "i18n",
"use": "zh_CN",
"en_US": "please select",
"zh_CN": "请选择"
},
"tips": {
"zh_CN": "",
"en_US": "",
"type": "i18n"
},
"size": "medium",
"behavior": "NORMAL",
"labelTipsTypes": "none",
"labelTipsIcon": "",
"labelTipsText": {
"type": "i18n",
"use": "zh_CN",
"en_US": null,
"zh_CN": ""
},
"mode": "single",
"hasClear": false,
"hasSelectAll": false,
"showSearch": false,
"filterLocal": true,
"dataSource": [
{
"text": {
"zh_CN": "选项一",
"en_US": "Option 1",
"type": "i18n"
},
"value": "1"
},
{
"text": {
"zh_CN": "选项二",
"en_US": "Option 2",
"type": "i18n"
},
"value": "2"
},
{
"text": {
"zh_CN": "选项三",
"en_US": "Option 3",
"type": "i18n"
},
"value": "3"
}
],
"validation": [],
"notFoundContent": {
"type": "i18n",
"use": "zh_CN",
"en_US": null
},
"hasArrow": true,
"hasBorder": true,
"autoWidth": true,
"searchDelay": 300,
"__style__": {},
"fieldId": "select_kadcb0oe"
}
},
{
"componentName": "Div",
"id": "node_10",
"props": {
"behavior": "NORMAL",
"__style__": {},
"fieldId": "div_kadcb0oh"
},
"children": [
{
"componentName": "Button",
"id": "node_11",
"props": {
"content": {
"type": "i18n",
"en_US": "Ok",
"zh_CN": "提交"
},
"type": "primary",
"size": "medium",
"behavior": "NORMAL",
"baseIcon": "",
"otherIcon": "",
"loading": false,
"triggerEventsWhenLoading": false,
"__style__": ":root {\n margin-right: 15px;\n}",
"fieldId": "button_kadcb0of",
"onClick": {
"type": "JSExpression",
"value": "function onSubmit(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').submit(function(data, error) {\n if (data) {\n console.log(data);\n // 往后端提交数据,一般写法如下\n // this.dataSourceMap['xxx'].load(data).then(() => {\n // this.utils.toast({\n // type: 'success',\n // title: '提交成功'\n // });\n // });\n }\n });\n}",
"extType": "function",
"events": [
{
"name": "onClick",
"params": {},
"func": {
"type": "js",
"source": "function onSubmit(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').submit(function(data, error) {\n if (data) {\n console.log(data);\n // 往后端提交数据,一般写法如下\n // this.dataSourceMap['xxx'].load(data).then(() => {\n // this.utils.toast({\n // type: 'success',\n // title: '提交成功'\n // });\n // });\n }\n });\n}",
"compiled": "function onSubmit(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').submit(function(data, error) {\n if (data) {\n console.log(data);\n // 往后端提交数据,一般写法如下\n // this.dataSourceMap['xxx'].load(data).then(() => {\n // this.utils.toast({\n // type: 'success',\n // title: '提交成功'\n // });\n // });\n }\n });\n}"
}
}
]
}
}
},
{
"componentName": "Button",
"id": "node_12",
"props": {
"content": {
"type": "i18n",
"en_US": "Reset",
"zh_CN": "重置"
},
"type": "secondary",
"size": "medium",
"behavior": "NORMAL",
"baseIcon": "",
"otherIcon": "",
"loading": false,
"triggerEventsWhenLoading": false,
"__style__": {},
"fieldId": "button_kadcb0og",
"onClick": {
"type": "JSExpression",
"value": "function onReset(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').reset();\n}",
"extType": "function",
"events": [
{
"name": "onClick",
"params": {},
"func": {
"type": "js",
"source": "function onReset(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').reset();\n}",
"compiled": "function onReset(){\n // 请将 fieldId 替换为表单容器的 fieldId\n this.$('fieldId').reset();\n}"
}
}
]
}
}
}
]
}
]
}
]
}
]
}
]
}
]
}
]
} }

View File

@ -1,155 +0,0 @@
module.exports = {
skeleton: {
config: {
package: '@ali/lowcode-editor-skeleton',
version: '^0.8.0'
}
},
theme: {
fusion: {
package: '@alife/theme-lowcode-light',
version: '^0.1.0'
},
scss: ''
},
constants: {
namespace: 'page'
},
utils: [],
plugins: {
topArea: [
{
pluginKey: 'logo',
type: 'Custom',
props: {
align: 'left',
width: 100
},
config: {
package: '@ali/lowcode-plugin-sample-logo',
version: '^0.8.0'
},
pluginProps: {
logo: 'https://img.alicdn.com/tfs/TB1hoI9x1H2gK0jSZFEXXcqMpXa-146-40.png',
href: '/'
}
},
{
pluginKey: 'undoRedo',
type: 'Custom',
props: {
align: 'right',
width: 88
},
config: {
package: '@ali/lowcode-plugin-undo-redo',
version: '^0.8.0'
}
},
{
pluginKey: 'divider',
type: 'Divider',
props: {
align: 'right'
}
},
{
pluginKey: 'samplePreview',
type: 'Custom',
props: {
align: 'right',
width: 64
},
config: {
package: '@ali/lowcode-plugin-sample-preview',
version: '^0.8.0'
}
}
],
leftArea: [
{
pluginKey: 'componentsPane',
type: 'PanelIcon',
props: {
align: 'top',
icon: 'zujianku',
title: '组件库',
floatable: true,
},
config: {
package: '@ali/lowcode-plugin-components-pane',
version: '^0.8.0'
},
pluginProps: {
}
},
{
pluginKey: 'outlinePane',
type: 'PanelIcon',
props: {
align: 'top',
icon: 'shuxingkongjian',
title: '大纲树'
},
config: {
package: '@ali/lowcode-plugin-outline-pane',
version: '^0.8.0'
},
pluginProps: {}
},
{
pluginKey: 'zhEn',
type: 'Custom',
props: {
align: 'bottom',
},
config: {
package: '@ali/lowcode-plugin-zh-en',
version: '^0.8.0'
},
pluginProps: {}
}
],
rightArea: [
{
pluginKey: 'settingsPane',
type: 'Panel',
props: {},
config: {
package: '@ali/lowcode-plugin-settings-pane',
version: '^0.8.0'
},
pluginProps: {}
}
],
centerArea: [
{
pluginKey: 'designer',
config: {
package: '@ali/lowcode-plugin-designer',
version: '^0.8.0'
}
},
{
pluginKey: 'eventBindDialog',
config: {
package: '@ali/lowcode-plugin-event-bind-dialog',
version: '^0.8.0'
}
}
]
},
hooks: [],
shortCuts: [],
lifeCycles: {
init: async function init(editor) {
const assets = await editor.utils.get('./assets.json');
editor.set('assets', assets);
editor.emit('assets.loaded', assets);
const schema = await editor.utils.get('./schema.json');
editor.set('schema', schema);
editor.emit('schema.loaded', schema);
}
}
};

View File

@ -8,7 +8,7 @@ import Preview from './plugins/provider';
app.registerRenderer(Renderer); app.registerRenderer(Renderer);
// 注册布局组件,可注册多个 // 注册布局组件,可注册多个
app.registerLayout('BasicLayout', BasicLayout); app.registerLayout(BasicLayout, { componentName: 'BasicLayout' });
// 注册页面 Loading // 注册页面 Loading
app.registerLoading(FusionLoading); app.registerLoading(FusionLoading);

View File

@ -0,0 +1,21 @@
import logo from '@ali/lowcode-plugin-sample-logo';
import samplePreview from '@ali/lowcode-plugin-sample-preview';
//import undoRedo from '@ali/lowcode-plugin-undo-redo';
import componentsPane from '@ali/lowcode-plugin-components-pane';
import outline, { OutlinePane } from '@ali/lowcode-plugin-outline-pane';
import zhEn from '@ali/lowcode-plugin-zh-en';
import eventBindDialog from '@ali/lowcode-plugin-event-bind-dialog';
import variableBindDialog from '@ali/lowcode-plugin-variable-bind-dialog';
import sourceEditor from '@ali/lowcode-plugin-source-editor';
export default {
logo,
samplePreview,
//undoRedo,
componentsPane,
outline,
zhEn,
eventBindDialog,
variableBindDialog,
sourceEditor,
}

View File

@ -0,0 +1,109 @@
export default {
plugins: {
topArea: [
{
pluginKey: 'logo',
type: 'Custom',
props: {
align: 'left',
width: 100,
},
pluginProps: {
logo: 'https://img.alicdn.com/tfs/TB1_SocGkT2gK0jSZFkXXcIQFXa-66-66.png',
href: '/',
},
},
/*
{
pluginKey: 'undoRedo',
type: 'Custom',
props: {
align: 'right',
width: 88,
},
},
{
pluginKey: 'divider',
type: 'Divider',
props: {
align: 'right',
},
},*/
{
pluginKey: 'samplePreview',
type: 'Custom',
props: {
align: 'right',
width: 64,
},
},
],
leftArea: [
{
pluginKey: 'componentsPane',
type: 'PanelIcon',
props: {
align: 'top',
icon: 'zujianku',
description: '组件库',
},
pluginProps: {},
},
{
pluginKey: 'outline',
type: 'PanelIcon',
props: {
align: 'top',
icon: 'shuxingkongjian',
description: '大纲树',
},
pluginProps: {},
},
{
pluginKey: 'sourceEditor',
type: 'PanelIcon',
props: {
align: 'top',
icon: 'wenjian',
description: '资源面板',
panelProps: {
floatable: true,
height: 300,
help: undefined,
hideTitleBar: true,
maxHeight: 800,
maxWidth: 1200,
title: "动作面板",
width: 600
},
}
},
{
pluginKey: 'zhEn',
type: 'Custom',
props: {
align: 'bottom',
},
pluginProps: {},
},
],
centerArea: [
{
pluginKey: 'eventBindDialog',
},
{
pluginKey: 'variableBindDialog',
},
]
},
shortCuts: [],
lifeCycles: {
init: async function init(editor) {
const assets = await editor.utils.get('./assets.json');
editor.set('assets', assets);
const schema = await editor.utils.get('./schema.json');
editor.set('schema', schema);
},
},
};

View File

@ -1,22 +0,0 @@
import LowcodeSkeleton from '@ali/lowcode-editor-skeleton';
import logo from '@ali/lowcode-plugin-sample-logo';
import undoRedo from '@ali/lowcode-plugin-undo-redo';
import samplePreview from '@ali/lowcode-plugin-sample-preview';
import componentsPane from '@ali/lowcode-plugin-components-pane';
import outlinePane from '@ali/lowcode-plugin-outline-pane';
import zhEn from '@ali/lowcode-plugin-zh-en';
import settingsPane from '@ali/lowcode-plugin-settings-pane';
import designer from '@ali/lowcode-plugin-designer';
import eventBindDialog from '@ali/lowcode-plugin-event-bind-dialog';
export default {
LowcodeSkeleton,
logo,
undoRedo,
samplePreview,
componentsPane,
outlinePane,
zhEn,
settingsPane,
designer,
eventBindDialog
};

View File

@ -1,3 +0,0 @@
export default {
"namespace": "page"
}

View File

@ -1 +0,0 @@
export default {};

View File

@ -1,10 +0,0 @@
import en_us from './en-US';
import zh_cn from './zh-CN';
import zh_tw from './zh-TW';
import ja_jp from './ja-JP';
export default {
'en-US': en_us,
'zh-CN': zh_cn,
'zh-TW': zh_tw,
'ja-JP': ja_jp
};

View File

@ -1 +0,0 @@
export default {};

View File

@ -1 +0,0 @@
export default {};

View File

@ -1 +0,0 @@
export default {};

View File

@ -1,140 +0,0 @@
export default {
"skeleton": {
"config": {
"package": "@ali/lowcode-editor-skeleton",
"version": "^0.8.0"
}
},
"theme": {
"fusion": {
"package": "@alife/theme-lowcode-light",
"version": "^0.1.0"
},
"scss": ""
},
"constants": {
"namespace": "page"
},
"utils": [],
"plugins": {
"topArea": [{
"pluginKey": "logo",
"type": "Custom",
"props": {
"align": "left",
"width": 100
},
"config": {
"package": "@ali/lowcode-plugin-sample-logo",
"version": "^0.8.0"
},
"pluginProps": {
"logo": "https://img.alicdn.com/tfs/TB1hoI9x1H2gK0jSZFEXXcqMpXa-146-40.png",
"href": "/"
}
}, {
"pluginKey": "undoRedo",
"type": "Custom",
"props": {
"align": "right",
"width": 88
},
"config": {
"package": "@ali/lowcode-plugin-undo-redo",
"version": "^0.8.0"
}
}, {
"pluginKey": "divider",
"type": "Divider",
"props": {
"align": "right"
}
}, {
"pluginKey": "samplePreview",
"type": "Custom",
"props": {
"align": "right",
"width": 64
},
"config": {
"package": "@ali/lowcode-plugin-sample-preview",
"version": "^0.8.0"
}
}],
"leftArea": [{
"pluginKey": "componentsPane",
"type": "PanelIcon",
"props": {
"align": "top",
"icon": "zujianku",
"title": "组件库",
"floatable": true
},
"config": {
"package": "@ali/lowcode-plugin-components-pane",
"version": "^0.8.0"
},
"pluginProps": {}
}, {
"pluginKey": "outlinePane",
"type": "PanelIcon",
"props": {
"align": "top",
"icon": "shuxingkongjian",
"title": "大纲树"
},
"config": {
"package": "@ali/lowcode-plugin-outline-pane",
"version": "^0.8.0"
},
"pluginProps": {}
}, {
"pluginKey": "zhEn",
"type": "Custom",
"props": {
"align": "bottom"
},
"config": {
"package": "@ali/lowcode-plugin-zh-en",
"version": "^0.8.0"
},
"pluginProps": {}
}],
"rightArea": [{
"pluginKey": "settingsPane",
"type": "Panel",
"props": {},
"config": {
"package": "@ali/lowcode-plugin-settings-pane",
"version": "^0.8.0"
},
"pluginProps": {}
}],
"centerArea": [{
"pluginKey": "designer",
"config": {
"package": "@ali/lowcode-plugin-designer",
"version": "^0.8.0"
}
}, {
"pluginKey": "eventBindDialog",
"config": {
"package": "@ali/lowcode-plugin-event-bind-dialog",
"version": "^0.8.0"
}
}]
},
"hooks": [],
"shortCuts": [],
"lifeCycles": {
"init": async function init(editor) {
const assets = await editor.utils.get('./assets.json');
editor.set('assets', assets);
editor.emit('assets.loaded', assets);
const schema = await editor.utils.get('./schema.json');
editor.set('schema', schema);
editor.emit('schema.loaded', schema);
}
}
};

View File

@ -1,3 +0,0 @@
export default {
};

View File

@ -5,3 +5,25 @@ body {
box-sizing: border-box; box-sizing: border-box;
} }
} }
body, #lce-container {
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;
}

View File

@ -1,20 +1,9 @@
import React from 'react'; import { render } from 'react-dom';
import ReactDOM from 'react-dom'; import GeneralWorkbench, { editor } from '../../../editor-preset-general/src';
import { registerSetters } from '@ali/lowcode-setters'; import config from './config';
import config from './config/skeleton'; import components from './components';
import components from './config/components';
import utils from './config/utils';
import './global.scss'; import './global.scss';
import './config/theme.scss';
registerSetters();
const Skeleton = components.LowcodeSkeleton;
const LCE_CONTAINER = document.getElementById('lce-container'); const LCE_CONTAINER = document.getElementById('lce-container');
if (!LCE_CONTAINER) { render(<GeneralWorkbench config={config} components={components} />, LCE_CONTAINER);
throw new Error('当前页面不存在 <div id="lce-container"></div> 节点.');
}
// @ts-ignore
ReactDOM.render(<Skeleton config={config} utils={utils} components={components} />, LCE_CONTAINER);

View File

@ -1 +1 @@
import './editor'; import './vision';

View File

@ -0,0 +1,517 @@
/* eslint-disable */
import { createElement } from 'react';
import { Button } from '@alifd/next';
import Engine, { Panes, Prototype } from '@ali/visualengine';
import { ActionUtil as actionUtil } from '@ali/visualengine-utils';
import getTrunkPane from '@ali/ve-trunk-pane';
import DatapoolPane from '@ali/ve-datapool-pane';
import PageHistoryManager from '@ali/ve-page-history';
import HistoryPane from '@ali/ve-history-pane';
import PageHistoryPane from '@ali/ve-page-history-pane';
// import I18nPane from '@ali/ve-i18n-pane';
import I18nManagePane from '@ali/ve-i18n-manage-pane';
import ActionPane from '@ali/ve-action-pane';
import SourceEditor from '@ali/lowcode-plugin-source-editor';
import fetchContext from '@ali/vu-legao-design-fetch-context';
import EventBindDialog from '@ali/lowcode-plugin-event-bind-dialog';
import loadUrls from './loader';
import { upgradeAssetsBundle } from './upgrade-assets';
import { isCSSUrl } from '@ali/lowcode-utils';
import VariableSetter from '@ali/vs-variable-setter';
import _isArray from "lodash/isArray";
import _isObject from "lodash/isObject";
import _get from 'lodash/get';
import funcParser from '@ali/vu-function-parser';
import {
NumberSetter,
BoolSetter,
ChoiceSetter,
CodeSetter,
ColorSetter,
DateSetter,
I18nSetter,
JsonSetter,
ListSetter,
SelectSetter,
OptionsSetter,
TextSetter,
ValidationSetter,
ActionSetter,
} from '@ali/visualengine-utils';
const { editor, skeleton, context, HOOKS, Trunk } = Engine;
Trunk.registerSetter('Input', TextSetter);
Trunk.registerSetter('StringSetter', TextSetter);
Trunk.registerSetter('TextArea', TextSetter);
Trunk.registerSetter('Object', JsonSetter);
Trunk.registerSetter('Function', ActionSetter);
Trunk.registerSetter('Node', CodeSetter);
Trunk.registerSetter('Mixin', CodeSetter);
Trunk.registerSetter('Expression', CodeSetter);
Trunk.registerSetter('List', ListSetter);
Trunk.registerSetter('Switch', BoolSetter);
Trunk.registerSetter('Number', NumberSetter);
Trunk.registerSetter('Select', SelectSetter);
Trunk.registerSetter('ActionSetter', ActionSetter);
Trunk.registerSetter('BoolSetter', BoolSetter);
Trunk.registerSetter('ChoiceSetter', ChoiceSetter);
Trunk.registerSetter('CodeSetter', CodeSetter);
Trunk.registerSetter('ColorSetter', ColorSetter);
Trunk.registerSetter('DateSetter', DateSetter);
Trunk.registerSetter('JsonSetter', JsonSetter);
Trunk.registerSetter('ListSetter', ListSetter);
Trunk.registerSetter('SelectSetter', SelectSetter);
Trunk.registerSetter('OptionsSetter', OptionsSetter);
Trunk.registerSetter('TextSetter', TextSetter);
Trunk.registerSetter('NumberSetter', NumberSetter);
Trunk.registerSetter('ValidationSetter', ValidationSetter);
// 需要额外覆盖配置的 setters
function wrapSetter(component: any, title: any, initialValueWrapper: any) {
return {
component,
title,
recommend: true,
initialValue: initialValueWrapper ? (field: any) => {
let defaultValueFromSetter;
if (component.initial) {
defaultValueFromSetter = component.initial.call(field, field.getValue());
}
const defaultValue = initialValueWrapper(defaultValueFromSetter);
return defaultValue;
} : undefined,
}
}
Trunk.registerSetter('I18nSetter', wrapSetter(
I18nSetter,
{ type: 'i18n', 'zh-CN': '国际化输入', 'en-US': 'International Input' },
(defaultValue: any) => {
if (defaultValue[defaultValue.use] && typeof defaultValue[defaultValue.use] !== 'string') {
defaultValue[defaultValue.use] = null;
}
return defaultValue;
}
));
context.use(HOOKS.VE_SETTING_FIELD_VARIABLE_SETTER, VariableSetter);
const externals = ['react', 'react-dom', 'prop-types', 'react-router', 'react-router-dom', '@ali/recore'];
async function loadAssets() {
const legaoAssets = await editor.utils.get('./legao-assets.json');
const assets = upgradeAssetsBundle(legaoAssets);
if (assets.packages) {
assets.packages.forEach((item: any) => {
if (item.package && externals.indexOf(item.package) > -1) {
item.urls = null;
}
});
}
if (assets['x-prototypes']) {
const tasks: Array<Promise<any>> = [];
const prototypeStyles: string[] = [];
assets['x-prototypes'].forEach((pkg: any) => {
if (pkg?.urls) {
const urls = Array.isArray(pkg.urls) ? pkg.urls : [pkg.urls];
urls.forEach((url: string) => {
if (isCSSUrl(url)) {
prototypeStyles.push(url);
}
});
tasks.push(loadUrls(urls));
}
});
if (prototypeStyles.length > 0) {
assets.packages.push({
library: '_prototypesStyle',
package: '_prototypes-style',
urls: prototypeStyles,
});
}
await Promise.all(tasks);
// proccess snippets
}
editor.set('legao-assets', legaoAssets);
editor.set('assets', assets);
}
async function loadSchema() {
const schema = await editor.utils.get('./schema.json');
editor.set('schema', schema);
}
// demo
function initDemoPanes() {
skeleton.add({
name: 'eventBindDialog',
type: 'Widget',
content: EventBindDialog,
});
// skeleton.add({
// area: 'left',
// name: 'sourceEditor',
// type: "PanelDock",
// content: SourceEditor,
// props: {
// align: undefined,
// description: "动作面板",
// onDestroy: undefined,
// icon: 'set',
// onInit: undefined
// },
// panelProps:{
// height: 300,
// help: undefined,
// hideTitleBar: true,
// maxHeight: 800,
// maxWidth: 1200,
// title: "动作面板",
// width: 600
// }
// });
// skeleton.add({
// area: 'leftArea',
// name: 'icon1',
// type: 'PanelDock',
// props: {
// align: 'bottom',
// icon: 'set',
// description: '设置'
// },
// });
skeleton.add({
area: 'leftArea',
name: 'icon2',
type: 'Dock',
props: {
align: 'bottom',
icon: 'help',
description: '帮助'
},
});
skeleton.add({
area: 'topArea',
type: 'Dock',
name: 'publish',
props: {
align: 'right',
},
content: createElement(Button, {
size: 'small',
type: 'secondary',
children: '发布',
}),
});
skeleton.add({
area: 'topArea',
type: 'Dock',
name: 'save',
props: {
align: 'right',
},
content: createElement(Button, {
size: 'small',
type: 'primary',
children: '保存',
}),
});
// skeleton.add({
// area: 'topArea',
// type: 'Dock',
// name: 'preview4',
// props: {
// align: 'center',
// },
// content: createElement('img', {
// src: 'https://img.alicdn.com/tfs/TB1WW.VC.z1gK0jSZLeXXb9kVXa-486-64.png',
// style: {
// height: 32,
// },
// }),
// });
skeleton.add({
area: 'topArea',
type: 'Dock',
name: 'preview1',
props: {
align: 'left',
},
content: createElement('img', {
src: 'https://img.alicdn.com/tfs/TB1zqBfDlr0gK0jSZFnXXbRRXXa-440-64.png',
style: {
height: 32,
},
}),
});
}
async function initTrunkPane() {
const assets = await editor.onceGot('legao-assets');
const config = {
disableLowCodeComponent: true,
disableComponentStore: true,
app: {
getAssetsData() {
return assets;
// return data;
},
},
};
const TrunkPane = getTrunkPane(config);
Panes.add(TrunkPane);
}
// 数据源面板
function initDataPoolPane() {
const dpConfigs = {};
if (!dpConfigs) {
return;
}
fetchContext.create('DataPoolPaneAPI', {
saveGlobalConfig: {
url: 'query/appConfig/saveGlobalConfig.json',
method: 'POST',
},
saveOrUpdateAppDataPool: {
url: 'query/appDataPool/saveOrUpdateAppDataPool.json',
method: 'POST',
},
batchSaveOrUpdateAppDataPool: {
url: 'query/appDataPool/batchSaveOrUpdateAppDataPool.json',
method: 'POST'
},
listAppDataPool: {
url: 'query/appDataPool/listAppDataPool.json',
method: 'GET',
},
getAppDataPool: {
url: 'query/appDataPool/getAppDataPool.json',
method: 'POST',
},
getEpaasApiInApp: {
url: 'query/formdesign/getEpaasApiInApp.jsonp',
method: 'GET',
},
getFormListOrder: {
url: 'query/formdesign/getFormListOrder.json',
method: 'GET',
},
// 实时修改 effectForm
operateAppDpBind: {
url: 'query/appDataPool/operateAppDpBind.json',
method: 'POST',
},
// 校验全局数据源是否被其他页面修改
checkAppDataPoolModified: {
url: 'query/appDataPool/checkAppDataPoolModified.json',
method: 'POST',
},
});
const props = {
enableGateService: true,
enableGlobalFitConfig: true,
enableOneAPIService: true,
formUuid: 'xxx',
api: fetchContext.api.DataPoolPaneAPI,
};
Panes.add(DatapoolPane, {
props,
});
}
// 国际化面板
function initI18nPane() {
fetchContext.create('I18nManagePaneAPI', {
// 绑定美杜莎
bindMedusa: {
url: 'query/app/createMedusa.json',
},
// 解除绑定
unbindMedusa: {
url: 'query/app/removeMedusa.json',
},
// 同步美杜莎
syncMedusa: {
url: 'query/formi18n/syncI18n.json',
},
});
Panes.add(I18nManagePane, {
props: {
enableMedusa: true,
api: fetchContext.api.I18nManagePaneAPI,
},
});
}
// 动作面板
function initActionPane() {
actionUtil.setActions({
module: {
compiled: "'use strict';\n\nexports.__esModule = true;\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };\n\nexports.submit = submit;\nexports.onLoadData = onLoadData;\nexports.add = add;\nexports.edit = edit;\nexports.del = del;\nexports.search = search;\nexports.reset = reset;\n/**\n* 点击弹框的“确认”\n*/\nfunction submit() {\n var _this = this;\n\n this.$('form').submit(function (data, error) {\n if (data) {\n _this.dataSourceMap['table_submit'].load(data).then(function (res) {\n _this.utils.toast({\n type: 'success',\n title: '提交成功'\n });\n _this.$('dialog').hide();\n _this.dataSourceMap['table_list'].load();\n }).catch(function () {\n _this.utils.toast({\n type: 'error',\n title: '提交失败'\n });\n });\n }\n });\n}\n\n/**\n* tablePc onLoadData\n* @param currentPage 当前页码\n* @param pageSize 每页显示条数\n* @param searchKey 搜索关键字\n* @param orderColumn 排序列\n* @param orderType 排序方式desc,asc\n* @param from 触发来源order,search,pagination\n*/\nfunction onLoadData(currentPage, pageSize, searchKey, orderColumn, orderType, from) {\n var tableParams = {\n currentPage: from === 'search' ? 1 : currentPage,\n pageSize: pageSize,\n searchKey: searchKey,\n orderColumn: orderColumn,\n orderType: orderType\n };\n this.setState({ tableParams: tableParams });\n}\n\n// 点击新增\nfunction add() {\n this.setState({\n formData: null\n });\n this.$('dialog').show();\n}\n\n// 点击编辑\nfunction edit(rowData) {\n this.setState({\n formData: rowData\n });\n this.$('dialog').show();\n}\n\n// 点击删除\nfunction del(rowData) {\n var _this2 = this;\n\n this.utils.dialog({\n method: 'confirm',\n title: '提示',\n content: '确认删除该条目吗?',\n onOk: function onOk() {\n _this2.dataSourceMap['table_delete'].load({ id: rowData.id }).then(function () {\n _this2.utils.toast({\n type: 'success',\n title: '删除成功'\n });\n _this2.dataSourceMap['table_list'].load();\n }).catch(function () {\n _this2.utils.toast({\n type: 'error',\n title: '删除失败'\n });\n });\n }\n });\n}\n\n/**\n* button onClick\n*/\nfunction search() {\n var filterData = this.$('filter').getValue();\n this.setState({\n filterData: filterData,\n tableParams: _extends({}, this.state.tableParams, {\n time: Date.now(),\n currentPage: 1\n })\n });\n}\n\n/**\n* button onClick\n*/\nfunction reset() {\n this.$('filter').reset();\n this.setState({\n filterData: {},\n tableParams: _extends({}, this.state.tableParams, {\n time: Date.now(),\n currentPage: 1\n })\n });\n}",
source: "/**\n* 点击弹框的“确认”\n*/\nexport function submit() {\n this.$('form').submit((data, error) => {\n if (data) {\n this.dataSourceMap['table_submit'].load(data).then((res) => {\n this.utils.toast({\n type: 'success',\n title: '提交成功'\n });\n this.$('dialog').hide();\n this.dataSourceMap['table_list'].load();\n }).catch(()=>{\n this.utils.toast({\n type: 'error',\n title: '提交失败'\n });\n })\n }\n })\n}\n\n/**\n* tablePc onLoadData\n* @param currentPage 当前页码\n* @param pageSize 每页显示条数\n* @param searchKey 搜索关键字\n* @param orderColumn 排序列\n* @param orderType 排序方式desc,asc\n* @param from 触发来源order,search,pagination\n*/\nexport function onLoadData(currentPage, pageSize, searchKey, orderColumn, orderType, from) {\n const tableParams = {\n currentPage: from === 'search' ? 1 : currentPage,\n pageSize,\n searchKey,\n orderColumn,\n orderType\n };\n this.setState({ tableParams });\n}\n\n// 点击新增\nexport function add() {\n this.setState({\n formData: null,\n });\n this.$('dialog').show();\n}\n\n\n// 点击编辑\nexport function edit(rowData) {\n this.setState({\n formData: rowData\n });\n this.$('dialog').show();\n}\n\n// 点击删除\nexport function del(rowData) {\n this.utils.dialog({\n method: 'confirm',\n title: '提示',\n content: '确认删除该条目吗?',\n onOk: () => {\n this.dataSourceMap['table_delete'].load({ id: rowData.id }).then(() => {\n this.utils.toast({\n type: 'success',\n title: '删除成功'\n });\n this.dataSourceMap['table_list'].load();\n }).catch(()=>{\n this.utils.toast({\n type: 'error',\n title: '删除失败'\n });\n })\n }\n })\n}\n\n/**\n* button onClick\n*/\nexport function search(){\n const filterData = this.$('filter').getValue();\n this.setState({\n filterData,\n tableParams: {\n ...this.state.tableParams,\n time: Date.now(),\n currentPage: 1\n }\n });\n}\n\n/**\n* button onClick\n*/\nexport function reset(){\n this.$('filter').reset();\n this.setState({\n filterData: {},\n tableParams: {\n ...this.state.tableParams,\n time: Date.now(),\n currentPage: 1\n }\n });\n}"
},
type: "FUNCTION",
list: [
{
"id": "submit",
"title": "submit"
},
{
"id": "onLoadData",
"title": "onLoadData"
},
{
"id": "add",
"title": "add"
},
{
"id": "edit",
"title": "edit"
},
{
"id": "del",
"title": "del"
},
{
"id": "search",
"title": "search"
},
{
"id": "reset",
"title": "reset"
}
]
});
const props = {
enableGlobalJS: false,
enableVsCodeEdit: false,
enableHeaderTip: true,
};
Panes.add(ActionPane, {
props,
});
}
function replaceFuncProp(props?: any){
const replaceProps: any = {};
for (const name in props) {
const prop = props[name];
if (!prop) {
continue;
}
if ((prop.compiled && prop.source) || prop.type === 'actionRef' || prop.type === 'js') {
replaceProps[name] = funcParser(prop);
} else if (_isObject(prop)) {
replaceFuncProp(prop);
} else if (_isArray(prop)) {
prop.map((propItem) => {
replaceFuncProp(propItem);
});
}
}
for (const name in replaceProps) {
props[name] = replaceProps[name];
}
return props;
};
// 操作历史与页面历史面板
function initHistoryPane() {
// let historyConfigs = {getDesignerModuleConfigs(
// this.designerConfigs,
// 'history',
// )};
let historyConfigs = {
enableRedoAndUndo: true,
enablePageHistory: true,
};;
const isDemoMode = false;
const isEnvSupportsHistoryPane = true;
const historyManager = PageHistoryManager.getManager();
console.log('PageHistoryManager', historyManager);
console.log('PageHistoryManager.onOpenPane', historyManager.onOpenPane);
// 历史撤销、重做以及唤起页面历史按钮
if (typeof HistoryPane === 'function') {
Panes.add(HistoryPane, {
props : {
showPageHistory:
isEnvSupportsHistoryPane
// && this.app.isForm()
&& !isDemoMode,
historyManager,
historyConfigs,
index: -940,
}
});
} else {
Panes.add(HistoryPane, {
index: -940,
});
}
// 页面历史 UI 面板
if (
PageHistoryPane
&& !isDemoMode
&& isEnvSupportsHistoryPane
) {
Panes.add(PageHistoryPane, {
props : {
historyManager: {
historyManager,
app: {
}
},
index: -940,
},
});
}
}
async function init() {
Engine.Env.setEnv('RE_VERSION', '7.2.0');
Engine.Env.setSupportFeatures({
subview: true,
i18nPane: true,
});
Prototype.addGlobalPropsReducer(replaceFuncProp);
await loadAssets();
await loadSchema();
await initTrunkPane();
// initDataPoolPane();
// initI18nPane();
// initActionPane();
initDemoPanes();
// initHistoryPane();
Engine.init();
}
init();

View File

@ -0,0 +1,172 @@
/* eslint-disable */
function getStylePoint(id, level) {
if (stylePointTable[id]) {
return stylePointTable[id];
}
const base = getBasePoint();
if (id === 'base') {
return base;
}
const point = new StylePoint(id, level || 2000);
if (level >= base.level) {
let prev = base;
let next = prev.next;
while (next && level >= next.level) {
prev = next;
next = prev.next;
}
prev.next = point;
point.prev = prev;
if (next) {
point.next = next;
next.prev = point;
}
} else {
let next = base;
let prev = next.prev;
while (prev && level < prev.level) {
next = prev;
prev = next.prev;
}
next.prev = point;
point.next = next;
if (prev) {
point.prev = prev;
prev.next = point;
}
}
point.insert();
stylePointTable[id] = point;
return point;
}
const stylePointTable = {};
function getBasePoint() {
if (!stylePointTable.base) {
stylePointTable.base = new StylePoint('base', 1000);
stylePointTable.base.insert();
}
return stylePointTable.base;
}
class StylePoint {
constructor(id, level, placeholder) {
this.lastContent = null;
this.lastUrl = null;
this.next = null;
this.prev = null;
this.id = id;
this.level = level;
if (placeholder) {
this.placeholder = placeholder;
} else {
this.placeholder = document.createTextNode('');
}
}
insert() {
if (this.next) {
document.head.insertBefore(this.placeholder, this.next.placeholder);
} else if (this.prev) {
document.head.insertBefore(this.placeholder, this.prev.placeholder.nextSibling);
} else {
document.head.appendChild(this.placeholder);
}
}
applyText(content) {
if (this.lastContent === content) {
return;
}
this.lastContent = content;
this.lastUrl = undefined;
const element = document.createElement('style');
element.setAttribute('type', 'text/css');
element.setAttribute('data-for', this.id);
element.appendChild(document.createTextNode(content));
document.head.insertBefore(element, this.placeholder);
document.head.removeChild(this.placeholder);
this.placeholder = element;
}
applyUrl(url) {
if (this.lastUrl === url) {
return;
}
this.lastContent = undefined;
this.lastUrl = url;
const element = document.createElement('link');
element.href = url;
element.rel = 'stylesheet';
element.setAttribute('data-for', this.id);
document.head.insertBefore(element, this.placeholder);
document.head.removeChild(this.placeholder);
this.placeholder = element;
}
}
function loadCSS(url) {
getStylePoint(url).applyUrl(url);
}
function isCSSUrl(url) {
return /\.css$/.test(url);
}
function loadScript(url) {
const node = document.createElement('script');
// node.setAttribute('crossorigin', 'anonymous');
node.onload = onload;
node.onerror = onload;
const i = {};
const promise = new Promise((resolve, reject) => {
i.resolve = resolve;
i.reject = reject;
});
function onload(e) {
node.onload = null;
node.onerror = null;
if (e.type === 'load') {
i.resolve();
} else {
i.reject();
}
// document.head.removeChild(node);
// node = null;
}
// node.async = true;
node.src = url;
document.head.appendChild(node);
return promise;
}
export default function loadUrls(urls) {
if (!urls || urls.length < 1) {
return Promise.resolve();
}
let promise = null;
urls.forEach((url) => {
if (isCSSUrl(url)) {
loadCSS(url);
} else if (!promise) {
promise = loadScript(url);
} else {
promise = promise.then(() => loadScript(url));
}
});
return promise || Promise.resolve();
}

13
packages/demo/src/vision/module.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
declare module '@ali/visualengine';
declare module '@ali/visualengine-utils';
declare module '@ali/ve-trunk-pane';
declare module '@ali/vs-variable-setter';
declare module '@ali/ve-datapool-pane';
declare module '@ali/ve-history-pane';
declare module '@ali/ve-page-history-pane';
declare module '@ali/ve-page-history';
declare module '@ali/ve-i18n-manage-pane';
declare module '@ali/ve-action-pane';
declare module '@ali/vu-legao-design-fetch-context';
declare module "@ali/vu-function-parser";
declare module "compare-versions";

View File

@ -0,0 +1,68 @@
/* eslint-disable */
export function upgradeAssetsBundle(assets) {
const components = [];
const xPrototypes = [];
const componentList = [];
const packages = assets.externals.map(({ urls, library, name, version }) => {
return {
package: name,
version,
urls,
library,
};
});
assets.componentDependencies.forEach((item) => {
const componentName = item.alias || item.library;
const metadata = {
componentName,
npm: {
package: item.packageName,
library: item.library,
version: item.version,
destructuring: false,
},
props: [],
};
if (item.prototypeConfigsUrl) {
xPrototypes.push({
package: item.packageName,
urls: item.prototypeConfigsUrl,
});
} else if (item.components) {
packages.push({
urls: item.urls,
library: item.library,
package: item.packageName,
version: item.version,
});
const meta = item.components[0];
metadata.componentName = meta.componentName;
metadata.configure = meta.configure;
metadata.title = meta.title;
components.push(metadata);
// TODO:
if (meta.snippets) {
componentList.push({
title: meta.category,
icon: '',
children: [
{
title: 'json格式化展示',
icon: '',
snippets: meta.snippets,
},
],
});
}
}
});
return {
"version": "1.0.0",
packages,
'x-prototypes': xPrototypes,
components,
componentList
};
}

View File

@ -3,6 +3,264 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
<a name="0.9.23"></a>
## [0.9.23](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.21...@ali/lowcode-designer@0.9.23) (2020-06-23)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.21"></a>
## [0.9.21](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.20...@ali/lowcode-designer@0.9.21) (2020-06-23)
### Bug Fixes
* 1. 修复dialog拖入不显示问题 2. dialog 只能在根节点下 3. 引入 modalNodeManager ([65977e7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/65977e7))
* add extraEnv ([9058ac8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/9058ac8))
* 修复低代码组件设计器、区块设计器根节点为 Page 的问题,修复 topArea 样式 ([e85b542](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/e85b542))
* 支持事件 VE_EVENTS.VE_PAGE_PAGE_READY ([935ffad](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/935ffad))
* 支持页面回滚 ([5d7dc2f](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/5d7dc2f))
* 更改生成 id 的规则, 否则命中 recore 解析 id 的一个限制 ([5adff44](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/5adff44))
* 根据目标元素的canDropIn函数判断是否能放入其他元素 ([21d4f64](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/21d4f64))
* 简化 onPageReady 实现逻辑 ([a36e5f2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a36e5f2))
### Features
* 增加 node replaceWith 方法 ([d44f95b](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/d44f95b))
* 引擎层埋点 ([69de533](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/69de533))
<a name="0.9.20"></a>
## [0.9.20](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.19...@ali/lowcode-designer@0.9.20) (2020-06-16)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.19"></a>
## [0.9.19](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.18...@ali/lowcode-designer@0.9.19) (2020-06-15)
### Bug Fixes
* force schema ([6d0a8ff](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6d0a8ff))
* patch prototype ([f20bfaa](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/f20bfaa))
* try get settingfield ([56f242f](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/56f242f))
* 禁止组件拉到 Page 的直接子节点, 以及替换 tab 组件 ([d93a291](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/d93a291))
### Features
* complete live-editing expr & i18n ([3ac08ba](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3ac08ba))
* get SettingField instead of SettingPropEntry for compatibility ([2787a12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2787a12))
* support prop.autorun ([c0a5235](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/c0a5235))
* ve事件埋点 ([700e5b0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/700e5b0))
<a name="0.9.18"></a>
## [0.9.18](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.17...@ali/lowcode-designer@0.9.18) (2020-05-20)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.17"></a>
## [0.9.17](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.16...@ali/lowcode-designer@0.9.17) (2020-05-19)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.16"></a>
## [0.9.16](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.15...@ali/lowcode-designer@0.9.16) (2020-05-18)
### Bug Fixes
* uniqueid ([8db52f0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/8db52f0))
<a name="0.9.15"></a>
## [0.9.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.14...@ali/lowcode-designer@0.9.15) (2020-05-16)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.14"></a>
## [0.9.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.13...@ali/lowcode-designer@0.9.14) (2020-05-16)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.13"></a>
## [0.9.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.12...@ali/lowcode-designer@0.9.13) (2020-05-16)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.12"></a>
## [0.9.12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.11...@ali/lowcode-designer@0.9.12) (2020-05-15)
### Features
* change reducer stage ([c2e83c7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/c2e83c7))
<a name="0.9.11"></a>
## [0.9.11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.10...@ali/lowcode-designer@0.9.11) (2020-05-15)
### Features
* add ? to component designer/src/designer/setting/utils.js ([0025e3c](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/0025e3c))
* add alias for get index ([e853a4f](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/e853a4f))
* add resize box ([14a55ae](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/14a55ae))
* bord resizing ([361f4f6](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/361f4f6))
* border resizing ([c7fc28c](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/c7fc28c))
* for box resizing ([77e325f](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/77e325f))
* for box resizing ([cb2854d](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/cb2854d))
<a name="0.9.10"></a>
## [0.9.10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.9...@ali/lowcode-designer@0.9.10) (2020-05-15)
### Bug Fixes
* handling the undefined variable ([0efe8b4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/0efe8b4))
* lc-borders-actions ([56d9f5f](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/56d9f5f))
* 修复 toolbar 弹出位置异常 ([b40b9a4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b40b9a4))
<a name="0.9.9"></a>
## [0.9.9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.8...@ali/lowcode-designer@0.9.9) (2020-05-13)
### Bug Fixes
* 🐛 add hotkey up/down/left/right ([9c8afe8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/9c8afe8))
* 🐛 error when quick search ([801d954](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/801d954))
* 🐛 快捷键支持 ([73374dd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/73374dd))
* 🐛 移动快捷键 ([7c8a27c](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/7c8a27c))
* cancel dragging on invalid position ([f961096](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/f961096))
* panel visible time ([18ac1fa](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/18ac1fa))
* quickSearch error ([a8009ef](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a8009ef))
* supports ([371b84c](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/371b84c))
### Features
* show value state ([bd49e50](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/bd49e50))
* support global inline editing ([4f7179b](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/4f7179b))
* support plaintext liveediting ([ea62f12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/ea62f12))
<a name="0.9.8"></a>
## [0.9.8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.7...@ali/lowcode-designer@0.9.8) (2020-05-08)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.7"></a>
## [0.9.7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.6...@ali/lowcode-designer@0.9.7) (2020-05-07)
### Bug Fixes
* 🐛 add pollyfill for vision page.getHistory ([0b905d0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/0b905d0))
* 🐛 title缺少icon字段临时转接一下 ([2f9bb25](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2f9bb25))
* 🐛 增加 getAddonData api ([68b7e29](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/68b7e29))
* 🐛 增加剪切快捷键 ([a73a82e](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a73a82e))
* border action style ([6b91535](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6b91535))
* documentModel toData 方法 ([1ea0d73](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/1ea0d73))
* settingfield添加props修复地区组件切换类型报错 ([88348f7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/88348f7))
* 在Transducer中添加对MixedSetter的支持 ([7317f2f](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/7317f2f))
### Features
* 🎸 增加icon获取api ([f1a0823](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/f1a0823))
* duplicate ([ec932aa](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/ec932aa))
* 修复状态切换失效 ([2e3f60d](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2e3f60d))
<a name="0.9.6"></a>
## [0.9.6](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.5...@ali/lowcode-designer@0.9.6) (2020-04-27)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.5"></a>
## [0.9.5](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.4...@ali/lowcode-designer@0.9.5) (2020-04-27)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.4"></a>
## [0.9.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.3...@ali/lowcode-designer@0.9.4) (2020-04-27)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.3"></a>
## [0.9.3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.2...@ali/lowcode-designer@0.9.3) (2020-04-16)
**Note:** Version bump only for package @ali/lowcode-designer
<a name="0.9.2"></a>
## [0.9.2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.1...@ali/lowcode-designer@0.9.2) (2020-04-15)
### Features
* 🎸 polyfill exchange ([7070557](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/7070557))
* 🎸 polyfill exchange ([286e7d8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/286e7d8))
* Exchange ([ef5a72e](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/ef5a72e))
* run vision polyfill ([33750b7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/33750b7))
<a name="0.9.1"></a> <a name="0.9.1"></a>
## [0.9.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.0...@ali/lowcode-designer@0.9.1) (2020-03-31) ## [0.9.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.9.0...@ali/lowcode-designer@0.9.1) (2020-03-31)

View File

@ -1,6 +1,6 @@
{ {
"name": "@ali/lowcode-designer", "name": "@ali/lowcode-designer",
"version": "0.9.1", "version": "0.9.23",
"description": "Designer for Ali LowCode Engine", "description": "Designer for Ali LowCode Engine",
"main": "lib/index.js", "main": "lib/index.js",
"module": "es/index.js", "module": "es/index.js",
@ -15,8 +15,11 @@
}, },
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@ali/lowcode-globals": "^0.9.1", "@ali/lowcode-editor-core": "^0.8.19",
"@ali/lowcode-types": "^0.8.9",
"@ali/lowcode-utils": "^0.8.10",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"event": "^1.0.0",
"react": "^16", "react": "^16",
"react-dom": "^16.7.0" "react-dom": "^16.7.0"
}, },

View File

@ -1,10 +1,10 @@
import { Component, Fragment, PureComponent } from 'react'; import { Component, Fragment, PureComponent } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { computed, observer, TitleContent, Title } from '@ali/lowcode-globals'; import { computed, observer, Title } from '@ali/lowcode-editor-core';
import { SimulatorContext } from '../context';
import { BuiltinSimulatorHost } from '../host'; import { BuiltinSimulatorHost } from '../host';
import { TitleContent } from '@ali/lowcode-types';
export class BorderHoveringInstance extends PureComponent<{ export class BorderDetectingInstance extends PureComponent<{
title: TitleContent; title: TitleContent;
rect: DOMRect | null; rect: DOMRect | null;
scale: number; scale: number;
@ -23,7 +23,7 @@ export class BorderHoveringInstance extends PureComponent<{
transform: `translate(${(scrollX + rect.left) * scale}px, ${(scrollY + rect.top) * scale}px)`, transform: `translate(${(scrollX + rect.left) * scale}px, ${(scrollY + rect.top) * scale}px)`,
}; };
const className = classNames('lc-borders lc-borders-hovering'); const className = classNames('lc-borders lc-borders-detecting');
// TODO: // TODO:
// 1. thinkof icon // 1. thinkof icon
@ -38,30 +38,28 @@ export class BorderHoveringInstance extends PureComponent<{
} }
@observer @observer
export class BorderHovering extends Component { export class BorderDetecting extends Component<{ host: BuiltinSimulatorHost }> {
static contextType = SimulatorContext;
shouldComponentUpdate() { shouldComponentUpdate() {
return false; return false;
} }
@computed get scale() { @computed get scale() {
return (this.context as BuiltinSimulatorHost).viewport.scale; return this.props.host.viewport.scale;
} }
@computed get scrollX() { @computed get scrollX() {
return (this.context as BuiltinSimulatorHost).viewport.scrollX; return this.props.host.viewport.scrollX;
} }
@computed get scrollY() { @computed get scrollY() {
return (this.context as BuiltinSimulatorHost).viewport.scrollY; return this.props.host.viewport.scrollY;
} }
@computed get current() { @computed get current() {
const host = this.context as BuiltinSimulatorHost; const host = this.props.host;
const doc = host.document; const doc = host.document;
const selection = doc.selection; const selection = doc.selection;
const current = host.designer.hovering.current; const current = host.designer.detecting.current;
if (!current || current.document !== doc || selection.has(current.id)) { if (!current || current.document !== doc || selection.has(current.id)) {
return null; return null;
} }
@ -69,38 +67,38 @@ export class BorderHovering extends Component {
} }
render() { render() {
const host = this.context as BuiltinSimulatorHost; const host = this.props.host;
const current = this.current; const current = this.current;
if (!current || host.viewport.scrolling) { if (!current || host.viewport.scrolling || host.liveEditing.editing) {
return <Fragment />; return null;
} }
const instances = host.getComponentInstances(current); const instances = host.getComponentInstances(current);
if (!instances || instances.length < 1) { if (!instances || instances.length < 1) {
return <Fragment />; return null;
} }
if (instances.length === 1) { if (instances.length === 1) {
return ( return (
<BorderHoveringInstance <BorderDetectingInstance
key="line-h" key="line-h"
title={current.title} title={current.title}
scale={this.scale} scale={this.scale}
scrollX={this.scrollX} scrollX={this.scrollX}
scrollY={this.scrollY} scrollY={this.scrollY}
rect={host.computeComponentInstanceRect(instances[0], current.componentMeta.rectSelector)} rect={host.computeComponentInstanceRect(instances[0], current.componentMeta.rootSelector)}
/> />
); );
} }
return ( return (
<Fragment> <Fragment>
{instances.map((inst, i) => ( {instances.map((inst, i) => (
<BorderHoveringInstance <BorderDetectingInstance
key={`line-h-${i}`} key={`line-h-${i}`}
title={current.title} title={current.title}
scale={this.scale} scale={this.scale}
scrollX={this.scrollX} scrollX={this.scrollX}
scrollY={this.scrollY} scrollY={this.scrollY}
rect={host.computeComponentInstanceRect(inst, current.componentMeta.rectSelector)} rect={host.computeComponentInstanceRect(inst, current.componentMeta.rootSelector)}
/> />
))} ))}
</Fragment> </Fragment>

View File

@ -0,0 +1,284 @@
import { Component, Fragment } from 'react';
import DragResizeEngine from './drag-resize-engine';
import { observer, computed, globalContext, Editor } from '@ali/lowcode-editor-core';
import classNames from 'classnames';
import { SimulatorContext } from '../context';
import { BuiltinSimulatorHost } from '../host';
import { OffsetObserver, Designer } from '../../designer';
@observer
export default class BoxResizing extends Component<{ host: BuiltinSimulatorHost }> {
static contextType = SimulatorContext;
get host(): BuiltinSimulatorHost {
return this.props.host;
}
get dragging(): boolean {
return this.host.designer.dragon.dragging;
}
@computed get selecting() {
const doc = this.host.document;
if (doc.suspensed) {
return null;
}
const selection = doc.selection;
return this.dragging ? selection.getTopNodes() : selection.getNodes();
}
shouldComponentUpdate() {
return false;
}
componentDidUpdate() {
// this.hoveringCapture.setBoundary(this.outline);
// this.willBind();
}
render() {
const selecting = this.selecting;
if (!selecting || selecting.length < 1) {
// DIRTY FIX, recore has a bug!
return <Fragment />;
}
// const componentMeta = selecting[0].componentMeta;
// const metaData = componentMeta.getMetadata();
return (
<Fragment>
{selecting.map((node) => (
<BoxResizingForNode key={node.id} node={node} host={this.props.host} />
))}
</Fragment>
);
}
}
@observer
export class BoxResizingForNode extends Component<{ host: BuiltinSimulatorHost; node: Node }> {
static contextType = SimulatorContext;
get host(): BuiltinSimulatorHost {
return this.props.host;
}
get dragging(): boolean {
return this.host.designer.dragon.dragging;
}
@computed get instances() {
return this.host.getComponentInstances(this.props.node);
}
shouldComponentUpdate() {
return false;
}
render() {
const { instances } = this;
const { node } = this.props;
const designer = this.host.designer;
if (!instances || instances.length < 1) {
return null;
}
return (
<Fragment key={node.id}>
{instances.map((instance: any) => {
const observed = designer.createOffsetObserver({
node,
instance,
});
if (!observed) {
return null;
}
return (
<BoxResizingInstance key={observed.id} dragging={this.dragging} designer={designer} observed={observed} />
);
})}
</Fragment>
);
}
}
@observer
export class BoxResizingInstance extends Component<{
observed: OffsetObserver;
highlight?: boolean;
dragging?: boolean;
designer?: Designer;
}> {
// private outline: any;
private willUnbind: () => any;
private outlineRight: any;
private outlineLeft: any;
private dragEngine: DragResizeEngine;
constructor(props: any) {
super(props);
this.dragEngine = new DragResizeEngine(props.designer);
}
componentWillUnmount() {
if (this.willUnbind) {
this.willUnbind();
}
this.props.observed.purge();
}
getExperiMentalFns = (metaData: any) => {
if (metaData.experimental && metaData.experimental.callbacks) {
return metaData.experimantal.callbacks;
}
};
componentDidMount() {
// this.hoveringCapture.setBoundary(this.outline);
this.willBind();
const resize = (e: MouseEvent, direction: string, node: any, moveX: number, moveY: number) => {
const metaData = node.componentMeta.getMetadata();
if (
metaData &&
metaData.experimental &&
metaData.experimental.callbacks &&
typeof metaData.experimental.callbacks.onResize === 'function'
) {
e.trigger = direction;
e.deltaX = moveX;
e.deltaY = moveY;
metaData.experimental.callbacks.onResize(e, node);
}
};
const resizeStart = (e: MouseEvent, direction: string, node: any) => {
const metaData = node.componentMeta.getMetadata();
if (
metaData &&
metaData.experimental &&
metaData.experimental.callbacks &&
typeof metaData.experimental.callbacks.onResizeStart === 'function'
) {
e.trigger = direction;
metaData.experimental.callbacks.onResizeStart(e, node);
}
};
const resizeEnd = (e: MouseEvent, direction: string, node: any) => {
const metaData = node.componentMeta.getMetadata();
if (
metaData &&
metaData.experimental &&
metaData.experimental.callbacks &&
typeof metaData.experimental.callbacks.onResizeEnd === 'function'
) {
e.trigger = direction;
metaData.experimental.callbacks.onResizeStart(e, node);
}
const editor = globalContext.get(Editor);
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.emit('designer.border.resize', {
selected,
layout: node?.parent?.getPropValue('layout') || '',
});
};
this.dragEngine.onResize(resize);
this.dragEngine.onResizeStart(resizeStart);
this.dragEngine.onResizeEnd(resizeEnd);
}
willBind() {
if (this.willUnbind) {
this.willUnbind();
}
if (!this.outlineRight && !this.outlineLeft) {
return;
}
const unBind: any[] = [];
unBind.push(
this.dragEngine.from(this.outlineRight, 'e', () => {
// if (!this.hoveringLine.hasOutline()) {
// return null;
// }
// return this.hoveringLine.getCurrentNode();
return this.props.observed.node;
}),
);
unBind.push(
this.dragEngine.from(this.outlineLeft, 'w', () => {
return this.props.observed.node;
// if (!this.hoveringLine.hasOutline()) {
// return null;
// }
// return this.hoveringLine.getCurrentNode();
}),
);
this.willUnbind = () => {
if (unBind && unBind.length > 0) {
unBind.forEach((item) => {
item();
});
}
this.willUnbind = () => {};
};
}
render() {
const { observed } = this.props;
if (!observed.hasOffset) {
return null;
}
const { node, offsetWidth, offsetHeight, offsetTop, offsetLeft } = observed;
let triggerVisible: any = [];
const metaData = node.componentMeta.getMetadata();
if (metaData && metaData.experimental && metaData.experimental.getResizingHandlers) {
triggerVisible = metaData.experimental.getResizingHandlers(node);
}
const className = classNames('lc-borders lc-resize-box');
return (
<div>
{triggerVisible.includes('w') && (
<div
ref={(ref) => {
this.outlineLeft = ref;
}}
className={className}
style={{
height: offsetHeight,
transform: `translate(${offsetLeft - 10}px, ${offsetTop}px)`,
width: 20,
}}
/>
)}
{triggerVisible.includes('e') && (
<div
className={className}
ref={(ref) => {
this.outlineRight = ref;
}}
style={{
height: offsetHeight,
transform: `translate(${offsetLeft + offsetWidth - 10}px, ${offsetTop}px)`,
width: 20,
}}
/>
)}
</div>
);
}
}

View File

@ -9,19 +9,13 @@ import {
ComponentType, ComponentType,
} from 'react'; } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { import { observer, computed, Tip, globalContext, Editor } from '@ali/lowcode-editor-core';
observer, import { createIcon, isReactComponent } from '@ali/lowcode-utils';
computed, import { ActionContentObject, isActionContentObject } from '@ali/lowcode-types';
createIcon,
EmbedTip,
isReactComponent,
ActionContentObject,
isActionContentObject,
} from '@ali/lowcode-globals';
import { SimulatorContext } from '../context';
import { BuiltinSimulatorHost } from '../host'; import { BuiltinSimulatorHost } from '../host';
import { OffsetObserver } from '../../../designer'; import { OffsetObserver } from '../../designer';
import { Node } from '../../../document'; import { Node } from '../../document';
import NodeSelector from '../node-selector';
@observer @observer
export class BorderSelectingInstance extends Component<{ export class BorderSelectingInstance extends Component<{
@ -75,7 +69,6 @@ class Toolbar extends Component<{ observed: OffsetObserver }> {
let style: any; let style: any;
if (observed.top > SPACE_HEIGHT) { if (observed.top > SPACE_HEIGHT) {
style = { style = {
right: Math.max(-BORDER, observed.right - width - BORDER),
top: -SPACE_HEIGHT, top: -SPACE_HEIGHT,
height: BAR_HEIGHT, height: BAR_HEIGHT,
}; };
@ -83,30 +76,34 @@ class Toolbar extends Component<{ observed: OffsetObserver }> {
style = { style = {
bottom: -SPACE_HEIGHT, bottom: -SPACE_HEIGHT,
height: BAR_HEIGHT, height: BAR_HEIGHT,
right: Math.max(-BORDER, observed.right - width - BORDER),
}; };
} else { } else {
style = { style = {
height: BAR_HEIGHT, height: BAR_HEIGHT,
top: Math.max(MARGIN, MARGIN - observed.top), top: Math.max(MARGIN, MARGIN - observed.top),
right: Math.max(MARGIN, MARGIN + observed.right - width),
}; };
} }
if (observed.width < 140) {
style.left = Math.max(-BORDER, observed.left - width - BORDER);
} else {
style.right = Math.max(-BORDER, observed.right - width - BORDER);
}
const { node } = observed; const { node } = observed;
const actions: ReactNodeArray = []; const actions: ReactNodeArray = [];
node.componentMeta.availableActions.forEach(action => { node.componentMeta.availableActions.forEach((action) => {
const { important, condition, content, name } = action; const { important, condition, content, name } = action;
if (node.isSlotRoot && (name === 'copy' || name === 'remove')) { if (node.isSlot() && (name === 'copy' || name === 'remove')) {
// FIXME: need this? // FIXME: need this?
return; return;
} }
if (important && (typeof condition === 'function' ? condition(node) : condition !== false)) { if (important && (typeof condition === 'function' ? condition(node) !== false : condition !== false)) {
actions.push(createAction(content, name, node)); actions.push(createAction(content, name, node));
} }
}); });
return ( return (
<div className="lc-borders-actions" style={style}> <div className="lc-borders-actions" style={style}>
{actions} {actions}
<NodeSelector node={node} />
</div> </div>
); );
} }
@ -120,17 +117,27 @@ function createAction(content: ReactNode | ComponentType<any> | ActionContentObj
return createElement(content, { key, node }); return createElement(content, { key, node });
} }
if (isActionContentObject(content)) { if (isActionContentObject(content)) {
const { action, description, icon } = content; const { action, title, icon } = content;
return ( return (
<div <div
key={key} key={key}
className="lc-borders-action" className="lc-borders-action"
onClick={() => { onClick={() => {
action && action(node); action && action(node);
const editor = globalContext.get(Editor);
const npm = node?.componentMeta?.npm;
const target =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.emit('designer.border.action', {
name: key,
target,
});
}} }}
> >
{icon && createIcon(icon)} {icon && createIcon(icon)}
<EmbedTip>{description}</EmbedTip> <Tip>{title}</Tip>
</div> </div>
); );
} }
@ -138,11 +145,9 @@ function createAction(content: ReactNode | ComponentType<any> | ActionContentObj
} }
@observer @observer
export class BorderSelectingForNode extends Component<{ node: Node }> { export class BorderSelectingForNode extends Component<{ host: BuiltinSimulatorHost; node: Node }> {
static contextType = SimulatorContext;
get host(): BuiltinSimulatorHost { get host(): BuiltinSimulatorHost {
return this.context; return this.props.host;
} }
get dragging(): boolean { get dragging(): boolean {
@ -167,7 +172,7 @@ export class BorderSelectingForNode extends Component<{ node: Node }> {
} }
return ( return (
<Fragment key={node.id}> <Fragment key={node.id}>
{instances.map(instance => { {instances.map((instance) => {
const observed = designer.createOffsetObserver({ const observed = designer.createOffsetObserver({
node, node,
instance, instance,
@ -183,11 +188,9 @@ export class BorderSelectingForNode extends Component<{ node: Node }> {
} }
@observer @observer
export class BorderSelecting extends Component { export class BorderSelecting extends Component<{ host: BuiltinSimulatorHost }> {
static contextType = SimulatorContext;
get host(): BuiltinSimulatorHost { get host(): BuiltinSimulatorHost {
return this.context; return this.props.host;
} }
get dragging(): boolean { get dragging(): boolean {
@ -196,7 +199,7 @@ export class BorderSelecting extends Component {
@computed get selecting() { @computed get selecting() {
const doc = this.host.document; const doc = this.host.document;
if (doc.suspensed) { if (doc.suspensed || this.host.liveEditing.editing) {
return null; return null;
} }
const selection = doc.selection; const selection = doc.selection;
@ -210,14 +213,13 @@ export class BorderSelecting extends Component {
render() { render() {
const selecting = this.selecting; const selecting = this.selecting;
if (!selecting || selecting.length < 1) { if (!selecting || selecting.length < 1) {
// DIRTY FIX, recore has a bug! return null;
return <Fragment />;
} }
return ( return (
<Fragment> <Fragment>
{selecting.map(node => ( {selecting.map((node) => (
<BorderSelectingForNode key={node.id} node={node} /> <BorderSelectingForNode key={node.id} host={this.props.host} node={node} />
))} ))}
</Fragment> </Fragment>
); );

View File

@ -25,9 +25,13 @@
align-items: stretch; align-items: stretch;
justify-content: flex-end; justify-content: flex-end;
pointer-events: all; pointer-events: all;
> * {
flex-shrink: 0;
}
} }
&-action { &-action,
.ve-icon-button.ve-action-save {
box-sizing: border-box; box-sizing: border-box;
cursor: pointer; cursor: pointer;
height: 20px; height: 20px;
@ -45,7 +49,29 @@
} }
} }
&&-hovering { &.lc-resize-box {
border-width: 0;
z-index: 1;
cursor: ew-resize;
pointer-events: auto;
align-items: center;
justify-content: center;
display: flex;
&:after {
content: "";
display: block;
height: calc(100% - 20px);
min-height: 50%;
width: 4px;
background: #738397;
border-radius: 2px;
// animation: flashing 1.5s infinite linear;
}
}
// &&-hovering {
&&-detecting {
z-index: 1; z-index: 1;
border-style: dashed; border-style: dashed;
background: rgba(0,121,242,.04); background: rgba(0,121,242,.04);

View File

@ -0,0 +1,154 @@
import { EventEmitter } from 'events';
import { ISimulatorHost, isSimulatorHost } from '../../simulator';
import { Designer, Point } from '../../designer';
import { setNativeSelection, cursor } from '@ali/lowcode-utils';
// import Cursor from './cursor';
// import Pages from './pages';
function makeEventsHandler(
boostEvent: MouseEvent | DragEvent,
sensors: ISimulatorHost[],
): (fn: (sdoc: Document) => void) => void {
const topDoc = window.top.document;
const sourceDoc = boostEvent.view?.document || topDoc;
// TODO: optimize this logic, reduce listener
// const boostPrevented = boostEvent.defaultPrevented;
const docs = new Set<Document>();
// if (boostPrevented || isDragEvent(boostEvent)) {
docs.add(topDoc);
// }
docs.add(sourceDoc);
// if (sourceDoc !== topDoc || isDragEvent(boostEvent)) {
sensors.forEach((sim) => {
const sdoc = sim.contentDocument;
if (sdoc) {
docs.add(sdoc);
}
});
// }
return (handle: (sdoc: Document) => void) => {
docs.forEach((doc) => handle(doc));
};
}
// 拖动缩放
export default class DragResizeEngine {
private emitter: EventEmitter;
private dragResizing = false;
constructor(readonly designer: Designer) {
this.designer = designer;
this.emitter = new EventEmitter();
}
private getMasterSensors(): ISimulatorHost[] {
return this.designer.project.documents
.map((doc) => {
// TODO: not use actived,
if (doc.actived && doc.simulator?.sensorAvailable) {
return doc.simulator;
}
return null;
})
.filter(Boolean) as any;
}
isDragResizing() {
return this.dragResizing;
}
/**
* drag reszie from
* @param shell
* @param direction n/s/e/w
* @param boost (e: MouseEvent) => VE.Node
*/
from(shell: Element, direction: string, boost: (e: MouseEvent) => any) {
let node: any;
let startEvent: Point;
if (!shell) {
return () => {};
}
const move = (e: MouseEvent) => {
const x = createResizeEvent(e);
const moveX = x.clientX - startEvent.clientX;
const moveY = x.clientY - startEvent.clientY;
this.emitter.emit('resize', e, direction, node, moveX, moveY);
};
const masterSensors = this.getMasterSensors();
const createResizeEvent = (e: MouseEvent | DragEvent): Point => {
const evt: any = {};
const sourceDocument = e.view?.document;
if (!sourceDocument || sourceDocument === document) {
return e;
}
const srcSim = masterSensors.find((sim) => sim.contentDocument === sourceDocument);
if (srcSim) {
return srcSim.viewport.toGlobalPoint(e);
}
return e;
};
const over = (e: MouseEvent) => {
const handleEvents = makeEventsHandler(e, masterSensors);
handleEvents((doc) => {
doc.removeEventListener('mousemove', move, true);
doc.removeEventListener('mouseup', over, true);
});
this.dragResizing = false;
cursor.release();
this.emitter.emit('resizeEnd', e, direction, node);
};
const mousedown = (e: MouseEvent) => {
node = boost(e);
startEvent = createResizeEvent(e);
const handleEvents = makeEventsHandler(e, masterSensors);
handleEvents((doc) => {
doc.addEventListener('mousemove', move, true);
doc.addEventListener('mouseup', over, true);
});
this.emitter.emit('resizestart', e, direction, node);
this.dragResizing = true;
cursor.addState('ew-resize');
};
shell.addEventListener('mousedown', mousedown);
return () => {
shell.removeEventListener('mousedown', mousedown);
};
}
onResizeStart(func: (e: MouseEvent, direction: string, node: any) => any) {
this.emitter.on('resizestart', func);
return () => {
this.emitter.removeListener('resizestart', func);
};
}
onResize(func: (e: MouseEvent, direction: string, node: any, moveX: number, moveY: number) => any) {
this.emitter.on('resize', func);
return () => {
this.emitter.removeListener('resize', func);
};
}
onResizeEnd(func: (e: MouseEvent, direction: string, node: any) => any) {
this.emitter.on('resizeEnd', func);
return () => {
this.emitter.removeListener('resizeEnd', func);
};
}
}
// new DragResizeEngine();

View File

@ -1,29 +1,28 @@
import { Component } from 'react'; import { Component } from 'react';
import { observer } from '@ali/lowcode-globals'; import { observer } from '@ali/lowcode-editor-core';
import { BorderHovering } from './border-hovering'; import { BorderDetecting } from './border-detecting';
import { SimulatorContext } from '../context';
import { BuiltinSimulatorHost } from '../host'; import { BuiltinSimulatorHost } from '../host';
import { BorderSelecting } from './border-selecting'; import { BorderSelecting } from './border-selecting';
import BorderResizing from './border-resizing';
import { InsertionView } from './insertion'; import { InsertionView } from './insertion';
import './bem-tools.less'; import './bem-tools.less';
import './borders.less'; import './borders.less';
@observer @observer
export class BemTools extends Component { export class BemTools extends Component<{ host: BuiltinSimulatorHost }> {
static contextType = SimulatorContext;
shouldComponentUpdate() { shouldComponentUpdate() {
return false; return false;
} }
render() { render() {
const host = this.context as BuiltinSimulatorHost; const host = this.props.host;
const { scrollX, scrollY, scale } = host.viewport; const { scrollX, scrollY, scale } = host.viewport;
return ( return (
<div className="lc-bem-tools" style={{ transform: `translate(${-scrollX * scale}px,${-scrollY * scale}px)` }}> <div className="lc-bem-tools" style={{ transform: `translate(${-scrollX * scale}px,${-scrollY * scale}px)` }}>
<BorderHovering key="hovering" /> <BorderDetecting key="hovering" host={host} />
<BorderSelecting key="selecting" /> <BorderSelecting key="selecting" host={host} />
<InsertionView key="insertion" /> <InsertionView key="insertion" host={host} />
<BorderResizing key="resizing" host={host} />
</div> </div>
); );
} }

View File

@ -1,5 +1,5 @@
import { Component } from 'react'; import { Component } from 'react';
import { computed, observer } from '@ali/lowcode-globals'; import { computed, observer } from '@ali/lowcode-editor-core';
import { SimulatorContext } from '../context'; import { SimulatorContext } from '../context';
import { BuiltinSimulatorHost } from '../host'; import { BuiltinSimulatorHost } from '../host';
import { import {
@ -10,7 +10,7 @@ import {
isVertical isVertical
} from '../../designer'; } from '../../designer';
import { ISimulatorHost, } from '../../simulator'; import { ISimulatorHost, } from '../../simulator';
import {NodeParent } from '../../document'; import { ParentalNode } from '../../document';
import './insertion.less'; import './insertion.less';
interface InsertionData { interface InsertionData {
@ -24,7 +24,7 @@ interface InsertionData {
/** /**
* (INode) * (INode)
*/ */
function processChildrenDetail(sim: ISimulatorHost, container: NodeParent, detail: LocationChildrenDetail): InsertionData { function processChildrenDetail(sim: ISimulatorHost, container: ParentalNode, detail: LocationChildrenDetail): InsertionData {
let edge = detail.edge || null; let edge = detail.edge || null;
if (!edge) { if (!edge) {
@ -106,30 +106,25 @@ function processDetail({ target, detail, document }: DropLocation): InsertionDat
if (!instances) { if (!instances) {
return {}; return {};
} }
const edge = sim.computeComponentInstanceRect(instances[0], target.componentMeta.rectSelector); const edge = sim.computeComponentInstanceRect(instances[0], target.componentMeta.rootSelector);
return edge ? { edge, insertType: 'cover', coverRect: edge } : {}; return edge ? { edge, insertType: 'cover', coverRect: edge } : {};
} }
} }
@observer @observer
export class InsertionView extends Component { export class InsertionView extends Component<{ host: BuiltinSimulatorHost }> {
static contextType = SimulatorContext;
@computed get host(): BuiltinSimulatorHost {
return this.context;
}
shouldComponentUpdate() { shouldComponentUpdate() {
return false; return false;
} }
render() { render() {
const loc = this.host.document.dropLocation; const { host } = this.props;
const loc = host.document.dropLocation;
if (!loc) { if (!loc) {
return null; return null;
} }
const { scale, scrollX, scrollY } = this.host.viewport; const { scale, scrollX, scrollY } = host.viewport;
const { edge, insertType, coverRect, nearRect, vertical } = processDetail(loc); const { edge, insertType, coverRect, nearRect, vertical } = processDetail(loc);
if (!edge) { if (!edge) {

View File

@ -1,7 +1,7 @@
// NOTE: 仅用作类型标注,切勿作为实体使用 // NOTE: 仅用作类型标注,切勿作为实体使用
import { BuiltinSimulatorHost } from './host'; import { BuiltinSimulatorHost } from './host';
import { AssetLevel, AssetLevels, AssetList, isAssetBundle, isAssetItem, AssetType, assetItem } from '@ali/lowcode-globals'; import { AssetLevel, AssetLevels, AssetList, isAssetBundle, isAssetItem, AssetType, assetItem } from '@ali/lowcode-utils';
import { isCSSUrl } from '@ali/lowcode-globals'; import { isCSSUrl } from '@ali/lowcode-utils';
import { BuiltinSimulatorRenderer } from './renderer'; import { BuiltinSimulatorRenderer } from './renderer';
export function createSimulator( export function createSimulator(

View File

@ -1,5 +1,5 @@
import { Component } from 'react'; import { Component } from 'react';
import { observer } from '@ali/lowcode-globals'; import { observer } from '@ali/lowcode-editor-core';
import { BuiltinSimulatorHost, BuiltinSimulatorProps } from './host'; import { BuiltinSimulatorHost, BuiltinSimulatorProps } from './host';
import { DocumentModel } from '../document'; import { DocumentModel } from '../document';
import { SimulatorContext } from './context'; import { SimulatorContext } from './context';
@ -38,23 +38,19 @@ export class BuiltinSimulatorHostView extends Component<SimulatorHostProps> {
} }
} }
render() { render() {
const { Provider } = SimulatorContext;
return ( return (
<div className="lc-simulator"> <div className="lc-simulator">
<Provider value={this.host}>
{/*progressing.visible ? <PreLoaderView /> : null*/} {/*progressing.visible ? <PreLoaderView /> : null*/}
<Canvas /> <Canvas host={this.host} />
</Provider>
</div> </div>
); );
} }
} }
@observer @observer
class Canvas extends Component { class Canvas extends Component<{ host: BuiltinSimulatorHost }> {
static contextType = SimulatorContext;
render() { render() {
const sim = this.context as BuiltinSimulatorHost; const sim = this.props.host;
let className = 'lc-simulator-canvas'; let className = 'lc-simulator-canvas';
if (sim.deviceClassName) { if (sim.deviceClassName) {
className += ` ${sim.deviceClassName}`; className += ` ${sim.deviceClassName}`;
@ -65,8 +61,8 @@ class Canvas extends Component {
return ( return (
<div className={className}> <div className={className}>
<div ref={elmt => sim.mountViewport(elmt)} className="lc-simulator-canvas-viewport"> <div ref={elmt => sim.mountViewport(elmt)} className="lc-simulator-canvas-viewport">
<BemTools /> <BemTools host={sim} />
<Content /> <Content host={sim} />
</div> </div>
</div> </div>
); );
@ -74,10 +70,9 @@ class Canvas extends Component {
} }
@observer @observer
class Content extends Component { class Content extends Component<{ host: BuiltinSimulatorHost }> {
static contextType = SimulatorContext;
render() { render() {
const sim = this.context as BuiltinSimulatorHost; const sim = this.props.host;
const viewport = sim.viewport; const viewport = sim.viewport;
let frameStyle = {}; let frameStyle = {};
if (viewport.scale < 1) { if (viewport.scale < 1) {

View File

@ -31,12 +31,13 @@
&-device-iphone6 { &-device-iphone6 {
left: 50%; left: 50%;
width: 368px; width: 378px;
transform: translateX(-50%); transform: translateX(-50%);
background: url(https://img.alicdn.com/tps/TB12GetLpXXXXXhXFXXXXXXXXXX-756-1544.png) no-repeat top; background: url(https://img.alicdn.com/tps/TB12GetLpXXXXXhXFXXXXXXXXXX-756-1544.png) no-repeat top;
background-size: 378px 772px; background-size: 378px 772px;
top: 8px; top: 8px;
.@{scope}-canvas-viewport { .@{scope}-canvas-viewport {
width: auto;
top: 114px; top: 114px;
left: 25px; left: 25px;
right: 25px; right: 25px;
@ -46,12 +47,12 @@
} }
&-device-default { &-device-default {
top: 15px; top: 16px;
right: 15px; right: 16px;
bottom: 15px; bottom: 16px;
left: 15px; left: 16px;
width: auto; width: auto;
box-shadow: 0 2px 10px 0 rgba(31,56,88,.15); box-shadow: 0 1px 4px 0 rgba(31, 50, 88, 0.125);
} }
&-content { &-content {

View File

@ -1,10 +1,10 @@
import { obx, autorun, computed } from '@ali/lowcode-globals'; import { obx, autorun, computed, getPublicPath, hotkey, focusTracker } from '@ali/lowcode-editor-core';
import { ISimulatorHost, Component, NodeInstance, ComponentInstance } from '../simulator'; import { ISimulatorHost, Component, NodeInstance, ComponentInstance } from '../simulator';
import Viewport from './viewport'; import Viewport from './viewport';
import { createSimulator } from './create-simulator'; import { createSimulator } from './create-simulator';
import { Node, NodeParent, DocumentModel, isNodeParent, isNode, contains, isRootNode } from '../document'; import { Node, ParentalNode, DocumentModel, isNode, contains, isRootNode } from '../document';
import ResourceConsumer from './resource-consumer'; import ResourceConsumer from './resource-consumer';
import { AssetLevel, Asset, AssetList, assetBundle, assetItem, AssetType, getPublicPath } from '@ali/lowcode-globals'; import { AssetLevel, Asset, AssetList, assetBundle, assetItem, AssetType, isElement, isFormEvent } from '@ali/lowcode-utils';
import { import {
DragObjectType, DragObjectType,
isShaken, isShaken,
@ -20,29 +20,29 @@ import {
getRectTarget, getRectTarget,
Rect, Rect,
CanvasPoint, CanvasPoint,
hotkey,
} from '../designer'; } from '../designer';
import { parseProps } from './utils/parse-props'; import { parseMetadata } from './utils/parse-metadata';
import { isElement } from '@ali/lowcode-globals'; import { ComponentMetadata, ComponentSchema } from '@ali/lowcode-types';
import { ComponentMetadata } from '@ali/lowcode-globals';
import { BuiltinSimulatorRenderer } from './renderer'; import { BuiltinSimulatorRenderer } from './renderer';
import clipboard from '../designer/clipboard'; import clipboard from '../designer/clipboard';
import { LiveEditing } from './live-editing/live-editing';
export interface LibraryItem { export interface LibraryItem {
package: string; package: string;
library: string; library: string;
urls: Asset; urls?: Asset;
} }
export interface BuiltinSimulatorProps { export interface BuiltinSimulatorProps {
// 从 documentModel 上获取 // 从 documentModel 上获取
// suspended?: boolean; // suspended?: boolean;
designMode?: 'live' | 'design' | 'mock' | 'extend' | 'border' | 'preview'; designMode?: 'live' | 'design' | 'preview' | 'extend' | 'border';
device?: 'mobile' | 'iphone' | string; device?: 'mobile' | 'iphone' | string;
deviceClassName?: string; deviceClassName?: string;
simulatorUrl?: Asset;
environment?: Asset; environment?: Asset;
extraEnvironment?: Asset;
library?: LibraryItem[]; library?: LibraryItem[];
simulatorUrl?: Asset;
theme?: Asset; theme?: Asset;
componentsAsset?: Asset; componentsAsset?: Asset;
[key: string]: any; [key: string]: any;
@ -55,7 +55,7 @@ const defaultSimulatorUrl = (() => {
if (dev) { if (dev) {
urls = [`${prefix}/css/react-simulator-renderer.css`, `${prefix}/js/react-simulator-renderer.js`]; urls = [`${prefix}/css/react-simulator-renderer.css`, `${prefix}/js/react-simulator-renderer.js`];
} else if (process.env.NODE_ENV === 'production') { } else if (process.env.NODE_ENV === 'production') {
urls = [`${prefix}/react-simulator-renderer.min.css`, `${prefix}/react-simulator-renderer.min.js`]; urls = [`${prefix}/react-simulator-renderer.css`, `${prefix}/react-simulator-renderer.js`];
} else { } else {
urls = [`${prefix}/react-simulator-renderer.css`, `${prefix}/react-simulator-renderer.js`]; urls = [`${prefix}/react-simulator-renderer.css`, `${prefix}/react-simulator-renderer.js`];
} }
@ -78,9 +78,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
readonly designer = this.document.designer; readonly designer = this.document.designer;
@computed get device(): string | undefined { @computed get device(): string {
// 根据 device 不同来做画布外框样式变化 渲染时可选择不同组件
// renderer 依赖
return this.get('device') || 'default'; return this.get('device') || 'default';
} }
@ -88,7 +86,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
return this.get('deviceClassName'); return this.get('deviceClassName');
} }
@computed get designMode(): 'live' | 'design' | 'extend' | 'border' | 'preview' { @computed get designMode(): 'live' | 'design' | 'preview' {
// renderer 依赖 // renderer 依赖
// TODO: 需要根据 design mode 不同切换鼠标响应情况 // TODO: 需要根据 design mode 不同切换鼠标响应情况
return this.get('designMode') || 'design'; return this.get('designMode') || 'design';
@ -180,7 +178,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
if (library) { if (library) {
library.forEach((item) => { library.forEach((item) => {
this.libraryMap[item.package] = item.library; this.libraryMap[item.package] = item.library;
if (item.urls) {
libraryAsset.push(item.urls); libraryAsset.push(item.urls);
}
}); });
} }
@ -188,6 +188,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
// required & use once // required & use once
assetBundle(this.get('environment') || defaultEnvironment, AssetLevel.Environment), assetBundle(this.get('environment') || defaultEnvironment, AssetLevel.Environment),
// required & use once // required & use once
assetBundle(this.get('extraEnvironment'), AssetLevel.Environment),
// required & use once
assetBundle(libraryAsset, AssetLevel.Library), assetBundle(libraryAsset, AssetLevel.Library),
// required & TODO: think of update // required & TODO: think of update
assetBundle(this.theme, AssetLevel.Theme), assetBundle(this.theme, AssetLevel.Theme),
@ -216,6 +218,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
// bind hotkey & clipboard // bind hotkey & clipboard
hotkey.mount(this._contentWindow); hotkey.mount(this._contentWindow);
focusTracker.mount(this._contentWindow);
clipboard.injectCopyPaster(this._contentDocument); clipboard.injectCopyPaster(this._contentDocument);
// TODO: dispose the bindings // TODO: dispose the bindings
} }
@ -225,7 +228,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
// just listen special callback // just listen special callback
// because iframe maybe reload // because iframe maybe reload
this.setupDragAndClick(); this.setupDragAndClick();
this.setupHovering(); this.setupDetecting();
this.setupLiveEditing();
this.setupContextMenu();
} }
setupDragAndClick() { setupDragAndClick() {
@ -239,10 +244,18 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
doc.addEventListener( doc.addEventListener(
'mousedown', 'mousedown',
(downEvent: MouseEvent) => { (downEvent: MouseEvent) => {
// fix for popups close logic
document.dispatchEvent(new Event('mousedown'));
if (this.liveEditing.editing) {
return;
}
// stop response document focus event // stop response document focus event
downEvent.stopPropagation(); downEvent.stopPropagation();
downEvent.preventDefault(); downEvent.preventDefault();
// FIXME: dirty fix remove label-for fro liveEditing
(downEvent.target as HTMLElement).removeAttribute('for');
const nodeInst = this.getNodeInstanceFromElement(downEvent.target as Element); const nodeInst = this.getNodeInstanceFromElement(downEvent.target as Element);
const node = nodeInst?.node || this.document.rootNode; const node = nodeInst?.node || this.document.rootNode;
const isMulti = downEvent.metaKey || downEvent.ctrlKey; const isMulti = downEvent.metaKey || downEvent.ctrlKey;
@ -251,11 +264,20 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
doc.removeEventListener('mouseup', checkSelect, true); doc.removeEventListener('mouseup', checkSelect, true);
if (!isShaken(downEvent, e)) { if (!isShaken(downEvent, e)) {
const id = node.id; const id = node.id;
designer.activeTracker.track(node); designer.activeTracker.track({ node, instance: nodeInst?.instance });
if (isMulti && !isRootNode(node) && selection.has(id)) { if (isMulti && !isRootNode(node) && selection.has(id)) {
selection.remove(id); selection.remove(id);
} else { } else {
selection.select(id); selection.select(id);
const editor = this.designer?.editor;
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.emit('designer.builtinSimulator.select', {
selected,
});
} }
} }
}; };
@ -266,7 +288,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
if (isMulti) { if (isMulti) {
// multi select mode, directily add // multi select mode, directily add
if (!selection.has(node.id)) { if (!selection.has(node.id)) {
designer.activeTracker.track(node); designer.activeTracker.track({ node, instance: nodeInst?.instance });
selection.add(node.id); selection.add(node.id);
ignoreUpSelected = true; ignoreUpSelected = true;
} }
@ -299,43 +321,38 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
doc.addEventListener( doc.addEventListener(
'click', 'click',
(e) => { (e) => {
// fix for popups close logic
const x = new Event('click');
x.initEvent('click', true);
this._iframe?.dispatchEvent(x);
const target = e.target as HTMLElement;
if (isFormEvent(e) || target?.closest('.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')) {
e.preventDefault();
e.stopPropagation();
}
// stop response document click event // stop response document click event
e.preventDefault();
e.stopPropagation();
// todo: catch link redirect // todo: catch link redirect
}, },
true, true,
); );
// cause edit
doc.addEventListener(
'dblclick',
(e: MouseEvent) => {
// stop response document dblclick event
e.stopPropagation();
e.preventDefault();
// todo: quick editing
},
true,
);
} }
private disableHovering?: () => void; private disableHovering?: () => void;
/** /**
* *
*/ */
setupHovering() { setupDetecting() {
const doc = this.contentDocument!; const doc = this.contentDocument!;
const hovering = this.document.designer.hovering; const detecting = this.document.designer.detecting;
const hover = (e: MouseEvent) => { const hover = (e: MouseEvent) => {
if (!hovering.enable) { if (!detecting.enable) {
return; return;
} }
const nodeInst = this.getNodeInstanceFromElement(e.target as Element); const nodeInst = this.getNodeInstanceFromElement(e.target as Element);
hovering.hover(nodeInst?.node || null); detecting.capture(nodeInst?.node || null);
e.stopPropagation(); e.stopPropagation();
}; };
const leave = () => hovering.leave(this.document); const leave = () => detecting.leave(this.document);
doc.addEventListener('mouseover', hover, true); doc.addEventListener('mouseover', hover, true);
doc.addEventListener('mouseleave', leave, false); doc.addEventListener('mouseleave', leave, false);
@ -350,13 +367,47 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
); );
this.disableHovering = () => { this.disableHovering = () => {
hovering.leave(this.document); detecting.leave(this.document);
doc.removeEventListener('mouseover', hover, true); doc.removeEventListener('mouseover', hover, true);
doc.removeEventListener('mouseleave', leave, false); doc.removeEventListener('mouseleave', leave, false);
this.disableHovering = undefined; this.disableHovering = undefined;
}; };
} }
readonly liveEditing = new LiveEditing();
setupLiveEditing() {
const doc = this.contentDocument!;
// cause edit
doc.addEventListener(
'dblclick',
(e: MouseEvent) => {
// stop response document dblclick event
e.stopPropagation();
e.preventDefault();
const targetElement = e.target as HTMLElement;
const nodeInst = this.getNodeInstanceFromElement(targetElement);
if (!nodeInst) {
return;
}
const node = nodeInst.node || this.document.rootNode;
const rootElement = this.findDOMNodes(nodeInst.instance, node.componentMeta.rootSelector)?.find(item => item.contains(targetElement)) as HTMLElement;
if (!rootElement) {
return;
}
this.liveEditing.apply({
node,
rootElement,
event: e,
});
},
true,
);
}
/** /**
* @see ISimulator * @see ISimulator
*/ */
@ -369,11 +420,35 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
} else { } else {
// weekup some autorun reaction // weekup some autorun reaction
if (!this.disableHovering) { if (!this.disableHovering) {
this.setupHovering(); this.setupDetecting();
} }
} }
} }
setupContextMenu() {
const doc = this.contentDocument!;
doc.addEventListener('contextmenu', (e: MouseEvent) => {
const targetElement = e.target as HTMLElement;
const nodeInst = this.getNodeInstanceFromElement(targetElement);
if (!nodeInst) {
return;
}
const node = nodeInst.node || this.document.rootNode;
if (!node) {
return;
}
const editor = this.designer?.editor;
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.emit('desiger.builtinSimulator.contextmenu', {
selected,
});
});
}
/** /**
* @see ISimulator * @see ISimulator
*/ */
@ -388,16 +463,19 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
const component = this.getComponent(componentName); const component = this.getComponent(componentName);
if (component) { if (!component) {
parseProps(component as any); return {
componentName,
};
} }
// TODO: // TODO:
// 1. generate builtin div/p/h1/h2 // 1. generate builtin div/p/h1/h2
// 2. read propTypes // 2. read propTypes
return { return {
componentName, componentName,
props: parseProps(this.getComponent(componentName)), ...parseMetadata(component),
}; };
} }
@ -408,6 +486,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
return this.renderer?.getComponent(componentName) || null; return this.renderer?.getComponent(componentName) || null;
} }
createComponent(schema: ComponentSchema): Component | null {
return this.renderer?.createComponent(schema) || null;
}
@obx.val private instancesMap = new Map<string, ComponentInstance[]>(); @obx.val private instancesMap = new Map<string, ComponentInstance[]>();
setInstance(id: string, instances: ComponentInstance[] | null) { setInstance(id: string, instances: ComponentInstance[] | null) {
if (instances == null) { if (instances == null) {
@ -453,7 +535,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
if (!instances) { if (!instances) {
return null; return null;
} }
return this.computeComponentInstanceRect(instances[0], node.componentMeta.rectSelector); return this.computeComponentInstanceRect(instances[0], node.componentMeta.rootSelector);
} }
/** /**
@ -461,19 +543,12 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
*/ */
computeComponentInstanceRect(instance: ComponentInstance, selector?: string): Rect | null { computeComponentInstanceRect(instance: ComponentInstance, selector?: string): Rect | null {
const renderer = this.renderer!; const renderer = this.renderer!;
const elements = renderer.findDOMNodes(instance); const elements = this.findDOMNodes(instance, selector);
if (!elements) { if (!elements) {
return null; return null;
} }
let elems = elements.slice(); const elems = elements.slice();
if (selector) {
const matched = getMatched(elems, selector);
if (!matched) {
return null;
}
elems = [matched];
}
let rects: DOMRect[] | undefined; let rects: DOMRect[] | undefined;
let last: { x: number; y: number; r: number; b: number } | undefined; let last: { x: number; y: number; r: number; b: number } | undefined;
let computed = false; let computed = false;
@ -532,8 +607,20 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/** /**
* @see ISimulator * @see ISimulator
*/ */
findDOMNodes(instance: ComponentInstance): Array<Element | Text> | null { findDOMNodes(instance: ComponentInstance, selector?: string): Array<Element | Text> | null {
return this._renderer?.findDOMNodes(instance) || null; const elements = this._renderer?.findDOMNodes(instance);
if (!elements) {
return null;
}
if (selector) {
const matched = getMatched(elements, selector);
if (!matched) {
return null;
}
return [matched];
}
return elements;
} }
/** /**
@ -705,7 +792,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
this.sensing = true; this.sensing = true;
this.scroller.scrolling(e); this.scroller.scrolling(e);
const dropContainer = this.getDropContainer(e); const dropContainer = this.getDropContainer(e);
if (!dropContainer) { if (!dropContainer ||
// too dirty
(typeof dropContainer.container?.componentMeta?.prototype?.options?.canDropIn === 'function' &&
!dropContainer.container?.componentMeta?.prototype?.options?.canDropIn(e.dragObject.nodes[0]))) {
return null; return null;
} }
@ -715,7 +805,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
const { container, instance: containerInstance } = dropContainer; const { container, instance: containerInstance } = dropContainer;
const edge = this.computeComponentInstanceRect(containerInstance, container.componentMeta.rectSelector); const edge = this.computeComponentInstanceRect(containerInstance, container.componentMeta.rootSelector);
if (!edge) { if (!edge) {
return null; return null;
@ -736,6 +826,15 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
event: e, event: e,
}; };
if (e.dragObject.nodes[0].getPrototype().isModal()) {
return this.designer.createLocation({
target: this.document.rootNode,
detail,
source: 'simulator' + this.document.id,
event: e,
});
}
if (!children || children.size < 1 || !edge) { if (!children || children.size < 1 || !edge) {
return this.designer.createLocation(locationData); return this.designer.createLocation(locationData);
} }
@ -756,7 +855,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
? instances.find((inst) => this.getClosestNodeInstance(inst, container.id)?.instance === containerInstance) ? instances.find((inst) => this.getClosestNodeInstance(inst, container.id)?.instance === containerInstance)
: instances[0] : instances[0]
: null; : null;
const rect = inst ? this.computeComponentInstanceRect(inst, node.componentMeta.rectSelector) : null; const rect = inst ? this.computeComponentInstanceRect(inst, node.componentMeta.rootSelector) : null;
if (!rect) { if (!rect) {
continue; continue;
@ -855,7 +954,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
container = currentRoot; container = currentRoot;
} }
if (!isNodeParent(container)) { if (!container.isParental()) {
container = container.parent || currentRoot; container = container.parent || currentRoot;
} }
@ -944,7 +1043,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
return null; return null;
} }
isAcceptable(container: NodeParent): boolean { isAcceptable(container: ParentalNode): boolean {
return false; return false;
/* /*
const meta = container.componentMeta; const meta = container.componentMeta;
@ -965,7 +1064,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
return this.document.checkDropTarget(container, dragObject as any); return this.document.checkDropTarget(container, dragObject as any);
} }
const meta = container.componentMeta; const meta = (container as Node).componentMeta;
// FIXME: get containerInstance for accept logic use // FIXME: get containerInstance for accept logic use
const acceptable: boolean = this.isAcceptable(container); const acceptable: boolean = this.isAcceptable(container);
@ -1007,7 +1106,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/** /**
* *
*/ */
getNearByContainer(container: NodeParent, e: LocateEvent) { getNearByContainer(container: ParentalNode, e: LocateEvent) {
/* /*
const children = container.children; const children = container.children;
if (!children || children.length < 1) { if (!children || children.length < 1) {
@ -1111,6 +1210,6 @@ function getMatched(elements: Array<Element | Text>, selector: string): Element
} }
interface DropContainer { interface DropContainer {
container: NodeParent; container: ParentalNode;
instance: ComponentInstance; instance: ComponentInstance;
} }

View File

@ -1,3 +1,4 @@
export * from './host'; export * from './host';
export * from './host-view'; export * from './host-view';
export * from './renderer'; export * from './renderer';
export * from './live-editing/live-editing';

View File

@ -0,0 +1,220 @@
import { obx, globalContext, Editor } from '@ali/lowcode-editor-core';
import { LiveTextEditingConfig } from '@ali/lowcode-types';
import { Node, Prop } from '../../document';
const EDITOR_KEY = 'data-setter-prop';
function getSetterPropElement(ele: HTMLElement, root: HTMLElement): HTMLElement | null {
const box = ele.closest(`[${EDITOR_KEY}]`);
if (!box || !root.contains(box)) {
return null;
}
return box as HTMLElement;
}
function defaultSaveContent(content: string, prop: Prop) {
prop.setValue(content);
}
export interface EditingTarget {
node: Node;
rootElement: HTMLElement;
event: MouseEvent;
}
const saveHandlers: SaveHandler[] = [];
function addLiveEditingSaveHandler(handler: SaveHandler) {
saveHandlers.push(handler);
}
const specificRules: SpecificRule[] = [];
function addLiveEditingSpecificRule(rule: SpecificRule) {
specificRules.push(rule);
}
export class LiveEditing {
static addLiveEditingSpecificRule = addLiveEditingSpecificRule;
static addLiveEditingSaveHandler = addLiveEditingSaveHandler;
@obx.ref private _editing: Prop | null = null;
apply(target: EditingTarget) {
const { node, event, rootElement } = target;
const targetElement = event.target as HTMLElement;
const liveTextEditing = node.componentMeta.liveTextEditing;
const editor = globalContext.get(Editor);
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') || node?.componentMeta?.componentName || '';
editor?.emit('designer.builinSimulator.LiveEditing', {
selected,
});
let setterPropElement = getSetterPropElement(targetElement, rootElement);
let propTarget = setterPropElement?.dataset.setterProp;
let matched: (LiveTextEditingConfig & { propElement?: HTMLElement; }) | undefined | null;
if (liveTextEditing) {
if (propTarget) {
// 已埋点命中 data-setter-prop="proptarget", 从 liveTextEditing 读取配置mode|onSaveContent
matched = liveTextEditing.find(config => config.propTarget == propTarget);
} else {
// 执行 embedTextEditing selector 规则,获得第一个节点 是否 contains e.target若匹配读取配置
matched = liveTextEditing.find(config => {
if (!config.selector) {
return false;
}
setterPropElement = queryPropElement(rootElement, targetElement, config.selector);
return setterPropElement ? true : false;
});
propTarget = matched?.propTarget;
}
} else {
specificRules.some((rule) => {
matched = rule(target);
return matched ? true : false;
});
if (matched) {
propTarget = matched.propTarget;
setterPropElement = matched.propElement || queryPropElement(rootElement, targetElement, matched.selector);
}
}
if (!propTarget) {
// 自动纯文本编辑满足一下情况:
// 1. children 内容都是 Leaf 且都是文本(一期)
// 2. DOM 节点是单层容器,子集都是文本节点 (已满足)
const isAllText = node.children?.every(item => {
return item.isLeaf() && item.getProp('children')?.type === 'literal';
});
// TODO:
}
if (propTarget && setterPropElement) {
const prop = node.getProp(propTarget, true)!;
if (this._editing === prop) {
return;
}
// 进入编辑
// 1. 设置contentEditable="plaintext|..."
// 2. 添加类名
// 3. focus & cursor locate
// 4. 监听 blur 事件
// 5. 设置编辑锁定disable hover | disable select | disable canvas drag
const onSaveContent = matched?.onSaveContent || saveHandlers.find(item => item.condition(prop))?.onSaveContent || defaultSaveContent;
setterPropElement.setAttribute('contenteditable', matched?.mode && matched.mode !== 'plaintext' ? 'true' : 'plaintext-only');
setterPropElement.classList.add('engine-live-editing');
// be sure
setterPropElement.focus();
setCaret(event);
this._save = () => {
onSaveContent(setterPropElement!.innerText, prop);
};
const keydown = (e: KeyboardEvent) => {
console.info(e.code);
switch (e.code) {
case 'Enter':
// TODO: check is richtext?
case 'Escape':
case 'Tab':
setterPropElement?.blur();
}
// esc
// enter
// tab
};
const focusout = (e: FocusEvent) => {
this.saveAndDispose();
};
setterPropElement.addEventListener('focusout', focusout);
setterPropElement.addEventListener('keydown', keydown, true);
this._dispose = () => {
setterPropElement!.classList.remove('engine-live-editing');
setterPropElement!.removeAttribute('contenteditable');
setterPropElement!.removeEventListener('focusout', focusout);
setterPropElement!.removeEventListener('keydown', keydown, true);
};
this._editing = prop;
}
// TODO: process enter | esc events & joint the FocusTracker
// TODO: upward testing for b/i/a html elements
}
get editing() {
return this._editing;
}
private _dispose?: () => void;
private _save?: () => void;
saveAndDispose() {
if (this._save) {
this._save();
this._save = undefined;
}
this.dispose();
}
dispose() {
if (this._dispose) {
this._dispose();
this._dispose = undefined;
}
this._editing = null;
}
}
export type SpecificRule = (target: EditingTarget) => (LiveTextEditingConfig & {
propElement?: HTMLElement;
}) | null;
export interface SaveHandler {
condition: (prop: Prop) => boolean;
onSaveContent: (content: string, prop: Prop) => void;
}
function setCaret(event: MouseEvent) {
const doc = event.view?.document!;
const range = doc.caretRangeFromPoint(event.clientX, event.clientY);
if (range) {
selectRange(doc, range);
setTimeout(() => selectRange(doc, range), 1);
}
}
function selectRange(doc: Document, range: Range) {
const selection = doc.getSelection();
if (selection) {
selection.removeAllRanges();
selection.addRange(range);
}
}
function queryPropElement(rootElement: HTMLElement, targetElement: HTMLElement, selector?: string) {
if (!selector) {
return null;
}
let propElement = selector === ':root' ? rootElement : rootElement.querySelector(selector);
if (!propElement) {
return null;
}
if (!propElement.contains(targetElement)) {
// try selectorAll
propElement = Array.from(rootElement.querySelectorAll(selector)).find(item => item.contains(targetElement)) as HTMLElement;
if (!propElement) {
return null;
}
}
return propElement as HTMLElement;
}

View 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;
}
}

View File

@ -0,0 +1,121 @@
import { Overlay } from '@alifd/next';
import React from 'react';
import { Title, globalContext, Editor } from '@ali/lowcode-editor-core';
import './index.less';
import { Node, ParentalNode } from '@ali/lowcode-designer';
const { Popup } = Overlay;
export interface IProps {
node: Node;
}
export interface IState {
parentNodes: Node[];
}
type UnionNode = Node | ParentalNode | null;
export default 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();
const editor = globalContext.get(Editor);
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.emit('designer.border.action', {
name: 'select',
selected,
});
}
};
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"
offset={[0, 2]}
>
<div className="instance-node-selector">{this.renderNodes(node)}</div>
</Popup>
</div>
);
}
}

View File

@ -1,7 +1,9 @@
import { ComponentInstance, NodeInstance, Component } from '../simulator'; import { ComponentInstance, NodeInstance, Component } from '../simulator';
import { ComponentSchema } from '@ali/lowcode-types';
export interface BuiltinSimulatorRenderer { export interface BuiltinSimulatorRenderer {
readonly isSimulatorRenderer: true; readonly isSimulatorRenderer: true;
createComponent(schema: ComponentSchema): Component | null;
getComponent(componentName: string): Component; getComponent(componentName: string): Component;
getComponentInstances(id: string): ComponentInstance[] | null; getComponentInstances(id: string): ComponentInstance[] | null;
getClosestNodeInstance(from: ComponentInstance, nodeId?: string): NodeInstance<ComponentInstance> | null; getClosestNodeInstance(from: ComponentInstance, nodeId?: string): NodeInstance<ComponentInstance> | null;

View File

@ -1,4 +1,4 @@
import { autorun, obx } from '@ali/lowcode-globals'; import { autorun, obx } from '@ali/lowcode-editor-core';
import { BuiltinSimulatorHost } from './host'; import { BuiltinSimulatorHost } from './host';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { BuiltinSimulatorRenderer, isSimulatorRenderer } from './renderer'; import { BuiltinSimulatorRenderer, isSimulatorRenderer } from './renderer';

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { isValidElement } from 'react'; import { isValidElement } from 'react';
import { isElement } from '@ali/lowcode-globals'; import { isElement } from '@ali/lowcode-utils';
import { PropConfig } from '@ali/lowcode-globals'; import { PropConfig } from '@ali/lowcode-types';
export const primitiveTypes = [ export const primitiveTypes = [
'string', 'string',
@ -198,3 +198,10 @@ export function parseProps(component: any): PropConfig[] {
return Object.keys(result).map(key => result[key]); return Object.keys(result).map(key => result[key]);
} }
export function parseMetadata(component: any): any {
return {
props: parseProps(component),
...component.componentMetadata,
};
}

View File

@ -1,4 +1,4 @@
import { obx, computed } from '@ali/lowcode-globals'; import { obx, computed } from '@ali/lowcode-editor-core';
import { Point, ScrollTarget } from '../designer'; import { Point, ScrollTarget } from '../designer';
import { AutoFit, IViewport } from '../simulator'; import { AutoFit, IViewport } from '../simulator';

View File

@ -6,24 +6,32 @@ import {
ComponentAction, ComponentAction,
TitleContent, TitleContent,
TransformedComponentMetadata, TransformedComponentMetadata,
getRegisteredMetadataTransducers, NestingFilter,
registerMetadataTransducer, isTitleConfig,
computed, I18nData,
} from '@ali/lowcode-globals'; LiveTextEditingConfig,
import { Node, NodeParent } from './document'; FieldConfig,
} from '@ali/lowcode-types';
import { computed } from '@ali/lowcode-editor-core';
import { Node, ParentalNode, TransformStage } from './document';
import { Designer } from './designer'; import { Designer } from './designer';
import { intl } from './locale'; import { intlNode } from './locale';
import { IconContainer } from './icons/container'; import { IconContainer } from './icons/container';
import { IconPage } from './icons/page'; import { IconPage } from './icons/page';
import { IconComponent } from './icons/component'; import { IconComponent } from './icons/component';
import { IconRemove } from './icons/remove'; import { IconRemove } from './icons/remove';
import { IconClone } from './icons/clone'; import { IconClone } from './icons/clone';
import { ReactElement } from 'react';
import { IconHidden } from './icons/hidden';
function ensureAList(list?: string | string[]): string[] | null { function ensureAList(list?: string | string[]): string[] | null {
if (!list) { if (!list) {
return null; return null;
} }
if (!Array.isArray(list)) { if (!Array.isArray(list)) {
if (typeof list !== 'string') {
return null;
}
list = list.split(/ *[ ,|] */).filter(Boolean); list = list.split(/ *[ ,|] */).filter(Boolean);
} }
if (list.length < 1) { if (list.length < 1) {
@ -32,44 +40,29 @@ function ensureAList(list?: string | string[]): string[] | null {
return list; return list;
} }
function npmToURI(npm: { function isRegExp(obj: any): obj is RegExp {
package: string; return obj && obj.test && obj.exec && obj.compile;
exportName?: string; }
subName?: string;
destructuring?: boolean;
main?: string;
version: string;
}): string {
const pkg = [];
if (npm.package) {
pkg.push(npm.package);
}
if (npm.main) {
if (npm.main[0] === '/') {
pkg.push(npm.main.slice(1));
} else if (npm.main.slice(0, 2) === './') {
pkg.push(npm.main.slice(2));
} else {
pkg.push(npm.main);
}
}
let uri = pkg.join('/'); function buildFilter(rule?: string | string[] | RegExp | NestingFilter) {
uri += `:${npm.destructuring && npm.exportName ? npm.exportName : 'default'}`; if (!rule) {
return null;
if (npm.subName) {
uri += `.${npm.subName}`;
} }
if (typeof rule === 'function') {
return uri; return rule;
}
if (isRegExp(rule)) {
return (testNode: Node | NodeSchema) => rule.test(testNode.componentName);
}
const list = ensureAList(rule);
if (!list) {
return null;
}
return (testNode: Node | NodeSchema) => list.includes(testNode.componentName);
} }
export class ComponentMeta { export class ComponentMeta {
readonly isComponentMeta = true; readonly isComponentMeta = true;
private _uri?: string;
get uri(): string {
return this._uri!;
}
private _npm?: NpmInfo; private _npm?: NpmInfo;
get npm() { get npm() {
return this._npm; return this._npm;
@ -90,9 +83,9 @@ export class ComponentMeta {
get descriptor(): string | undefined { get descriptor(): string | undefined {
return this._descriptor; return this._descriptor;
} }
private _rectSelector?: string; private _rootSelector?: string;
get rectSelector(): string | undefined { get rootSelector(): string | undefined {
return this._rectSelector; return this._rootSelector;
} }
private _transformedMetadata?: TransformedComponentMetadata; private _transformedMetadata?: TransformedComponentMetadata;
get configure() { get configure() {
@ -100,16 +93,29 @@ export class ComponentMeta {
return config?.combined || config?.props || []; return config?.combined || config?.props || [];
} }
private parentWhitelist?: string[] | null; private _liveTextEditing?: LiveTextEditingConfig[];
private childWhitelist?: string[] | null; get liveTextEditing() {
return this._liveTextEditing;
}
private parentWhitelist?: NestingFilter | null;
private childWhitelist?: NestingFilter | null;
private _title?: TitleContent; private _title?: TitleContent;
get title() { get title(): string | I18nData | ReactElement {
// TODO: 标记下。这块需要康师傅加一下API页面正常渲染。
// string | i18nData | ReactElement
// TitleConfig title.label
if (isTitleConfig(this._title)) {
return (this._title.label as any) || this.componentName;
}
return this._title || this.componentName; return this._title || this.componentName;
} }
@computed get icon() { @computed get icon() {
// TODO: 标记下。这块需要康师傅加一下API页面正常渲染。
// give Slot default icon // give Slot default icon
// if _title is TitleConfig get _title.icon
return ( return (
this._transformedMetadata?.icon || this._transformedMetadata?.icon ||
(this.componentName === 'Page' ? IconPage : this.isContainer ? IconContainer : IconComponent) (this.componentName === 'Page' ? IconPage : this.isContainer ? IconContainer : IconComponent)
@ -125,24 +131,51 @@ export class ComponentMeta {
this.parseMetadata(metadata); this.parseMetadata(metadata);
} }
setNpm(info: NpmInfo) {
if (!this._npm) {
this._npm = info;
}
}
private parseMetadata(metadta: ComponentMetadata) { private parseMetadata(metadta: ComponentMetadata) {
const { componentName, uri, npm } = metadta; const { componentName, npm } = metadta;
this._npm = npm; this._npm = npm;
this._uri = uri || (npm ? npmToURI(npm) : componentName);
this._componentName = componentName; this._componentName = componentName;
metadta.uri = this._uri;
// 额外转换逻辑 // 额外转换逻辑
this._transformedMetadata = this.transformMetadata(metadta); this._transformedMetadata = this.transformMetadata(metadta);
const title = this._transformedMetadata.title; const title = this._transformedMetadata.title;
if (title && typeof title === 'string') { if (title) {
this._title = { this._title =
typeof title === 'string'
? {
type: 'i18n', type: 'i18n',
'en-US': this.componentName, 'en-US': this.componentName,
'zh-CN': title, 'zh-CN': title,
};
} }
: title;
}
const liveTextEditing = this._transformedMetadata.experimental?.liveTextEditing || [];
function collectLiveTextEditing(items: FieldConfig[]) {
items.forEach(config => {
if (config.items) {
collectLiveTextEditing(config.items);
} else {
const liveConfig = config.liveTextEditing || config.extraProps?.liveTextEditing;
if (liveConfig) {
liveTextEditing.push({
propTarget: String(config.name),
...liveConfig,
});
}
}
});
}
collectLiveTextEditing(this.configure);
this._liveTextEditing = liveTextEditing.length > 0 ? liveTextEditing : undefined;
const { configure = {} } = this._transformedMetadata; const { configure = {} } = this._transformedMetadata;
this._acceptable = false; this._acceptable = false;
@ -152,11 +185,11 @@ export class ComponentMeta {
this._isContainer = component.isContainer ? true : false; this._isContainer = component.isContainer ? true : false;
this._isModal = component.isModal ? true : false; this._isModal = component.isModal ? true : false;
this._descriptor = component.descriptor; this._descriptor = component.descriptor;
this._rectSelector = component.rectSelector; this._rootSelector = component.rootSelector;
if (component.nestingRule) { if (component.nestingRule) {
const { parentWhitelist, childWhitelist } = component.nestingRule; const { parentWhitelist, childWhitelist } = component.nestingRule;
this.parentWhitelist = ensureAList(parentWhitelist); this.parentWhitelist = buildFilter(parentWhitelist);
this.childWhitelist = ensureAList(childWhitelist); this.childWhitelist = buildFilter(childWhitelist);
} }
} else { } else {
this._isContainer = false; this._isContainer = false;
@ -181,12 +214,14 @@ export class ComponentMeta {
@computed get availableActions() { @computed get availableActions() {
let { disableBehaviors, actions } = this._transformedMetadata?.configure.component || {}; let { disableBehaviors, actions } = this._transformedMetadata?.configure.component || {};
const disabled = ensureAList(disableBehaviors) || (this.isRootComponent() ? ['copy', 'remove'] : null);
actions = builtinComponentActions.concat(this.designer.getGlobalComponentActions() || [], actions || []); actions = builtinComponentActions.concat(this.designer.getGlobalComponentActions() || [], actions || []);
if (!disableBehaviors && this.isRootComponent()) {
disableBehaviors = ['copy', 'remove']; if (disabled) {
if (disabled.includes('*')) {
return actions.filter((action) => action.condition === 'always');
} }
if (disableBehaviors) { return actions.filter((action) => disabled.indexOf(action.name) < 0);
return actions.filter(action => disableBehaviors!.indexOf(action.name) < 0);
} }
return actions; return actions;
} }
@ -199,19 +234,28 @@ export class ComponentMeta {
return this._transformedMetadata!; return this._transformedMetadata!;
} }
checkNestingUp(my: Node | NodeData, parent: NodeParent) { checkNestingUp(my: Node | NodeData, parent: ParentalNode) {
// 检查父子关系,直接约束型,在画布中拖拽直接掠过目标容器
if (this.parentWhitelist) { if (this.parentWhitelist) {
return this.parentWhitelist.includes(parent.componentName); return this.parentWhitelist(parent, my);
} }
return true; return true;
} }
checkNestingDown(my: Node, target: Node | NodeSchema) { checkNestingDown(my: Node, target: Node | NodeSchema) {
// 检查父子关系,直接约束型,在画布中拖拽直接掠过目标容器
if (this.childWhitelist) { if (this.childWhitelist) {
return this.childWhitelist.includes(target.componentName); return this.childWhitelist(target, my);
} }
return true; return true;
} }
// compatiable vision
prototype?: any;
}
export function isComponentMeta(obj: any): obj is ComponentMeta {
return obj && obj.isComponentMeta;
} }
function preprocessMetadata(metadata: ComponentMetadata): TransformedComponentMetadata { function preprocessMetadata(metadata: ComponentMetadata): TransformedComponentMetadata {
@ -233,7 +277,39 @@ function preprocessMetadata(metadata: ComponentMetadata): TransformedComponentMe
}; };
} }
registerMetadataTransducer(metadata => {
export interface MetadataTransducer {
(prev: TransformedComponentMetadata): TransformedComponentMetadata;
/**
* 0 - 9 system
* 10 - 99 builtin-plugin
* 100 - app & plugin
*/
level?: number;
/**
* use to replace TODO
*/
id?: string;
}
const metadataTransducers: MetadataTransducer[] = [];
export function registerMetadataTransducer(transducer: MetadataTransducer, level: number = 100, id?: string) {
transducer.level = level;
transducer.id = id;
const i = metadataTransducers.findIndex((item) => item.level != null && item.level > level);
if (i < 0) {
metadataTransducers.push(transducer);
} else {
metadataTransducers.splice(i, 0, transducer);
}
}
export function getRegisteredMetadataTransducers(): MetadataTransducer[] {
return metadataTransducers;
}
registerMetadataTransducer((metadata) => {
const { configure, componentName } = metadata; const { configure, componentName } = metadata;
const { component = {} } = configure; const { component = {} } = configure;
if (!component.nestingRule) { if (!component.nestingRule) {
@ -278,22 +354,48 @@ const builtinComponentActions: ComponentAction[] = [
name: 'remove', name: 'remove',
content: { content: {
icon: IconRemove, icon: IconRemove,
description: intl('remove'), title: intlNode('remove'),
action(node: Node) { action(node: Node) {
node.remove(); node.remove();
}, },
}, },
important: true, important: true,
}, },
{
name: 'hide',
content: {
icon: IconHidden,
title: intlNode('hide'),
action(node: Node) {
node.getExtraProp('hidden', true)?.setValue(true);
},
},
condition: (node: Node) => {
return node.componentMeta.isModal;
},
important: true,
},
{ {
name: 'copy', name: 'copy',
content: { content: {
icon: IconClone, icon: IconClone,
description: intl('copy'), title: intlNode('copy'),
action(node: Node) { action(node: Node) {
// node.remove(); // node.remove();
const { document: doc, parent, index } = node;
parent && doc.insertNode(parent, node, index, true);
}, },
}, },
important: true, important: true,
}, },
]; ];
export function removeBuiltinComponentAction(name: string) {
const i = builtinComponentActions.findIndex((action) => action.name === name);
if (i > -1) {
builtinComponentActions.splice(i, 1);
}
}
export function addBuiltinComponentAction(action: ComponentAction) {
builtinComponentActions.push(action);
}

View File

@ -1,17 +1,38 @@
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { LocationDetail } from './location'; import { LocationDetail } from './location';
import { Node, isNode } from '../document/node/node'; import { Node, isNode } from '../document/node/node';
import { ComponentInstance } from '../simulator';
import { obx } from '@ali/lowcode-editor-core';
export interface ActiveTarget { export interface ActiveTarget {
node: Node; node: Node;
detail?: LocationDetail; detail?: LocationDetail;
instance?: ComponentInstance;
} }
export class ActiveTracker { export class ActiveTracker {
private emitter = new EventEmitter(); private emitter = new EventEmitter();
@obx.ref private _target?: ActiveTarget;
track(target: ActiveTarget | Node) { track(target: ActiveTarget | Node) {
this.emitter.emit('change', isNode(target) ? { node: target } : target); if (isNode(target)) {
target = { node: target };
}
this._target = target;
this.emitter.emit('change', target);
}
get currentNode() {
return this._target?.node;
}
get detail() {
return this._target?.detail;
}
get intance() {
return this._target?.instance;
} }
onChange(fn: (target: ActiveTarget) => void): () => void { onChange(fn: (target: ActiveTarget) => void): () => void {

View File

@ -0,0 +1,319 @@
import { hotkey } from '@ali/lowcode-editor-core';
import { isFormEvent } from '@ali/lowcode-utils';
import { focusing } from './focusing';
import { insertChildren, TransformStage } from '../document';
import clipboard from './clipboard';
function getNextForSelect(next: any, head?: any, parent?: any): any {
if (next) {
if (!head) {
return next;
}
let ret;
if (next.isContainer()) {
const children = next.getChildren() || [];
if (children && !children.isEmpty()) {
ret = getNextForSelect(children.get(0));
if (ret) {
return ret;
}
}
}
ret = getNextForSelect(next.nextSibling);
if (ret) {
return ret;
}
}
if (parent) {
return getNextForSelect(parent.nextSibling, false, parent.getParent());
}
return null;
}
function getPrevForSelect(prev: any, head?: any, parent?: any): any {
if (prev) {
let ret;
if (!head && prev.isContainer()) {
const children = prev.getChildren() || [];
const lastChild = children && !children.isEmpty() ? children.get(children.size - 1) : null;
ret = getPrevForSelect(lastChild);
if (ret) {
return ret;
}
}
if (!head) {
return prev;
}
ret = getPrevForSelect(prev.prevSibling);
if (ret) {
return ret;
}
}
if (parent) {
return parent;
}
return null;
}
// hotkey binding
hotkey.bind(['backspace', 'del'], (e: KeyboardEvent) => {
// TODO: use focus-tracker
const doc = focusing.focusDesigner?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const sel = doc.selection;
const topItems = sel.getTopNodes();
// TODO: check can remove
topItems.forEach((node) => {
doc.removeNode(node);
});
sel.clear();
});
hotkey.bind('escape', (e: KeyboardEvent) => {
// const currentFocus = focusing.current;
const sel = focusing.focusDesigner?.currentDocument?.selection;
if (isFormEvent(e) || !sel) {
return;
}
e.preventDefault();
sel.clear();
// currentFocus.esc();
});
// command + c copy command + x cut
hotkey.bind(['command+c', 'ctrl+c', 'command+x', 'ctrl+x'], (e, action) => {
const doc = focusing.focusDesigner?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) return;
const componentsMap = {};
const componentsTree = selected.map((item) => item.export(TransformStage.Clone));
// FIXME: clear node.id
const data = { type: 'nodeSchema', componentsMap, componentsTree };
clipboard.setData(data);
const cutMode = action && action.indexOf('x') > 0;
if (cutMode) {
selected.forEach((node) => {
const parentNode = node.getParent();
parentNode?.select();
node.remove();
});
}
});
// command + v paste
hotkey.bind(['command+v', 'ctrl+v'], (e) => {
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !designer || !doc) {
return;
}
clipboard.waitPasteData(e, ({ componentsTree }) => {
if (componentsTree) {
const { target, index } = designer.getSuitableInsertion() || {};
if (!target) {
return;
}
const nodes = insertChildren(target, componentsTree, index);
if (nodes) {
doc.selection.selectAll(nodes.map((o) => o.id));
setTimeout(() => designer.activeTracker.track(nodes[0]), 10);
}
}
});
});
// command + z undo
hotkey.bind(['command+z', 'ctrl+z'], (e) => {
const his = focusing.focusDesigner?.currentHistory;
if (isFormEvent(e) || !his) {
return;
}
e.preventDefault();
his.back();
});
// command + shift + z redo
hotkey.bind(['command+y', 'ctrl+y', 'command+shift+z'], (e) => {
const his = focusing.focusDesigner?.currentHistory;
if (isFormEvent(e) || !his) {
return;
}
e.preventDefault();
his.forward();
});
// sibling selection
hotkey.bind(['left', 'right'], (e, action) => {
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) {
return;
}
const firstNode = selected[0];
const silbing = action === 'left' ? firstNode?.prevSibling : firstNode?.nextSibling;
silbing?.select();
});
hotkey.bind(['up', 'down'], (e, action) => {
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) {
return;
}
const firstNode = selected[0];
if (action === 'down') {
const next = getNextForSelect(firstNode, true, firstNode.getParent());
next?.select();
} else if (action === 'up') {
const prev = getPrevForSelect(firstNode, true, firstNode.getParent());
prev?.select();
}
});
hotkey.bind(['option+left', 'option+right'], (e, action) => {
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) {
return;
}
// TODO: 此处需要增加判断当前节点是否可被操作移动原ve里是用 node.canOperating()来判断
// TODO: 移动逻辑也需要重新梳理,对于移动目标位置的选择,是否可以移入,需要增加判断
const firstNode = selected[0];
const parent = firstNode.getParent();
if (!parent) return;
const isPrev = action && /(left)$/.test(action);
const silbing = isPrev ? firstNode.prevSibling : firstNode.nextSibling;
if (silbing) {
if (isPrev) {
parent.insertBefore(firstNode, silbing);
} else {
parent.insertAfter(firstNode, silbing);
}
firstNode?.select();
return;
}
});
hotkey.bind(['option+up'], (e, action) => {
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) {
return;
}
// TODO: 此处需要增加判断当前节点是否可被操作移动原ve里是用 node.canOperating()来判断
// TODO: 移动逻辑也需要重新梳理,对于移动目标位置的选择,是否可以移入,需要增加判断
const firstNode = selected[0];
const parent = firstNode.getParent();
if (!parent) {
return;
}
const silbing = firstNode.prevSibling;
if (silbing) {
if (silbing.isContainer()) {
const place = silbing.getSuitablePlace(firstNode, null);
place.container.insertAfter(firstNode, place.ref);
} else {
parent.insertBefore(firstNode, silbing);
}
firstNode?.select();
return;
} else {
const place = parent.getSuitablePlace(firstNode, null); // upwards
if (place) {
place.container.insertBefore(firstNode, place.ref);
firstNode?.select();
}
}
});
hotkey.bind(['option+down'], (e, action) => {
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) {
return;
}
// TODO: 此处需要增加判断当前节点是否可被操作移动原ve里是用 node.canOperating()来判断
// TODO: 移动逻辑也需要重新梳理,对于移动目标位置的选择,是否可以移入,需要增加判断
const firstNode = selected[0];
const parent = firstNode.getParent();
if (!parent) {
return;
}
const silbing = firstNode.nextSibling;
if (silbing) {
if (silbing.isContainer()) {
// const place = silbing.getSuitablePlace(firstNode, null);
silbing.insertBefore(firstNode, undefined);
// place.container.insertBefore(firstNode, place.ref);
} else {
parent.insertAfter(firstNode, silbing);
}
firstNode?.select();
return;
} else {
const place = parent.getSuitablePlace(firstNode, null); // upwards
if (place) {
place.container.insertAfter(firstNode, place.ref);
firstNode?.select();
}
}
});

View File

@ -6,7 +6,17 @@ function getDataFromPasteEvent(event: ClipboardEvent) {
try { try {
// { componentsMap, componentsTree, ... } // { componentsMap, componentsTree, ... }
return JSON.parse(clipboardData.getData('text/plain')); const data = JSON.parse(clipboardData.getData('text/plain'));
if (!data) {
return {};
}
if (data.componentsTree) {
return data;
} else if (data.componentName) {
return {
componentsTree: [ data ]
};
}
} catch (error) { } catch (error) {
/* /*
const html = clipboardData.getData('text/html'); const html = clipboardData.getData('text/html');
@ -19,7 +29,7 @@ function getDataFromPasteEvent(event: ClipboardEvent) {
} }
*/ */
// TODO: open the parser implement // TODO: open the parser implement
return null; return { };
/* /*
return { return {
code: clipboardData.getData('text/plain'), code: clipboardData.getData('text/plain'),
@ -64,7 +74,7 @@ class Clipboard {
return; return;
} }
const copyPaster = document.createElement<'textarea'>('textarea'); const copyPaster = document.createElement<'textarea'>('textarea');
copyPaster.style.cssText = 'position: relative;left: -9999px;'; copyPaster.style.cssText = 'position: absolute;left: -9999px;top:-100px';
document.body.appendChild(copyPaster); document.body.appendChild(copyPaster);
const dispose = this.initCopyPaster(copyPaster); const dispose = this.initCopyPaster(copyPaster);
return () => { return () => {

View File

@ -1,18 +1,25 @@
import { Component } from 'react'; import { Component } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { TipContainer } from '@ali/lowcode-globals';
import BuiltinDragGhostComponent from './drag-ghost'; import BuiltinDragGhostComponent from './drag-ghost';
import { Designer, DesignerProps } from './designer'; import { Designer, DesignerProps } from './designer';
import { ProjectView } from '../project'; import { ProjectView } from '../project';
import './designer.less'; import './designer.less';
import clipboard from './clipboard'; import clipboard from './clipboard';
export class DesignerView extends Component<DesignerProps> { export class DesignerView extends Component<DesignerProps & {
designer?: Designer;
}> {
readonly designer: Designer; readonly designer: Designer;
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.designer = new Designer(props); const { designer, ...designerProps } = props;
if (designer) {
this.designer = designer;
designer.setProps(designerProps);
} else {
this.designer = new Designer(designerProps);
}
} }
shouldComponentUpdate(nextProps: DesignerProps) { shouldComponentUpdate(nextProps: DesignerProps) {
@ -49,7 +56,6 @@ export class DesignerView extends Component<DesignerProps> {
<div className={classNames('lc-designer', className)} style={style}> <div className={classNames('lc-designer', className)} style={style}>
<DragGhost designer={this.designer} /> <DragGhost designer={this.designer} />
<ProjectView designer={this.designer} /> <ProjectView designer={this.designer} />
<TipContainer />
</div> </div>
); );
} }

View File

@ -1,48 +1,7 @@
@import 'variables.less';
.lc-designer { .lc-designer {
--font-family: @font-family;
--font-size-label: @fontSize-4;
--font-size-text: @fontSize-5;
--font-size-btn-large: @fontSize-3;
--font-size-btn-medium: @fontSize-4;
--font-size-btn-small: @fontSize-5;
--color-brand: #006cff;
--color-brand-light: #197aff;
--color-brand-dark: #0060e5;
--color-icon: rgba(255, 255, 255, 0.8);
--color-visited: rgba(179, 182, 201, 0.4);
--color-actived: #498ee6;
--color-border: @white-alpha-7;
--color-btn: #0079F2;
--color-btn-border: rgba(0, 121, 242, 0.3);
--color-btn-bg: #212938;
--color-form-bg: #272A35;
--color-form-border: rgba(63,70,93,1);
--color-text: @white-alpha-3;
--color-text-light: @white-alpha-1;
--color-field-placeholder: @white-alpha-5;
--color-pane-label: rgba(255, 255, 255, 0.9);
--color-border: rgba(63, 70, 93, 1);
--color-field-border: rgba(118, 137, 199, 0.6);
--color-function-warning: rgb(204, 131, 98);
--color-field-border-hover: rgb(118, 137, 199, 0.8);
--color-field-border-active: rgb(118, 137, 199);
--color-block-background-disabled: rgba(118, 137, 199, 0.35);
--global-border-radius: @global-border-radius;
--input-border-radius: @input-border-radius;
--popup-border-radius: @popup-border-radius;
position: relative; position: relative;
font-family: var(--font-family); font-family: var(--font-family);
font-size: var(--font-size-text); font-size: var(--font-size-text);
min-width: 500px;
min-height: 500px;
box-sizing: border-box; box-sizing: border-box;
* { * {

View File

@ -1,27 +1,29 @@
import { ComponentType } from 'react'; import { ComponentType } from 'react';
import { EventEmitter } from 'events'; import { obx, computed, autorun } from '@ali/lowcode-editor-core';
import { import {
ProjectSchema, ProjectSchema,
ComponentMetadata, ComponentMetadata,
ComponentAction, ComponentAction,
NpmInfo, NpmInfo,
obx, IEditor,
computed, CompositeObject,
autorun, PropsList,
} from '@ali/lowcode-globals'; } from '@ali/lowcode-types';
import { Project } from '../project'; import { Project } from '../project';
import { Node, DocumentModel, insertChildren, isRootNode, NodeParent } from '../document'; import { Node, DocumentModel, insertChildren, isRootNode, ParentalNode, TransformStage } from '../document';
import { ComponentMeta } from '../component-meta'; import { ComponentMeta } from '../component-meta';
import { INodeSelector } from '../simulator'; import { INodeSelector, Component } from '../simulator';
import { Scroller, IScrollable } from './scroller'; import { Scroller, IScrollable } from './scroller';
import { Dragon, isDragNodeObject, isDragNodeDataObject, LocateEvent, DragObject } from './dragon'; import { Dragon, isDragNodeObject, isDragNodeDataObject, LocateEvent, DragObject } from './dragon';
import { ActiveTracker } from './active-tracker'; import { ActiveTracker } from './active-tracker';
import { Hovering } from './hovering'; import { Detecting } from './detecting';
import { DropLocation, LocationData, isLocationChildrenDetail } from './location'; import { DropLocation, LocationData, isLocationChildrenDetail } from './location';
import { OffsetObserver, createOffsetObserver } from './offset-observer'; import { OffsetObserver, createOffsetObserver } from './offset-observer';
import { focusing } from './focusing'; import { focusing } from './focusing';
import { SettingTopEntry } from './setting';
export interface DesignerProps { export interface DesignerProps {
editor: IEditor;
className?: string; className?: string;
style?: object; style?: object;
defaultSchema?: ProjectSchema; defaultSchema?: ProjectSchema;
@ -31,7 +33,6 @@ export interface DesignerProps {
dragGhostComponent?: ComponentType<any>; dragGhostComponent?: ComponentType<any>;
suspensed?: boolean; suspensed?: boolean;
componentMetadatas?: ComponentMetadata[]; componentMetadatas?: ComponentMetadata[];
eventPipe?: EventEmitter;
globalComponentActions?: ComponentAction[]; globalComponentActions?: ComponentAction[];
onMount?: (designer: Designer) => void; onMount?: (designer: Designer) => void;
onDragstart?: (e: LocateEvent) => void; onDragstart?: (e: LocateEvent) => void;
@ -43,8 +44,9 @@ export interface DesignerProps {
export class Designer { export class Designer {
readonly dragon = new Dragon(this); readonly dragon = new Dragon(this);
readonly activeTracker = new ActiveTracker(); readonly activeTracker = new ActiveTracker();
readonly hovering = new Hovering(); readonly detecting = new Detecting();
readonly project: Project; readonly project: Project;
readonly editor: IEditor;
get currentDocument() { get currentDocument() {
return this.project.currentDocument; return this.project.currentDocument;
@ -59,17 +61,32 @@ export class Designer {
} }
constructor(props: DesignerProps) { constructor(props: DesignerProps) {
const { editor } = props;
this.editor = editor;
this.setProps(props); this.setProps(props);
this.project = new Project(this, props.defaultSchema); this.project = new Project(this, props.defaultSchema);
let startTime: any;
let src = '';
this.dragon.onDragstart((e) => { this.dragon.onDragstart((e) => {
this.hovering.enable = false; startTime = Date.now() / 1000;
this.detecting.enable = false;
const { dragObject } = e; const { dragObject } = e;
if (isDragNodeObject(dragObject)) { if (isDragNodeObject(dragObject)) {
const node = dragObject.nodes[0]?.parent;
const npm = node?.componentMeta?.npm;
src =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
if (dragObject.nodes.length === 1) { if (dragObject.nodes.length === 1) {
if (dragObject.nodes[0].parent) {
// ensure current selecting // ensure current selecting
dragObject.nodes[0].select(); dragObject.nodes[0].select();
} else {
this.currentSelection?.clear();
}
} }
} else { } else {
this.currentSelection?.clear(); this.currentSelection?.clear();
@ -91,7 +108,7 @@ export class Designer {
const { dragObject, copy } = e; const { dragObject, copy } = e;
const loc = this._dropLocation; const loc = this._dropLocation;
if (loc) { if (loc) {
if (isLocationChildrenDetail(loc.detail)) { if (isLocationChildrenDetail(loc.detail) && loc.detail.valid !== false) {
let nodes: Node[] | undefined; let nodes: Node[] | undefined;
if (isDragNodeObject(dragObject)) { if (isDragNodeObject(dragObject)) {
nodes = insertChildren(loc.target, dragObject.nodes, loc.detail.index, copy); nodes = insertChildren(loc.target, dragObject.nodes, loc.detail.index, copy);
@ -103,15 +120,40 @@ export class Designer {
if (nodes) { if (nodes) {
loc.document.selection.selectAll(nodes.map((o) => o.id)); loc.document.selection.selectAll(nodes.map((o) => o.id));
setTimeout(() => this.activeTracker.track(nodes![0]), 10); setTimeout(() => this.activeTracker.track(nodes![0]), 10);
const endTime: any = Date.now() / 1000;
const parent = nodes[0]?.parent;
const npm = parent?.componentMeta?.npm;
const dest =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
parent?.componentMeta?.componentName ||
'';
this.editor?.emit('designer.drag', {
time: (endTime - startTime).toFixed(2),
selected: nodes
?.map((n) => {
if (!n) {
return;
}
const npm = n?.componentMeta?.npm;
return (
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
n?.componentMeta?.componentName
);
})
.join('&'),
align: loc?.detail?.near?.align || '',
pos: loc?.detail?.near?.pos || '',
src,
dest,
});
} }
} }
} }
this.clearLocation();
if (this.props?.onDragend) { if (this.props?.onDragend) {
this.props.onDragend(e, loc); this.props.onDragend(e, loc);
} }
this.postEvent('dragend', e, loc); this.postEvent('dragend', e, loc);
this.hovering.enable = true; this.detecting.enable = true;
}); });
this.activeTracker.onChange(({ node, detail }) => { this.activeTracker.onChange(({ node, detail }) => {
@ -124,11 +166,11 @@ export class Designer {
selectionDispose(); selectionDispose();
selectionDispose = undefined; selectionDispose = undefined;
} }
this.postEvent('selection-change', this.currentSelection); this.postEvent('selection.change', this.currentSelection);
if (this.currentSelection) { if (this.currentSelection) {
const currentSelection = this.currentSelection; const currentSelection = this.currentSelection;
selectionDispose = currentSelection.onSelectionChange(() => { selectionDispose = currentSelection.onSelectionChange(() => {
this.postEvent('selection-change', currentSelection); this.postEvent('selection.change', currentSelection);
}); });
} }
}; };
@ -138,18 +180,18 @@ export class Designer {
historyDispose(); historyDispose();
historyDispose = undefined; historyDispose = undefined;
} }
this.postEvent('history-change', this.currentHistory); this.postEvent('history.change', this.currentHistory);
if (this.currentHistory) { if (this.currentHistory) {
const currentHistory = this.currentHistory; const currentHistory = this.currentHistory;
historyDispose = currentHistory.onStateChange(() => { historyDispose = currentHistory.onStateChange(() => {
this.postEvent('history-change', currentHistory); this.postEvent('history.change', currentHistory);
}); });
} }
}; };
this.project.onCurrentDocumentChange(() => { this.project.onCurrentDocumentChange(() => {
this.postEvent('current-document-change', this.currentDocument); this.postEvent('current-document.change', this.currentDocument);
this.postEvent('selection-change', this.currentSelection); this.postEvent('selection.change', this.currentSelection);
this.postEvent('history-change', this.currentHistory); this.postEvent('history.change', this.currentHistory);
setupSelection(); setupSelection();
setupHistory(); setupHistory();
}); });
@ -162,10 +204,14 @@ export class Designer {
} }
postEvent(event: string, ...args: any[]) { postEvent(event: string, ...args: any[]) {
this.props?.eventPipe?.emit(`designer.${event}`, ...args); this.editor.emit(`designer.${event}`, ...args);
} }
private _dropLocation?: DropLocation; private _dropLocation?: DropLocation;
get dropLocation() {
return this._dropLocation;
}
/** /**
* dragon * dragon
*/ */
@ -194,14 +240,40 @@ export class Designer {
return new Scroller(scrollable); return new Scroller(scrollable);
} }
private oobxList: OffsetObserver[] = [];
createOffsetObserver(nodeInstance: INodeSelector): OffsetObserver | null { createOffsetObserver(nodeInstance: INodeSelector): OffsetObserver | null {
return createOffsetObserver(nodeInstance); const oobx = createOffsetObserver(nodeInstance);
this.clearOobxList();
if (oobx) {
this.oobxList.push(oobx);
}
return oobx;
}
private clearOobxList(force?: boolean) {
let l = this.oobxList.length;
if (l > 20 || force) {
while (l-- > 0) {
if (this.oobxList[l].isPurged()) {
this.oobxList.splice(l, 1);
}
}
}
}
touchOffsetObserver() {
this.clearOobxList(true);
this.oobxList.forEach((item) => item.compute());
}
createSettingEntry(nodes: Node[]) {
return new SettingTopEntry(this.editor, nodes);
} }
/** /**
* *
*/ */
getSuitableInsertion(): { target: NodeParent; index?: number } | null { getSuitableInsertion(): { target: ParentalNode; index?: number } | null {
const activedDoc = this.project.currentDocument; const activedDoc = this.project.currentDocument;
if (!activedDoc) { if (!activedDoc) {
return null; return null;
@ -306,7 +378,10 @@ export class Designer {
private _lostComponentMetasMap = new Map<string, ComponentMeta>(); private _lostComponentMetasMap = new Map<string, ComponentMeta>();
private buildComponentMetasMap(metas: ComponentMetadata[]) { private buildComponentMetasMap(metas: ComponentMetadata[]) {
metas.forEach((data) => { metas.forEach((data) => this.createComponentMeta(data));
}
createComponentMeta(data: ComponentMetadata): ComponentMeta {
const key = data.componentName; const key = data.componentName;
let meta = this._componentMetasMap.get(key); let meta = this._componentMetasMap.get(key);
if (meta) { if (meta) {
@ -323,7 +398,7 @@ export class Designer {
this._componentMetasMap.set(key, meta); this._componentMetasMap.set(key, meta);
} }
}); return meta;
} }
getGlobalComponentActions(): ComponentAction[] | null { getGlobalComponentActions(): ComponentAction[] | null {
@ -349,16 +424,56 @@ export class Designer {
return meta; return meta;
} }
@computed get componentsMap(): { [key: string]: NpmInfo } { @computed get componentsMap(): { [key: string]: NpmInfo | Component } {
const maps: any = {}; const maps: any = {};
this._componentMetasMap.forEach((config, key) => { this._componentMetasMap.forEach((config, key) => {
if (config.npm) { const metaData = config.getMetadata();
if (metaData.devMode === 'lowcode') {
maps[key] = this.currentDocument?.simulator?.createComponent(metaData.schema!);
} else {
const view = metaData.experimental?.view;
if (view) {
maps[key] = view;
} else if (config.npm) {
maps[key] = config.npm; maps[key] = config.npm;
} }
}
}); });
return maps; return maps;
} }
private propsReducers = new Map<TransformStage, PropsReducer[]>();
transformProps(props: CompositeObject | PropsList, node: Node, stage: TransformStage) {
if (Array.isArray(props)) {
// current not support, make this future
return props;
}
const reducers = this.propsReducers.get(stage);
if (!reducers) {
return props;
}
return reducers.reduce((xprops, reducer) => {
try {
return reducer(xprops, node)
} catch (e) {
// todo: add log
console.warn(e);
return xprops;
}
}, props);
}
addPropsReducer(reducer: PropsReducer, stage: TransformStage) {
const reducers = this.propsReducers.get(stage);
if (reducers) {
reducers.push(reducer);
} else {
this.propsReducers.set(stage, [reducer]);
}
}
autorun(action: (context: { firstRun: boolean }) => void, sync = false): () => void { autorun(action: (context: { firstRun: boolean }) => void, sync = false): () => void {
return autorun(action, sync as true); return autorun(action, sync as true);
} }
@ -367,3 +482,5 @@ export class Designer {
// todo: // todo:
} }
} }
export type PropsReducer = (props: CompositeObject, node: Node) => CompositeObject;

View File

@ -1,7 +1,7 @@
import { obx } from '@ali/lowcode-globals'; import { obx } from '@ali/lowcode-editor-core';
import { Node, DocumentModel } from '../document'; import { Node, DocumentModel } from '../document';
export class Hovering { export class Detecting {
@obx.ref private _enable = true; @obx.ref private _enable = true;
get enable() { get enable() {
return this._enable; return this._enable;
@ -19,11 +19,11 @@ export class Hovering {
return this._current; return this._current;
} }
hover(node: Node | null) { capture(node: Node | null) {
this._current = node; this._current = node;
} }
unhover(node: Node) { release(node: Node) {
if (this._current === node) { if (this._current === node) {
this._current = null; this._current = null;
} }

View File

@ -8,7 +8,7 @@
align-items: center; align-items: center;
pointer-events: none; pointer-events: none;
background-color: rgba(0, 0, 0, 0.4); background-color: rgba(0, 0, 0, 0.4);
opacity: 0.5; //opacity: 0.9;
box-shadow: 0 0 6px grey; box-shadow: 0 0 6px grey;
transform: translate(-10%, -50%); transform: translate(-10%, -50%);
.lc-ghost { .lc-ghost {

View File

@ -1,5 +1,5 @@
import { Component } from 'react'; import { Component } from 'react';
import { observer, obx, Title } from '@ali/lowcode-globals'; import { observer, obx, Title } from '@ali/lowcode-editor-core';
import { Designer } from '../designer'; import { Designer } from '../designer';
import { DragObject, isDragNodeObject, isDragNodeDataObject } from '../dragon'; import { DragObject, isDragNodeObject, isDragNodeDataObject } from '../dragon';
import './ghost.less'; import './ghost.less';

View File

@ -1,11 +1,11 @@
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { NodeSchema, obx } from '@ali/lowcode-globals'; import { obx } from '@ali/lowcode-editor-core';
import { NodeSchema } from '@ali/lowcode-types';
import { setNativeSelection, cursor } from '@ali/lowcode-utils';
import { DropLocation } from './location'; import { DropLocation } from './location';
import { Node, DocumentModel } from '../document'; import { Node, DocumentModel } from '../document';
import { ISimulatorHost, isSimulatorHost } from '../simulator'; import { ISimulatorHost, isSimulatorHost } from '../simulator';
import { Designer } from './designer'; import { Designer } from './designer';
import { setNativeSelection } from '@ali/lowcode-globals';
import { cursor } from '@ali/lowcode-globals';
export interface LocateEvent { export interface LocateEvent {
readonly type: 'LocateEvent'; readonly type: 'LocateEvent';
@ -88,7 +88,6 @@ export interface DragNodeObject {
export interface DragNodeDataObject { export interface DragNodeDataObject {
type: DragObjectType.NodeData; type: DragObjectType.NodeData;
data: NodeSchema | NodeSchema[]; data: NodeSchema | NodeSchema[];
maps?: { [componentName: string]: string };
thumbnail?: string; thumbnail?: string;
description?: string; description?: string;
[extra: string]: any; [extra: string]: any;
@ -153,6 +152,9 @@ function getSourceSensor(dragObject: DragObject): ISimulatorHost | null {
return dragObject.nodes[0]?.document.simulator || null; return dragObject.nodes[0]?.document.simulator || null;
} }
/**
* make a handler that listen all sensors:document, avoid frame lost
*/
function makeEventsHandler( function makeEventsHandler(
boostEvent: MouseEvent | DragEvent, boostEvent: MouseEvent | DragEvent,
sensors: ISimulatorHost[], sensors: ISimulatorHost[],
@ -160,20 +162,15 @@ function makeEventsHandler(
const topDoc = window.top.document; const topDoc = window.top.document;
const sourceDoc = boostEvent.view?.document || topDoc; const sourceDoc = boostEvent.view?.document || topDoc;
// TODO: optimize this logic, reduce listener // TODO: optimize this logic, reduce listener
// const boostPrevented = boostEvent.defaultPrevented;
const docs = new Set<Document>(); const docs = new Set<Document>();
// if (boostPrevented || isDragEvent(boostEvent)) {
docs.add(topDoc); docs.add(topDoc);
// }
docs.add(sourceDoc); docs.add(sourceDoc);
// if (sourceDoc !== topDoc || isDragEvent(boostEvent)) {
sensors.forEach((sim) => { sensors.forEach((sim) => {
const sdoc = sim.contentDocument; const sdoc = sim.contentDocument;
if (sdoc) { if (sdoc) {
docs.add(sdoc); docs.add(sdoc);
} }
}); });
// }
return (handle: (sdoc: Document) => void) => { return (handle: (sdoc: Document) => void) => {
docs.forEach((doc) => handle(doc)); docs.forEach((doc) => handle(doc));
@ -184,6 +181,9 @@ function isDragEvent(e: any): e is DragEvent {
return e?.type?.substr(0, 4) === 'drag'; return e?.type?.substr(0, 4) === 'drag';
} }
/**
* Drag-on
*/
export class Dragon { export class Dragon {
private sensors: ISensor[] = []; private sensors: ISensor[] = [];
@ -201,12 +201,15 @@ export class Dragon {
} }
private emitter = new EventEmitter(); private emitter = new EventEmitter();
// private emptyImage: HTMLImageElement = new Image();
constructor(readonly designer: Designer) { constructor(readonly designer: Designer) {
// this.emptyImage.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
} }
/**
* Quick listen a shell(container element) drag behavior
* @param shell container element
* @param boost boost got a drag object
*/
from(shell: Element, boost: (e: MouseEvent) => DragObject | null) { from(shell: Element, boost: (e: MouseEvent) => DragObject | null) {
const mousedown = (e: MouseEvent) => { const mousedown = (e: MouseEvent) => {
// ESC or RightClick // ESC or RightClick
@ -228,12 +231,18 @@ export class Dragon {
}; };
} }
/**
* boost your dragObject for dragging(flying)
*
* @param dragObject
* @param boostEvent
*/
boost(dragObject: DragObject, boostEvent: MouseEvent | DragEvent) { boost(dragObject: DragObject, boostEvent: MouseEvent | DragEvent) {
const designer = this.designer; const designer = this.designer;
const masterSensors = this.getMasterSensors(); const masterSensors = this.getMasterSensors();
const handleEvents = makeEventsHandler(boostEvent, masterSensors); const handleEvents = makeEventsHandler(boostEvent, masterSensors);
const newBie = !isDragNodeObject(dragObject); const newBie = !isDragNodeObject(dragObject);
const forceCopyState = isDragNodeObject(dragObject) && dragObject.nodes.some((node) => node.isSlotRoot); const forceCopyState = isDragNodeObject(dragObject) && dragObject.nodes.some((node) => node.isSlot());
const isBoostFromDragAPI = boostEvent.type.substr(0, 4) === 'drag'; const isBoostFromDragAPI = boostEvent.type.substr(0, 4) === 'drag';
let lastSensor: ISensor | undefined; let lastSensor: ISensor | undefined;
@ -319,16 +328,20 @@ export class Dragon {
this.emitter.emit('dragstart', locateEvent); this.emitter.emit('dragstart', locateEvent);
}; };
// route: drag-move
const move = (e: MouseEvent | DragEvent) => { const move = (e: MouseEvent | DragEvent) => {
if (isBoostFromDragAPI) { if (isBoostFromDragAPI) {
e.preventDefault(); e.preventDefault();
} }
if (this._dragging) { if (this._dragging) {
// process dragging
drag(e); drag(e);
return; return;
} }
// first move check is shaken
if (isShaken(boostEvent, e)) { if (isShaken(boostEvent, e)) {
// is shaken dragstart
dragstart(); dragstart();
drag(e); drag(e);
} }
@ -341,6 +354,7 @@ export class Dragon {
didDrop = true; didDrop = true;
}; };
// end-tail drag process
const over = (e?: any) => { const over = (e?: any) => {
if (e && isDragEvent(e)) { if (e && isDragEvent(e)) {
e.preventDefault(); e.preventDefault();
@ -366,6 +380,7 @@ export class Dragon {
exception = ex; exception = ex;
} }
} }
designer.clearLocation();
handleEvents((doc) => { handleEvents((doc) => {
if (isBoostFromDragAPI) { if (isBoostFromDragAPI) {
@ -386,6 +401,7 @@ export class Dragon {
} }
}; };
// create drag locate event
const createLocateEvent = (e: MouseEvent | DragEvent): LocateEvent => { const createLocateEvent = (e: MouseEvent | DragEvent): LocateEvent => {
const evt: any = { const evt: any = {
type: 'LocateEvent', type: 'LocateEvent',
@ -396,12 +412,14 @@ export class Dragon {
const sourceDocument = e.view?.document; const sourceDocument = e.view?.document;
// event from current document
if (!sourceDocument || sourceDocument === document) { if (!sourceDocument || sourceDocument === document) {
evt.globalX = e.clientX; evt.globalX = e.clientX;
evt.globalY = e.clientY; evt.globalY = e.clientY;
} else { } else { // event from simulator sandbox
let srcSim: ISimulatorHost | undefined; let srcSim: ISimulatorHost | undefined;
const lastSim = lastSensor && isSimulatorHost(lastSensor) ? lastSensor : null; const lastSim = lastSensor && isSimulatorHost(lastSensor) ? lastSensor : null;
// check source simulator
if (lastSim && lastSim.contentDocument === sourceDocument) { if (lastSim && lastSim.contentDocument === sourceDocument) {
srcSim = lastSim; srcSim = lastSim;
} else { } else {
@ -411,6 +429,7 @@ export class Dragon {
} }
} }
if (srcSim) { if (srcSim) {
// transform point by simulator
const g = srcSim.viewport.toGlobalPoint(e); const g = srcSim.viewport.toGlobalPoint(e);
evt.globalX = g.clientX; evt.globalX = g.clientX;
evt.globalY = g.clientY; evt.globalY = g.clientY;
@ -459,9 +478,7 @@ export class Dragon {
const { dataTransfer } = boostEvent; const { dataTransfer } = boostEvent;
if (dataTransfer) { if (dataTransfer) {
// dataTransfer.setDragImage(this.emptyImage, 0, 0);
dataTransfer.effectAllowed = 'all'; dataTransfer.effectAllowed = 'all';
// dataTransfer.dropEffect = newBie || forceCopyState ? 'copy' : 'move';
try { try {
dataTransfer.setData('application/json', '{}'); dataTransfer.setData('application/json', '{}');

View File

@ -1,7 +1,6 @@
import { Designer } from './designer'; import { Designer } from './designer';
// TODO: // TODO: use focus-tracker replace
// 当前激活区域管理
class Focusing { class Focusing {
focusDesigner?: Designer; focusDesigner?: Designer;
} }

View File

@ -1,117 +0,0 @@
import { Hotkey, isFormEvent } from '@ali/lowcode-globals';
import { focusing } from './focusing';
import { insertChildren } from '../document';
import clipboard from './clipboard';
export const hotkey = new Hotkey();
// hotkey binding
hotkey.bind(['backspace', 'del'], (e: KeyboardEvent) => {
const doc = focusing.focusDesigner?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const sel = doc.selection;
const topItems = sel.getTopNodes();
// TODO: check can remove
topItems.forEach(node => {
doc.removeNode(node);
});
sel.clear();
});
hotkey.bind('escape', (e: KeyboardEvent) => {
// const currentFocus = focusing.current;
const sel = focusing.focusDesigner?.currentDocument?.selection;
if (isFormEvent(e) || !sel) {
return;
}
e.preventDefault();
sel.clear();
// currentFocus.esc();
});
// command + c copy command + x cut
hotkey.bind(['command+c', 'ctrl+c', 'command+x', 'ctrl+x'], (e, action) => {
const doc = focusing.focusDesigner?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
/*
const doc = getCurrentDocument();
if (isFormEvent(e) || !doc || !(focusing.id === 'outline' || focusing.id === 'canvas')) {
return;
}
e.preventDefault();
*/
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) return;
const componentsMap = {};
const componentsTree = selected.map(item => item.export(false));
const data = { type: 'nodeSchema', componentsMap, componentsTree };
clipboard.setData(data);
/*
const cutMode = action.indexOf('x') > 0;
if (cutMode) {
const parentNode = selected.getParent();
parentNode.select();
selected.remove();
}
*/
});
// command + v paste
hotkey.bind(['command+v', 'ctrl+v'], (e) => {
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !designer || !doc) {
return;
}
clipboard.waitPasteData(e, ({ componentsTree }) => {
if (componentsTree) {
const { target, index } = designer.getSuitableInsertion() || {};
if (!target) {
return;
}
const nodes = insertChildren(target, componentsTree, index);
if (nodes) {
doc.selection.selectAll(nodes.map(o => o.id));
setTimeout(() => designer.activeTracker.track(nodes[0]), 10);
}
}
});
});
// command + z undo
hotkey.bind(['command+z', 'ctrl+z'], (e) => {
const his = focusing.focusDesigner?.currentHistory;
if (isFormEvent(e) || !his) {
return;
}
e.preventDefault();
his.back();
});
// command + shift + z redo
hotkey.bind(['command+y', 'ctrl+y', 'command+shift+z'], (e) => {
const his = focusing.focusDesigner?.currentHistory;
if (isFormEvent(e) || !his) {
return;
}
e.preventDefault();
his.forward();
});
hotkey.mount(window);

View File

@ -1,8 +1,9 @@
import './builtin-hotkey';
export * from './designer'; export * from './designer';
export * from './designer-view'; export * from './designer-view';
export * from './dragon'; export * from './dragon';
export * from './hotkey'; export * from './detecting';
export * from './hovering';
export * from './location'; export * from './location';
export * from './offset-observer'; export * from './offset-observer';
export * from './scroller'; export * from './scroller';
export * from './setting';

View File

@ -1,8 +1,8 @@
import { DocumentModel, Node as ComponentNode, NodeParent } from '../document'; import { DocumentModel, Node as ComponentNode, ParentalNode } from '../document';
import { LocateEvent } from './dragon'; import { LocateEvent } from './dragon';
export interface LocationData { export interface LocationData {
target: NodeParent; // shadowNode | ConditionFlow | ElementNode | RootNode target: ParentalNode; // shadowNode | ConditionFlow | ElementNode | RootNode
detail: LocationDetail; detail: LocationDetail;
source: string; source: string;
event: LocateEvent; event: LocateEvent;
@ -27,7 +27,7 @@ export interface LocationChildrenDetail {
rect?: Rect; rect?: Rect;
align?: 'V' | 'H'; align?: 'V' | 'H';
}; };
focus?: { type: 'slots' } | { type: 'node'; node: NodeParent }; focus?: { type: 'slots' } | { type: 'node'; node: ParentalNode };
} }
export interface LocationPropDetail { export interface LocationPropDetail {
@ -126,7 +126,7 @@ export function getWindow(elem: Element | Document): Window {
} }
export class DropLocation { export class DropLocation {
readonly target: NodeParent; readonly target: ParentalNode;
readonly detail: LocationDetail; readonly detail: LocationDetail;
readonly event: LocateEvent; readonly event: LocateEvent;
readonly source: string; readonly source: string;

View File

@ -1,5 +1,5 @@
import { obx, computed } from '@ali/lowcode-globals'; import { obx, computed } from '@ali/lowcode-editor-core';
import { uniqueId } from '@ali/lowcode-globals'; import { uniqueId } from '@ali/lowcode-utils';
import { INodeSelector, IViewport } from '../simulator'; import { INodeSelector, IViewport } from '../simulator';
import { isRootNode, Node } from '../document'; import { isRootNode, Node } from '../document';
@ -82,6 +82,8 @@ export class OffsetObserver {
private isRoot: boolean; private isRoot: boolean;
readonly node: Node; readonly node: Node;
readonly compute: () => void;
constructor(readonly nodeInstance: INodeSelector) { constructor(readonly nodeInstance: INodeSelector) {
const { node, instance } = nodeInstance; const { node, instance } = nodeInstance;
this.node = node; this.node = node;
@ -103,7 +105,7 @@ export class OffsetObserver {
return; return;
} }
const rect = host.computeComponentInstanceRect(instance!, node.componentMeta.rectSelector); const rect = host.computeComponentInstanceRect(instance!, node.componentMeta.rootSelector);
if (!rect) { if (!rect) {
this.hasOffset = false; this.hasOffset = false;
@ -121,6 +123,8 @@ export class OffsetObserver {
this.pid = pid = (window as any).requestIdleCallback(compute); this.pid = pid = (window as any).requestIdleCallback(compute);
}; };
this.compute = compute;
// try first // try first
compute(); compute();
// try second, ensure the dom mounted // try second, ensure the dom mounted
@ -133,6 +137,10 @@ export class OffsetObserver {
} }
this.pid = undefined; this.pid = undefined;
} }
isPurged() {
return this.pid == null;
}
} }
export function createOffsetObserver(nodeInstance: INodeSelector): OffsetObserver | null { export function createOffsetObserver(nodeInstance: INodeSelector): OffsetObserver | null {

View File

@ -1,4 +1,4 @@
import { isElement } from '@ali/lowcode-globals'; import { isElement } from '@ali/lowcode-utils';
export class ScrollTarget { export class ScrollTarget {
get left() { get left() {

View File

@ -0,0 +1,3 @@
export * from './setting-field';
export * from './setting-top-entry';
export * from './setting-entry';

View File

@ -0,0 +1,17 @@
import { SettingTarget } from '@ali/lowcode-types';
import { ComponentMeta } from '../../component-meta';
import { Designer } from '../designer';
import { Node } from '../../document';
export interface SettingEntry extends SettingTarget {
readonly nodes: Node[];
readonly componentMeta: ComponentMeta | null;
readonly designer: Designer;
// 顶端
readonly top: SettingEntry;
// 父级
readonly parent: SettingEntry;
get(propName: string | number): SettingEntry;
}

View File

@ -0,0 +1,145 @@
import { TitleContent, isDynamicSetter, SetterType, DynamicSetter, FieldExtraProps, FieldConfig, CustomView, isCustomView } from '@ali/lowcode-types';
import { Transducer } from './utils';
import { SettingPropEntry } from './setting-prop-entry';
import { SettingEntry } from './setting-entry';
import { computed, obx } from '@ali/lowcode-editor-core';
import { cloneDeep } from '@ali/lowcode-utils';
export class SettingField extends SettingPropEntry implements SettingEntry {
readonly isSettingField = true;
readonly isRequired: boolean;
readonly transducer: Transducer;
extraProps: FieldExtraProps;
// ==== dynamic properties ====
private _title?: TitleContent;
get title() {
// FIXME! intl
return this._title || (typeof this.name === 'number' ? `项目 ${this.name}` : this.name);
}
private _setter?: SetterType | DynamicSetter;
@computed get setter(): SetterType | null {
if (!this._setter) {
return null;
}
if (isDynamicSetter(this._setter)) {
return this._setter.call(this,this);
}
return this._setter;
}
@obx.ref private _expanded = true;
get expanded(): boolean {
return this._expanded;
}
setExpanded(value: boolean) {
this._expanded = value;
}
constructor(readonly parent: SettingEntry, config: FieldConfig, settingFieldCollector?: (name: string | number, field: SettingField) => void) {
super(parent, config.name, config.type);
const { title, items, setter, extraProps, ...rest } = config;
this._title = title;
this._setter = setter;
this.extraProps = {
...rest,
...extraProps,
};
this.isRequired = config.isRequired || (setter as any)?.isRequired;
this._expanded = extraProps?.defaultCollapsed ? false : true;
// initial items
if (this.type === 'group' && items) {
this.initItems(items, settingFieldCollector);
} else if (settingFieldCollector && config.name) {
settingFieldCollector(config.name, this);
}
// compatiable old config
this.transducer = new Transducer(this, { setter });
}
private _items: Array<SettingField | CustomView> = [];
get items(): Array<SettingField | CustomView> {
return this._items;
}
private initItems(items: Array<FieldConfig | CustomView>, settingFieldCollector?: { (name: string | number, field: SettingField): void; (name: string, field: SettingField): void; }) {
this._items = items.map((item) => {
if (isCustomView(item)) {
return item;
}
return new SettingField(this, item, settingFieldCollector);
});
}
private disposeItems() {
this._items.forEach(item => isSettingField(item) && item.purge());
this._items = [];
}
// 创建子配置项,通常用于 object/array 类型数据
createField(config: FieldConfig): SettingField {
return new SettingField(this, config);
}
purge() {
this.disposeItems();
}
private hotValue: any;
// ======= compatibles for vision ======
setValue(val: any, isHotValue?: boolean, force?: boolean, extraOptions?: any) {
if (isHotValue) {
this.setHotValue(val, extraOptions);
return;
}
super.setValue(val, false, false, extraOptions);
}
getHotValue(): any {
if (this.hotValue) {
return this.hotValue;
}
// avoid View modify
let v = cloneDeep(this.getMockOrValue());
if (v == null) {
v = this.extraProps.defaultValue;
}
return this.transducer.toHot(v);
}
setHotValue(data: any, options?: any) {
this.hotValue = data;
const v = this.transducer.toNative(data);
if (this.isUseVariable()) {
const ov = this.getValue();
this.setValue({
type: 'JSExpression',
value: ov.value,
mock: v,
}, false, false, options);
} else {
this.setValue(v, false, false, options);
}
// dirty fix list setter
if (Array.isArray(data) && data[0] && data[0].__sid__) {
return;
}
this.valueChange();
}
onEffect(action: () => void): () => void {
return this.designer.autorun(action, true);
}
}
export function isSettingField(obj: any): obj is SettingField {
return obj && obj.isSettingField;
}

View File

@ -0,0 +1,326 @@
import { obx, computed, autorun } from '@ali/lowcode-editor-core';
import { IEditor, isJSExpression } from '@ali/lowcode-types';
import { uniqueId } from '@ali/lowcode-utils';
import { SettingEntry } from './setting-entry';
import { Node } from '../../document';
import { ComponentMeta } from '../../component-meta';
import { Designer } from '../designer';
import { EventEmitter } from 'events';
export class SettingPropEntry implements SettingEntry {
// === static properties ===
readonly editor: IEditor;
readonly isSameComponent: boolean;
readonly isMultiple: boolean;
readonly isSingle: boolean;
readonly nodes: Node[];
readonly componentMeta: ComponentMeta | null;
readonly designer: Designer;
readonly top: SettingEntry;
readonly isGroup: boolean;
readonly type: 'field' | 'group';
readonly id = uniqueId('entry');
readonly emitter = new EventEmitter();
// ==== dynamic properties ====
@obx.ref private _name: string | number;
get name() {
return this._name;
}
@computed get path() {
const path = this.parent.path.slice();
if (this.type === 'field') {
path.push(this.name);
}
return path;
}
extraProps: any = {};
constructor(readonly parent: SettingEntry, name: string | number, type?: 'field' | 'group') {
if (type == null) {
const c = typeof name === 'string' ? name.substr(0, 1) : '';
if (c === '#') {
this.type = 'group';
} else {
this.type = 'field';
}
} else {
this.type = type;
}
// initial self properties
this._name = name;
this.isGroup = this.type === 'group';
// copy parent static properties
this.editor = parent.editor;
this.nodes = parent.nodes;
this.componentMeta = parent.componentMeta;
this.isSameComponent = parent.isSameComponent;
this.isMultiple = parent.isMultiple;
this.isSingle = parent.isSingle;
this.designer = parent.designer;
this.top = parent.top;
}
getId() {
return this.id;
}
setKey(key: string | number) {
if (this.type !== 'field') {
return;
}
const propName = this.path.join('.');
let l = this.nodes.length;
while (l-- > 1) {
this.nodes[l].getProp(propName, true)!.key = key;
}
this._name = key;
}
getKey() {
return this._name;
}
remove() {
if (this.type !== 'field') {
return;
}
const propName = this.path.join('.');
let l = this.nodes.length;
while (l-- > 1) {
this.nodes[l].getProp(propName)?.remove();
}
}
// ====== 当前属性读写 =====
/**
*
* -1
* 0
* 1
* 2
*/
@computed get valueState(): number {
if (this.type !== 'field') {
const { getValue } = this.extraProps;
return getValue ? (getValue(this, undefined) === undefined ? 0 : 1) : 0;
}
const propName = this.path.join('.');
const first = this.nodes[0].getProp(propName)!;
let l = this.nodes.length;
let state = 2;
while (l-- > 1) {
const next = this.nodes[l].getProp(propName, false);
const s = first.compare(next);
if (s > 1) {
return -1;
}
if (s === 1) {
state = 1;
}
}
if (state === 2 && first.isUnset()) {
return 0;
}
return state;
}
/**
*
*/
@computed getValue(): any {
let val: any = undefined;
if (this.type === 'field') {
val = this.parent.getPropValue(this.name);
}
const { getValue } = this.extraProps;
try {
return getValue ? getValue(this, val) : val;
} catch (e) {
// todo: add log
console.warn(e);
return val;
}
}
/**
*
*/
setValue(val: any, isHotValue?: boolean, force?: boolean, extraOptions?: any) {
if (this.type === 'field') {
this.parent.setPropValue(this.name, val);
}
if (!extraOptions) {
extraOptions = {};
}
const { setValue } = this.extraProps;
if (setValue && !extraOptions.disableMutator) {
try {
setValue(this, val);
} catch (e) {
// todo: add log
console.warn(e);
}
}
}
/**
*
*/
clearValue() {
if (this.type === 'field') {
this.parent.clearPropValue(this.name);
}
const { setValue } = this.extraProps;
if (setValue) {
try {
setValue(this, undefined);
} catch (e) {
// todo: add log
console.warn(e);
}
}
}
/**
*
*/
get(propName: string | number) {
const path = this.path.concat(propName).join('.');
return this.top.get(path);
}
/**
*
*/
setPropValue(propName: string | number, value: any) {
const path = this.path.concat(propName).join('.');
this.top.setPropValue(path, value);
}
/**
*
*/
clearPropValue(propName: string | number) {
const path = this.path.concat(propName).join('.');
this.top.clearPropValue(path);
}
/**
*
*/
getPropValue(propName: string | number): any {
return this.top.getPropValue(this.path.concat(propName).join('.'));
}
/**
*
*/
getExtraPropValue(propName: string) {
return this.top.getExtraPropValue(propName);
}
/**
*
*/
setExtraPropValue(propName: string, value: any) {
this.top.setExtraPropValue(propName, value);
}
// ======= compatibles for vision ======
getNode() {
return this.nodes[0];
}
getName(): string {
return this.path.join('.');
}
getProps() {
return this.top;
}
// add settingfield props
get props() {
return this.top;
}
onValueChange(func: () => any) {
this.emitter.on('valuechange', func);
return () => {
this.emitter.removeListener('valuechange', func);
};
}
/**
* @deprecated
*/
valueChange() {
this.emitter.emit('valuechange');
}
getDefaultValue() {
return this.extraProps.defaultValue;
}
isIgnore() {
return false;
}
/*
getConfig<K extends keyof IPropConfig>(configName?: K): IPropConfig[K] | IPropConfig {
if (configName) {
return this.config[configName];
}
return this.config;
}
*/
getVariableValue() {
const v = this.getValue();
if (isJSExpression(v)) {
return v.value;
}
return '';
}
setVariableValue(value: string) {
const v = this.getValue();
this.setValue({
type: 'JSExpression',
value,
mock: isJSExpression(v) ? v.mock : v,
});
}
setUseVariable(flag: boolean) {
if (this.isUseVariable() === flag) {
return;
}
const v = this.getValue();
if (isJSExpression(v)) {
this.setValue(v.mock);
} else {
this.setValue({
type: 'JSExpression',
value: '',
mock: v,
});
}
}
isUseVariable() {
return isJSExpression(this.getValue());
}
get useVariable() {
return this.isUseVariable();
}
getMockOrValue() {
const v = this.getValue();
if (isJSExpression(v)) {
return v.mock;
}
return v;
}
}

View File

@ -0,0 +1,234 @@
import { EventEmitter } from 'events';
import { CustomView, isCustomView, IEditor } from '@ali/lowcode-types';
import { computed } from '@ali/lowcode-editor-core';
import { SettingEntry } from './setting-entry';
import { SettingField, isSettingField } from './setting-field';
import { SettingPropEntry } from './setting-prop-entry';
import { Node } from '../../document';
import { ComponentMeta } from '../../component-meta';
import { Designer } from '../designer';
function generateSessionId(nodes: Node[]) {
return nodes
.map((node) => node.id)
.sort()
.join(',');
}
export class SettingTopEntry implements SettingEntry {
private emitter = new EventEmitter();
private _items: Array<SettingField | CustomView> = [];
private _componentMeta: ComponentMeta | null = null;
private _isSame: boolean = true;
private _settingFieldMap: { [prop: string]: SettingField } = {};
readonly path = [];
readonly top = this;
readonly parent = this;
get componentMeta() {
return this._componentMeta;
}
get items() {
return this._items;
}
/**
*
*/
get isSameComponent(): boolean {
return this._isSame;
}
/**
*
*/
get isSingle(): boolean {
return this.nodes.length === 1;
}
/**
*
*/
get isMultiple(): boolean {
return this.nodes.length > 1;
}
readonly id: string;
readonly first: Node;
readonly designer: Designer;
constructor(readonly editor: IEditor, readonly nodes: Node[]) {
if (nodes.length < 1) {
throw new ReferenceError('nodes should not be empty');
}
this.id = generateSessionId(nodes);
this.first = nodes[0];
this.designer = this.first.document.designer;
// setups
this.setupComponentMeta();
// clear fields
this.setupItems();
}
private setupComponentMeta() {
// todo: enhance compile a temp configure.compiled
const first = this.first;
const meta = first.componentMeta;
const l = this.nodes.length;
let theSame = true;
for (let i = 1; i < l; i++) {
const other = this.nodes[i];
if (other.componentMeta !== meta) {
theSame = false;
break;
}
}
if (theSame) {
this._isSame = true;
this._componentMeta = meta;
} else {
this._isSame = false;
this._componentMeta = null;
}
}
private setupItems() {
if (this.componentMeta) {
const settingFieldMap: { [prop: string]: SettingField } = {};
const settingFieldCollector = (name: string | number, field: SettingField) => {
settingFieldMap[name] = field;
}
this._items = this.componentMeta.configure.map((item) => {
if (isCustomView(item)) {
return item;
}
return new SettingField(this, item as any, settingFieldCollector);
});
this._settingFieldMap = settingFieldMap;
}
}
/**
*
*/
@computed getValue(): any {
this.first.propsData;
}
/**
*
*/
setValue(val: any) {
this.setProps(val);
// TODO: emit value change
}
/**
*
*/
get(propName: string | number): SettingPropEntry {
return this._settingFieldMap[propName] || (new SettingPropEntry(this, propName));
}
/**
*
*/
setPropValue(propName: string, value: any) {
this.nodes.forEach((node) => {
node.setPropValue(propName, value);
});
}
/**
*
*/
clearPropValue(propName: string) {
this.nodes.forEach((node) => {
node.clearPropValue(propName);
});
}
/**
*
*/
getPropValue(propName: string): any {
return this.first.getProp(propName, true)?.getValue();
}
/**
*
*/
getExtraPropValue(propName: string) {
return this.first.getExtraProp(propName, false)?.getValue();
}
/**
*
*/
setExtraPropValue(propName: string, value: any) {
this.nodes.forEach((node) => {
node.getExtraProp(propName, true)?.setValue(value);
});
}
// 设置多个属性值,替换原有值
setProps(data: object) {
this.nodes.forEach((node) => {
node.setProps(data as any);
});
}
// 设置多个属性值,和原有值合并
mergeProps(data: object) {
this.nodes.forEach((node) => {
node.mergeProps(data as any);
});
}
private disposeItems() {
this._items.forEach((item) => isPurgeable(item) && item.purge());
this._items = [];
}
purge() {
this.disposeItems();
this.emitter.removeAllListeners();
}
// ==== compatibles for vision =====
getProp(propName: string | number) {
return this.get(propName);
}
// ==== copy some Node api =====
// `VE.Node.getProps`
getStatus() {
}
setStatus() {
}
getChildren() {
// this.nodes.map()
}
getDOMNode() {
}
getId() {
return this.id;
}
getPage() {
return this.first.document;
}
}
interface Purgeable {
purge(): void;
}
function isPurgeable(obj: any): obj is Purgeable {
return obj && obj.purge;
}

View File

@ -0,0 +1,67 @@
// all this file for polyfill vision logic
import { isValidElement } from 'react';
import { isSetterConfig } from '@ali/lowcode-types';
import { getSetter } from '@ali/lowcode-editor-core';
function getHotterFromSetter(setter) {
return setter && (setter.Hotter || (setter.type && setter.type.Hotter)) || []; // eslint-disable-line
}
function getTransducerFromSetter(setter) {
return setter && (
setter.transducer || setter.Transducer
|| (setter.type && (setter.type.transducer || setter.type.Transducer))
) || null; // eslint-disable-line
}
function combineTransducer(transducer, arr, context) {
if (!transducer && Array.isArray(arr)) {
const [toHot, toNative] = arr;
transducer = { toHot, toNative };
}
return {
toHot: (transducer && transducer.toHot || (x => x)).bind(context), // eslint-disable-line
toNative: (transducer && transducer.toNative || (x => x)).bind(context), // eslint-disable-line
};
}
export class Transducer {
constructor(context, config) {
let { setter } = config;
// 1. validElement
// 2. SetterConfig
// 3. SetterConfig[]
if (Array.isArray(setter)) {
setter = setter[0];
} else if (isValidElement(setter) && setter.type.displayName === 'MixedSetter') {
setter = setter.props.setters[0];
} else if (typeof setter === 'object' && setter.componentName === 'MixedSetter') {
setter = setter.props.setters?.[0];
}
if (isSetterConfig(setter)) {
setter = setter.componentName;
}
if (typeof setter === 'string') {
setter = getSetter(setter)?.component;
}
this.setterTransducer = combineTransducer(
getTransducerFromSetter(setter),
getHotterFromSetter(setter),
context,
);
this.context = context;
}
toHot(data) {
return this.setterTransducer.toHot(data);
}
toNative(data) {
return this.setterTransducer.toNative(data);
}
}

View File

@ -1,170 +0,0 @@
/*
* 基础的 DPL 定义使用了 kuma base 的定义,参考:
* https://github.com/uxcore/kuma-base/tree/master/variables
*/
/**
* ===========================================================
* ==================== Font Family ==========================
* ===========================================================
*/
/*
* @font-family: "STHeiti", "Microsoft Yahei", "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
*/
@font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Helvetica, Arial, sans-serif;
@font-family-code: Monaco, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Helvetica, Arial, sans-serif;
/**
* ===========================================================
* ===================== Color DPL ===========================
* ===========================================================
*/
@brand-color-1: rgba(0, 108, 255, 1);
@brand-color-2: rgba(25, 122, 255, 1);
@brand-color-3: rgba(0, 96, 229, 1);
@brand-color-1-3: rgba(0, 108, 255, 0.6);
@brand-color-1-4: rgba(0, 108, 255, 0.4);
@brand-color-1-5: rgba(0, 108, 255, 0.3);
@brand-color-1-6: rgba(0, 108, 255, 0.2);
@brand-color-1-7: rgba(0, 108, 255, 0.1);
@brand-color: @brand-color-1;
@white-alpha-1: rgb(255, 255, 255); // W-1
@white-alpha-2: rgba(255, 255, 255, 0.8); // W-2 A80
@white-alpha-3: rgba(255, 255, 255, 0.6); // W-3 A60
@white-alpha-4: rgba(255, 255, 255, 0.4); // W-4 A40
@white-alpha-5: rgba(255, 255, 255, 0.3); // W-5 A30
@white-alpha-6: rgba(255, 255, 255, 0.2); // W-6 A20
@white-alpha-7: rgba(255, 255, 255, 0.1); // W-7 A10
@white-alpha-8: rgba(255, 255, 255, 0.06); // W-8 A6
@dark-alpha-1: rgba(0, 0, 0, 1); // D-1 A100
@dark-alpha-2: rgba(0, 0, 0, 0.8); // D-2 A80
@dark-alpha-3: rgba(0, 0, 0, 0.6); // D-3 A60
@dark-alpha-4: rgba(0, 0, 0, 0.4); // D-4 A40
@dark-alpha-5: rgba(0, 0, 0, 0.3); // D-5 A30
@dark-alpha-6: rgba(0, 0, 0, 0.2); // D-6 A20
@dark-alpha-7: rgba(0, 0, 0, 0.1); // D-7 A10
@dark-alpha-8: rgba(0, 0, 0, 0.06); // D-8 A6
@dark-alpha-9: rgba(0, 0, 0, 0.04); // D-9 A4
@normal-alpha-1: rgba(31, 56, 88, 1); // N-1 A100
@normal-alpha-2: rgba(31, 56, 88, 0.8); // N-2 A80
@normal-alpha-3: rgba(31, 56, 88, 0.6); // N-3 A60
@normal-alpha-4: rgba(31, 56, 88, 0.4); // N-4 A40
@normal-alpha-5: rgba(31, 56, 88, 0.3); // N-5 A30
@normal-alpha-6: rgba(31, 56, 88, 0.2); // N-6 A20
@normal-alpha-7: rgba(31, 56, 88, 0.1); // N-7 A10
@normal-alpha-8: rgba(31, 56, 88, 0.06); // N-8 A6
@normal-alpha-9: rgba(31, 56, 88, 0.04); // N-9 A4
@normal-3: #77879c;
@normal-4: #a3aebd;
@normal-5: #bac3cc;
@normal-6: #d1d7de;
@gray-dark: #333; // N2_4
@gray: #666; // N2_3
@gray-light: #999; // N2_2
@gray-lighter: #ccc; // N2_1
@brand-secondary: #2c2f33; // B2_3
// 补色
@brand-complement: #00b3e8; // B3_1
// 复合
@brand-comosite: #00c587; // B3_2
// 浓度
@brand-deep: #73461d; // B3_3
// F1-1
@brand-danger: rgb(240, 70, 49);
// F1-2 (10% white)
@brand-danger-hover: rgba(240, 70, 49, 0.9);
// F1-3 (5% black)
@brand-danger-focus: rgba(240, 70, 49, 0.95);
// F2-1
@brand-warning: rgb(250, 189, 14);
// F3-1
@brand-success: rgb(102, 188, 92);
// F4-1
@brand-link: rgb(102, 188, 92);
// F4-2
@brand-link-hover: #2e76a6;
// F1-1-7 A10
@brand-danger-alpha-7: rgba(240, 70, 49, 0.9);
// F1-1-8 A6
@brand-danger-alpha-8: rgba(240, 70, 49, 0.8);
// F2-1-2 A80
@brand-warning-alpha-2: rgba(250, 189, 14, 0.8);
// F2-1-7 A10
@brand-warning-alpha-7: rgba(250, 189, 14, 0.9);
// F3-1-2 A80
@brand-success-alpha-2: rgba(102, 188, 92, 0.8);
// F3-1-7 A10
@brand-success-alpha-7: rgba(102, 188, 92, 0.9);
// F4-1-7 A10
@brand-link-alpha-7: rgba(102, 188, 92, 0.9);
// 文本色
@text-primary-color: @dark-alpha-3;
@text-secondary-color: @normal-alpha-3;
@text-thirdary-color: @dark-alpha-4;
@text-disabled-color: @normal-alpha-5;
@text-helper-color: @dark-alpha-4;
@text-danger-color: @brand-danger;
@text-ali-color: #ec6c00;
/**
* ===========================================================
* =================== Shadow Box ============================
* ===========================================================
*/
@box-shadow-1: 0 1px 4px 0 rgba(31, 56, 88, 0.15); // 1 级阴影,物体由原来存在于底面的物体展开,物体和底面关联紧密
@box-shadow-2: 0 2px 10px 0 rgba(31, 56, 88, 0.15); // 2 级阴影hover状态物体层级较高
@box-shadow-3: 0 4px 15px 0 rgba(31, 56, 88, 0.15); // 3 级阴影,当物体层级高于所有界面元素,弹窗用
/**
* ===========================================================
* ================= FontSize of Level =======================
* ===========================================================
*/
@fontSize-1: 26px;
@fontSize-2: 20px;
@fontSize-3: 16px;
@fontSize-4: 14px;
@fontSize-5: 12px;
@fontLineHeight-1: 38px;
@fontLineHeight-2: 30px;
@fontLineHeight-3: 26px;
@fontLineHeight-4: 24px;
@fontLineHeight-5: 20px;
/**
* ===========================================================
* ================= FontSize of Level =======================
* ===========================================================
*/
@global-border-radius: 3px;
@input-border-radius: 3px;
@popup-border-radius: 6px;
/**
* ===========================================================
* ===================== Transistion =========================
* ===========================================================
*/
@transition-duration: 0.3s;
@transition-ease: cubic-bezier(0.23, 1, 0.32, 1);
@transition-delay: 0s;

View File

@ -1,24 +1,24 @@
import { import { computed, obx } from '@ali/lowcode-editor-core';
RootSchema, import { NodeData, isJSExpression, isDOMText, NodeSchema, isNodeSchema, RootSchema } from '@ali/lowcode-types';
NodeData, import { EventEmitter } from 'events';
isJSExpression,
isDOMText,
NodeSchema,
computed,
obx,
autorun,
isNodeSchema,
uniqueId,
} from '@ali/lowcode-globals';
import { Project } from '../project'; import { Project } from '../project';
import { ISimulatorHost } from '../simulator'; import { ISimulatorHost } from '../simulator';
import { ComponentMeta } from '../component-meta'; import { ComponentMeta } from '../component-meta';
import { isDragNodeDataObject, DragNodeObject, DragNodeDataObject, DropLocation } from '../designer'; import { isDragNodeDataObject, DragNodeObject, DragNodeDataObject, DropLocation } from '../designer';
import { Node, isNodeParent, insertChildren, insertChild, NodeParent, isNode } from './node/node'; import { Node, insertChildren, insertChild, isNode, RootNode, ParentalNode } from './node/node';
import { Selection } from './selection'; import { Selection } from './selection';
import { RootNode } from './node/root-node';
import { History } from './history'; import { History } from './history';
import { Prop } from './node/props/prop'; import { TransformStage } from './node';
import { uniqueId } from '@ali/lowcode-utils';
import ModalNodesManager from './node/modalNodesManager';
export type GetDataType<T, NodeType> = T extends undefined
? NodeType extends {
schema: infer R;
}
? R
: any
: T;
export class DocumentModel { export class DocumentModel {
/** /**
@ -28,7 +28,7 @@ export class DocumentModel {
/** /**
* *
*/ */
readonly id: string = uniqueId('doc'); id: string = uniqueId('doc');
/** /**
* *
*/ */
@ -42,6 +42,9 @@ export class DocumentModel {
@obx.val private nodes = new Set<Node>(); @obx.val private nodes = new Set<Node>();
private seqId = 0; private seqId = 0;
private _simulator?: ISimulatorHost; private _simulator?: ISimulatorHost;
private emitter: EventEmitter;
private rootNodeVisitorMap: { [visitorName: string]: any } = {};
private modalNodesManager: ModalNodesManager;
/** /**
* *
@ -58,7 +61,7 @@ export class DocumentModel {
this.rootNode.getExtraProp('fileName', true)?.setValue(fileName); this.rootNode.getExtraProp('fileName', true)?.setValue(fileName);
} }
private _modalNode?: NodeParent; private _modalNode?: ParentalNode;
private _blank?: boolean; private _blank?: boolean;
get modalNode() { get modalNode() {
return this._modalNode; return this._modalNode;
@ -68,29 +71,47 @@ export class DocumentModel {
return this.modalNode || this.rootNode; return this.modalNode || this.rootNode;
} }
private inited = false;
constructor(readonly project: Project, schema?: RootSchema) { constructor(readonly project: Project, schema?: RootSchema) {
/*
// TODO
// use special purge process
autorun(() => { autorun(() => {
this.nodes.forEach((item) => { console.info(this.willPurgeSpace);
if (item.parent == null && item !== this.rootNode) {
item.purge();
}
});
}, true); }, true);
*/
this.emitter = new EventEmitter();
if (!schema) { if (!schema) {
this._blank = true; this._blank = true;
} }
this.rootNode = this.createRootNode(schema || { this.rootNode = this.createNode<RootNode>(
schema || {
componentName: 'Page', componentName: 'Page',
fileName: '' id: 'root',
}); fileName: '',
},
);
this.history = new History( this.history = new History(
() => this.schema, () => this.export(TransformStage.Serilize),
(schema) => this.import(schema as RootSchema, true), (schema) => this.import(schema as RootSchema, true),
); );
this.setupListenActiveNodes(); this.setupListenActiveNodes();
this.modalNodesManager = new ModalNodesManager(this);
this.inited = true;
}
@obx.val private willPurgeSpace: Node[] = [];
addWillPurge(node: Node) {
this.willPurgeSpace.push(node);
}
removeWillPurge(node: Node) {
const i = this.willPurgeSpace.indexOf(node);
if (i > -1) {
this.willPurgeSpace.splice(i, 1);
}
} }
@computed isBlank() { @computed isBlank() {
@ -98,12 +119,19 @@ export class DocumentModel {
} }
readonly designer = this.project.designer; readonly designer = this.project.designer;
// getAddonData(name: string) {
// const addon = this.addons.find((item) => item.name === name);
// if (addon) {
// return addon.exportData();
// }
// return this.addonsData[name];
// }
/** /**
* id * id
*/ */
nextId() { nextId() {
return (++this.seqId).toString(36).toLocaleLowerCase(); return this.id + (++this.seqId).toString(36).toLocaleLowerCase();
} }
/** /**
@ -130,7 +158,7 @@ export class DocumentModel {
/** /**
* schema * schema
*/ */
createNode(data: NodeData, slotFor?: Prop): Node { createNode<T extends Node = Node, C = undefined>(data: GetDataType<C, T>): T {
let schema: any; let schema: any;
if (isDOMText(data) || isJSExpression(data)) { if (isDOMText(data) || isJSExpression(data)) {
schema = { schema = {
@ -142,6 +170,9 @@ export class DocumentModel {
} }
let node: Node | null = null; let node: Node | null = null;
if (!this.inited) {
schema.id = null;
}
if (schema.id) { if (schema.id) {
node = this.getNode(schema.id); node = this.getNode(schema.id);
if (node && node.componentName === schema.componentName) { if (node && node.componentName === schema.componentName) {
@ -150,46 +181,45 @@ export class DocumentModel {
// will move to another position // will move to another position
// todo: this.activeNodes?.push(node); // todo: this.activeNodes?.push(node);
} }
node.internalSetSlotFor(slotFor);
node.import(schema, true); node.import(schema, true);
} else if (node) { } else if (node) {
node = null; node = null;
} }
} }
if (!node) { if (!node) {
node = new Node(this, schema, slotFor); node = new Node(this, schema);
// will add // will add
// todo: this.activeNodes?.push(node); // todo: this.activeNodes?.push(node);
} }
if (this.nodesMap.has(node.id)) { const origin = this.nodesMap.get(node.id);
this.nodesMap.get(node.id)!.internalSetParent(null); if (origin && origin !== node) {
// almost will not go here, ensure the id is unique
origin.internalSetWillPurge();
} }
this.nodesMap.set(node.id, node); this.nodesMap.set(node.id, node);
this.nodes.add(node); this.nodes.add(node);
return node; this.emitter.emit('nodecreate', node);
return node as any;
} }
private createRootNode(schema: RootSchema) { public destroyNode(node: Node) {
const node = new RootNode(this, schema); this.emitter.emit('nodedestroy', node);
this.nodesMap.set(node.id, node);
this.nodes.add(node);
return node;
} }
/** /**
* *
*/ */
insertNode(parent: NodeParent, thing: Node | NodeData, at?: number | null, copy?: boolean): Node { insertNode(parent: ParentalNode, thing: Node | NodeData, at?: number | null, copy?: boolean): Node {
return insertChild(parent, thing, at, copy); return insertChild(parent, thing, at, copy);
} }
/** /**
* *
*/ */
insertNodes(parent: NodeParent, thing: Node[] | NodeData[], at?: number | null, copy?: boolean) { insertNodes(parent: ParentalNode, thing: Node[] | NodeData[], at?: number | null, copy?: boolean) {
return insertChildren(parent, thing, at, copy); return insertChildren(parent, thing, at, copy);
} }
@ -224,6 +254,14 @@ export class DocumentModel {
this.selection.remove(node.id); this.selection.remove(node.id);
node.remove(); node.remove();
} }
getAddonData(name: string) {
const addon = this.getNode(name);
if (addon) {
// 无法确定是否有这个api
// return addon.exportData();
}
return addon;
}
@obx.ref private _dropLocation: DropLocation | null = null; @obx.ref private _dropLocation: DropLocation | null = null;
/** /**
@ -249,7 +287,7 @@ export class DocumentModel {
return null; return null;
} }
const wrapper = this.createNode(schema); const wrapper = this.createNode(schema);
if (isNodeParent(wrapper)) { if (wrapper.isParental()) {
const first = nodes[0]; const first = nodes[0];
// TODO: check nesting rules x 2 // TODO: check nesting rules x 2
insertChild(first.parent!, wrapper, first.index); insertChild(first.parent!, wrapper, first.index);
@ -270,11 +308,15 @@ export class DocumentModel {
} }
import(schema: RootSchema, checkId = false) { import(schema: RootSchema, checkId = false) {
this.rootNode.import(schema, checkId); this.rootNode.import(schema as any, checkId);
// todo: purge something // todo: purge something
// todo: select added and active track added // todo: select added and active track added
} }
export(stage: TransformStage = TransformStage.Serilize) {
return this.rootNode.export(stage);
}
/** /**
* *
*/ */
@ -376,7 +418,7 @@ export class DocumentModel {
/** /**
* *
*/ */
open(): void { open(): DocumentModel {
const originState = this._opened; const originState = this._opened;
this._opened = true; this._opened = true;
if (originState === false) { if (originState === false) {
@ -387,6 +429,7 @@ export class DocumentModel {
} else { } else {
this.project.checkExclusive(this); this.project.checkExclusive(this);
} }
return this;
} }
/** /**
@ -409,7 +452,7 @@ export class DocumentModel {
// todo: // todo:
} }
checkNesting(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean { checkNesting(dropTarget: ParentalNode, dragObject: DragNodeObject | DragNodeDataObject): boolean {
let items: Array<Node | NodeSchema>; let items: Array<Node | NodeSchema>;
if (isDragNodeDataObject(dragObject)) { if (isDragNodeDataObject(dragObject)) {
items = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data]; items = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
@ -419,7 +462,7 @@ export class DocumentModel {
return items.every((item) => this.checkNestingDown(dropTarget, item)); return items.every((item) => this.checkNestingDown(dropTarget, item));
} }
checkDropTarget(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean { checkDropTarget(dropTarget: ParentalNode, dragObject: DragNodeObject | DragNodeDataObject): boolean {
let items: Array<Node | NodeSchema>; let items: Array<Node | NodeSchema>;
if (isDragNodeDataObject(dragObject)) { if (isDragNodeDataObject(dragObject)) {
items = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data]; items = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
@ -432,7 +475,7 @@ export class DocumentModel {
/** /**
* parentWhitelist * parentWhitelist
*/ */
checkNestingUp(parent: NodeParent, obj: NodeSchema | Node): boolean { checkNestingUp(parent: ParentalNode, obj: NodeSchema | Node): boolean {
if (isNode(obj) || isNodeSchema(obj)) { if (isNode(obj) || isNodeSchema(obj)) {
const config = isNode(obj) ? obj.componentMeta : this.getComponentMeta(obj.componentName); const config = isNode(obj) ? obj.componentMeta : this.getComponentMeta(obj.componentName);
if (config) { if (config) {
@ -446,10 +489,75 @@ export class DocumentModel {
/** /**
* childWhitelist * childWhitelist
*/ */
checkNestingDown(parent: NodeParent, obj: NodeSchema | Node): boolean { checkNestingDown(parent: ParentalNode, obj: NodeSchema | Node): boolean {
const config = parent.componentMeta; const config = parent.componentMeta;
return config.checkNestingDown(parent, obj) && this.checkNestingUp(parent, obj); return config.checkNestingDown(parent, obj) && this.checkNestingUp(parent, obj);
} }
// ======= compatibles for vision
getRoot() {
return this.rootNode;
}
// add toData
toData() {
const node = this.project?.currentDocument?.export(TransformStage.Save);
return { componentsTree: [node] };
}
getHistory(): History {
return this.history;
}
get root() {
return this.rootNode;
}
onRendererReady(fn: (args: any) => void): () => void {
this.emitter.on('lowcode_engine_renderer_ready', fn);
return () => {
this.emitter.removeListener('lowcode_engine_renderer_ready', fn);
};
}
setRendererReady(renderer) {
this.emitter.emit('lowcode_engine_renderer_ready', renderer);
}
acceptRootNodeVisitor(
visitorName: string = 'default',
visitorFn: (node: RootNode) => any ) {
let visitorResult = {};
if (!visitorName) {
/* tslint:disable no-console */
console.warn('Invalid or empty RootNodeVisitor name.');
}
try {
visitorResult = visitorFn.call(this, this.rootNode);
this.rootNodeVisitorMap[visitorName] = visitorResult;
} catch (e) {
console.error('RootNodeVisitor is not valid.');
}
return visitorResult;
}
getRootNodeVisitor(name: string) {
return this.rootNodeVisitorMap[name];
}
onNodeCreate(func: (node: Node) => void) {
this.emitter.on('nodecreate', func);
return () => {
this.emitter.removeListener('nodecreate', func);
};
}
onNodeDestroy(func: (node: Node) => void) {
this.emitter.on('nodedestroy', func);
return () => {
this.emitter.removeListener('nodedestroy', func);
};
}
} }
export function isDocumentModel(obj: any): obj is DocumentModel { export function isDocumentModel(obj: any): obj is DocumentModel {

View File

@ -1,6 +1,6 @@
import { Component } from 'react'; import { Component } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { observer } from '@ali/lowcode-globals'; import { observer } from '@ali/lowcode-editor-core';
import { DocumentModel } from './document-model'; import { DocumentModel } from './document-model';
import { BuiltinSimulatorHostView } from '../builtin-simulator'; import { BuiltinSimulatorHostView } from '../builtin-simulator';

View File

@ -1,5 +1,6 @@
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { NodeSchema, autorun, Reaction, untracked } from '@ali/lowcode-globals'; import { autorun, Reaction, untracked, globalContext, Editor } from '@ali/lowcode-editor-core';
import { NodeSchema } from '@ali/lowcode-types';
// TODO: cache to localStorage // TODO: cache to localStorage
@ -113,6 +114,11 @@ export class History {
} }
const cursor = this.session.cursor - 1; const cursor = this.session.cursor - 1;
this.go(cursor); this.go(cursor);
const editor = globalContext.get(Editor);
if (!editor) {
return;
}
editor.emit('history.back', cursor);
} }
forward() { forward() {
@ -121,6 +127,11 @@ export class History {
} }
const cursor = this.session.cursor + 1; const cursor = this.session.cursor + 1;
this.go(cursor); this.go(cursor);
const editor = globalContext.get(Editor);
if (!editor) {
return;
}
editor.emit('history.forward', cursor);
} }
savePoint() { savePoint() {
@ -173,6 +184,10 @@ export class History {
this.emitter.removeAllListeners(); this.emitter.removeAllListeners();
this.records = []; this.records = [];
} }
isModified() {
return this.point !== this.session.cursor;
}
} }
class Session { class Session {

View File

@ -1,5 +1,6 @@
import { obx, computed, TitleContent } from '@ali/lowcode-globals'; import { obx, computed } from '@ali/lowcode-editor-core';
import { uniqueId } from '@ali/lowcode-globals'; import { uniqueId } from '@ali/lowcode-utils';
import { TitleContent } from '@ali/lowcode-types';
import { Node } from './node'; import { Node } from './node';
import { intl } from '../../locale'; import { intl } from '../../locale';

View File

@ -1,7 +1,7 @@
export * from './exclusive-group'; export * from './exclusive-group';
export * from './node'; export * from './node';
export * from './node-children'; export * from './node-children';
export * from './root-node';
export * from './props/prop'; export * from './props/prop';
export * from './props/prop-stash'; export * from './props/prop-stash';
export * from './props/props'; export * from './props/props';
export * from './transform-stage';

View File

@ -0,0 +1,135 @@
import { EventEmitter } from 'events';
import { Node } from './node';
import { DocumentModel } from '../document-model';
function getModalNodes(node: Node) {
let nodes: any = [];
const prototype = node.getPrototype();
if (prototype && prototype.isModal()) {
nodes.push(node);
}
const children = node.getChildren();
if (children) {
children.forEach((child) => {
nodes = nodes.concat(getModalNodes(child));
});
}
return nodes;
}
export default class ModalNodesManager {
public willDestroy: any;
private page: DocumentModel;
private modalNodes: [Node];
private nodeRemoveEvents: any;
private emitter: EventEmitter;
constructor(page: DocumentModel) {
this.page = page;
this.emitter = new EventEmitter();
this.nodeRemoveEvents = {};
this.setNodes();
this.hideModalNodes();
this.willDestroy = [
page.onNodeCreate((node) => this.addNode(node)),
page.onNodeDestroy((node) => this.removeNode(node)),
];
}
public getModalNodes() {
return this.modalNodes;
}
public getVisibleModalNode() {
const visibleNode = this.modalNodes
? this.modalNodes.find((node: Node) => {
return !node.getExtraProp('hidden');
})
: null;
return visibleNode;
}
public hideModalNodes() {
if (this.modalNodes) {
this.modalNodes.forEach((node: Node) => {
node.getExtraProp('hidden')?.setValue(true);
});
}
}
public setVisible(node: Node) {
this.hideModalNodes();
node.getExtraProp('hidden')?.setValue(false);
}
public setInvisible(node: Node) {
node.getExtraProp('hidden')?.setValue(true);
}
public onVisibleChange(func: () => any) {
this.emitter.on('visibleChange', func);
return () => {
this.emitter.removeListener('visibleChange', func);
};
}
public onModalNodesChange(func: () => any) {
this.emitter.on('modalNodesChange', func);
return () => {
this.emitter.removeListener('modalNodesChange', func);
};
}
private addNode(node: Node) {
const prototype = node.getPrototype();
if (prototype && prototype.isModal()) {
this.hideModalNodes();
this.modalNodes.push(node);
this.addNodeEvent(node);
this.emitter.emit('modalNodesChange');
this.emitter.emit('visibleChange');
}
}
private removeNode(node: Node) {
const prototype = node.getPrototype();
if (prototype && prototype.isModal()) {
const index = this.modalNodes.indexOf(node);
if (index >= 0) {
this.modalNodes.splice(index, 1);
}
this.removeNodeEvent(node);
this.emitter.emit('modalNodesChange');
if (!node.getExtraProp('hidden')) {
this.emitter.emit('visibleChange');
}
}
}
private addNodeEvent(node: Node) {
// this.nodeRemoveEvents[node.getId()] =
// node.onStatusChange((status: any, field: any) => {
// if (field === 'visibility') {
// this.emitter.emit('visibleChange');
// }
// });
}
private removeNodeEvent(node: Node) {
// if (this.nodeRemoveEvents[node.getId()]) {
// this.nodeRemoveEvents[node.getId()]();
// delete this.nodeRemoveEvents[node.getId()];
// }
}
private setNodes() {
const nodes = getModalNodes(this.page.getRoot());
this.modalNodes = nodes;
this.modalNodes.forEach((node: Node) => {
this.addNodeEvent(node);
});
this.emitter.emit('modalNodesChange');
}
}

View File

@ -1,9 +1,15 @@
import { NodeData, isNodeSchema, obx, computed } from '@ali/lowcode-globals'; import { obx, computed } from '@ali/lowcode-editor-core';
import { Node, NodeParent } from './node'; import { Node, ParentalNode } from './node';
import { TransformStage } from './transform-stage';
import { NodeData, isNodeSchema } from '@ali/lowcode-types';
import { shallowEqual } from '@ali/lowcode-utils';
import { EventEmitter } from 'events';
export class NodeChildren { export class NodeChildren {
@obx.val private children: Node[]; @obx.val private children: Node[];
constructor(readonly owner: NodeParent, data: NodeData | NodeData[]) { private emitter = new EventEmitter();
constructor(readonly owner: ParentalNode, data: NodeData | NodeData[]) {
this.children = (Array.isArray(data) ? data : [data]).map(child => { this.children = (Array.isArray(data) ? data : [data]).map(child => {
return this.owner.document.createNode(child); return this.owner.document.createNode(child);
}); });
@ -15,10 +21,16 @@ export class NodeChildren {
/** /**
* schema * schema
* @param serialize id
*/ */
export(serialize = false): NodeData[] { export(stage: TransformStage = TransformStage.Save): NodeData[] {
return this.children.map(node => node.export(serialize)); return this.children.map(node => {
const data = node.export(stage);
if (node.isLeaf() && TransformStage.Save === stage) {
// FIXME: filter empty
return data.children as NodeData;
}
return data;
});
} }
import(data?: NodeData | NodeData[], checkId = false) { import(data?: NodeData | NodeData[], checkId = false) {
@ -44,6 +56,9 @@ export class NodeChildren {
this.children = children; this.children = children;
this.interalInitParent(); this.interalInitParent();
if (!shallowEqual(children, originChildren)) {
this.emitter.emit('change');
}
} }
/** /**
@ -78,6 +93,7 @@ export class NodeChildren {
deleted.internalSetParent(null); deleted.internalSetParent(null);
deleted.purge(); deleted.purge();
} }
this.emitter.emit('change');
return false; return false;
} }
@ -110,6 +126,8 @@ export class NodeChildren {
children.splice(index, 0, node); children.splice(index, 0, node);
} }
this.emitter.emit('change');
// check condition group // check condition group
if (node.conditionGroup) { if (node.conditionGroup) {
if ( if (
@ -140,6 +158,13 @@ export class NodeChildren {
return this.children.indexOf(node); return this.children.indexOf(node);
} }
/**
*
*/
splice(start: number, deleteCount: number, node: Node): Node[] {
return this.children.splice(start, deleteCount, node);
}
/** /**
* *
*/ */
@ -195,6 +220,60 @@ export class NodeChildren {
}); });
} }
every(fn: (item: Node, index: number) => any): boolean {
return this.children.every((child, index) => fn(child, index));
}
some(fn: (item: Node, index: number) => any): boolean {
return this.children.some((child, index) => fn(child, index));
}
filter(fn: (item: Node, index: number) => item is Node) {
return this.children.filter(fn);
}
mergeChildren(remover: () => any, adder: (children: Node[]) => NodeData[] | null, sorter: () => any) {
let changed = false;
if (remover) {
const willRemove = this.children.filter(remover);
if (willRemove.length > 0) {
willRemove.forEach((node) => {
const i = this.children.indexOf(node);
if (i > -1) {
this.children.splice(i, 1);
node.remove();
}
});
changed = true;
}
}
if (adder) {
const items = adder(this.children);
if (items && items.length > 0) {
items.forEach((child: NodeData) => {
const node = this.owner.document.createNode(child);
this.children.push(node);
node.internalSetParent(this.owner);
});
changed = true;
}
}
if (sorter) {
this.children = this.children.sort(sorter);
changed = true;
}
if (changed) {
this.emitter.emit('change');
}
}
onChange(fn: () => void) {
this.emitter.on('change', fn);
return () => {
this.emitter.removeListener('change', fn);
};
}
private purged = false; private purged = false;
/** /**
* *
@ -206,4 +285,9 @@ export class NodeChildren {
this.purged = true; this.purged = true;
this.children.forEach(child => child.purge()); this.children.forEach(child => child.purge());
} }
get [Symbol.toStringTag]() {
// 保证向前兼容性
return "Array";
}
} }

View File

@ -1,3 +1,4 @@
import { obx, computed, autorun } from '@ali/lowcode-editor-core';
import { import {
isDOMText, isDOMText,
isJSExpression, isJSExpression,
@ -6,15 +7,21 @@ import {
PropsList, PropsList,
NodeData, NodeData,
TitleContent, TitleContent,
obx, I18nData,
computed, SlotSchema,
} from '@ali/lowcode-globals'; PageSchema,
import { Props, EXTRA_KEY_PREFIX } from './props/props'; ComponentSchema,
NodeStatus,
} from '@ali/lowcode-types';
import { Props, getConvertedExtraKey } from './props/props';
import { DocumentModel } from '../document-model'; import { DocumentModel } from '../document-model';
import { NodeChildren } from './node-children'; import { NodeChildren } from './node-children';
import { Prop } from './props/prop'; import { Prop } from './props/prop';
import { ComponentMeta } from '../../component-meta'; import { ComponentMeta } from '../../component-meta';
import { ExclusiveGroup, isExclusiveGroup } from './exclusive-group'; import { ExclusiveGroup, isExclusiveGroup } from './exclusive-group';
import { TransformStage } from './transform-stage';
import { ReactElement } from 'react';
import { SettingTopEntry } from 'designer/src/designer';
/** /**
* *
@ -35,8 +42,36 @@ import { ExclusiveGroup, isExclusiveGroup } from './exclusive-group';
* locked can not select/hover/ item on canvas but can control on outline * locked can not select/hover/ item on canvas but can control on outline
* hidden not visible on canvas * hidden not visible on canvas
* slotArgs like loopArgs, for slot node * slotArgs like loopArgs, for slot node
*
*
*
* [Node Properties]
* componentName: Page/Block/Component
* props
* children
*
* [Root Container Extra Properties]
* fileName
* meta
* state
* defaultProps
* dataSource
* lifeCycles
* methods
* css
*
* [Directives **not used**]
* loop
* loopArgs
* condition
* ------- future support -----
* conditionGroup
* title
* ignore
* locked
* hidden
*/ */
export class Node { export class Node<Schema extends NodeSchema = NodeSchema> {
/** /**
* *
*/ */
@ -63,11 +98,15 @@ export class Node {
*/ */
readonly props: Props; readonly props: Props;
protected _children?: NodeChildren; protected _children?: NodeChildren;
@obx.ref private _parent: NodeParent | null = null; /**
* @deprecated
*/
private _addons: { [key: string]: any } = {};
@obx.ref private _parent: ParentalNode | null = null;
/** /**
* *
*/ */
get parent(): NodeParent | null { get parent(): ParentalNode | null {
return this._parent; return this._parent;
} }
/** /**
@ -86,7 +125,7 @@ export class Node {
return 0; return 0;
} }
@computed get title(): TitleContent { @computed get title(): string | I18nData | ReactElement {
let t = this.getExtraProp('title'); let t = this.getExtraProp('title');
if (!t && this.componentMeta.descriptor) { if (!t && this.componentMeta.descriptor) {
t = this.getProp(this.componentMeta.descriptor, false); t = this.getProp(this.componentMeta.descriptor, false);
@ -100,49 +139,122 @@ export class Node {
return this.componentMeta.title; return this.componentMeta.title;
} }
get isSlotRoot(): boolean { get icon() {
return this._slotFor != null; return this.componentMeta.icon;
} }
constructor(readonly document: DocumentModel, nodeSchema: NodeSchema, slotFor?: Prop) { readonly settingEntry: SettingTopEntry;
constructor(readonly document: DocumentModel, nodeSchema: Schema) {
const { componentName, id, children, props, ...extras } = nodeSchema; const { componentName, id, children, props, ...extras } = nodeSchema;
this.id = id || `node$${document.nextId()}`; this.id = id || `node_${document.nextId()}`;
this.componentName = componentName; this.componentName = componentName;
this._slotFor = slotFor; if (this.componentName === 'Leaf') {
let _props: Props; this.props = new Props(this, {
if (isNodeParent(this)) {
_props = new Props(this, props, extras);
this._children = new NodeChildren(this as NodeParent, children || []);
this._children.interalInitParent();
} else {
_props = new Props(this, {
children: isDOMText(children) || isJSExpression(children) ? children : '', children: isDOMText(children) || isJSExpression(children) ? children : '',
}); });
} else {
this.props = new Props(this, props, extras);
this._children = new NodeChildren(this as ParentalNode, this.initialChildren(children));
this._children.interalInitParent();
this.props.import(this.transformProps(props || {}), extras);
this.setupAutoruns();
} }
this.props = _props;
this.settingEntry = this.document.designer.createSettingEntry([ this ]);
}
private transformProps(props: any): any {
// FIXME! support PropsList
return this.document.designer.transformProps(props, this, TransformStage.Init);
// TODO: run transducers in metadata.experimental
}
private autoruns?: Array<() => void>;
private setupAutoruns() {
const autoruns = this.componentMeta.getMetadata().experimental?.autoruns;
if (!autoruns || autoruns.length < 1) {
return;
}
this.autoruns = autoruns.map((item) => {
return autorun(() => {
item.autorun(this.props.get(item.name, true) as any);
}, true);
});
}
private initialChildren(children: any): NodeData[] {
// FIXME! this is dirty code
if (children == null) {
const initialChildren = this.componentMeta.getMetadata().experimental?.initialChildren;
if (initialChildren) {
if (typeof initialChildren === 'function') {
return initialChildren(this as any) || [];
}
return initialChildren;
}
}
return children || [];
}
isContainer(): boolean {
return this.isParental() && this.componentMeta.isContainer;
}
isRoot(): boolean {
return this.document.rootNode == (this as any);
}
isPage(): boolean {
return this.isRoot() && this.componentName === 'Page';
}
isComponent(): boolean {
return this.isRoot() && this.componentName === 'Component';
}
isSlot(): boolean {
return this._slotFor != null && this.componentName === 'Slot';
} }
/** /**
* *
*/ */
get isNodeParent(): boolean { isParental(): this is ParentalNode {
return this.componentName !== 'Leaf'; return !this.isLeaf();
} }
/**
*
*/
isLeaf(): this is LeafNode {
return this.componentName === 'Leaf';
}
internalSetWillPurge() {
this.internalSetParent(null);
this.document.addWillPurge(this);
}
/** /**
* 使 * 使
*/ */
internalSetParent(parent: NodeParent | null) { internalSetParent(parent: ParentalNode | null) {
if (this._parent === parent) { if (this._parent === parent) {
return; return;
} }
if (this._parent && !this.isSlotRoot) { if (this._parent) {
if (this.isSlot()) {
this._parent.removeSlot(this, false);
} else {
this._parent.children.delete(this); this._parent.children.delete(this);
} }
}
this._parent = parent; this._parent = parent;
if (parent && !this.conditionGroup) { if (parent) {
this.document.removeWillPurge(this);
if (!this.conditionGroup) {
// initial conditionGroup // initial conditionGroup
const grp = this.getExtraProp('conditionGroup', false)?.getAsString(); const grp = this.getExtraProp('conditionGroup', false)?.getAsString();
if (grp) { if (grp) {
@ -150,12 +262,16 @@ export class Node {
} }
} }
} }
}
private _slotFor?: Prop | null = null; private _slotFor?: Prop | null = null;
internalSetSlotFor(slotFor: Prop | null | undefined) { internalSetSlotFor(slotFor: Prop | null | undefined) {
this._slotFor = slotFor; this._slotFor = slotFor;
} }
/**
*
*/
get slotFor() { get slotFor() {
return this._slotFor; return this._slotFor;
} }
@ -164,10 +280,14 @@ export class Node {
* *
*/ */
remove() { remove() {
if (this.parent && !this.isSlotRoot) { if (this.parent) {
if (this.isSlot()) {
this.parent.removeSlot(this, true);
} else {
this.parent.children.delete(this, true); this.parent.children.delete(this, true);
} }
} }
}
/** /**
* *
@ -181,9 +301,9 @@ export class Node {
*/ */
hover(flag = true) { hover(flag = true) {
if (flag) { if (flag) {
this.document.designer.hovering.hover(this); this.document.designer.detecting.capture(this);
} else { } else {
this.document.designer.hovering.unhover(this); this.document.designer.detecting.release(this);
} }
} }
@ -195,34 +315,19 @@ export class Node {
} }
@computed get propsData(): PropsMap | PropsList | null { @computed get propsData(): PropsMap | PropsList | null {
if (!this.isNodeParent || this.componentName === 'Fragment') { if (!this.isParental() || this.componentName === 'Fragment') {
return null; return null;
} }
return this.props.export(true).props || null; return this.props.export(TransformStage.Serilize).props || null;
} }
isContainer() { @obx.val _slots: Node[] = [];
return this.isNodeParent && this.componentMeta.isContainer; @computed hasSlots() {
return this._slots.length > 0;
} }
@computed isSlotContainer() { get slots() {
for (const item of this.props) { return this._slots;
if (item.type === 'slot') {
return true;
}
}
return false;
}
@computed get slots() {
// TODO: optimize recore/obx, array maked every time, donot as changed
const slots: Node[] = [];
this.props.forEach(item => {
if (item.type === 'slot') {
slots.push(item.slotNode!);
}
});
return slots;
} }
@obx.ref private _conditionGroup: ExclusiveGroup | null = null; @obx.ref private _conditionGroup: ExclusiveGroup | null = null;
@ -268,7 +373,7 @@ export class Node {
@computed hasCondition() { @computed hasCondition() {
const v = this.getExtraProp('condition', false)?.getValue(); const v = this.getExtraProp('condition', false)?.getValue();
return v != null && v !== ''; return v != null && v !== '' && v !== true;
} }
@computed hasLoop() { @computed hasLoop() {
@ -276,13 +381,37 @@ export class Node {
return v != null && v !== ''; return v != null && v !== '';
} }
wrapWith(schema: NodeSchema) { wrapWith(schema: Schema) {
// todo // todo
} }
replaceWith(schema: NodeSchema, migrate = true) { replaceWith(schema: Schema, migrate = false): any {
// reuse the same id? or replaceSelection // reuse the same id? or replaceSelection
// schema = Object.assign({}, migrate ? this.export() : {}, schema);
return this.parent?.replaceChild(this, schema);
}
/**
*
*
* @param {Node} node
* @param {object} data
*/
replaceChild(node: Node, data: any): Node {
if (this.children?.has(node)) {
const selected = this.document.selection.has(node.id);
delete data.id;
const newNode = this.document.createNode(data);
this.insertBefore(newNode, node);
node.remove();
if (selected) {
this.document.selection.select(newNode.id);
}
}
return node;
} }
getProp(path: string, stash = true): Prop | null { getProp(path: string, stash = true): Prop | null {
@ -290,7 +419,7 @@ export class Node {
} }
getExtraProp(key: string, stash = true): Prop | null { getExtraProp(key: string, stash = true): Prop | null {
return this.props.get(EXTRA_KEY_PREFIX + key, stash) || null; return this.props.get(getConvertedExtraKey(key), stash) || null;
} }
/** /**
@ -307,6 +436,13 @@ export class Node {
this.getProp(path, true)!.setValue(value); this.getProp(path, true)!.setValue(value);
} }
/**
*
*/
clearPropValue(path: string): void {
this.getProp(path, false)?.unset();
}
/** /**
* *
*/ */
@ -362,18 +498,18 @@ export class Node {
/** /**
* - schema * - schema
*/ */
get schema(): NodeSchema { get schema(): Schema {
return this.export(true); return this.export(TransformStage.Save);
} }
set schema(data: NodeSchema) { set schema(data: Schema) {
this.import(data); this.import(data);
} }
import(data: NodeSchema, checkId = false) { import(data: Schema, checkId = false) {
const { componentName, id, children, props, ...extras } = data; const { componentName, id, children, props, ...extras } = data;
if (isNodeParent(this)) { if (this.isParental()) {
this.props.import(props, extras); this.props.import(props, extras);
(this._children as NodeChildren).import(children, checkId); (this._children as NodeChildren).import(children, checkId);
} else { } else {
@ -381,34 +517,48 @@ export class Node {
} }
} }
toData() {
return this.export();
}
/** /**
* schema * schema
* @param serialize id
*/ */
export(serialize = false): NodeSchema { export(stage: TransformStage = TransformStage.Save): Schema {
const baseSchema: any = { const baseSchema: any = {
componentName: this.componentName === 'Leaf' ? 'Fragment' : this.componentName, componentName: this.componentName,
}; };
if (serialize) { if (stage !== TransformStage.Clone) {
baseSchema.id = this.id; baseSchema.id = this.id;
} }
if (!isNodeParent(this)) { if (this.isLeaf()) {
baseSchema.children = this.props.get('children')?.export(serialize); baseSchema.children = this.props.get('children')?.export(stage);
// FIXME! return baseSchema;
return baseSchema.children;
} }
const { props = {}, extras } = this.props.export(serialize) || {}; const { props = {}, extras } = this.props.export(stage) || {};
const schema: any = { const _extras_: { [key: string]: any } = {
...baseSchema,
props,
...extras, ...extras,
}; };
if (_extras_) {
Object.keys(_extras_).forEach((key) => {
const addon = this._addons[key];
if (addon) {
_extras_[key] = addon();
}
});
}
if (this.children.size > 0) { const schema: any = {
schema.children = this.children.export(serialize); ...baseSchema,
props: this.document.designer.transformProps(props, this, stage),
...this.document.designer.transformProps(_extras_, this, stage),
};
if (this.isParental() && this.children.size > 0) {
schema.children = this.children.export(stage);
} }
return schema; return schema;
@ -440,6 +590,28 @@ export class Node {
return comparePosition(this, otherNode); return comparePosition(this, otherNode);
} }
/**
* Slot节点
*/
removeSlot(slotNode: Node, purge = false): boolean {
const i = this._slots.indexOf(slotNode);
if (i < 0) {
return false;
}
const deleted = this._slots.splice(i, 1)[0];
if (purge) {
// should set parent null
deleted.internalSetParent(null);
deleted.purge();
}
return false;
}
addSlot(slotNode: Node) {
slotNode.internalSetParent(this as ParentalNode);
this._slots.push(slotNode);
}
private purged = false; private purged = false;
/** /**
* *
@ -460,25 +632,182 @@ export class Node {
return; return;
} }
this.purged = true; this.purged = true;
if (isNodeParent(this)) { if (this.isParental()) {
this.children.purge(); this.children.purge();
} }
this.autoruns?.forEach((dispose) => dispose());
this.props.purge(); this.props.purge();
this.document.internalRemoveAndPurgeNode(this); this.document.internalRemoveAndPurgeNode(this);
this.document.destroyNode(this);
}
// ======= compatible apis ====
isEmpty(): boolean {
return this.children ? this.children.isEmpty() : true;
}
getChildren() {
return this.children;
}
getComponentName() {
return this.componentName;
}
insertBefore(node: Node, ref?: Node) {
this.children?.insert(node, ref ? ref.index : null);
}
insertAfter(node: any, ref?: Node) {
if (!isNode(node)) {
if (node.getComponentName) {
node = this.document.createNode({
componentName: node.getComponentName(),
});
} else {
node = this.document.createNode(node);
}
}
this.children?.insert(node, ref ? ref.index + 1 : null);
}
getParent() {
return this.parent;
}
getId() {
return this.id;
}
getIndex() {
return this.index;
}
getNode() {
return this;
}
getRoot() {
return this.document.rootNode;
}
getProps() {
return this.props;
}
onChildrenChange(fn: () => void) {
return this.children?.onChange(fn);
}
mergeChildren(remover: () => any, adder: (children: Node[]) => NodeData[] | null, sorter: () => any) {
this.children?.mergeChildren(remover, adder, sorter);
}
@obx.val status: NodeStatus = {
inPlaceEditing: false,
locking: false,
pseudo: false,
};
/**
* @deprecated
*/
getStatus(field?: keyof NodeStatus) {
if (field && this.status[field] != null) {
return this.status[field];
}
return this.status;
}
/**
* @deprecated
*/
setStatus(field: keyof NodeStatus, flag: boolean) {
if (!this.status.hasOwnProperty(field)) {
return;
}
if (flag !== this.status[field]) {
this.status[field] = flag;
}
}
/**
* @deprecated
*/
getDOMNode(): any {
const instance = this.document.simulator?.getComponentInstances(this)?.[0];
if (!instance) {
return;
}
return this.document.simulator?.findDOMNodes(instance)?.[0];
}
/**
* @deprecated
*/
getPage() {
console.warn('getPage is deprecated, use document instead');
return this.document;
}
/**
* @deprecated
*/
getSuitablePlace(node: Node, ref: any): any {
// TODO:
if (this.isRoot()) {
return { container: this, ref };
}
return { container: this.parent, ref: this };
}
/**
* @deprecated
*/
getAddonData(key: string) {
const addon = this._addons[key];
if (addon) {
return addon();
}
return this.getExtraProp(key)?.value;
}
/**
* @deprecated
*/
registerAddon(key: string, exportData: any) {
if (this._addons[key]) {
throw new Error(`node addon ${key} exist`);
}
this._addons[key] = exportData;
}
getRect(): DOMRect | null {
if (this.isRoot()) {
return this.document.simulator?.viewport.contentBounds || null;
}
return this.document.simulator?.computeRect(this) || null;
}
getPrototype() {
return this.componentMeta.prototype;
}
getIcon() {
return this.icon;
}
toString() {
return this.id;
} }
} }
export interface NodeParent extends Node { export interface ParentalNode<T extends NodeSchema = NodeSchema> extends Node<T> {
readonly children: NodeChildren; readonly children: NodeChildren;
readonly props: Props;
} }
export interface LeafNode extends Node {
readonly children: null;
}
export type SlotNode = ParentalNode<SlotSchema>;
export type PageNode = ParentalNode<PageSchema>;
export type ComponentNode = ParentalNode<ComponentSchema>;
export type RootNode = PageNode | ComponentNode;
export function isNode(node: any): node is Node { export function isNode(node: any): node is Node {
return node && node.isNode; return node && node.isNode;
} }
export function isNodeParent(node: Node): node is NodeParent { export function isRootNode(node: Node): node is RootNode {
return node.isNodeParent; return node && node.isRoot();
} }
export function getZLevelTop(child: Node, zLevel: number): Node | null { export function getZLevelTop(child: Node, zLevel: number): Node | null {
@ -501,7 +830,7 @@ export function contains(node1: Node, node2: Node): boolean {
return true; return true;
} }
if (!node1.isNodeParent || !node2.parent) { if (!node1.isParental || !node2.parent) {
return false; return false;
} }
@ -550,10 +879,10 @@ export function comparePosition(node1: Node, node2: Node): PositionNO {
return PositionNO.BeforeOrAfter; return PositionNO.BeforeOrAfter;
} }
export function insertChild(container: NodeParent, thing: Node | NodeData, at?: number | null, copy?: boolean): Node { export function insertChild(container: ParentalNode, thing: Node | NodeData, at?: number | null, copy?: boolean): Node {
let node: Node; let node: Node;
if (isNode(thing) && (copy || thing.isSlotRoot)) { if (isNode(thing) && (copy || thing.isSlot())) {
thing = thing.export(false); thing = thing.export(TransformStage.Clone);
} }
if (isNode(thing)) { if (isNode(thing)) {
node = thing; node = thing;
@ -567,7 +896,7 @@ export function insertChild(container: NodeParent, thing: Node | NodeData, at?:
} }
export function insertChildren( export function insertChildren(
container: NodeParent, container: ParentalNode,
nodes: Node[] | NodeData[], nodes: Node[] | NodeData[],
at?: number | null, at?: number | null,
copy?: boolean, copy?: boolean,

View File

@ -1,6 +1,7 @@
import { obx, autorun, untracked, computed } from '@ali/lowcode-globals'; import { obx, autorun, untracked, computed } from '@ali/lowcode-editor-core';
import { Prop, IPropParent, UNSET } from './prop'; import { Prop, IPropParent, UNSET } from './prop';
import { Props } from './props'; import { Props } from './props';
import { Node } from '../node';
export type PendingItem = Prop[]; export type PendingItem = Prop[];
export class PropStash implements IPropParent { export class PropStash implements IPropParent {
@ -15,8 +16,10 @@ export class PropStash implements IPropParent {
return maps; return maps;
} }
private willPurge: () => void; private willPurge: () => void;
readonly owner: Node;
constructor(readonly props: Props, write: (item: Prop) => void) { constructor(readonly props: Props, write: (item: Prop) => void) {
this.owner = props.owner;
this.willPurge = autorun(() => { this.willPurge = autorun(() => {
if (this.space.size < 1) { if (this.space.size < 1) {
return; return;

View File

@ -1,20 +1,11 @@
import { import { untracked, computed, obx } from '@ali/lowcode-editor-core';
CompositeValue, import { CompositeValue, isJSExpression, isJSSlot, JSSlot, SlotSchema } from '@ali/lowcode-types';
isJSExpression, import { uniqueId, isPlainObject, hasOwnProperty } from '@ali/lowcode-utils';
isJSSlot,
NodeData,
isNodeSchema,
untracked,
computed,
obx
} from '@ali/lowcode-globals';
import { uniqueId } from '@ali/lowcode-globals';
import { isPlainObject } from '@ali/lowcode-globals';
import { hasOwnProperty } from '@ali/lowcode-globals';
import { PropStash } from './prop-stash'; import { PropStash } from './prop-stash';
import { valueToSource } from './value-to-source'; import { valueToSource } from './value-to-source';
import { Props } from './props'; import { Props } from './props';
import { Node } from '../node'; import { SlotNode, Node } from '../node';
import { TransformStage } from '../transform-stage';
export const UNSET = Symbol.for('unset'); export const UNSET = Symbol.for('unset');
export type UNSET = typeof UNSET; export type UNSET = typeof UNSET;
@ -22,12 +13,35 @@ export type UNSET = typeof UNSET;
export interface IPropParent { export interface IPropParent {
delete(prop: Prop): void; delete(prop: Prop): void;
readonly props: Props; readonly props: Props;
readonly owner: Node;
} }
export type ValueTypes = 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot'; export type ValueTypes = 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot';
export class Prop implements IPropParent { export class Prop implements IPropParent {
readonly isProp = true; readonly isProp = true;
readonly owner: Node;
/**
* @see SettingTarget
*/
getPropValue(propName: string | number): any {
return this.get(propName)!.getValue();
}
/**
* @see SettingTarget
*/
setPropValue(propName: string | number, value: any): void {
this.set(propName, value);
}
/**
* @see SettingTarget
*/
clearPropValue(propName: string | number): void {
this.get(propName, false)?.unset();
}
readonly id = uniqueId('prop$'); readonly id = uniqueId('prop$');
@ -45,10 +59,10 @@ export class Prop implements IPropParent {
* *
*/ */
@computed get value(): CompositeValue | UNSET { @computed get value(): CompositeValue | UNSET {
return this.export(true); return this.export(TransformStage.Serilize);
} }
export(serialize = false): CompositeValue | UNSET { export(stage: TransformStage = TransformStage.Save): CompositeValue | UNSET {
const type = this._type; const type = this._type;
if (type === 'unset') { if (type === 'unset') {
@ -60,9 +74,18 @@ export class Prop implements IPropParent {
} }
if (type === 'slot') { if (type === 'slot') {
const schema = this._slotNode!.export(stage);
if (stage === TransformStage.Render) {
return { return {
type: 'JSSlot', type: 'JSSlot',
value: this._slotNode!.export(serialize), params: schema.params,
value: schema,
};
}
return {
type: 'JSSlot',
params: schema.params,
value: schema.children,
}; };
} }
@ -72,9 +95,9 @@ export class Prop implements IPropParent {
} }
const maps: any = {}; const maps: any = {};
this.items!.forEach((prop, key) => { this.items!.forEach((prop, key) => {
const v = prop.export(serialize); const v = prop.export(stage);
if (v !== UNSET) { if (v !== UNSET) {
maps[key] = v; maps[prop.key == null ? key : prop.key] = v;
} }
}); });
return maps; return maps;
@ -85,12 +108,12 @@ export class Prop implements IPropParent {
return this._value; return this._value;
} }
return this.items!.map((prop) => { return this.items!.map((prop) => {
const v = prop.export(serialize); const v = prop.export(stage);
return v === UNSET ? null : v; return v === UNSET ? undefined : v;
}); });
} }
return null; return undefined;
} }
private _code: string | null = null; private _code: string | null = null;
@ -103,7 +126,7 @@ export class Prop implements IPropParent {
} }
// todo: JSFunction ... // todo: JSFunction ...
if (this.type === 'slot') { if (this.type === 'slot') {
return JSON.stringify(this._slotNode!.export(false)); return JSON.stringify(this._slotNode!.export(TransformStage.Save));
} }
return this._code != null ? this._code : JSON.stringify(this.value); return this._code != null ? this._code : JSON.stringify(this.value);
} }
@ -153,7 +176,7 @@ export class Prop implements IPropParent {
this._code = null; this._code = null;
const t = typeof val; const t = typeof val;
if (val == null) { if (val == null) {
this._value = null; this._value = undefined;
this._type = 'literal'; this._type = 'literal';
} else if (t === 'string' || t === 'number' || t === 'boolean') { } else if (t === 'string' || t === 'number' || t === 'boolean') {
this._type = 'literal'; this._type = 'literal';
@ -161,7 +184,7 @@ export class Prop implements IPropParent {
this._type = 'list'; this._type = 'list';
} else if (isPlainObject(val)) { } else if (isPlainObject(val)) {
if (isJSSlot(val)) { if (isJSSlot(val)) {
this.setAsSlot(val.value); this.setAsSlot(val);
return; return;
} }
if (isJSExpression(val)) { if (isJSExpression(val)) {
@ -169,7 +192,6 @@ export class Prop implements IPropParent {
} else { } else {
this._type = 'map'; this._type = 'map';
} }
this._type = 'map';
} else { } else {
this._type = 'expression'; this._type = 'expression';
this._value = { this._value = {
@ -181,9 +203,9 @@ export class Prop implements IPropParent {
} }
@computed getValue(): CompositeValue { @computed getValue(): CompositeValue {
const v = this.export(true); const v = this.export(TransformStage.Serilize);
if (v === UNSET) { if (v === UNSET) {
return null; return undefined;
} }
return v; return v;
} }
@ -204,24 +226,25 @@ export class Prop implements IPropParent {
} }
} }
private _slotNode?: Node; private _slotNode?: SlotNode;
get slotNode() { get slotNode() {
return this._slotNode; return this._slotNode;
} }
setAsSlot(data: NodeData) { setAsSlot(data: JSSlot) {
this._type = 'slot'; this._type = 'slot';
if ( const slotSchema: SlotSchema = {
this._slotNode && componentName: 'Slot',
isNodeSchema(data) && title: data.title,
(!data.id || this._slotNode.id === data.id) && params: data.params,
this._slotNode.componentName === data.componentName children: data.value,
) { };
this._slotNode.import(data); if (this._slotNode) {
this._slotNode.import(slotSchema);
} else { } else {
this._slotNode?.internalSetParent(null);
const owner = this.props.owner; const owner = this.props.owner;
this._slotNode = owner.document.createNode(data, this); this._slotNode = owner.document.createNode<SlotNode>(slotSchema);
this._slotNode.internalSetParent(owner as any); owner.addSlot(this._slotNode);
this._slotNode.internalSetSlotFor(this);
} }
this.dispose(); this.dispose();
} }
@ -244,7 +267,9 @@ export class Prop implements IPropParent {
return typeof this.key === 'string' && this.key.charAt(0) === '!'; return typeof this.key === 'string' && this.key.charAt(0) === '!';
} }
// TODO: improve this logic /**
* @returns 0: the same 1: maybe & like 2: not the same
*/
compare(other: Prop | null): number { compare(other: Prop | null): number {
if (!other || other.isUnset()) { if (!other || other.isUnset()) {
return this.isUnset() ? 0 : 2; return this.isUnset() ? 0 : 2;
@ -326,6 +351,7 @@ export class Prop implements IPropParent {
key?: string | number, key?: string | number,
spread = false, spread = false,
) { ) {
this.owner = parent.owner;
this.props = parent.props; this.props = parent.props;
if (value !== UNSET) { if (value !== UNSET) {
this.setValue(value); this.setValue(value);
@ -338,7 +364,7 @@ export class Prop implements IPropParent {
* *
* @param stash * @param stash
*/ */
get(path: string | number, stash = true): Prop | null { get(path: string | number, stash: boolean = true): Prop | null {
const type = this._type; const type = this._type;
if (type !== 'map' && type !== 'list' && type !== 'unset' && !stash) { if (type !== 'map' && type !== 'list' && type !== 'unset' && !stash) {
return null; return null;
@ -585,6 +611,14 @@ export class Prop implements IPropParent {
return isMap ? fn(item, item.key) : fn(item, index); return isMap ? fn(item, item.key) : fn(item, index);
}); });
} }
getProps() {
return this.parent;
}
getNode() {
return this.owner;
}
} }
export function isProp(obj: any): obj is Prop { export function isProp(obj: any): obj is Prop {

View File

@ -1,9 +1,25 @@
import { PropsMap, PropsList, CompositeValue, computed, obx, uniqueId } from '@ali/lowcode-globals'; import { computed, obx } from '@ali/lowcode-editor-core';
import { PropsMap, PropsList, CompositeValue } from '@ali/lowcode-types';
import { uniqueId } from '@ali/lowcode-utils';
import { PropStash } from './prop-stash'; import { PropStash } from './prop-stash';
import { Prop, IPropParent, UNSET } from './prop'; import { Prop, IPropParent, UNSET } from './prop';
import { Node } from '../node'; import { Node } from '../node';
import { TransformStage } from '../transform-stage';
export const EXTRA_KEY_PREFIX = '__'; export const EXTRA_KEY_PREFIX = '___';
export function getConvertedExtraKey(key: string): string {
if (!key) {
return '';
}
let _key = key;
if (key.indexOf('.') > 0) {
_key = key.split('.')[0];
}
return EXTRA_KEY_PREFIX + _key + EXTRA_KEY_PREFIX + key.substr(_key.length);
}
export function getOriginalExtraKey(key: string): string {
return key.replace(new RegExp(`${EXTRA_KEY_PREFIX}`, 'g'), '');
}
export class Props implements IPropParent { export class Props implements IPropParent {
readonly id = uniqueId('props'); readonly id = uniqueId('props');
@ -47,7 +63,7 @@ export class Props implements IPropParent {
} }
if (extras) { if (extras) {
Object.keys(extras).forEach(key => { Object.keys(extras).forEach(key => {
this.items.push(new Prop(this, (extras as any)[key], EXTRA_KEY_PREFIX + key)); this.items.push(new Prop(this, (extras as any)[key], getConvertedExtraKey(key)));
}); });
} }
} }
@ -67,7 +83,7 @@ export class Props implements IPropParent {
} }
if (extras) { if (extras) {
Object.keys(extras).forEach(key => { Object.keys(extras).forEach(key => {
this.items.push(new Prop(this, (extras as any)[key], EXTRA_KEY_PREFIX + key)); this.items.push(new Prop(this, (extras as any)[key], getConvertedExtraKey(key)));
}); });
} }
originItems.forEach(item => item.purge()); originItems.forEach(item => item.purge());
@ -79,7 +95,7 @@ export class Props implements IPropParent {
}); });
} }
export(serialize = false): { props?: PropsMap | PropsList; extras?: object } { export(stage: TransformStage = TransformStage.Save): { props?: PropsMap | PropsList; extras?: object } {
if (this.items.length < 1) { if (this.items.length < 1) {
return {}; return {};
} }
@ -88,13 +104,13 @@ export class Props implements IPropParent {
if (this.type === 'list') { if (this.type === 'list') {
props = []; props = [];
this.items.forEach(item => { this.items.forEach(item => {
let value = item.export(serialize); let value = item.export(stage);
if (value === UNSET) { if (value === UNSET) {
value = null; value = undefined;
} }
let name = item.key as string; let name = item.key as string;
if (name && typeof name === 'string' && name.startsWith(EXTRA_KEY_PREFIX)) { if (name && typeof name === 'string' && name.startsWith(EXTRA_KEY_PREFIX)) {
name = name.substr(EXTRA_KEY_PREFIX.length); name = getOriginalExtraKey(name);
extras[name] = value; extras[name] = value;
} else { } else {
props.push({ props.push({
@ -111,12 +127,12 @@ export class Props implements IPropParent {
// todo ...spread // todo ...spread
return; return;
} }
let value = item.export(serialize); let value = item.export(stage);
if (value === UNSET) { if (value === UNSET) {
value = null; value = undefined;
} }
if (typeof name === 'string' && name.startsWith(EXTRA_KEY_PREFIX)) { if (typeof name === 'string' && name.startsWith(EXTRA_KEY_PREFIX)) {
name = name.substr(EXTRA_KEY_PREFIX.length); name = getOriginalExtraKey(name);
extras[name] = value; extras[name] = value;
} else { } else {
props[name] = value; props[name] = value;
@ -295,4 +311,22 @@ export class Props implements IPropParent {
this.stash.purge(); this.stash.purge();
this.items.forEach(item => item.purge()); this.items.forEach(item => item.purge());
} }
getProp(path: string, stash = true): Prop | null {
return this.query(path, stash as any) || null;
}
/**
*
*/
getPropValue(path: string): any {
return this.getProp(path, false)?.value;
}
/**
*
*/
setPropValue(path: string, value: any) {
this.getProp(path, true)!.setValue(value);
}
} }

View File

@ -1,81 +0,0 @@
import { RootSchema } from '@ali/lowcode-globals';
import { Node, NodeParent } from './node';
import { DocumentModel } from '../document-model';
import { NodeChildren } from './node-children';
/**
*
*
* [Node Properties]
* componentName: Page/Block/Component
* props
* children
*
* [Root Container Extra Properties]
* fileName
* meta
* state
* defaultProps
* dataSource
* lifeCycles
* methods
* css
*
* [Directives **not used**]
* loop
* loopArgs
* condition
* ------- future support -----
* conditionGroup
* title
* ignore
* locked
* hidden
*/
export class RootNode extends Node implements NodeParent {
readonly isRootNode = true;
get isNodeParent() {
return true;
}
get index() {
return 0;
}
get nextSibling() {
return null;
}
get prevSibling() {
return null;
}
get zLevel() {
return 0;
}
get parent() {
return null;
}
get children(): NodeChildren {
return this._children as NodeChildren;
}
internalSetParent(parent: null) {
// empty
}
constructor(readonly document: DocumentModel, rootSchema: RootSchema) {
super(document, rootSchema);
}
isPage() {
return this.componentName === 'Page';
}
isComponent() {
return this.componentName === 'Component';
}
isBlock() {
return this.componentName === 'Block';
}
}
export function isRootNode(node: any): node is RootNode {
return node && node.isRootNode;
}

View File

@ -0,0 +1 @@
export { TransformStage } from '@ali/lowcode-types';

View File

@ -1,5 +1,5 @@
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { obx } from '@ali/lowcode-globals'; import { obx } from '@ali/lowcode-editor-core';
import { Node, comparePosition, PositionNO } from './node/node'; import { Node, comparePosition, PositionNO } from './node/node';
import { DocumentModel } from './document-model'; import { DocumentModel } from './document-model';

View File

@ -1,4 +1,4 @@
import { SVGIcon, IconProps } from "@ali/lowcode-globals"; import { SVGIcon, IconProps } from "@ali/lowcode-utils";
export function IconClone(props: IconProps) { export function IconClone(props: IconProps) {
return ( return (

View File

@ -1,4 +1,4 @@
import { SVGIcon, IconProps } from "@ali/lowcode-globals"; import { SVGIcon, IconProps } from "@ali/lowcode-utils";
export function IconComponent(props: IconProps) { export function IconComponent(props: IconProps) {
return ( return (

View File

@ -1,4 +1,4 @@
import { SVGIcon, IconProps } from "@ali/lowcode-globals"; import { SVGIcon, IconProps } from "@ali/lowcode-utils";
export function IconContainer(props: IconProps) { export function IconContainer(props: IconProps) {
return ( return (

Some files were not shown because too many files have changed in this diff Show More