mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-13 01:21:58 +00:00
Merge commit '8cbdd383cef4c9631db3e851a8a1b474392e262e' into feat/code-generator
* commit '8cbdd383cef4c9631db3e851a8a1b474392e262e': (95 commits)
chore: fix addowner
Publish
refactor: move icons to local
comment
fix: [material-parser]fix bug of main field & remove useless debugger
feat: support localizing
refactor: runtime
fix: fix bug of build errors
fix: fix bug of missing types in material-parser
feat: 🎸 support parsing fusion source code
chore: @ali/lowcode-runtime@0.8.5
feat: style setter 国际化
refactor: runtime
Publish
chore: output sourceMap
fix(settings-pane): overflow problem
feat(designer): add builtin hotkeys
fix(designer): fix insertion style
feat: add favicon for preview
feat(designer): add blank page logic
...
This commit is contained in:
commit
abc602b4a1
@ -4,3 +4,4 @@ build/
|
||||
.*
|
||||
~*
|
||||
node_modules
|
||||
|
||||
3
.eslintrc
Normal file
3
.eslintrc
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "./node_modules/@ali/lowcode-config/.eslintrc"
|
||||
}
|
||||
16
.gitignore
vendored
16
.gitignore
vendored
@ -1,3 +1,19 @@
|
||||
# project custom
|
||||
build
|
||||
dist
|
||||
packages/*/lib/
|
||||
packages/*/es/
|
||||
packages/*/dist/
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
deploy-space/packages
|
||||
deploy-space/.env
|
||||
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
@ -2,5 +2,7 @@
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"arrowParens": "always"
|
||||
}
|
||||
|
||||
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2020-present Alibaba Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
16
README.md
16
README.md
@ -10,21 +10,13 @@
|
||||
|
||||
#### 跑起来:
|
||||
|
||||
- `tnpm i`
|
||||
- `npm run boot`
|
||||
- `npm run build`
|
||||
- `npm run setup`
|
||||
- `npm start`
|
||||
|
||||
#### Link & unlink
|
||||
|
||||
- `cd packages/<package-name> && tnpm link -g`
|
||||
- `tnpm link @ali/<package-name>`
|
||||
|
||||
- `tnpm unlink @ali/<package-name>`
|
||||
|
||||
#### 开发过程中:
|
||||
#### 开发提交:
|
||||
|
||||
- `git add <your-files>`
|
||||
- `npm run commit`
|
||||
- `npm run commit` # 在根目录
|
||||
|
||||
## 发布
|
||||
|
||||
|
||||
11
abc.json
Normal file
11
abc.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "lowcode-engine",
|
||||
"assets": {
|
||||
"type": "command",
|
||||
"command": {
|
||||
"cmd": [
|
||||
"./scripts/deploy.sh $BUILD_DEST"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
29
deploy-space/html/index.html
Normal file
29
deploy-space/html/index.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!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>
|
||||
23
deploy-space/html/preview.html
Normal file
23
deploy-space/html/preview.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!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>
|
||||
9
deploy-space/lerna.json
Normal file
9
deploy-space/lerna.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"version": "independent",
|
||||
"npmClient": "yarn",
|
||||
"registry": "http://registry.npm.alibaba-inc.com",
|
||||
"useWorkspaces": true,
|
||||
"packages": [
|
||||
"packages/*"
|
||||
]
|
||||
}
|
||||
16
deploy-space/package.json
Normal file
16
deploy-space/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"nohoist": [
|
||||
"**/css-modules-typescript-loader",
|
||||
"**/@alife/theme-lowcode-*"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^1.11.1",
|
||||
"typescript": "^3.8.3"
|
||||
}
|
||||
}
|
||||
40
deploy-space/tsconfig.json
Normal file
40
deploy-space/tsconfig.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": false,
|
||||
"lib": ["es2015", "dom"],
|
||||
// Target latest version of ECMAScript.
|
||||
"target": "esnext",
|
||||
// Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.
|
||||
"module": "esnext",
|
||||
// Search under node_modules for non-relative imports.
|
||||
"moduleResolution": "node",
|
||||
// Process & infer types from .js files.
|
||||
"allowJs": true,
|
||||
// Report errors in .js files.
|
||||
"checkJs": false,
|
||||
// Don't emit; allow Babel to transform files.
|
||||
// "noEmit": true,
|
||||
// Enable strictest settings like strictNullChecks & noImplicitAny.
|
||||
"strict": false,
|
||||
// Allow default imports from modules with no default export. This does not affect code emit, just typechecking.
|
||||
"allowSyntheticDefaultImports": true,
|
||||
// Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'.
|
||||
"esModuleInterop": true,
|
||||
// Specify JSX code generation: 'preserve', 'react-native', or 'react'.
|
||||
"jsx": "preserve",
|
||||
// Import emit helpers (e.g. __extends, __rest, etc..) from tslib
|
||||
"importHelpers": true,
|
||||
// Enables experimental support for ES7 decorators.
|
||||
"experimentalDecorators": true,
|
||||
// Generates corresponding .map file.
|
||||
"sourceMap": false,
|
||||
// Disallow inconsistently-cased references to the same file.
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
// Allow json import
|
||||
"resolveJsonModule": true,
|
||||
// skip type checking of declaration files
|
||||
"skipLibCheck": true,
|
||||
"outDir": "lib"
|
||||
},
|
||||
"exclude": ["**/test", "**/lib", "**/es", "node_modules"]
|
||||
}
|
||||
@ -10,7 +10,7 @@
|
||||
- 使用 `camelCase` 为属性或本地变量命名
|
||||
- 不要为私有属性名添加 `_` 前缀
|
||||
- 尽可能使用完整的单词拼写命名
|
||||
- 文件夹命名统一使用小写
|
||||
- 文件夹/文件命名统一使用小写 `get-custom-data.ts`
|
||||
|
||||
### 组件
|
||||
|
||||
|
||||
20
lerna.json
20
lerna.json
@ -1,17 +1,27 @@
|
||||
{
|
||||
"lerna": "2.11.0",
|
||||
"version": "independent",
|
||||
"npmClient": "tnpm",
|
||||
"npmClient": "tyarn",
|
||||
"registry": "http://registry.npm.alibaba-inc.com",
|
||||
"useWorkspaces": true,
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"command": {
|
||||
"bootstrap": {
|
||||
"npmClientArgs": [
|
||||
"--no-package-lock"
|
||||
]
|
||||
},
|
||||
"publish": {
|
||||
"npmClient": "tnpm",
|
||||
"verifyRegistry": false,
|
||||
"verifyAccess": false,
|
||||
"ignoreChanges": [
|
||||
"**/*.md",
|
||||
"**/test/**"
|
||||
],
|
||||
"conventionalCommits": true
|
||||
}
|
||||
},
|
||||
"packages": [
|
||||
"packages/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
83
package.json
83
package.json
@ -1,53 +1,52 @@
|
||||
{
|
||||
"name": "ali-lowcode-engine",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"boot": "lerna bootstrap --registry http://registry.npm.alibaba-inc.com --hoist",
|
||||
"clean": "rimraf ./packages/*/lib ./packages/*/node_modules",
|
||||
"pub": "npm run test && lerna publish --registry http://registry.npm.alibaba-inc.com",
|
||||
"lint": "tslint -p tsconfig.json",
|
||||
"lint:fix": "tslint --fix -p tsconfig.json",
|
||||
"build": "lerna run build",
|
||||
"test": "lerna run test",
|
||||
"test:snapshot": "lerna run test:snapshot",
|
||||
"commit": "git-cz"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ice/spec": "^0.1.4",
|
||||
"@types/node": "^10.12.18",
|
||||
"ava": "^1.0.1",
|
||||
"commitizen": "^3.0.5",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"eslint": "^6.0.1",
|
||||
"git-cz": "^4.3.1",
|
||||
"husky": "^1.3.1",
|
||||
"lerna": "^2.11.0",
|
||||
"lint-staged": "^8.1.0",
|
||||
"prettier": "^1.15.3",
|
||||
"rimraf": "^2.6.3",
|
||||
"ts-node": "^7.0.1",
|
||||
"tslint": "^6.1.0",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"typescript": "^3.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^8 || ^10"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.ts": [
|
||||
"prettier --write",
|
||||
"tslint --fix",
|
||||
"git add"
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"nohoist": [
|
||||
"**/css-modules-typescript-loader",
|
||||
"**/@alife/theme-lowcode-*"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
"scripts": {
|
||||
"build": "lerna run build --stream",
|
||||
"clean": "rm -rf ./packages/*/lib ./packages/*/es ./packages/*/dist ./packages/*/build",
|
||||
"commit": "git-cz",
|
||||
"pub": "lerna publish",
|
||||
"setup": "./scripts/setup.sh",
|
||||
"start": "./scripts/start.sh",
|
||||
"test": "lerna run test --stream",
|
||||
"test:snapshot": "lerna run test:snapshot"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{tsx,ts}": [
|
||||
"eslint --quiet",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ali/lowcode-config": "^2.0.5",
|
||||
"ava": "^1.0.1",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"commitizen": "^3.0.5",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"eslint": "^6.5.1",
|
||||
"git-cz": "^4.3.1",
|
||||
"husky": "^4.2.3",
|
||||
"lerna": "^2.11.0",
|
||||
"lint-staged": "^8.1.0",
|
||||
"prettier": "^1.15.3",
|
||||
"ts-node": "^7.0.1",
|
||||
"tslib": "^1.9.3",
|
||||
"typescript": "^3.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
52
packages/code-generator/CHANGELOG.md
Normal file
52
packages/code-generator/CHANGELOG.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<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)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-code-generator
|
||||
|
||||
<a name="0.8.3"></a>
|
||||
## [0.8.3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.8.2...@ali/lowcode-code-generator@0.8.3) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-code-generator
|
||||
|
||||
<a name="0.8.2"></a>
|
||||
## 0.8.2 (2020-03-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* code generator main process ([021d6e0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/021d6e0))
|
||||
* demo schema & complex children type ([a5ee6bd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a5ee6bd))
|
||||
* fix gaps ([32af3d3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/32af3d3))
|
||||
* project builder fix & publish demo to disk ([26983b3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/26983b3))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.1"></a>
|
||||
## 0.8.1 (2020-03-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
<<<<<<< HEAD
|
||||
* code generator main process ([021d6e0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/021d6e0))
|
||||
* demo schema & complex children type ([a5ee6bd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a5ee6bd))
|
||||
* fix gaps ([32af3d3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/32af3d3))
|
||||
* project builder fix & publish demo to disk ([26983b3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/26983b3))
|
||||
=======
|
||||
* code generator main process ([021d6e0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/021d6e0fe9fb29a8b6c1c5d5f4d06ec71896faa5))
|
||||
* demo schema & complex children type ([a5ee6bd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a5ee6bd55806fc9aea695096ccd4c7f50b8e31c4))
|
||||
* fix gaps ([32af3d3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/32af3d3a3ca4d5aca15be25e05c840c8ea0cb6ae))
|
||||
* project builder fix & publish demo to disk ([26983b3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/26983b38c2b0f1d39d79964eb54d8ce60250dd82))
|
||||
>>>>>>> df955e1db90ff104cd11160def80113cfd6faccc
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-engine-code-generator",
|
||||
"version": "0.0.1",
|
||||
"name": "@ali/lowcode-code-generator",
|
||||
"version": "0.8.4",
|
||||
"description": "出码引擎 for LowCode Engine",
|
||||
"main": "lib/index.js",
|
||||
"files": [
|
||||
|
||||
81
packages/demo/CHANGELOG.md
Normal file
81
packages/demo/CHANGELOG.md
Normal file
@ -0,0 +1,81 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<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)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-demo
|
||||
|
||||
<a name="0.8.7"></a>
|
||||
## [0.8.7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.6...@ali/lowcode-demo@0.8.7) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-demo
|
||||
|
||||
<a name="0.8.6"></a>
|
||||
## [0.8.6](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.5...@ali/lowcode-demo@0.8.6) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-demo
|
||||
|
||||
<a name="0.8.5"></a>
|
||||
## [0.8.5](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.4...@ali/lowcode-demo@0.8.5) (2020-03-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* depend ([c90996d](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/c90996d))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.4"></a>
|
||||
## [0.8.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.3...@ali/lowcode-demo@0.8.4) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-demo
|
||||
|
||||
<a name="0.8.3"></a>
|
||||
## [0.8.3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@0.8.2...@ali/lowcode-demo@0.8.3) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-demo
|
||||
|
||||
<a name="0.8.2"></a>
|
||||
## 0.8.2 (2020-03-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* complet preview ([56c16ff](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/56c16ff))
|
||||
* double outline & ZH_EN support ([b379bd7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b379bd7))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.1"></a>
|
||||
## 0.8.1 (2020-03-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
<<<<<<< HEAD
|
||||
* complet preview ([56c16ff](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/56c16ff))
|
||||
* double outline & ZH_EN support ([b379bd7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b379bd7))
|
||||
=======
|
||||
* complet preview ([56c16ff](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/56c16ffa5c39c2d01abd9cfa90fea49a4539da1d))
|
||||
* double outline & ZH_EN support ([b379bd7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b379bd7c0c488ef24f825760750a13d3fa083c96))
|
||||
>>>>>>> df955e1db90ff104cd11160def80113cfd6faccc
|
||||
38
packages/demo/build.json
Normal file
38
packages/demo/build.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"entry": {
|
||||
"index": "src/index.ts",
|
||||
"react-simulator-renderer": "../react-simulator-renderer/src/index.ts",
|
||||
"preview": "src/preview.ts"
|
||||
},
|
||||
"vendor": false,
|
||||
"devServer": {
|
||||
"hot": false
|
||||
},
|
||||
"publicPath": "/",
|
||||
"externals": {
|
||||
"react": "window.React",
|
||||
"react-dom": "window.ReactDOM",
|
||||
"prop-types": "window.PropTypes",
|
||||
"@alifd/next": "window.Next"
|
||||
},
|
||||
"plugins": [
|
||||
[
|
||||
"build-plugin-react-app"
|
||||
],
|
||||
[
|
||||
"build-plugin-fusion",
|
||||
{
|
||||
"themePackage": "@alife/theme-lowcode-light"
|
||||
}
|
||||
],
|
||||
[
|
||||
"build-plugin-moment-locales",
|
||||
{
|
||||
"locales": [
|
||||
"zh-cn"
|
||||
]
|
||||
}
|
||||
],
|
||||
"./build.plugin.js"
|
||||
]
|
||||
}
|
||||
13
packages/demo/build.plugin.js
Normal file
13
packages/demo/build.plugin.js
Normal file
@ -0,0 +1,13 @@
|
||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||
|
||||
module.exports = ({ onGetWebpackConfig }) => {
|
||||
onGetWebpackConfig((config) => {
|
||||
config.resolve
|
||||
.plugin('tsconfigpaths')
|
||||
.use(TsconfigPathsPlugin, [{
|
||||
configFile: "./tsconfig.json"
|
||||
}]);
|
||||
config.plugins.delete('hot');
|
||||
config.devServer.hot(false);
|
||||
});
|
||||
};
|
||||
33
packages/demo/cloud-build.json
Normal file
33
packages/demo/cloud-build.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"entry": {
|
||||
"lowcode-editor": "src/index.ts",
|
||||
"lowcode-preview": "src/preview.ts"
|
||||
},
|
||||
"vendor": false,
|
||||
"externals": {
|
||||
"react": "window.React",
|
||||
"react-dom": "window.ReactDOM",
|
||||
"prop-types": "window.PropTypes",
|
||||
"@ali/lowcode-globals": "window.LCEGlobals"
|
||||
},
|
||||
"minify": false,
|
||||
"sourcemap": true,
|
||||
"outputAssetsPath": {
|
||||
"js": "", "css": ""
|
||||
},
|
||||
"plugins": [
|
||||
["build-plugin-react-app"],
|
||||
[
|
||||
"build-plugin-fusion",
|
||||
{
|
||||
"themePackage": "@alife/theme-lowcode-light"
|
||||
}
|
||||
],
|
||||
[
|
||||
"build-plugin-moment-locales",
|
||||
{
|
||||
"locales": ["zh-cn"]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
46
packages/demo/package.json
Normal file
46
packages/demo/package.json
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "@ali/lowcode-demo",
|
||||
"version": "0.8.8",
|
||||
"private": true,
|
||||
"description": "低代码引擎 DEMO",
|
||||
"scripts": {
|
||||
"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"
|
||||
},
|
||||
"config": {},
|
||||
"dependencies": {
|
||||
"@ali/lowcode-editor-core": "^0.8.4",
|
||||
"@ali/lowcode-editor-skeleton": "^0.8.0",
|
||||
"@ali/lowcode-plugin-components-pane": "^0.8.0",
|
||||
"@ali/lowcode-plugin-designer": "^0.9.1",
|
||||
"@ali/lowcode-plugin-event-bind-dialog": "^0.8.0",
|
||||
"@ali/lowcode-plugin-outline-pane": "^0.8.7",
|
||||
"@ali/lowcode-plugin-sample-logo": "^0.8.0",
|
||||
"@ali/lowcode-plugin-sample-preview": "^0.8.6",
|
||||
"@ali/lowcode-plugin-settings-pane": "^0.8.8",
|
||||
"@ali/lowcode-plugin-undo-redo": "^0.8.0",
|
||||
"@ali/lowcode-plugin-variable-bind-dialog": "^0.8.2",
|
||||
"@ali/lowcode-plugin-zh-en": "^0.8.6",
|
||||
"@ali/lowcode-react-renderer": "^0.8.4",
|
||||
"@ali/lowcode-runtime": "^0.8.7",
|
||||
"@ali/lowcode-setters": "^0.8.6",
|
||||
"@alifd/next": "^1.19.12",
|
||||
"@alife/theme-lowcode-dark": "^0.1.0",
|
||||
"@alife/theme-lowcode-light": "^0.1.0",
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ali/iceluna-cli": "^0.0.16",
|
||||
"@alib/build-scripts": "^0.1.18",
|
||||
"@types/events": "^3.0.0",
|
||||
"@types/react": "^16.8.3",
|
||||
"@types/react-dom": "^16.8.2",
|
||||
"build-plugin-fusion": "^0.1.0",
|
||||
"build-plugin-moment-locales": "^0.1.0",
|
||||
"build-plugin-react-app": "^1.1.2",
|
||||
"tsconfig-paths-webpack-plugin": "^3.2.0"
|
||||
}
|
||||
}
|
||||
1594
packages/demo/public/assets.json
Normal file
1594
packages/demo/public/assets.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
packages/demo/public/favicon.png
Normal file
BIN
packages/demo/public/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
@ -15,7 +15,7 @@
|
||||
<script src="https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="dark">
|
||||
<div id="ice-container"></div>
|
||||
<body>
|
||||
<div id="lce-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
21
packages/demo/public/preview.html
Normal file
21
packages/demo/public/preview.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!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 DEMO</title>
|
||||
<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>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="lce-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
110
packages/demo/public/schema.json
Normal file
110
packages/demo/public/schema.json
Normal file
@ -0,0 +1,110 @@
|
||||
{
|
||||
"componentName": "Page",
|
||||
"fileName": "test",
|
||||
"dataSource": {
|
||||
"list": []
|
||||
},
|
||||
"state": {
|
||||
"text": "outter"
|
||||
},
|
||||
"props": {
|
||||
"ref": "outterView",
|
||||
"autoLoading": true,
|
||||
"style": {
|
||||
"padding": 20
|
||||
}
|
||||
},
|
||||
"children": [{
|
||||
"componentName": "Form",
|
||||
"props": {
|
||||
"labelCol": 3,
|
||||
"style": {},
|
||||
"ref": "testForm"
|
||||
},
|
||||
"children": [{
|
||||
"componentName": "Form.Item",
|
||||
"props": {
|
||||
"label": "姓名:",
|
||||
"name": "name",
|
||||
"initValue": "李雷"
|
||||
},
|
||||
"children": [{
|
||||
"componentName": "Input",
|
||||
"props": {
|
||||
"placeholder": "请输入",
|
||||
"size": "medium",
|
||||
"style": {
|
||||
"width": 320
|
||||
}
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
"componentName": "Form.Item",
|
||||
"props": {
|
||||
"label": "年龄:",
|
||||
"name": "age",
|
||||
"initValue": "22"
|
||||
},
|
||||
"children": [{
|
||||
"componentName": "NumberPicker",
|
||||
"props": {
|
||||
"size": "medium",
|
||||
"type": "normal"
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
"componentName": "Form.Item",
|
||||
"props": {
|
||||
"label": "职业:",
|
||||
"name": "profession"
|
||||
},
|
||||
"children": [{
|
||||
"componentName": "Select",
|
||||
"props": {
|
||||
"dataSource": [{
|
||||
"label": "教师",
|
||||
"value": "t"
|
||||
}, {
|
||||
"label": "医生",
|
||||
"value": "d"
|
||||
}, {
|
||||
"label": "歌手",
|
||||
"value": "s"
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
"componentName": "Div",
|
||||
"props": {
|
||||
"style": {
|
||||
"textAlign": "center"
|
||||
}
|
||||
},
|
||||
"children": [{
|
||||
"componentName": "Button.Group",
|
||||
"props": {},
|
||||
"children": [{
|
||||
"componentName": "Button",
|
||||
"props": {
|
||||
"type": "primary",
|
||||
"style": {
|
||||
"margin": "0 5px 0 5px"
|
||||
},
|
||||
"htmlType": "submit"
|
||||
},
|
||||
"children": "提交"
|
||||
}, {
|
||||
"componentName": "Button",
|
||||
"props": {
|
||||
"type": "normal",
|
||||
"style": {
|
||||
"margin": "0 5px 0 5px"
|
||||
},
|
||||
"htmlType": "reset"
|
||||
},
|
||||
"children": "重置"
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
155
packages/demo/skeleton.config.js
Normal file
155
packages/demo/skeleton.config.js
Normal file
@ -0,0 +1,155 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
21
packages/demo/src/app/config/app.ts
Normal file
21
packages/demo/src/app/config/app.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export default {
|
||||
sdkVersion: '1.0.3',
|
||||
history: 'hash', // 浏览器路由:brower 哈希路由:hash
|
||||
containerId: 'lce-container',
|
||||
layout: {
|
||||
componentName: 'BasicLayout',
|
||||
props: {
|
||||
name: '低代码引擎预览 demo',
|
||||
logo: {
|
||||
src: 'https://img.alicdn.com/tfs/TB1L.1QAeL2gK0jSZFmXXc7iXXa-90-90.png',
|
||||
width: 25,
|
||||
height: 25,
|
||||
},
|
||||
},
|
||||
},
|
||||
theme: {
|
||||
package: '@alife/theme-fusion',
|
||||
version: '^0.1.0',
|
||||
},
|
||||
compDependencies: [],
|
||||
};
|
||||
5
packages/demo/src/app/config/components.ts
Normal file
5
packages/demo/src/app/config/components.ts
Normal file
@ -0,0 +1,5 @@
|
||||
/**
|
||||
* 内置组件
|
||||
*/
|
||||
|
||||
export default {};
|
||||
53
packages/demo/src/app/config/componentsMap.ts
Normal file
53
packages/demo/src/app/config/componentsMap.ts
Normal file
@ -0,0 +1,53 @@
|
||||
export default {
|
||||
Button: {
|
||||
package: '@alifd/next',
|
||||
version: '1.19.18',
|
||||
destructuring: true,
|
||||
exportName: 'Button',
|
||||
},
|
||||
'Button.Group': {
|
||||
package: '@alifd/next',
|
||||
version: '1.19.18',
|
||||
destructuring: true,
|
||||
exportName: 'Button',
|
||||
subName: 'Group',
|
||||
},
|
||||
Input: {
|
||||
package: '@alifd/next',
|
||||
version: '1.19.18',
|
||||
destructuring: true,
|
||||
exportName: 'Input',
|
||||
},
|
||||
Form: {
|
||||
package: '@alifd/next',
|
||||
version: '1.19.18',
|
||||
destructuring: true,
|
||||
exportName: 'Form',
|
||||
},
|
||||
'Form.Item': {
|
||||
package: '@alifd/next',
|
||||
version: '1.19.18',
|
||||
destructuring: true,
|
||||
exportName: 'Form',
|
||||
subName: 'Item',
|
||||
},
|
||||
NumberPicker: {
|
||||
package: '@alifd/next',
|
||||
version: '1.19.18',
|
||||
destructuring: true,
|
||||
exportName: 'NumberPicker',
|
||||
},
|
||||
Select: {
|
||||
package: '@alifd/next',
|
||||
version: '1.19.18',
|
||||
destructuring: true,
|
||||
exportName: 'Select',
|
||||
},
|
||||
'Select.Option': {
|
||||
package: '@alifd/next',
|
||||
version: '1.19.18',
|
||||
destructuring: true,
|
||||
exportName: 'Select',
|
||||
subName: 'Option',
|
||||
},
|
||||
};
|
||||
20
packages/demo/src/app/index.ts
Normal file
20
packages/demo/src/app/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { app } from '@ali/lowcode-runtime';
|
||||
import Renderer from '@ali/lowcode-react-renderer';
|
||||
import FusionLoading from './plugins/loading/fusion';
|
||||
import BasicLayout from './layouts/BasicLayout';
|
||||
import Preview from './plugins/provider';
|
||||
|
||||
// 注册渲染模块
|
||||
app.registerRenderer(Renderer);
|
||||
|
||||
// 注册布局组件,可注册多个
|
||||
app.registerLayout('BasicLayout', BasicLayout);
|
||||
|
||||
// 注册页面 Loading
|
||||
app.registerLoading(FusionLoading);
|
||||
|
||||
// appKey:应用唯一标识
|
||||
app.registerProvider(Preview);
|
||||
|
||||
// 启动应用
|
||||
app.run();
|
||||
24
packages/demo/src/app/layouts/BasicLayout/index.scss
Normal file
24
packages/demo/src/app/layouts/BasicLayout/index.scss
Normal file
@ -0,0 +1,24 @@
|
||||
$header-height: 52px;
|
||||
|
||||
.avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.basic-shell {
|
||||
min-height: 100vh;
|
||||
.next-shell-header {
|
||||
height: $header-height;
|
||||
}
|
||||
.next-shell-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
min-height: calc(100% - $header-height);
|
||||
.next-shell-sub-main {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
packages/demo/src/app/layouts/BasicLayout/index.tsx
Normal file
34
packages/demo/src/app/layouts/BasicLayout/index.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { Search, Icon, Shell } from '@alifd/next';
|
||||
import './index.scss';
|
||||
|
||||
export default ({
|
||||
name,
|
||||
children,
|
||||
logo,
|
||||
}: {
|
||||
name: string;
|
||||
children: any;
|
||||
logo: { src: string; width: number; height: number };
|
||||
}) => (
|
||||
<Shell className="basic-shell" style={{ border: '1px solid #eee' }}>
|
||||
<Shell.Branding>
|
||||
<img src={logo.src} width={logo.width} height={logo.height} alt="logo" />
|
||||
<span style={{ marginLeft: 10 }}>{name}</span>
|
||||
</Shell.Branding>
|
||||
<Shell.Navigation direction="hoz">
|
||||
<Search key="2" shape="simple" type="dark" style={{ width: '200px' }} />
|
||||
</Shell.Navigation>
|
||||
<Shell.Action>
|
||||
<Icon type="ic_tongzhi" />
|
||||
<img src="https://img.alicdn.com/tfs/TB1.ZBecq67gK0jSZFHXXa9jVXa-904-826.png" className="avatar" alt="用户头像" />
|
||||
<span style={{ marginLeft: 10 }}>MyName</span>
|
||||
</Shell.Action>
|
||||
|
||||
<Shell.Content className="content">{children}</Shell.Content>
|
||||
|
||||
<Shell.Footer>
|
||||
<span>Alibaba Fusion</span>
|
||||
<span>@ 2019 Alibaba Piecework 版权所有</span>
|
||||
</Shell.Footer>
|
||||
</Shell>
|
||||
);
|
||||
9
packages/demo/src/app/plugins/loading/fusion/index.scss
Normal file
9
packages/demo/src/app/plugins/loading/fusion/index.scss
Normal file
@ -0,0 +1,9 @@
|
||||
.fusion-loading {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -24px;
|
||||
margin-left: -24px;
|
||||
}
|
||||
4
packages/demo/src/app/plugins/loading/fusion/index.tsx
Normal file
4
packages/demo/src/app/plugins/loading/fusion/index.tsx
Normal file
@ -0,0 +1,4 @@
|
||||
import { Loading } from '@alifd/next';
|
||||
import './index.scss';
|
||||
|
||||
export default () => <Loading tip="加载中..." className="fusion-loading" />;
|
||||
51
packages/demo/src/app/plugins/provider.ts
Normal file
51
packages/demo/src/app/plugins/provider.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { ReactProvider, Utils } from '@ali/lowcode-runtime';
|
||||
import appConfig from '../config/app';
|
||||
import builtInComps from '../config/components';
|
||||
import componentsMap from '../config/componentsMap';
|
||||
import constants from '../config/constants';
|
||||
import utils from '../config/utils';
|
||||
|
||||
// 定制加载应用配置的逻辑
|
||||
export default class Preview extends ReactProvider {
|
||||
// 定制获取、处理应用配置(组件、插件、路由模式、布局等)的逻辑
|
||||
async getAppData(): Promise<any> {
|
||||
const { history, layout, containerId } = appConfig;
|
||||
const appSchemaStr: any = localStorage.getItem('lce-dev-store');
|
||||
if (!appSchemaStr) {
|
||||
return;
|
||||
}
|
||||
const appSchema = JSON.parse(appSchemaStr);
|
||||
if (!appSchema) {
|
||||
return;
|
||||
}
|
||||
const routes: any = {};
|
||||
appSchema.componentsTree.forEach((page: any) => {
|
||||
if (!page.fileName) {
|
||||
return;
|
||||
}
|
||||
const pageId = page.fileName;
|
||||
routes[pageId] = `/${pageId}`;
|
||||
});
|
||||
return {
|
||||
history,
|
||||
layout,
|
||||
routes,
|
||||
containerId,
|
||||
components: { ...builtInComps, ...Utils.buildComponents({ '@alifd/next': 'Next' }, componentsMap) },
|
||||
componentsMap,
|
||||
utils: utils,
|
||||
constants,
|
||||
};
|
||||
}
|
||||
|
||||
// 定制获取、处理页面 schema 的逻辑
|
||||
async getPageData(pageId: string) {
|
||||
const appSchemaStr = localStorage.getItem('lce-dev-store');
|
||||
const appSchema = JSON.parse(appSchemaStr || '');
|
||||
const idx = appSchema.componentsTree.findIndex(
|
||||
(page: any, idx: number) => (page.fileName || `page${idx}`) === pageId,
|
||||
);
|
||||
const schema = appSchema.componentsTree[idx];
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
22
packages/demo/src/editor/config/components.js
Normal file
22
packages/demo/src/editor/config/components.js
Normal file
@ -0,0 +1,22 @@
|
||||
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
|
||||
};
|
||||
3
packages/demo/src/editor/config/constants.js
Normal file
3
packages/demo/src/editor/config/constants.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
"namespace": "page"
|
||||
}
|
||||
@ -7,4 +7,4 @@ export default {
|
||||
'zh-CN': zh_cn,
|
||||
'zh-TW': zh_tw,
|
||||
'ja-JP': ja_jp
|
||||
};
|
||||
};
|
||||
1
packages/demo/src/editor/config/locale/zh-CN.js
Normal file
1
packages/demo/src/editor/config/locale/zh-CN.js
Normal file
@ -0,0 +1 @@
|
||||
export default {};
|
||||
1
packages/demo/src/editor/config/locale/zh-TW.js
Normal file
1
packages/demo/src/editor/config/locale/zh-TW.js
Normal file
@ -0,0 +1 @@
|
||||
export default {};
|
||||
140
packages/demo/src/editor/config/skeleton.js
Normal file
140
packages/demo/src/editor/config/skeleton.js
Normal file
@ -0,0 +1,140 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
3
packages/demo/src/editor/config/utils.js
Normal file
3
packages/demo/src/editor/config/utils.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
|
||||
};
|
||||
20
packages/demo/src/editor/index.tsx
Normal file
20
packages/demo/src/editor/index.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { registerSetters } from '@ali/lowcode-setters';
|
||||
import config from './config/skeleton';
|
||||
import components from './config/components';
|
||||
import utils from './config/utils';
|
||||
|
||||
import './global.scss';
|
||||
import './config/theme.scss';
|
||||
|
||||
registerSetters();
|
||||
const Skeleton = components.LowcodeSkeleton;
|
||||
const LCE_CONTAINER = document.getElementById('lce-container');
|
||||
|
||||
if (!LCE_CONTAINER) {
|
||||
throw new Error('当前页面不存在 <div id="lce-container"></div> 节点.');
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
ReactDOM.render(<Skeleton config={config} utils={utils} components={components} />, LCE_CONTAINER);
|
||||
1
packages/demo/src/index.ts
Normal file
1
packages/demo/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
import './editor';
|
||||
1
packages/demo/src/preview.ts
Normal file
1
packages/demo/src/preview.ts
Normal file
@ -0,0 +1 @@
|
||||
import './app';
|
||||
9
packages/demo/tsconfig.json
Normal file
9
packages/demo/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": [
|
||||
"./src/"
|
||||
]
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Tab indentation
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
@ -1,6 +0,0 @@
|
||||
.idea/
|
||||
.vscode/
|
||||
build/
|
||||
.*
|
||||
~*
|
||||
node_modules
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "./node_modules/@recore/config/.eslintrc"
|
||||
}
|
||||
41
packages/designer/.gitignore
vendored
41
packages/designer/.gitignore
vendored
@ -1,41 +0,0 @@
|
||||
node_modules/
|
||||
coverage/
|
||||
build/
|
||||
dist/
|
||||
.idea/
|
||||
.vscode/
|
||||
.theia/
|
||||
.recore/
|
||||
demo/
|
||||
~*
|
||||
package-lock.json
|
||||
|
||||
# Packages #
|
||||
############
|
||||
# it's better to unpack these files and commit the raw source
|
||||
# git has its own built in compression methods
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store
|
||||
.Trash*
|
||||
*.swp
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
74
packages/designer/CHANGELOG.md
Normal file
74
packages/designer/CHANGELOG.md
Normal file
@ -0,0 +1,74 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<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)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-designer
|
||||
|
||||
<a name="0.9.0"></a>
|
||||
# [0.9.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.8.5...@ali/lowcode-designer@0.9.0) (2020-03-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **designer:** fix insertion style ([82c10d2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/82c10d2))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **designer:** add blank page logic ([aeeb9ba](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/aeeb9ba))
|
||||
* **designer:** add builtin hotkeys ([2ec5883](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2ec5883))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.5"></a>
|
||||
## [0.8.5](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.8.4...@ali/lowcode-designer@0.8.5) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-designer
|
||||
|
||||
<a name="0.8.4"></a>
|
||||
## [0.8.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@0.8.3...@ali/lowcode-designer@0.8.4) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-designer
|
||||
|
||||
<a name="0.8.3"></a>
|
||||
## 0.8.3 (2020-03-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 merge material-parser ([b40c286](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b40c286))
|
||||
* history log ([fbb3577](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/fbb3577))
|
||||
* import react-docgen to parse propTypes ([6e66168](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6e66168))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.2"></a>
|
||||
## 0.8.2 (2020-03-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
<<<<<<< HEAD
|
||||
* 🎸 merge material-parser ([b40c286](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b40c286))
|
||||
* history log ([fbb3577](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/fbb3577))
|
||||
* import react-docgen to parse propTypes ([6e66168](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6e66168))
|
||||
=======
|
||||
* 🎸 merge material-parser ([b40c286](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b40c2869a0bc901d855279735fe86b84dabaa04d))
|
||||
* history log ([fbb3577](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/fbb3577bd434c0ac77cc907abc36e3efe110fe8c))
|
||||
* import react-docgen to parse propTypes ([6e66168](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6e661686e4693e69279c496f3be1dd173703c55e))
|
||||
>>>>>>> df955e1db90ff104cd11160def80113cfd6faccc
|
||||
@ -1 +1,4 @@
|
||||
编排模块
|
||||
|
||||
|
||||
simulator/renderer 发 CDN
|
||||
|
||||
7
packages/designer/build.json
Normal file
7
packages/designer/build.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"plugins": [
|
||||
[
|
||||
"build-plugin-component"
|
||||
]
|
||||
]
|
||||
}
|
||||
9380
packages/designer/package-lock.json
generated
9380
packages/designer/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,40 +1,45 @@
|
||||
{
|
||||
"name": "lowcode-designer",
|
||||
"version": "0.9.0",
|
||||
"description": "alibaba lowcode designer",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"name": "@ali/lowcode-designer",
|
||||
"version": "0.9.1",
|
||||
"description": "Designer for Ali LowCode Engine",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"es"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "build-scripts build --skip-demo",
|
||||
"test": "ava",
|
||||
"test:snapshot": "ava --update-snapshots"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ali/iceluna-sdk": "^1.0.5-beta.12",
|
||||
"@recore/obx": "^1.0.8",
|
||||
"@recore/obx-react": "^1.0.7",
|
||||
"@types/medium-editor": "^5.0.3",
|
||||
"@ali/lowcode-globals": "^0.9.1",
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16",
|
||||
"react-dom": "^16.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@recore/config": "^2.0.0",
|
||||
"@alib/build-scripts": "^0.1.18",
|
||||
"@types/classnames": "^2.2.7",
|
||||
"@types/jest": "^24.0.16",
|
||||
"@types/medium-editor": "^5.0.3",
|
||||
"@types/node": "^13.7.1",
|
||||
"@types/react": "^16",
|
||||
"@types/react-dom": "^16",
|
||||
"eslint": "^6.5.1",
|
||||
"husky": "^1.1.2",
|
||||
"jest": "^23.4.1",
|
||||
"lint-staged": "^7.1.2",
|
||||
"prettier": "^1.18.2",
|
||||
"ts-jest": "^24.0.2",
|
||||
"ts-node": "^8.0.1",
|
||||
"tslib": "^1.9.3",
|
||||
"typescript": "^3.1.3"
|
||||
"build-plugin-component": "^0.2.10"
|
||||
},
|
||||
"lint-staged": {
|
||||
"./src/**/*.{ts,tsx}": [
|
||||
"eslint --fix",
|
||||
"git add"
|
||||
"ava": {
|
||||
"compileEnhancements": false,
|
||||
"snapshotDir": "test/fixtures/__snapshots__",
|
||||
"extensions": [
|
||||
"ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register"
|
||||
]
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npm.alibaba-inc.com"
|
||||
}
|
||||
}
|
||||
|
||||
1
packages/designer/src/builtin-simulator/README.md
Normal file
1
packages/designer/src/builtin-simulator/README.md
Normal file
@ -0,0 +1 @@
|
||||
内置模拟器主进程
|
||||
@ -1,4 +1,4 @@
|
||||
.lc-auxiliary {
|
||||
.lc-bem-tools {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -1,12 +1,11 @@
|
||||
import { Component, Fragment, PureComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@recore/obx-react';
|
||||
import { computed, observer, TitleContent, Title } from '@ali/lowcode-globals';
|
||||
import { SimulatorContext } from '../context';
|
||||
import { SimulatorHost } from '../host';
|
||||
import { computed } from '@recore/obx';
|
||||
import { BuiltinSimulatorHost } from '../host';
|
||||
|
||||
export class OutlineHoveringInstance extends PureComponent<{
|
||||
title: string;
|
||||
export class BorderHoveringInstance extends PureComponent<{
|
||||
title: TitleContent;
|
||||
rect: DOMRect | null;
|
||||
scale: number;
|
||||
scrollX: number;
|
||||
@ -24,7 +23,7 @@ export class OutlineHoveringInstance extends PureComponent<{
|
||||
transform: `translate(${(scrollX + rect.left) * scale}px, ${(scrollY + rect.top) * scale}px)`,
|
||||
};
|
||||
|
||||
const className = classNames('lc-outlines lc-outlines-hovering');
|
||||
const className = classNames('lc-borders lc-borders-hovering');
|
||||
|
||||
// TODO:
|
||||
// 1. thinkof icon
|
||||
@ -32,14 +31,14 @@ export class OutlineHoveringInstance extends PureComponent<{
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
<a className="lc-outlines-title">{title}</a>
|
||||
<Title title={title} className="lc-borders-title" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export class OutlineHovering extends Component {
|
||||
export class BorderHovering extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
shouldComponentUpdate() {
|
||||
@ -47,19 +46,19 @@ export class OutlineHovering extends Component {
|
||||
}
|
||||
|
||||
@computed get scale() {
|
||||
return (this.context as SimulatorHost).viewport.scale;
|
||||
return (this.context as BuiltinSimulatorHost).viewport.scale;
|
||||
}
|
||||
|
||||
@computed get scrollX() {
|
||||
return (this.context as SimulatorHost).viewport.scrollX;
|
||||
return (this.context as BuiltinSimulatorHost).viewport.scrollX;
|
||||
}
|
||||
|
||||
@computed get scrollY() {
|
||||
return (this.context as SimulatorHost).viewport.scrollY;
|
||||
return (this.context as BuiltinSimulatorHost).viewport.scrollY;
|
||||
}
|
||||
|
||||
@computed get current() {
|
||||
const host = this.context as SimulatorHost;
|
||||
const host = this.context as BuiltinSimulatorHost;
|
||||
const doc = host.document;
|
||||
const selection = doc.selection;
|
||||
const current = host.designer.hovering.current;
|
||||
@ -70,7 +69,7 @@ export class OutlineHovering extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const host = this.context as SimulatorHost;
|
||||
const host = this.context as BuiltinSimulatorHost;
|
||||
const current = this.current;
|
||||
if (!current || host.viewport.scrolling) {
|
||||
return <Fragment />;
|
||||
@ -82,26 +81,26 @@ export class OutlineHovering extends Component {
|
||||
|
||||
if (instances.length === 1) {
|
||||
return (
|
||||
<OutlineHoveringInstance
|
||||
<BorderHoveringInstance
|
||||
key="line-h"
|
||||
title={current.title}
|
||||
scale={this.scale}
|
||||
scrollX={this.scrollX}
|
||||
scrollY={this.scrollY}
|
||||
rect={host.computeComponentInstanceRect(instances[0])}
|
||||
rect={host.computeComponentInstanceRect(instances[0], current.componentMeta.rectSelector)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Fragment>
|
||||
{instances.map((inst, i) => (
|
||||
<OutlineHoveringInstance
|
||||
<BorderHoveringInstance
|
||||
key={`line-h-${i}`}
|
||||
title={current.title}
|
||||
scale={this.scale}
|
||||
scrollX={this.scrollX}
|
||||
scrollY={this.scrollY}
|
||||
rect={host.computeComponentInstanceRect(inst)}
|
||||
rect={host.computeComponentInstanceRect(inst, current.componentMeta.rectSelector)}
|
||||
/>
|
||||
))}
|
||||
</Fragment>
|
||||
@ -0,0 +1,225 @@
|
||||
import {
|
||||
Component,
|
||||
Fragment,
|
||||
ReactNodeArray,
|
||||
isValidElement,
|
||||
cloneElement,
|
||||
createElement,
|
||||
ReactNode,
|
||||
ComponentType,
|
||||
} from 'react';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
observer,
|
||||
computed,
|
||||
createIcon,
|
||||
EmbedTip,
|
||||
isReactComponent,
|
||||
ActionContentObject,
|
||||
isActionContentObject,
|
||||
} from '@ali/lowcode-globals';
|
||||
import { SimulatorContext } from '../context';
|
||||
import { BuiltinSimulatorHost } from '../host';
|
||||
import { OffsetObserver } from '../../../designer';
|
||||
import { Node } from '../../../document';
|
||||
|
||||
@observer
|
||||
export class BorderSelectingInstance extends Component<{
|
||||
observed: OffsetObserver;
|
||||
highlight?: boolean;
|
||||
dragging?: boolean;
|
||||
}> {
|
||||
componentWillUnmount() {
|
||||
this.props.observed.purge();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { observed, highlight, dragging } = this.props;
|
||||
if (!observed.hasOffset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { offsetWidth, offsetHeight, offsetTop, offsetLeft } = observed;
|
||||
|
||||
const style = {
|
||||
width: offsetWidth,
|
||||
height: offsetHeight,
|
||||
transform: `translate3d(${offsetLeft}px, ${offsetTop}px, 0)`,
|
||||
};
|
||||
|
||||
const className = classNames('lc-borders lc-borders-selecting', {
|
||||
highlight,
|
||||
dragging,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
{!dragging && <Toolbar observed={observed} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class Toolbar extends Component<{ observed: OffsetObserver }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { observed } = this.props;
|
||||
const { height, width } = observed.viewport;
|
||||
const BAR_HEIGHT = 20;
|
||||
const MARGIN = 1;
|
||||
const BORDER = 2;
|
||||
const SPACE_HEIGHT = BAR_HEIGHT + MARGIN + BORDER;
|
||||
let style: any;
|
||||
if (observed.top > SPACE_HEIGHT) {
|
||||
style = {
|
||||
right: Math.max(-BORDER, observed.right - width - BORDER),
|
||||
top: -SPACE_HEIGHT,
|
||||
height: BAR_HEIGHT,
|
||||
};
|
||||
} else if (observed.bottom + SPACE_HEIGHT < height) {
|
||||
style = {
|
||||
bottom: -SPACE_HEIGHT,
|
||||
height: BAR_HEIGHT,
|
||||
right: Math.max(-BORDER, observed.right - width - BORDER),
|
||||
};
|
||||
} else {
|
||||
style = {
|
||||
height: BAR_HEIGHT,
|
||||
top: Math.max(MARGIN, MARGIN - observed.top),
|
||||
right: Math.max(MARGIN, MARGIN + observed.right - width),
|
||||
};
|
||||
}
|
||||
const { node } = observed;
|
||||
const actions: ReactNodeArray = [];
|
||||
node.componentMeta.availableActions.forEach(action => {
|
||||
const { important, condition, content, name } = action;
|
||||
if (node.isSlotRoot && (name === 'copy' || name === 'remove')) {
|
||||
// FIXME: need this?
|
||||
return;
|
||||
}
|
||||
if (important && (typeof condition === 'function' ? condition(node) : condition !== false)) {
|
||||
actions.push(createAction(content, name, node));
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div className="lc-borders-actions" style={style}>
|
||||
{actions}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function createAction(content: ReactNode | ComponentType<any> | ActionContentObject, key: string, node: Node) {
|
||||
if (isValidElement(content)) {
|
||||
return cloneElement(content, { key, node });
|
||||
}
|
||||
if (isReactComponent(content)) {
|
||||
return createElement(content, { key, node });
|
||||
}
|
||||
if (isActionContentObject(content)) {
|
||||
const { action, description, icon } = content;
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
className="lc-borders-action"
|
||||
onClick={() => {
|
||||
action && action(node);
|
||||
}}
|
||||
>
|
||||
{icon && createIcon(icon)}
|
||||
<EmbedTip>{description}</EmbedTip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class BorderSelectingForNode extends Component<{ node: Node }> {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
get host(): BuiltinSimulatorHost {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
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 => {
|
||||
const observed = designer.createOffsetObserver({
|
||||
node,
|
||||
instance,
|
||||
});
|
||||
if (!observed) {
|
||||
return null;
|
||||
}
|
||||
return <BorderSelectingInstance key={observed.id} dragging={this.dragging} observed={observed} />;
|
||||
})}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export class BorderSelecting extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
get host(): BuiltinSimulatorHost {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
render() {
|
||||
const selecting = this.selecting;
|
||||
if (!selecting || selecting.length < 1) {
|
||||
// DIRTY FIX, recore has a bug!
|
||||
return <Fragment />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{selecting.map(node => (
|
||||
<BorderSelectingForNode key={node.id} node={node} />
|
||||
))}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
@scope: lc-outlines;
|
||||
@scope: lc-borders;
|
||||
|
||||
.@{scope} {
|
||||
box-sizing: border-box;
|
||||
@ -18,6 +18,32 @@
|
||||
transform: translateY(-100%);
|
||||
font-weight: lighter;
|
||||
}
|
||||
& > &-actions {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
align-items: stretch;
|
||||
justify-content: flex-end;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
&-action {
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--color-brand, #006cff);
|
||||
opacity: 1;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
color: white;
|
||||
&:hover {
|
||||
background: var(--color-brand-light, #006cff);
|
||||
}
|
||||
}
|
||||
|
||||
&&-hovering {
|
||||
z-index: 1;
|
||||
30
packages/designer/src/builtin-simulator/bem-tools/index.tsx
Normal file
30
packages/designer/src/builtin-simulator/bem-tools/index.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { Component } from 'react';
|
||||
import { observer } from '@ali/lowcode-globals';
|
||||
import { BorderHovering } from './border-hovering';
|
||||
import { SimulatorContext } from '../context';
|
||||
import { BuiltinSimulatorHost } from '../host';
|
||||
import { BorderSelecting } from './border-selecting';
|
||||
import { InsertionView } from './insertion';
|
||||
import './bem-tools.less';
|
||||
import './borders.less';
|
||||
|
||||
@observer
|
||||
export class BemTools extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const host = this.context as BuiltinSimulatorHost;
|
||||
const { scrollX, scrollY, scale } = host.viewport;
|
||||
return (
|
||||
<div className="lc-bem-tools" style={{ transform: `translate(${-scrollX * scale}px,${-scrollY * scale}px)` }}>
|
||||
<BorderHovering key="hovering" />
|
||||
<BorderSelecting key="selecting" />
|
||||
<InsertionView key="insertion" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,23 +1,28 @@
|
||||
.lc-insertion {
|
||||
position: absolute;
|
||||
top: -1.5px;
|
||||
top: -2px;
|
||||
left: 0;
|
||||
z-index: 12;
|
||||
pointer-events: none !important;
|
||||
background-color: var(--color-brand-light);
|
||||
height: 3px;
|
||||
height: 4px;
|
||||
|
||||
&.cover {
|
||||
top: 0;
|
||||
height: auto;
|
||||
width: auto;
|
||||
border: none;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
&.vertical {
|
||||
top: 0;
|
||||
left: -1.5px;
|
||||
width: 3px;
|
||||
left: -2px;
|
||||
width: 4px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
&.invalid {
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,16 @@
|
||||
import { Component } from 'react';
|
||||
import { computed } from '@recore/obx';
|
||||
import { observer } from '@recore/obx-react';
|
||||
import { computed, observer } from '@ali/lowcode-globals';
|
||||
import { SimulatorContext } from '../context';
|
||||
import { SimulatorHost } from '../host';
|
||||
import Location, {
|
||||
import { BuiltinSimulatorHost } from '../host';
|
||||
import {
|
||||
DropLocation,
|
||||
Rect,
|
||||
isLocationChildrenDetail,
|
||||
LocationChildrenDetail,
|
||||
isVertical,
|
||||
} from '../../../../designer/helper/location';
|
||||
import { ISimulator } from '../../../../designer/simulator';
|
||||
import { NodeParent } from '../../../../designer/document/node/node';
|
||||
isVertical
|
||||
} from '../../designer';
|
||||
import { ISimulatorHost, } from '../../simulator';
|
||||
import {NodeParent } from '../../document';
|
||||
import './insertion.less';
|
||||
|
||||
interface InsertionData {
|
||||
@ -24,15 +24,14 @@ interface InsertionData {
|
||||
/**
|
||||
* 处理拖拽子节点(INode)情况
|
||||
*/
|
||||
function processChildrenDetail(sim: ISimulator, target: NodeParent, detail: LocationChildrenDetail): InsertionData {
|
||||
function processChildrenDetail(sim: ISimulatorHost, container: NodeParent, detail: LocationChildrenDetail): InsertionData {
|
||||
let edge = detail.edge || null;
|
||||
|
||||
if (edge) {
|
||||
edge = sim.computeRect(target);
|
||||
}
|
||||
|
||||
if (!edge) {
|
||||
return {};
|
||||
edge = sim.computeRect(container);
|
||||
if (!edge) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const ret: any = {
|
||||
@ -43,18 +42,33 @@ function processChildrenDetail(sim: ISimulator, target: NodeParent, detail: Loca
|
||||
if (detail.near) {
|
||||
const { node, pos, rect, align } = detail.near;
|
||||
ret.nearRect = rect || sim.computeRect(node);
|
||||
ret.vertical = align ? align === 'V' : isVertical(ret.nearRect);
|
||||
ret.insertType = pos;
|
||||
if (pos === 'replace') {
|
||||
// FIXME: ret.nearRect mybe null
|
||||
ret.coverRect = ret.nearRect;
|
||||
ret.insertType = 'cover';
|
||||
} else if (!ret.nearRect || (ret.nearRect.width === 0 && ret.nearRect.height === 0)) {
|
||||
ret.nearRect = ret.edge;
|
||||
ret.insertType = 'after';
|
||||
ret.vertical = isVertical(ret.nearRect);
|
||||
} else {
|
||||
ret.insertType = pos;
|
||||
ret.vertical = align ? align === 'V' : isVertical(ret.nearRect);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// from outline-tree: has index, but no near
|
||||
// TODO: think of shadowNode & ConditionFlow
|
||||
const { index } = detail;
|
||||
let nearNode = target.children.get(index);
|
||||
if (index == null) {
|
||||
ret.coverRect = ret.edge;
|
||||
ret.insertType = 'cover';
|
||||
return ret;
|
||||
}
|
||||
let nearNode = container.children.get(index);
|
||||
if (!nearNode) {
|
||||
// index = 0, eg. nochild,
|
||||
nearNode = target.children.get(index > 0 ? index - 1 : 0);
|
||||
nearNode = container.children.get(index > 0 ? index - 1 : 0);
|
||||
if (!nearNode) {
|
||||
ret.insertType = 'cover';
|
||||
ret.coverRect = edge;
|
||||
@ -64,7 +78,14 @@ function processChildrenDetail(sim: ISimulator, target: NodeParent, detail: Loca
|
||||
}
|
||||
if (nearNode) {
|
||||
ret.nearRect = sim.computeRect(nearNode);
|
||||
if (!ret.nearRect || (ret.nearRect.width === 0 && ret.nearRect.height === 0)) {
|
||||
ret.nearRect = ret.edge;
|
||||
ret.insertType = 'after';
|
||||
}
|
||||
ret.vertical = isVertical(ret.nearRect);
|
||||
} else {
|
||||
ret.insertType = 'cover';
|
||||
ret.coverRect = edge;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -72,7 +93,7 @@ function processChildrenDetail(sim: ISimulator, target: NodeParent, detail: Loca
|
||||
/**
|
||||
* 将 detail 信息转换为页面"坐标"信息
|
||||
*/
|
||||
function processDetail({ target, detail, document }: Location): InsertionData {
|
||||
function processDetail({ target, detail, document }: DropLocation): InsertionData {
|
||||
const sim = document.simulator;
|
||||
if (!sim) {
|
||||
return {};
|
||||
@ -85,7 +106,7 @@ function processDetail({ target, detail, document }: Location): InsertionData {
|
||||
if (!instances) {
|
||||
return {};
|
||||
}
|
||||
const edge = sim.computeComponentInstanceRect(instances[0]);
|
||||
const edge = sim.computeComponentInstanceRect(instances[0], target.componentMeta.rectSelector);
|
||||
return edge ? { edge, insertType: 'cover', coverRect: edge } : {};
|
||||
}
|
||||
}
|
||||
@ -94,7 +115,7 @@ function processDetail({ target, detail, document }: Location): InsertionData {
|
||||
export class InsertionView extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
@computed get host(): SimulatorHost {
|
||||
@computed get host(): BuiltinSimulatorHost {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
@ -116,6 +137,9 @@ export class InsertionView extends Component {
|
||||
}
|
||||
|
||||
let className = 'lc-insertion';
|
||||
if ((loc.detail as any)?.valid === false) {
|
||||
className += ' invalid';
|
||||
}
|
||||
const style: any = {};
|
||||
let x: number;
|
||||
let y: number;
|
||||
4
packages/designer/src/builtin-simulator/context.ts
Normal file
4
packages/designer/src/builtin-simulator/context.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { createContext } from 'react';
|
||||
import { BuiltinSimulatorHost } from './host';
|
||||
|
||||
export const SimulatorContext = createContext<BuiltinSimulatorHost>({} as any);
|
||||
@ -1,14 +1,14 @@
|
||||
// NOTE: 仅用作类型标注,切勿作为实体使用
|
||||
import { SimulatorRenderer } from '../renderer/renderer';
|
||||
import { SimulatorHost } from './host';
|
||||
import { AssetLevel, AssetLevels, AssetList, isAssetBundle, isAssetItem, AssetType, assetItem } from '../utils/asset';
|
||||
import { isCSSUrl } from '../../../utils/is-css-url';
|
||||
import { BuiltinSimulatorHost } from './host';
|
||||
import { AssetLevel, AssetLevels, AssetList, isAssetBundle, isAssetItem, AssetType, assetItem } from '@ali/lowcode-globals';
|
||||
import { isCSSUrl } from '@ali/lowcode-globals';
|
||||
import { BuiltinSimulatorRenderer } from './renderer';
|
||||
|
||||
export function createSimulator(
|
||||
host: SimulatorHost,
|
||||
host: BuiltinSimulatorHost,
|
||||
iframe: HTMLIFrameElement,
|
||||
vendors: AssetList = [],
|
||||
): Promise<SimulatorRenderer> {
|
||||
): Promise<BuiltinSimulatorRenderer> {
|
||||
const win: any = iframe.contentWindow;
|
||||
const doc = iframe.contentDocument!;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Component } from 'react';
|
||||
import { observer } from '@recore/obx-react';
|
||||
import { SimulatorHost, SimulatorProps } from './host';
|
||||
import DocumentModel from '../../../designer/document/document-model';
|
||||
import { observer } from '@ali/lowcode-globals';
|
||||
import { BuiltinSimulatorHost, BuiltinSimulatorProps } from './host';
|
||||
import { DocumentModel } from '../document';
|
||||
import { SimulatorContext } from './context';
|
||||
import { AuxiliaryView } from './auxilary';
|
||||
import { BemTools } from './bem-tools';
|
||||
import './host.less';
|
||||
|
||||
/*
|
||||
@ -15,20 +15,20 @@ import './host.less';
|
||||
Auxiliary 辅助显示层,初始相对 Content 位置 0,0,紧贴 Canvas, 根据 Content 滚动位置,改变相对位置
|
||||
*/
|
||||
|
||||
type SimulatorHostProps = SimulatorProps & {
|
||||
type SimulatorHostProps = BuiltinSimulatorProps & {
|
||||
documentContext: DocumentModel;
|
||||
onMount?: (host: SimulatorHost) => void;
|
||||
onMount?: (host: BuiltinSimulatorHost) => void;
|
||||
};
|
||||
|
||||
export class SimulatorHostView extends Component<SimulatorHostProps> {
|
||||
readonly host: SimulatorHost;
|
||||
export class BuiltinSimulatorHostView extends Component<SimulatorHostProps> {
|
||||
readonly host: BuiltinSimulatorHost;
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
const { documentContext } = this.props;
|
||||
this.host = (documentContext.simulator as SimulatorHost) || new SimulatorHost(documentContext);
|
||||
this.host = (documentContext.simulator as BuiltinSimulatorHost) || new BuiltinSimulatorHost(documentContext);
|
||||
this.host.setProps(this.props);
|
||||
}
|
||||
shouldComponentUpdate(nextProps: SimulatorProps) {
|
||||
shouldComponentUpdate(nextProps: BuiltinSimulatorProps) {
|
||||
this.host.setProps(nextProps);
|
||||
return false;
|
||||
}
|
||||
@ -54,7 +54,7 @@ export class SimulatorHostView extends Component<SimulatorHostProps> {
|
||||
class Canvas extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
render() {
|
||||
const sim = this.context as SimulatorHost;
|
||||
const sim = this.context as BuiltinSimulatorHost;
|
||||
let className = 'lc-simulator-canvas';
|
||||
if (sim.deviceClassName) {
|
||||
className += ` ${sim.deviceClassName}`;
|
||||
@ -65,7 +65,7 @@ class Canvas extends Component {
|
||||
return (
|
||||
<div className={className}>
|
||||
<div ref={elmt => sim.mountViewport(elmt)} className="lc-simulator-canvas-viewport">
|
||||
<AuxiliaryView />
|
||||
<BemTools />
|
||||
<Content />
|
||||
</div>
|
||||
</div>
|
||||
@ -77,7 +77,7 @@ class Canvas extends Component {
|
||||
class Content extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
render() {
|
||||
const sim = this.context as SimulatorHost;
|
||||
const sim = this.context as BuiltinSimulatorHost;
|
||||
const viewport = sim.viewport;
|
||||
let frameStyle = {};
|
||||
if (viewport.scale < 1) {
|
||||
@ -1,23 +1,16 @@
|
||||
import { obx, autorun, computed } from '@recore/obx';
|
||||
import { ISimulator, Component, NodeInstance } from '../../../designer/simulator';
|
||||
import { obx, autorun, computed } from '@ali/lowcode-globals';
|
||||
import { ISimulatorHost, Component, NodeInstance, ComponentInstance } from '../simulator';
|
||||
import Viewport from './viewport';
|
||||
import { createSimulator } from './create-simulator';
|
||||
import { SimulatorRenderer } from '../renderer/renderer';
|
||||
import Node, { NodeParent, isNodeParent, isNode, contains } from '../../../designer/document/node/node';
|
||||
import DocumentModel from '../../../designer/document/document-model';
|
||||
import { Node, NodeParent, DocumentModel, isNodeParent, isNode, contains, isRootNode } from '../document';
|
||||
import ResourceConsumer from './resource-consumer';
|
||||
import { AssetLevel, Asset, AssetList, assetBundle, assetItem, AssetType } from '../utils/asset';
|
||||
import { AssetLevel, Asset, AssetList, assetBundle, assetItem, AssetType, getPublicPath } from '@ali/lowcode-globals';
|
||||
import {
|
||||
DragObjectType,
|
||||
isShaken,
|
||||
LocateEvent,
|
||||
DragNodeObject,
|
||||
DragNodeDataObject,
|
||||
isDragAnyObject,
|
||||
isDragNodeObject,
|
||||
isDragNodeDataObject,
|
||||
} from '../../../designer/helper/dragon';
|
||||
import {
|
||||
LocationData,
|
||||
isLocationData,
|
||||
LocationChildrenDetail,
|
||||
@ -27,12 +20,13 @@ import {
|
||||
getRectTarget,
|
||||
Rect,
|
||||
CanvasPoint,
|
||||
} from '../../../designer/helper/location';
|
||||
import { isNodeSchema, NodeSchema } from '../../../designer/schema';
|
||||
import { ComponentMetadata } from '../../../designer/component-meta';
|
||||
import { ReactInstance } from 'react';
|
||||
import { isRootNode } from '../../../designer/document/node/root-node';
|
||||
import { parseProps } from '../utils/parse-props';
|
||||
hotkey,
|
||||
} from '../designer';
|
||||
import { parseProps } from './utils/parse-props';
|
||||
import { isElement } from '@ali/lowcode-globals';
|
||||
import { ComponentMetadata } from '@ali/lowcode-globals';
|
||||
import { BuiltinSimulatorRenderer } from './renderer';
|
||||
import clipboard from '../designer/clipboard';
|
||||
|
||||
export interface LibraryItem {
|
||||
package: string;
|
||||
@ -40,7 +34,7 @@ export interface LibraryItem {
|
||||
urls: Asset;
|
||||
}
|
||||
|
||||
export interface SimulatorProps {
|
||||
export interface BuiltinSimulatorProps {
|
||||
// 从 documentModel 上获取
|
||||
// suspended?: boolean;
|
||||
designMode?: 'live' | 'design' | 'mock' | 'extend' | 'border' | 'preview';
|
||||
@ -54,14 +48,16 @@ export interface SimulatorProps {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const publicPath = (document.currentScript as HTMLScriptElement).src.replace(/^(.*\/)[^/]+$/, '$1');
|
||||
|
||||
const defaultSimulatorUrl = (() => {
|
||||
const publicPath = getPublicPath();
|
||||
let urls;
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
urls = [`${publicPath}../css/simulator-renderer.min.css`, `${publicPath}simulator-renderer.min.js`];
|
||||
const [_, prefix = '', dev] = /^(.+?)(\/js)?\/?$/.exec(publicPath) || [];
|
||||
if (dev) {
|
||||
urls = [`${prefix}/css/react-simulator-renderer.css`, `${prefix}/js/react-simulator-renderer.js`];
|
||||
} else if (process.env.NODE_ENV === 'production') {
|
||||
urls = [`${prefix}/react-simulator-renderer.min.css`, `${prefix}/react-simulator-renderer.min.js`];
|
||||
} else {
|
||||
urls = [`${publicPath}../css/simulator-renderer.css`, `${publicPath}simulator-renderer.js`];
|
||||
urls = [`${prefix}/react-simulator-renderer.css`, `${prefix}/react-simulator-renderer.js`];
|
||||
}
|
||||
return urls;
|
||||
})();
|
||||
@ -73,10 +69,9 @@ const defaultEnvironment = [
|
||||
AssetType.JSText,
|
||||
'window.PropTypes=parent.PropTypes;React.PropTypes=parent.PropTypes; window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;',
|
||||
),
|
||||
assetItem(AssetType.JSUrl, '/statics/lowcode-renderer.js'),
|
||||
];
|
||||
|
||||
export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProps> {
|
||||
readonly isSimulator = true;
|
||||
|
||||
constructor(readonly document: DocumentModel) {}
|
||||
@ -112,11 +107,11 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
return this.designer.componentsMap;
|
||||
}
|
||||
|
||||
@obx.ref _props: SimulatorProps = {};
|
||||
@obx.ref _props: BuiltinSimulatorProps = {};
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
setProps(props: SimulatorProps) {
|
||||
setProps(props: BuiltinSimulatorProps) {
|
||||
this._props = props;
|
||||
}
|
||||
set(key: string, value: any) {
|
||||
@ -132,7 +127,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
/**
|
||||
* 有 Renderer 进程连接进来,设置同步机制
|
||||
*/
|
||||
connect(renderer: SimulatorRenderer, fn: (context: { dispose: () => void; firstRun: boolean }) => void) {
|
||||
connect(renderer: BuiltinSimulatorRenderer, fn: (context: { dispose: () => void; firstRun: boolean }) => void) {
|
||||
this._renderer = renderer;
|
||||
return autorun(fn as any, true);
|
||||
}
|
||||
@ -158,7 +153,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
return this._contentDocument;
|
||||
}
|
||||
|
||||
private _renderer?: SimulatorRenderer;
|
||||
private _renderer?: BuiltinSimulatorRenderer;
|
||||
get renderer() {
|
||||
return this._renderer;
|
||||
}
|
||||
@ -183,7 +178,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
const library = this.get('library') as LibraryItem[];
|
||||
const libraryAsset: AssetList = [];
|
||||
if (library) {
|
||||
library.forEach(item => {
|
||||
library.forEach((item) => {
|
||||
this.libraryMap[item.package] = item.library;
|
||||
libraryAsset.push(item.urls);
|
||||
});
|
||||
@ -203,6 +198,8 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
// wait 准备 iframe 内容、依赖库注入
|
||||
const renderer = await createSimulator(this, iframe, vendors);
|
||||
|
||||
// TODO: !!! thinkof reload onload
|
||||
|
||||
// wait 业务组件被第一次消费,否则会渲染出错
|
||||
await this.componentsConsumer.waitFirstConsume();
|
||||
|
||||
@ -216,11 +213,17 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
this._contentDocument = this._contentWindow.document;
|
||||
this.viewport.setScrollTarget(this._contentWindow);
|
||||
this.setupEvents();
|
||||
// hotkey.mount(this.contentWindow);
|
||||
// clipboard.injectCopyPaster(this.ownerDocument);
|
||||
|
||||
// bind hotkey & clipboard
|
||||
hotkey.mount(this._contentWindow);
|
||||
clipboard.injectCopyPaster(this._contentDocument);
|
||||
// TODO: dispose the bindings
|
||||
}
|
||||
|
||||
setupEvents() {
|
||||
// TODO: Thinkof move events control to simulator renderer
|
||||
// just listen special callback
|
||||
// because iframe maybe reload
|
||||
this.setupDragAndClick();
|
||||
this.setupHovering();
|
||||
}
|
||||
@ -295,7 +298,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
|
||||
doc.addEventListener(
|
||||
'click',
|
||||
e => {
|
||||
(e) => {
|
||||
// stop response document click event
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@ -405,8 +408,8 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
return this.renderer?.getComponent(componentName) || null;
|
||||
}
|
||||
|
||||
@obx.val private instancesMap = new Map<string, ReactInstance[]>();
|
||||
setInstance(id: string, instances: ReactInstance[] | null) {
|
||||
@obx.val private instancesMap = new Map<string, ComponentInstance[]>();
|
||||
setInstance(id: string, instances: ComponentInstance[] | null) {
|
||||
if (instances == null) {
|
||||
this.instancesMap.delete(id);
|
||||
} else {
|
||||
@ -417,14 +420,14 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
getComponentInstances(node: Node): ReactInstance[] | null {
|
||||
getComponentInstances(node: Node): ComponentInstance[] | null {
|
||||
return this.instancesMap.get(node.id) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
getComponentInstanceId(instance: ReactInstance) {
|
||||
getComponentInstanceId(instance: ComponentInstance) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
@ -438,7 +441,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
getClosestNodeInstance(from: ReactInstance, specId?: string): NodeInstance<ReactInstance> | null {
|
||||
getClosestNodeInstance(from: ComponentInstance, specId?: string): NodeInstance<ComponentInstance> | null {
|
||||
return this.renderer?.getClosestNodeInstance(from, specId) || null;
|
||||
}
|
||||
|
||||
@ -450,36 +453,36 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
if (!instances) {
|
||||
return null;
|
||||
}
|
||||
return this.computeComponentInstanceRect(instances[0]);
|
||||
return this.computeComponentInstanceRect(instances[0], node.componentMeta.rectSelector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
computeComponentInstanceRect(instance: ReactInstance): Rect | null {
|
||||
computeComponentInstanceRect(instance: ComponentInstance, selector?: string): Rect | null {
|
||||
const renderer = this.renderer!;
|
||||
const elements = renderer.findDOMNodes(instance);
|
||||
if (!elements) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let elems = elements.slice();
|
||||
if (selector) {
|
||||
const matched = getMatched(elems, selector);
|
||||
if (!matched) {
|
||||
return null;
|
||||
}
|
||||
elems = [matched];
|
||||
}
|
||||
let rects: DOMRect[] | undefined;
|
||||
let last: { x: number; y: number; r: number; b: number } | undefined;
|
||||
let computed = false;
|
||||
const elems = elements.slice();
|
||||
const commonParent: Element | null = null;
|
||||
while (true) {
|
||||
if (!rects || rects.length < 1) {
|
||||
const elem = elems.pop();
|
||||
if (!elem) {
|
||||
break;
|
||||
}
|
||||
/*
|
||||
if (!commonParent) {
|
||||
commonParent = elem.parentElement;
|
||||
} else if (elem.parentElement !== commonParent) {
|
||||
continue;
|
||||
}*/
|
||||
rects = renderer.getClientRects(elem);
|
||||
}
|
||||
const rect = rects.pop();
|
||||
@ -529,14 +532,14 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
findDOMNodes(instance: ReactInstance): Array<Element | Text> | null {
|
||||
findDOMNodes(instance: ComponentInstance): Array<Element | Text> | null {
|
||||
return this._renderer?.findDOMNodes(instance) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 DOM 节点获取节点,依赖 simulator 的接口
|
||||
*/
|
||||
getNodeInstanceFromElement(target: Element | null): NodeInstance | null {
|
||||
getNodeInstanceFromElement(target: Element | null): NodeInstance<ComponentInstance> | null {
|
||||
if (!target) {
|
||||
return null;
|
||||
}
|
||||
@ -701,28 +704,24 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
locate(e: LocateEvent): any {
|
||||
this.sensing = true;
|
||||
this.scroller.scrolling(e);
|
||||
const dropTarget = this.getDropTarget(e);
|
||||
if (!dropTarget) {
|
||||
const dropContainer = this.getDropContainer(e);
|
||||
if (!dropContainer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isLocationData(dropTarget)) {
|
||||
return this.designer.createLocation(dropTarget);
|
||||
if (isLocationData(dropContainer)) {
|
||||
return this.designer.createLocation(dropContainer);
|
||||
}
|
||||
|
||||
const target = dropTarget;
|
||||
const { container, instance: containerInstance } = dropContainer;
|
||||
|
||||
// FIXME: e.target is #document, etc., does not has e.targetInstance
|
||||
|
||||
const targetInstance = e.targetInstance as ReactInstance;
|
||||
const parentInstance = this.getClosestNodeInstance(targetInstance, target.id);
|
||||
const edge = this.computeComponentInstanceRect(parentInstance?.instance as any);
|
||||
const edge = this.computeComponentInstanceRect(containerInstance, container.componentMeta.rectSelector);
|
||||
|
||||
if (!edge) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const children = target.children;
|
||||
const children = container.children;
|
||||
|
||||
const detail: LocationChildrenDetail = {
|
||||
type: LocationDetailType.Children,
|
||||
@ -731,8 +730,10 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
};
|
||||
|
||||
const locationData = {
|
||||
target,
|
||||
target: container,
|
||||
detail,
|
||||
source: 'simulator' + this.document.id,
|
||||
event: e,
|
||||
};
|
||||
|
||||
if (!children || children.size < 1 || !edge) {
|
||||
@ -752,10 +753,10 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
const instances = this.getComponentInstances(node);
|
||||
const inst = instances
|
||||
? instances.length > 1
|
||||
? instances.find(inst => this.getClosestNodeInstance(inst, target.id)?.instance === targetInstance)
|
||||
? instances.find((inst) => this.getClosestNodeInstance(inst, container.id)?.instance === containerInstance)
|
||||
: instances[0]
|
||||
: null;
|
||||
const rect = inst ? this.computeComponentInstanceRect(inst) : null;
|
||||
const rect = inst ? this.computeComponentInstanceRect(inst, node.componentMeta.rectSelector) : null;
|
||||
|
||||
if (!rect) {
|
||||
continue;
|
||||
@ -795,6 +796,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
const inline = el ? isChildInline(el) : false;
|
||||
const row = el ? isRowContainer(el.parentElement!) : false;
|
||||
const vertical = inline || row;
|
||||
|
||||
// TODO: fix type
|
||||
const near: any = {
|
||||
node: nearNode,
|
||||
@ -827,61 +829,109 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
return this.designer.createLocation(locationData);
|
||||
}
|
||||
|
||||
getDropTarget(e: LocateEvent): NodeParent | LocationData | null {
|
||||
/**
|
||||
* 查找合适的投放容器
|
||||
*/
|
||||
getDropContainer(e: LocateEvent): DropContainer | LocationData | null {
|
||||
const { target, dragObject } = e;
|
||||
const isAny = isDragAnyObject(dragObject);
|
||||
let container: any;
|
||||
const { modalNode, currentRoot } = this.document;
|
||||
let container: Node;
|
||||
let nodeInstance: NodeInstance<ComponentInstance> | undefined;
|
||||
|
||||
if (target) {
|
||||
const ref = this.getNodeInstanceFromElement(target);
|
||||
if (ref?.node) {
|
||||
e.targetInstance = ref.instance;
|
||||
e.targetNode = ref.node;
|
||||
nodeInstance = ref;
|
||||
container = ref.node;
|
||||
} else if (isAny) {
|
||||
return null;
|
||||
} else {
|
||||
container = this.document.rootNode;
|
||||
container = currentRoot;
|
||||
}
|
||||
} else if (isAny) {
|
||||
return null;
|
||||
} else {
|
||||
container = this.document.rootNode;
|
||||
container = currentRoot;
|
||||
}
|
||||
|
||||
if (!isNodeParent(container) && !isRootNode(container)) {
|
||||
container = container.parent;
|
||||
if (!isNodeParent(container)) {
|
||||
container = container.parent || currentRoot;
|
||||
}
|
||||
|
||||
// check container if in modalNode layer, if not, use modalNode
|
||||
if (modalNode && !modalNode.contains(container)) {
|
||||
container = modalNode;
|
||||
}
|
||||
|
||||
// TODO: use spec container to accept specialData
|
||||
if (isAny) {
|
||||
// TODO: use spec container to accept specialData
|
||||
// will return locationData
|
||||
return null;
|
||||
}
|
||||
|
||||
// get common parent, avoid drop container contains by dragObject
|
||||
// TODO: renderengine support pointerEvents: none for acceleration
|
||||
const drillDownExcludes = new Set<Node>();
|
||||
if (isDragNodeObject(dragObject)) {
|
||||
const nodes = dragObject.nodes;
|
||||
let i = nodes.length;
|
||||
let p: any = container;
|
||||
while (i-- > 0) {
|
||||
if (contains(nodes[i], p)) {
|
||||
p = nodes[i].parent;
|
||||
}
|
||||
}
|
||||
if (p !== container) {
|
||||
container = p || this.document.rootNode;
|
||||
drillDownExcludes.add(container);
|
||||
}
|
||||
}
|
||||
|
||||
const ret: any = {
|
||||
container,
|
||||
};
|
||||
if (nodeInstance) {
|
||||
if (nodeInstance.node === container) {
|
||||
ret.instance = nodeInstance.instance;
|
||||
} else {
|
||||
ret.instance = this.getClosestNodeInstance(nodeInstance.instance as any, container.id)?.instance;
|
||||
}
|
||||
} else {
|
||||
ret.instance = this.getComponentInstances(container)?.[0];
|
||||
}
|
||||
|
||||
let res: any;
|
||||
let upward: any;
|
||||
// TODO: improve AT_CHILD logic, mark has checked
|
||||
// TODO: complete drill down logic
|
||||
while (container) {
|
||||
res = this.acceptNodes(container, e);
|
||||
if (ret.container !== container) {
|
||||
ret.container = container;
|
||||
ret.instance = this.getClosestNodeInstance(ret.instance, container.id)?.instance;
|
||||
}
|
||||
res = this.handleAccept(ret, e);
|
||||
if (isLocationData(res)) {
|
||||
return res;
|
||||
}
|
||||
if (res === true) {
|
||||
return container;
|
||||
return ret;
|
||||
}
|
||||
if (!res) {
|
||||
drillDownExcludes.add(container);
|
||||
if (upward) {
|
||||
container = upward;
|
||||
upward = null;
|
||||
} else {
|
||||
} else if (container.parent) {
|
||||
container = container.parent;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (isNode(res)) {
|
||||
/* else if (res === AT_CHILD) {
|
||||
/* else if (res === DRILL_DOWN) {
|
||||
if (!upward) {
|
||||
upward = container.parent;
|
||||
}
|
||||
container = this.getNearByContainer(container, e);
|
||||
container = this.getNearByContainer(container, drillExcludes, e);
|
||||
if (!container) {
|
||||
container = upward;
|
||||
upward = null;
|
||||
@ -894,38 +944,71 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
return null;
|
||||
}
|
||||
|
||||
acceptNodes(container: NodeParent, e: LocateEvent) {
|
||||
const { dragObject } = e;
|
||||
if (isRootNode(container)) {
|
||||
return this.checkDropTarget(container, dragObject as any);
|
||||
isAcceptable(container: NodeParent): boolean {
|
||||
return false;
|
||||
/*
|
||||
const meta = container.componentMeta;
|
||||
const instance: any = this.document.getView(container);
|
||||
if (instance && '$accept' in instance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const config = container.componentMeta;
|
||||
|
||||
if (!config.isContainer) {
|
||||
return false;
|
||||
}
|
||||
// check is contains, get common parent
|
||||
if (isDragNodeObject(dragObject)) {
|
||||
const nodes = dragObject.nodes;
|
||||
let i = nodes.length;
|
||||
let p: any = container;
|
||||
while (i-- > 0) {
|
||||
if (contains(nodes[i], p)) {
|
||||
p = nodes[i].parent;
|
||||
}
|
||||
}
|
||||
if (p !== container) {
|
||||
return p || this.document.rootNode;
|
||||
}
|
||||
}
|
||||
|
||||
return this.checkNesting(container, dragObject as any);
|
||||
return meta.acceptable;
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
getNearByContainer(container: NodeParent, e: LocateEvent) {
|
||||
/**
|
||||
* 控制接受
|
||||
*/
|
||||
handleAccept({ container, instance }: DropContainer, e: LocateEvent) {
|
||||
const { dragObject } = e;
|
||||
if (isRootNode(container)) {
|
||||
return this.document.checkDropTarget(container, dragObject as any);
|
||||
}
|
||||
|
||||
const meta = container.componentMeta;
|
||||
|
||||
// FIXME: get containerInstance for accept logic use
|
||||
const acceptable: boolean = this.isAcceptable(container);
|
||||
if (!meta.isContainer && !acceptable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// first use accept
|
||||
if (acceptable) {
|
||||
/*
|
||||
const view: any = this.document.getView(container);
|
||||
if (view && '$accept' in view) {
|
||||
if (view.$accept === false) {
|
||||
return false;
|
||||
}
|
||||
if (view.$accept === AT_CHILD || view.$accept === '@CHILD') {
|
||||
return AT_CHILD;
|
||||
}
|
||||
if (typeof view.$accept === 'function') {
|
||||
const ret = view.$accept(container, e);
|
||||
if (ret || ret === false) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (proto.acceptable) {
|
||||
const ret = proto.accept(container, e);
|
||||
if (ret || ret === false) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// check nesting
|
||||
return this.document.checkNesting(container, dragObject as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找邻近容器
|
||||
*/
|
||||
getNearByContainer(container: NodeParent, e: LocateEvent) {
|
||||
/*
|
||||
const children = container.children;
|
||||
if (!children || children.length < 1) {
|
||||
return null;
|
||||
@ -960,43 +1043,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
}
|
||||
|
||||
return nearBy;
|
||||
}
|
||||
*/
|
||||
|
||||
checkNesting(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean {
|
||||
let items: Array<Node | NodeSchema>;
|
||||
if (isDragNodeDataObject(dragObject)) {
|
||||
items = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
|
||||
} else {
|
||||
items = dragObject.nodes;
|
||||
}
|
||||
return items.every(item => this.checkNestingDown(dropTarget, item));
|
||||
}
|
||||
|
||||
checkDropTarget(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean {
|
||||
let items: Array<Node | NodeSchema>;
|
||||
if (isDragNodeDataObject(dragObject)) {
|
||||
items = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
|
||||
} else {
|
||||
items = dragObject.nodes;
|
||||
}
|
||||
return items.every(item => this.checkNestingUp(dropTarget, item));
|
||||
}
|
||||
|
||||
checkNestingUp(parent: NodeParent, target: NodeSchema | Node): boolean {
|
||||
if (isNode(target) || isNodeSchema(target)) {
|
||||
const config = isNode(target) ? target.componentMeta : this.document.getComponentMeta(target.componentName);
|
||||
if (config) {
|
||||
return config.checkNestingUp(target, parent);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
checkNestingDown(parent: NodeParent, target: NodeSchema | Node): boolean {
|
||||
const config = parent.componentMeta;
|
||||
return config.checkNestingDown(parent, target) && this.checkNestingUp(parent, target);
|
||||
*/
|
||||
}
|
||||
// #endregion
|
||||
}
|
||||
@ -1046,3 +1093,24 @@ function isNearAfter(point: CanvasPoint, rect: Rect, inline: boolean) {
|
||||
}
|
||||
return Math.abs(point.canvasY - rect.top) > Math.abs(point.canvasY - rect.bottom);
|
||||
}
|
||||
|
||||
function getMatched(elements: Array<Element | Text>, selector: string): Element | null {
|
||||
let firstQueried: Element | null = null;
|
||||
for (const elem of elements) {
|
||||
if (isElement(elem)) {
|
||||
if (elem.matches(selector)) {
|
||||
return elem;
|
||||
}
|
||||
|
||||
if (!firstQueried) {
|
||||
firstQueried = elem.querySelector(selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
return firstQueried;
|
||||
}
|
||||
|
||||
interface DropContainer {
|
||||
container: NodeParent;
|
||||
instance: ComponentInstance;
|
||||
}
|
||||
@ -1,2 +1,3 @@
|
||||
export * from './host';
|
||||
export * from './host-view';
|
||||
export * from './renderer';
|
||||
19
packages/designer/src/builtin-simulator/renderer.ts
Normal file
19
packages/designer/src/builtin-simulator/renderer.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { ComponentInstance, NodeInstance, Component } from '../simulator';
|
||||
|
||||
export interface BuiltinSimulatorRenderer {
|
||||
readonly isSimulatorRenderer: true;
|
||||
getComponent(componentName: string): Component;
|
||||
getComponentInstances(id: string): ComponentInstance[] | null;
|
||||
getClosestNodeInstance(from: ComponentInstance, nodeId?: string): NodeInstance<ComponentInstance> | null;
|
||||
findDOMNodes(instance: ComponentInstance): Array<Element | Text> | null;
|
||||
getClientRects(element: Element | Text): DOMRect[];
|
||||
setNativeSelection(enableFlag: boolean): void;
|
||||
setDraggingState(state: boolean): void;
|
||||
setCopyState(state: boolean): void;
|
||||
clearState(): void;
|
||||
run(): void;
|
||||
}
|
||||
|
||||
export function isSimulatorRenderer(obj: any): obj is BuiltinSimulatorRenderer {
|
||||
return obj && obj.isSimulatorRenderer;
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
import { SimulatorRenderer } from '../renderer/renderer';
|
||||
import { autorun, obx } from '@recore/obx';
|
||||
import { SimulatorHost } from './host';
|
||||
import { autorun, obx } from '@ali/lowcode-globals';
|
||||
import { BuiltinSimulatorHost } from './host';
|
||||
import { EventEmitter } from 'events';
|
||||
import { BuiltinSimulatorRenderer, isSimulatorRenderer } from './renderer';
|
||||
|
||||
const UNSET = Symbol('unset');
|
||||
export type MasterProvider = (master: SimulatorHost) => any;
|
||||
export type RendererConsumer<T> = (renderer: SimulatorRenderer, data: T) => Promise<any>;
|
||||
export type MasterProvider = (master: BuiltinSimulatorHost) => any;
|
||||
export type RendererConsumer<T> = (renderer: BuiltinSimulatorRenderer, data: T) => Promise<any>;
|
||||
|
||||
// master 进程
|
||||
// 0. 初始化该对象,因为需要响应变更发生在 master 进程
|
||||
@ -19,10 +19,6 @@ export type RendererConsumer<T> = (renderer: SimulatorRenderer, data: T) => Prom
|
||||
// 1. 被消费数据协议
|
||||
// 2. 消费机制(渲染进程自定 + 传递进入)
|
||||
|
||||
function isSimulatorRenderer(obj: any): obj is SimulatorRenderer {
|
||||
return obj && obj.isSimulatorRenderer;
|
||||
}
|
||||
|
||||
export default class ResourceConsumer<T = any> {
|
||||
private emitter = new EventEmitter();
|
||||
@obx.ref private _data: T | typeof UNSET = UNSET;
|
||||
@ -34,9 +30,8 @@ export default class ResourceConsumer<T = any> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private _consuming?: () => void;
|
||||
consume(consumerOrRenderer: SimulatorRenderer | ((data: T) => any)) {
|
||||
consume(consumerOrRenderer: BuiltinSimulatorRenderer | ((data: T) => any)) {
|
||||
if (this._consuming) {
|
||||
return;
|
||||
}
|
||||
@ -48,7 +43,7 @@ export default class ResourceConsumer<T = any> {
|
||||
}
|
||||
const rendererConsumer = this.consumer!;
|
||||
|
||||
consumer = (data) => rendererConsumer(consumerOrRenderer, data);
|
||||
consumer = data => rendererConsumer(consumerOrRenderer, data);
|
||||
} else {
|
||||
consumer = consumerOrRenderer;
|
||||
}
|
||||
@ -76,14 +71,14 @@ export default class ResourceConsumer<T = any> {
|
||||
this.emitter.removeAllListeners();
|
||||
}
|
||||
|
||||
private _firstConsumed: boolean = false;
|
||||
private _firstConsumed = false;
|
||||
private resovleFirst?: () => void;
|
||||
|
||||
waitFirstConsume(): Promise<any> {
|
||||
if (this._firstConsumed) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
this.resovleFirst = resolve;
|
||||
});
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { isValidElement } from 'react';
|
||||
import { isElement } from '../../../utils/is-element';
|
||||
import { PropType, PropConfig } from '../../../designer/prop-config';
|
||||
import { isElement } from '@ali/lowcode-globals';
|
||||
import { PropConfig } from '@ali/lowcode-globals';
|
||||
|
||||
export const primitiveTypes = [
|
||||
'string',
|
||||
@ -1,7 +1,6 @@
|
||||
import { obx, computed } from '@recore/obx';
|
||||
import { Point } from '../../../designer/helper/location';
|
||||
import { ScrollTarget } from '../../../designer/helper/scroller';
|
||||
import { AutoFit, IViewport } from '../../../designer/simulator';
|
||||
import { obx, computed } from '@ali/lowcode-globals';
|
||||
import { Point, ScrollTarget } from '../designer';
|
||||
import { AutoFit, IViewport } from '../simulator';
|
||||
|
||||
export default class Viewport implements IViewport {
|
||||
@obx.ref private rect?: DOMRect;
|
||||
@ -1 +0,0 @@
|
||||
主进程
|
||||
@ -1,30 +0,0 @@
|
||||
import { observer } from '@recore/obx-react';
|
||||
import { Component } from 'react';
|
||||
import { OutlineHovering } from './outline-hovering';
|
||||
import { SimulatorContext } from '../context';
|
||||
import { SimulatorHost } from '../host';
|
||||
import { OutlineSelecting } from './outline-selecting';
|
||||
import { InsertionView } from './insertion';
|
||||
import './auxiliary.less';
|
||||
import './outlines.less';
|
||||
|
||||
@observer
|
||||
export class AuxiliaryView extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const host = this.context as SimulatorHost;
|
||||
const { scrollX, scrollY, scale } = host.viewport;
|
||||
return (
|
||||
<div className="lc-auxiliary" style={{ transform: `translate(${-scrollX * scale}px,${-scrollY * scale}px)` }}>
|
||||
<OutlineHovering key="hovering" />
|
||||
<OutlineSelecting key="selecting" />
|
||||
<InsertionView key="insertion" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export * from './auxiliary';
|
||||
@ -1,132 +0,0 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@recore/obx-react';
|
||||
import { SimulatorContext } from '../context';
|
||||
import { SimulatorHost } from '../host';
|
||||
import { computed } from '@recore/obx';
|
||||
import OffsetObserver from '../../../../designer/helper/offset-observer';
|
||||
import Node from '../../../../designer/document/node/node';
|
||||
|
||||
@observer
|
||||
export class OutlineSelectingInstance extends Component<{
|
||||
observed: OffsetObserver;
|
||||
highlight?: boolean;
|
||||
dragging?: boolean;
|
||||
}> {
|
||||
componentWillUnmount() {
|
||||
this.props.observed.purge();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { observed, highlight, dragging } = this.props;
|
||||
if (!observed.hasOffset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { offsetWidth, offsetHeight, offsetTop, offsetLeft } = observed;
|
||||
|
||||
const style = {
|
||||
width: offsetWidth,
|
||||
height: offsetHeight,
|
||||
transform: `translate3d(${offsetLeft}px, ${offsetTop}px, 0)`,
|
||||
};
|
||||
|
||||
const className = classNames('lc-outlines lc-outlines-selecting', {
|
||||
highlight,
|
||||
dragging,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
<a className="lc-outlines-title">{observed.nodeInstance.node.title}</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export class OutlineSelectingForNode extends Component<{ node: Node }> {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
get host(): SimulatorHost {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
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 => {
|
||||
const observed = designer.createOffsetObserver({
|
||||
node,
|
||||
instance,
|
||||
});
|
||||
if (!observed) {
|
||||
return null;
|
||||
}
|
||||
return <OutlineSelectingInstance key={observed.id} dragging={this.dragging} observed={observed} />;
|
||||
})}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export class OutlineSelecting extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
get host(): SimulatorHost {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
render() {
|
||||
const selecting = this.selecting;
|
||||
if (!selecting || selecting.length < 1) {
|
||||
// DIRTY FIX, recore has a bug!
|
||||
return <Fragment />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{selecting.map(node => (
|
||||
<OutlineSelectingForNode key={node.id} node={node} />
|
||||
))}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
import { SimulatorHost } from './host';
|
||||
|
||||
export const SimulatorContext = createContext<SimulatorHost>({} as any);
|
||||
@ -1,5 +0,0 @@
|
||||
import { SimulatorHostView } from './host/host-view';
|
||||
|
||||
export * from './host/host';
|
||||
export * from './host/host-view';
|
||||
export default SimulatorHostView;
|
||||
@ -1,4 +0,0 @@
|
||||
// NOTE: 仅做类型标注,切勿做其它用途
|
||||
import { SimulatorHost } from '../host';
|
||||
|
||||
export const host: SimulatorHost = (window as any).LCSimulatorHost;
|
||||
@ -1,284 +0,0 @@
|
||||
import { ReactElement, createElement, ReactType } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const mocksCache = new Map<string, (props: any) => ReactElement>();
|
||||
// endpoint element: input,select,video,audio,canvas,textarea
|
||||
//
|
||||
function getBlockElement(tag: string): (props: any) => ReactElement {
|
||||
if (mocksCache.has(tag)) {
|
||||
return mocksCache.get(tag)!;
|
||||
}
|
||||
const mock = ({ className, children, ...rest }: any = {}) => {
|
||||
const props = {
|
||||
...rest,
|
||||
className: classNames('my-intrinsic-container', className),
|
||||
};
|
||||
return createElement(tag, props, children);
|
||||
};
|
||||
|
||||
mock.prototypeConfig = {
|
||||
uri: `@html:${tag}`,
|
||||
selfControlled: true,
|
||||
...(prototypeMap as any)[tag],
|
||||
};
|
||||
|
||||
mocksCache.set(tag, mock);
|
||||
return mock;
|
||||
}
|
||||
|
||||
const HTMLBlock = [
|
||||
'div',
|
||||
'p',
|
||||
'article',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'aside',
|
||||
'blockquote',
|
||||
'footer',
|
||||
'form',
|
||||
'header',
|
||||
'table',
|
||||
'tbody',
|
||||
'section',
|
||||
'ul',
|
||||
'li',
|
||||
'span',
|
||||
];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const HTMLInlineBlock = ['a', 'b', 'span', 'em'];
|
||||
export function getIntrinsicMock(tag: string): ReactType {
|
||||
if (HTMLBlock.indexOf(tag) > -1) {
|
||||
return getBlockElement(tag);
|
||||
}
|
||||
|
||||
return tag as any;
|
||||
}
|
||||
|
||||
const prototypeMap = {
|
||||
div: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: 'p',
|
||||
},
|
||||
},
|
||||
ul: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
childWhitelist: 'li',
|
||||
},
|
||||
},
|
||||
p: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: 'button,p',
|
||||
},
|
||||
},
|
||||
li: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
parentWhitelist: 'ui,ol',
|
||||
},
|
||||
},
|
||||
span: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
},
|
||||
a: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!a',
|
||||
},
|
||||
},
|
||||
b: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
},
|
||||
strong: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
},
|
||||
em: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
},
|
||||
i: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
},
|
||||
form: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!form,!button',
|
||||
},
|
||||
},
|
||||
table: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
caption: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
select: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
button: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
input: {
|
||||
isContainer: false,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button,!h1,!h2,!h3,!h4,!h5',
|
||||
},
|
||||
},
|
||||
textarea: {
|
||||
isContainer: false,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
image: {
|
||||
isContainer: false,
|
||||
selfControlled: true,
|
||||
},
|
||||
canvas: {
|
||||
isContainer: false,
|
||||
selfControlled: true,
|
||||
},
|
||||
br: {
|
||||
isContainer: false,
|
||||
selfControlled: true,
|
||||
},
|
||||
h1: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!p,!h1,!h2,!h3,!h4,!h5,!h6,!button',
|
||||
},
|
||||
},
|
||||
h2: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!p,!h1,!h2,!h3,!h4,!h5,!h6,!button',
|
||||
},
|
||||
},
|
||||
h3: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!p,!h1,!h2,!h3,!h4,!h5,!h6,!button',
|
||||
},
|
||||
},
|
||||
h4: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!p,!h1,!h2,!h3,!h4,!h5,!h6,!button',
|
||||
},
|
||||
},
|
||||
h5: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!p,!h1,!h2,!h3,!h4,!h5,!h6,!button',
|
||||
},
|
||||
},
|
||||
h6: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!p,!h1,!h2,!h3,!h4,!h5,!h6,!button',
|
||||
},
|
||||
},
|
||||
article: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
aside: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
footer: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
header: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
blockquote: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
address: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
section: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!p,!h1,!h2,!h3,!h4,!h5,!h6,!button',
|
||||
},
|
||||
},
|
||||
summary: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
nav: {
|
||||
isContainer: true,
|
||||
selfControlled: true,
|
||||
nesting: {
|
||||
ancestorBlacklist: '!button',
|
||||
},
|
||||
},
|
||||
};
|
||||
299
packages/designer/src/component-meta.ts
Normal file
299
packages/designer/src/component-meta.ts
Normal file
@ -0,0 +1,299 @@
|
||||
import {
|
||||
ComponentMetadata,
|
||||
NpmInfo,
|
||||
NodeData,
|
||||
NodeSchema,
|
||||
ComponentAction,
|
||||
TitleContent,
|
||||
TransformedComponentMetadata,
|
||||
getRegisteredMetadataTransducers,
|
||||
registerMetadataTransducer,
|
||||
computed,
|
||||
} from '@ali/lowcode-globals';
|
||||
import { Node, NodeParent } from './document';
|
||||
import { Designer } from './designer';
|
||||
import { intl } from './locale';
|
||||
import { IconContainer } from './icons/container';
|
||||
import { IconPage } from './icons/page';
|
||||
import { IconComponent } from './icons/component';
|
||||
import { IconRemove } from './icons/remove';
|
||||
import { IconClone } from './icons/clone';
|
||||
|
||||
function ensureAList(list?: string | string[]): string[] | null {
|
||||
if (!list) {
|
||||
return null;
|
||||
}
|
||||
if (!Array.isArray(list)) {
|
||||
list = list.split(/ *[ ,|] */).filter(Boolean);
|
||||
}
|
||||
if (list.length < 1) {
|
||||
return null;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
function npmToURI(npm: {
|
||||
package: string;
|
||||
exportName?: string;
|
||||
subName?: string;
|
||||
destructuring?: boolean;
|
||||
main?: string;
|
||||
version: string;
|
||||
}): string {
|
||||
const pkg = [];
|
||||
if (npm.package) {
|
||||
pkg.push(npm.package);
|
||||
}
|
||||
if (npm.main) {
|
||||
if (npm.main[0] === '/') {
|
||||
pkg.push(npm.main.slice(1));
|
||||
} else if (npm.main.slice(0, 2) === './') {
|
||||
pkg.push(npm.main.slice(2));
|
||||
} else {
|
||||
pkg.push(npm.main);
|
||||
}
|
||||
}
|
||||
|
||||
let uri = pkg.join('/');
|
||||
uri += `:${npm.destructuring && npm.exportName ? npm.exportName : 'default'}`;
|
||||
|
||||
if (npm.subName) {
|
||||
uri += `.${npm.subName}`;
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
export class ComponentMeta {
|
||||
readonly isComponentMeta = true;
|
||||
private _uri?: string;
|
||||
get uri(): string {
|
||||
return this._uri!;
|
||||
}
|
||||
private _npm?: NpmInfo;
|
||||
get npm() {
|
||||
return this._npm;
|
||||
}
|
||||
private _componentName?: string;
|
||||
get componentName(): string {
|
||||
return this._componentName!;
|
||||
}
|
||||
private _isContainer?: boolean;
|
||||
get isContainer(): boolean {
|
||||
return this._isContainer! || this.isRootComponent();
|
||||
}
|
||||
private _isModal?: boolean;
|
||||
get isModal(): boolean {
|
||||
return this._isModal!;
|
||||
}
|
||||
private _descriptor?: string;
|
||||
get descriptor(): string | undefined {
|
||||
return this._descriptor;
|
||||
}
|
||||
private _rectSelector?: string;
|
||||
get rectSelector(): string | undefined {
|
||||
return this._rectSelector;
|
||||
}
|
||||
private _transformedMetadata?: TransformedComponentMetadata;
|
||||
get configure() {
|
||||
const config = this._transformedMetadata?.configure;
|
||||
return config?.combined || config?.props || [];
|
||||
}
|
||||
|
||||
private parentWhitelist?: string[] | null;
|
||||
private childWhitelist?: string[] | null;
|
||||
|
||||
private _title?: TitleContent;
|
||||
get title() {
|
||||
return this._title || this.componentName;
|
||||
}
|
||||
|
||||
@computed get icon() {
|
||||
// give Slot default icon
|
||||
return (
|
||||
this._transformedMetadata?.icon ||
|
||||
(this.componentName === 'Page' ? IconPage : this.isContainer ? IconContainer : IconComponent)
|
||||
);
|
||||
}
|
||||
|
||||
private _acceptable?: boolean;
|
||||
get acceptable(): boolean {
|
||||
return this._acceptable!;
|
||||
}
|
||||
|
||||
constructor(readonly designer: Designer, metadata: ComponentMetadata) {
|
||||
this.parseMetadata(metadata);
|
||||
}
|
||||
|
||||
private parseMetadata(metadta: ComponentMetadata) {
|
||||
const { componentName, uri, npm } = metadta;
|
||||
this._npm = npm;
|
||||
this._uri = uri || (npm ? npmToURI(npm) : componentName);
|
||||
this._componentName = componentName;
|
||||
|
||||
metadta.uri = this._uri;
|
||||
// 额外转换逻辑
|
||||
this._transformedMetadata = this.transformMetadata(metadta);
|
||||
|
||||
const title = this._transformedMetadata.title;
|
||||
if (title && typeof title === 'string') {
|
||||
this._title = {
|
||||
type: 'i18n',
|
||||
'en-US': this.componentName,
|
||||
'zh-CN': title,
|
||||
};
|
||||
}
|
||||
|
||||
const { configure = {} } = this._transformedMetadata;
|
||||
this._acceptable = false;
|
||||
|
||||
const { component } = configure;
|
||||
if (component) {
|
||||
this._isContainer = component.isContainer ? true : false;
|
||||
this._isModal = component.isModal ? true : false;
|
||||
this._descriptor = component.descriptor;
|
||||
this._rectSelector = component.rectSelector;
|
||||
if (component.nestingRule) {
|
||||
const { parentWhitelist, childWhitelist } = component.nestingRule;
|
||||
this.parentWhitelist = ensureAList(parentWhitelist);
|
||||
this.childWhitelist = ensureAList(childWhitelist);
|
||||
}
|
||||
} else {
|
||||
this._isContainer = false;
|
||||
this._isModal = false;
|
||||
}
|
||||
}
|
||||
|
||||
private transformMetadata(metadta: ComponentMetadata): TransformedComponentMetadata {
|
||||
const result = getRegisteredMetadataTransducers().reduce((prevMetadata, current) => {
|
||||
return current(prevMetadata);
|
||||
}, preprocessMetadata(metadta));
|
||||
|
||||
if (!result.configure) {
|
||||
result.configure = {};
|
||||
}
|
||||
return result as any;
|
||||
}
|
||||
|
||||
isRootComponent() {
|
||||
return this.componentName === 'Page' || this.componentName === 'Block' || this.componentName === 'Component';
|
||||
}
|
||||
|
||||
@computed get availableActions() {
|
||||
let { disableBehaviors, actions } = this._transformedMetadata?.configure.component || {};
|
||||
actions = builtinComponentActions.concat(this.designer.getGlobalComponentActions() || [], actions || []);
|
||||
if (!disableBehaviors && this.isRootComponent()) {
|
||||
disableBehaviors = ['copy', 'remove'];
|
||||
}
|
||||
if (disableBehaviors) {
|
||||
return actions.filter(action => disableBehaviors!.indexOf(action.name) < 0);
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
setMetadata(metadata: ComponentMetadata) {
|
||||
this.parseMetadata(metadata);
|
||||
}
|
||||
|
||||
getMetadata(): TransformedComponentMetadata {
|
||||
return this._transformedMetadata!;
|
||||
}
|
||||
|
||||
checkNestingUp(my: Node | NodeData, parent: NodeParent) {
|
||||
if (this.parentWhitelist) {
|
||||
return this.parentWhitelist.includes(parent.componentName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
checkNestingDown(my: Node, target: Node | NodeSchema) {
|
||||
if (this.childWhitelist) {
|
||||
return this.childWhitelist.includes(target.componentName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function preprocessMetadata(metadata: ComponentMetadata): TransformedComponentMetadata {
|
||||
if (metadata.configure) {
|
||||
if (Array.isArray(metadata.configure)) {
|
||||
return {
|
||||
...metadata,
|
||||
configure: {
|
||||
props: metadata.configure,
|
||||
},
|
||||
};
|
||||
}
|
||||
return metadata as any;
|
||||
}
|
||||
|
||||
return {
|
||||
...metadata,
|
||||
configure: {},
|
||||
};
|
||||
}
|
||||
|
||||
registerMetadataTransducer(metadata => {
|
||||
const { configure, componentName } = metadata;
|
||||
const { component = {} } = configure;
|
||||
if (!component.nestingRule) {
|
||||
let m;
|
||||
// uri match xx.Group set subcontrolling: true, childWhiteList
|
||||
if ((m = /^(.+)\.Group$/.exec(componentName))) {
|
||||
// component.subControlling = true;
|
||||
if (!component.nestingRule) {
|
||||
component.nestingRule = {
|
||||
childWhitelist: [`${m[1]}`],
|
||||
};
|
||||
}
|
||||
}
|
||||
// uri match xx.Node set selfControlled: false, parentWhiteList
|
||||
else if ((m = /^(.+)\.Node$/.exec(componentName))) {
|
||||
// component.selfControlled = false;
|
||||
component.nestingRule = {
|
||||
parentWhitelist: [`${m[1]}`, componentName],
|
||||
};
|
||||
}
|
||||
// uri match .Item .Node .Option set parentWhiteList
|
||||
else if ((m = /^(.+)\.(Item|Node|Option)$/.exec(componentName))) {
|
||||
component.nestingRule = {
|
||||
parentWhitelist: [`${m[1]}`],
|
||||
};
|
||||
}
|
||||
}
|
||||
if (component.isModal == null && /Dialog/.test(componentName)) {
|
||||
component.isModal = true;
|
||||
}
|
||||
return {
|
||||
...metadata,
|
||||
configure: {
|
||||
...configure,
|
||||
component,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const builtinComponentActions: ComponentAction[] = [
|
||||
{
|
||||
name: 'remove',
|
||||
content: {
|
||||
icon: IconRemove,
|
||||
description: intl('remove'),
|
||||
action(node: Node) {
|
||||
node.remove();
|
||||
},
|
||||
},
|
||||
important: true,
|
||||
},
|
||||
{
|
||||
name: 'copy',
|
||||
content: {
|
||||
icon: IconClone,
|
||||
description: intl('copy'),
|
||||
action(node: Node) {
|
||||
// node.remove();
|
||||
},
|
||||
},
|
||||
important: true,
|
||||
},
|
||||
];
|
||||
@ -1,13 +1,13 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { LocationDetail } from './location';
|
||||
import Node, { isNode } from '../document/node/node';
|
||||
import { Node, isNode } from '../document/node/node';
|
||||
|
||||
interface ActiveTarget {
|
||||
export interface ActiveTarget {
|
||||
node: Node;
|
||||
detail?: LocationDetail;
|
||||
}
|
||||
|
||||
export default class ActiveTracker {
|
||||
export class ActiveTracker {
|
||||
private emitter = new EventEmitter();
|
||||
|
||||
track(target: ActiveTarget | Node) {
|
||||
@ -5,6 +5,7 @@ function getDataFromPasteEvent(event: ClipboardEvent) {
|
||||
}
|
||||
|
||||
try {
|
||||
// { componentsMap, componentsTree, ... }
|
||||
return JSON.parse(clipboardData.getData('text/plain'));
|
||||
} catch (error) {
|
||||
/*
|
||||
@ -17,11 +18,13 @@ function getDataFromPasteEvent(event: ClipboardEvent) {
|
||||
};
|
||||
}
|
||||
*/
|
||||
// paste the text by div
|
||||
// TODO: open the parser implement
|
||||
return null;
|
||||
/*
|
||||
return {
|
||||
code: clipboardData.getData('text/plain'),
|
||||
maps: {},
|
||||
};
|
||||
};*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +36,7 @@ class Clipboard {
|
||||
this.isCopyPaster(e.target);
|
||||
}
|
||||
|
||||
isCopyPaster(el: any) {
|
||||
private isCopyPaster(el: any) {
|
||||
return this.copyPasters.includes(el);
|
||||
}
|
||||
|
||||
@ -57,6 +60,9 @@ class Clipboard {
|
||||
}
|
||||
|
||||
injectCopyPaster(document: Document) {
|
||||
if (this.copyPasters.find(x => x.ownerDocument === document)) {
|
||||
return;
|
||||
}
|
||||
const copyPaster = document.createElement<'textarea'>('textarea');
|
||||
copyPaster.style.cssText = 'position: relative;left: -9999px;';
|
||||
document.body.appendChild(copyPaster);
|
||||
@ -1,224 +0,0 @@
|
||||
import { ReactNode } from 'react';
|
||||
import Node, { NodeParent } from './document/node/node';
|
||||
import { NodeData, NodeSchema } from './schema';
|
||||
import { PropConfig } from './prop-config';
|
||||
|
||||
export interface NestingRule {
|
||||
childWhitelist?: string[];
|
||||
parentWhitelist?: string[];
|
||||
}
|
||||
|
||||
export interface Configure {
|
||||
props?: any[];
|
||||
styles?: object;
|
||||
events?: object;
|
||||
component?: {
|
||||
isContainer?: boolean;
|
||||
isModal?: boolean;
|
||||
descriptor?: string;
|
||||
nestingRule?: NestingRule;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ComponentMetadata {
|
||||
componentName: string;
|
||||
/**
|
||||
* unique id
|
||||
*/
|
||||
uri?: string;
|
||||
/**
|
||||
* title or description
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* svg icon for component
|
||||
*/
|
||||
icon?: string | ReactNode;
|
||||
tags?: string[];
|
||||
description?: string;
|
||||
docUrl?: string;
|
||||
screenshot?: string;
|
||||
devMode?: 'procode' | 'lowcode';
|
||||
npm?: {
|
||||
package: string;
|
||||
exportName: string;
|
||||
subName: string;
|
||||
main: string;
|
||||
destructuring: boolean;
|
||||
version: string;
|
||||
};
|
||||
props?: PropConfig[];
|
||||
configure?: any[] | Configure;
|
||||
}
|
||||
|
||||
interface TransformedComponentMetadata extends ComponentMetadata {
|
||||
configure?: Configure & {
|
||||
combined?: any[];
|
||||
};
|
||||
}
|
||||
|
||||
function ensureAList(list?: string | string[]): string[] | null {
|
||||
if (!list) {
|
||||
return null;
|
||||
}
|
||||
if (!Array.isArray(list)) {
|
||||
list = list.split(/ *[ ,|] */).filter(Boolean);
|
||||
}
|
||||
if (list.length < 1) {
|
||||
return null;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
function npmToURI(npm: {
|
||||
package: string;
|
||||
exportName?: string;
|
||||
subName?: string;
|
||||
destructuring?: boolean;
|
||||
main?: string;
|
||||
version: string;
|
||||
}): string {
|
||||
const pkg = [];
|
||||
if (npm.package) {
|
||||
pkg.push(npm.package);
|
||||
}
|
||||
if (npm.main) {
|
||||
if (npm.main[0] === '/') {
|
||||
pkg.push(npm.main.slice(1));
|
||||
} else if (npm.main.slice(0, 2) === './') {
|
||||
pkg.push(npm.main.slice(2));
|
||||
} else {
|
||||
pkg.push(npm.main);
|
||||
}
|
||||
}
|
||||
|
||||
let uri = pkg.join('/');
|
||||
uri += `:${npm.destructuring && npm.exportName ? npm.exportName : 'default'}`;
|
||||
|
||||
if (npm.subName) {
|
||||
uri += `.${npm.subName}`;
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
export type MetadataTransducer = (prev: ComponentMetadata) => TransformedComponentMetadata;
|
||||
const metadataTransducers: MetadataTransducer[] = [];
|
||||
|
||||
export function registerMetadataTransducer(transducer: MetadataTransducer) {
|
||||
metadataTransducers.push(transducer);
|
||||
}
|
||||
|
||||
export class ComponentMeta {
|
||||
readonly isComponentMeta = true;
|
||||
private _uri?: string;
|
||||
get uri(): string {
|
||||
return this._uri!;
|
||||
}
|
||||
private _componentName?: string;
|
||||
get componentName(): string {
|
||||
return this._componentName!;
|
||||
}
|
||||
private _isContainer?: boolean;
|
||||
get isContainer(): boolean {
|
||||
return this._isContainer! || this.isRootComponent();
|
||||
}
|
||||
private _isModal?: boolean;
|
||||
get isModal(): boolean {
|
||||
return this._isModal!;
|
||||
}
|
||||
private _descriptor?: string;
|
||||
get descriptor(): string {
|
||||
return this._descriptor!;
|
||||
}
|
||||
private _acceptable?: boolean;
|
||||
get acceptable(): boolean {
|
||||
return this._acceptable!;
|
||||
}
|
||||
private _transformedMetadata?: TransformedComponentMetadata;
|
||||
get configure() {
|
||||
const config = this._transformedMetadata?.configure;
|
||||
return config?.combined || config?.props || [];
|
||||
}
|
||||
|
||||
private parentWhitelist?: string[] | null;
|
||||
private childWhitelist?: string[] | null;
|
||||
|
||||
get title() {
|
||||
return this._metadata.title || this.componentName;
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return this._metadata.icon;
|
||||
}
|
||||
|
||||
constructor(private _metadata: ComponentMetadata) {
|
||||
this.parseMetadata(_metadata);
|
||||
}
|
||||
|
||||
private parseMetadata(metadta: ComponentMetadata) {
|
||||
const { componentName, uri, npm, props } = metadta;
|
||||
this._uri = uri || (npm ? npmToURI(npm) : componentName);
|
||||
this._componentName = componentName;
|
||||
|
||||
metadta.uri = this._uri;
|
||||
// 额外转换逻辑
|
||||
this._transformedMetadata = this.transformMetadata(metadta);
|
||||
|
||||
const { configure = {} } = this._transformedMetadata;
|
||||
this._acceptable = false;
|
||||
|
||||
const { component } = configure;
|
||||
if (component) {
|
||||
this._isContainer = component.isContainer ? true : false;
|
||||
this._isModal = component.isModal ? true : false;
|
||||
this._descriptor = component.descriptor;
|
||||
if (component.nestingRule) {
|
||||
const { parentWhitelist, childWhitelist } = component.nestingRule;
|
||||
this.parentWhitelist = ensureAList(parentWhitelist);
|
||||
this.childWhitelist = ensureAList(childWhitelist);
|
||||
}
|
||||
} else {
|
||||
this._isContainer = false;
|
||||
this._isModal = false;
|
||||
}
|
||||
}
|
||||
|
||||
private transformMetadata(metadta: ComponentMetadata): TransformedComponentMetadata {
|
||||
const result = metadataTransducers.reduce((prevMetadata, current) => {
|
||||
return current(prevMetadata);
|
||||
}, metadta);
|
||||
|
||||
if (!result.configure) {
|
||||
result.configure = {};
|
||||
}
|
||||
return result as any;
|
||||
}
|
||||
|
||||
isRootComponent() {
|
||||
return this.componentName === 'Page' || this.componentName === 'Block' || this.componentName === 'Component';
|
||||
}
|
||||
|
||||
set metadata(metadata: ComponentMetadata) {
|
||||
this._metadata = metadata;
|
||||
this.parseMetadata(metadata);
|
||||
}
|
||||
|
||||
get metadata(): ComponentMetadata {
|
||||
return this._metadata;
|
||||
}
|
||||
|
||||
checkNestingUp(my: Node | NodeData, parent: NodeParent) {
|
||||
if (this.parentWhitelist) {
|
||||
return this.parentWhitelist.includes(parent.componentName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
checkNestingDown(my: Node, target: Node | NodeSchema) {
|
||||
if (this.childWhitelist) {
|
||||
return this.childWhitelist.includes(target.componentName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,13 @@
|
||||
import { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import Designer, { DesignerProps } from './designer';
|
||||
import BuiltinDragGhostComponent from '../builtins/drag-ghost';
|
||||
import ProjectView from './project-view';
|
||||
import { TipContainer } from '@ali/lowcode-globals';
|
||||
import BuiltinDragGhostComponent from './drag-ghost';
|
||||
import { Designer, DesignerProps } from './designer';
|
||||
import { ProjectView } from '../project';
|
||||
import './designer.less';
|
||||
import clipboard from './clipboard';
|
||||
|
||||
export default class DesignerView extends Component<DesignerProps> {
|
||||
export class DesignerView extends Component<DesignerProps> {
|
||||
readonly designer: Designer;
|
||||
|
||||
constructor(props: any) {
|
||||
@ -18,7 +20,7 @@ export default class DesignerView extends Component<DesignerProps> {
|
||||
const props = this.props;
|
||||
if (
|
||||
nextProps.className !== props.className ||
|
||||
nextProps.style != props.style ||
|
||||
nextProps.style !== props.style ||
|
||||
nextProps.dragGhostComponent !== props.dragGhostComponent
|
||||
) {
|
||||
return true;
|
||||
@ -31,6 +33,8 @@ export default class DesignerView extends Component<DesignerProps> {
|
||||
if (onMount) {
|
||||
onMount(this.designer);
|
||||
}
|
||||
clipboard.injectCopyPaster(document)
|
||||
this.designer.postEvent('mount', this.designer);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
@ -45,6 +49,7 @@ export default class DesignerView extends Component<DesignerProps> {
|
||||
<div className={classNames('lc-designer', className)} style={style}>
|
||||
<DragGhost designer={this.designer} />
|
||||
<ProjectView designer={this.designer} />
|
||||
<TipContainer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -48,26 +48,6 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.lc-project {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.lc-document {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
&-hidden {
|
||||
// todo:
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lc-simulator-shell {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,20 +1,25 @@
|
||||
import { ComponentType as ReactComponentType } from 'react';
|
||||
import { obx, computed, autorun } from '@recore/obx';
|
||||
import BuiltinSimulatorView from '../builtins/simulator';
|
||||
import Project from './project';
|
||||
import { ProjectSchema, NpmInfo } from './schema';
|
||||
import Dragon, { isDragNodeObject, isDragNodeDataObject, LocateEvent, DragObject } from './helper/dragon';
|
||||
import ActiveTracker from './helper/active-tracker';
|
||||
import Hovering from './helper/hovering';
|
||||
import Location, { LocationData, isLocationChildrenDetail } from './helper/location';
|
||||
import DocumentModel from './document/document-model';
|
||||
import Node, { insertChildren } from './document/node/node';
|
||||
import { isRootNode } from './document/node/root-node';
|
||||
import { ComponentMetadata, ComponentMeta } from './component-meta';
|
||||
import Scroller, { IScrollable } from './helper/scroller';
|
||||
import { INodeSelector } from './simulator';
|
||||
import OffsetObserver, { createOffsetObserver } from './helper/offset-observer';
|
||||
import { ComponentType } from 'react';
|
||||
import { EventEmitter } from 'events';
|
||||
import {
|
||||
ProjectSchema,
|
||||
ComponentMetadata,
|
||||
ComponentAction,
|
||||
NpmInfo,
|
||||
obx,
|
||||
computed,
|
||||
autorun,
|
||||
} from '@ali/lowcode-globals';
|
||||
import { Project } from '../project';
|
||||
import { Node, DocumentModel, insertChildren, isRootNode, NodeParent } from '../document';
|
||||
import { ComponentMeta } from '../component-meta';
|
||||
import { INodeSelector } from '../simulator';
|
||||
import { Scroller, IScrollable } from './scroller';
|
||||
import { Dragon, isDragNodeObject, isDragNodeDataObject, LocateEvent, DragObject } from './dragon';
|
||||
import { ActiveTracker } from './active-tracker';
|
||||
import { Hovering } from './hovering';
|
||||
import { DropLocation, LocationData, isLocationChildrenDetail } from './location';
|
||||
import { OffsetObserver, createOffsetObserver } from './offset-observer';
|
||||
import { focusing } from './focusing';
|
||||
|
||||
export interface DesignerProps {
|
||||
className?: string;
|
||||
@ -22,20 +27,20 @@ export interface DesignerProps {
|
||||
defaultSchema?: ProjectSchema;
|
||||
hotkeys?: object;
|
||||
simulatorProps?: object | ((document: DocumentModel) => object);
|
||||
simulatorComponent?: ReactComponentType<any>;
|
||||
dragGhostComponent?: ReactComponentType<any>;
|
||||
simulatorComponent?: ComponentType<any>;
|
||||
dragGhostComponent?: ComponentType<any>;
|
||||
suspensed?: boolean;
|
||||
componentsDescription?: ComponentMetadata[];
|
||||
componentMetadatas?: ComponentMetadata[];
|
||||
eventPipe?: EventEmitter;
|
||||
globalComponentActions?: ComponentAction[];
|
||||
onMount?: (designer: Designer) => void;
|
||||
onDragstart?: (e: LocateEvent) => void;
|
||||
onDrag?: (e: LocateEvent) => void;
|
||||
onDragend?: (e: { dragObject: DragObject; copy: boolean }, loc?: Location) => void;
|
||||
onDragend?: (e: { dragObject: DragObject; copy: boolean }, loc?: DropLocation) => void;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export default class Designer {
|
||||
// readonly hotkey: Hotkey;
|
||||
export class Designer {
|
||||
readonly dragon = new Dragon(this);
|
||||
readonly activeTracker = new ActiveTracker();
|
||||
readonly hovering = new Hovering();
|
||||
@ -58,7 +63,7 @@ export default class Designer {
|
||||
|
||||
this.project = new Project(this, props.defaultSchema);
|
||||
|
||||
this.dragon.onDragstart(e => {
|
||||
this.dragon.onDragstart((e) => {
|
||||
this.hovering.enable = false;
|
||||
const { dragObject } = e;
|
||||
if (isDragNodeObject(dragObject)) {
|
||||
@ -75,14 +80,14 @@ export default class Designer {
|
||||
this.postEvent('dragstart', e);
|
||||
});
|
||||
|
||||
this.dragon.onDrag(e => {
|
||||
this.dragon.onDrag((e) => {
|
||||
if (this.props?.onDrag) {
|
||||
this.props.onDrag(e);
|
||||
}
|
||||
this.postEvent('drag', e);
|
||||
});
|
||||
|
||||
this.dragon.onDragend(e => {
|
||||
this.dragon.onDragend((e) => {
|
||||
const { dragObject, copy } = e;
|
||||
const loc = this._dropLocation;
|
||||
if (loc) {
|
||||
@ -96,7 +101,7 @@ export default class Designer {
|
||||
nodes = insertChildren(loc.target, nodeData, loc.detail.index);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -148,22 +153,27 @@ export default class Designer {
|
||||
setupSelection();
|
||||
setupHistory();
|
||||
});
|
||||
this.postEvent('designer.init', this);
|
||||
setupSelection();
|
||||
setupHistory();
|
||||
|
||||
this.postEvent('designer.ready', this);
|
||||
// TODO: 先简单实现,后期通过焦点赋值
|
||||
focusing.focusDesigner = this;
|
||||
}
|
||||
|
||||
postEvent(event: string, ...args: any[]) {
|
||||
this.props?.eventPipe?.emit(`designer.${event}`, ...args);
|
||||
}
|
||||
|
||||
private _dropLocation?: Location;
|
||||
private _dropLocation?: DropLocation;
|
||||
/**
|
||||
* 创建插入位置,考虑放到 dragon 中
|
||||
*/
|
||||
createLocation(locationData: LocationData): Location {
|
||||
const loc = new Location(locationData);
|
||||
createLocation(locationData: LocationData): DropLocation {
|
||||
const loc = new DropLocation(locationData);
|
||||
if (this._dropLocation && this._dropLocation.document !== loc.document) {
|
||||
this._dropLocation.document.internalSetDropLocation(null);
|
||||
}
|
||||
this._dropLocation = loc;
|
||||
loc.document.internalSetDropLocation(loc);
|
||||
this.activeTracker.track({ node: loc.target, detail: loc.detail });
|
||||
@ -191,7 +201,7 @@ export default class Designer {
|
||||
/**
|
||||
* 获得合适的插入位置
|
||||
*/
|
||||
getSuitableInsertion() {
|
||||
getSuitableInsertion(): { target: NodeParent; index?: number } | null {
|
||||
const activedDoc = this.project.currentDocument;
|
||||
if (!activedDoc) {
|
||||
return null;
|
||||
@ -215,7 +225,8 @@ export default class Designer {
|
||||
}
|
||||
|
||||
private props?: DesignerProps;
|
||||
setProps(props: DesignerProps) {
|
||||
setProps(nextProps: DesignerProps) {
|
||||
const props = this.props ? { ...this.props, ...nextProps } : nextProps;
|
||||
if (this.props) {
|
||||
// check hotkeys
|
||||
// TODO:
|
||||
@ -229,8 +240,8 @@ export default class Designer {
|
||||
if (props.suspensed !== this.props.suspensed && props.suspensed != null) {
|
||||
this.suspensed = props.suspensed;
|
||||
}
|
||||
if (props.componentsDescription !== this.props.componentsDescription && props.componentsDescription != null) {
|
||||
this.buildComponentMetasMap(props.componentsDescription);
|
||||
if (props.componentMetadatas !== this.props.componentMetadatas && props.componentMetadatas != null) {
|
||||
this.buildComponentMetasMap(props.componentMetadatas);
|
||||
}
|
||||
} else {
|
||||
// init hotkeys
|
||||
@ -246,8 +257,8 @@ export default class Designer {
|
||||
if (props.suspensed != null) {
|
||||
this.suspensed = props.suspensed;
|
||||
}
|
||||
if (props.componentsDescription != null) {
|
||||
this.buildComponentMetasMap(props.componentsDescription);
|
||||
if (props.componentMetadatas != null) {
|
||||
this.buildComponentMetasMap(props.componentMetadatas);
|
||||
}
|
||||
}
|
||||
this.props = props;
|
||||
@ -257,10 +268,10 @@ export default class Designer {
|
||||
return this.props ? this.props[key] : null;
|
||||
}
|
||||
|
||||
@obx.ref private _simulatorComponent?: ReactComponentType<any>;
|
||||
@obx.ref private _simulatorComponent?: ComponentType<any>;
|
||||
|
||||
@computed get simulatorComponent(): ReactComponentType<any> {
|
||||
return this._simulatorComponent || BuiltinSimulatorView;
|
||||
@computed get simulatorComponent(): ComponentType<any> | undefined {
|
||||
return this._simulatorComponent;
|
||||
}
|
||||
|
||||
@obx.ref private _simulatorProps?: object | ((document: DocumentModel) => object);
|
||||
@ -284,30 +295,30 @@ export default class Designer {
|
||||
}
|
||||
|
||||
get schema(): ProjectSchema {
|
||||
return this.project.schema;
|
||||
return this.project.getSchema();
|
||||
}
|
||||
|
||||
set schema(schema: ProjectSchema) {
|
||||
// todo:
|
||||
setSchema(schema?: ProjectSchema) {
|
||||
this.project.load(schema);
|
||||
}
|
||||
|
||||
@obx.val private _componentMetasMap = new Map<string, ComponentMeta>();
|
||||
private _lostComponentMetasMap = new Map<string, ComponentMeta>();
|
||||
|
||||
private buildComponentMetasMap(metas: ComponentMetadata[]) {
|
||||
metas.forEach(data => {
|
||||
metas.forEach((data) => {
|
||||
const key = data.componentName;
|
||||
let meta = this._componentMetasMap.get(key);
|
||||
if (meta) {
|
||||
meta.metadata = data;
|
||||
meta.setMetadata(data);
|
||||
} else {
|
||||
meta = this._lostComponentMetasMap.get(key);
|
||||
|
||||
if (meta) {
|
||||
meta.metadata = data;
|
||||
meta.setMetadata(data);
|
||||
this._lostComponentMetasMap.delete(key);
|
||||
} else {
|
||||
meta = new ComponentMeta(data);
|
||||
meta = new ComponentMeta(this, data);
|
||||
}
|
||||
|
||||
this._componentMetasMap.set(key, meta);
|
||||
@ -315,6 +326,10 @@ export default class Designer {
|
||||
});
|
||||
}
|
||||
|
||||
getGlobalComponentActions(): ComponentAction[] | null {
|
||||
return this.props?.globalComponentActions || null;
|
||||
}
|
||||
|
||||
getComponentMeta(componentName: string, generateMetadata?: () => ComponentMetadata | null): ComponentMeta {
|
||||
if (this._componentMetasMap.has(componentName)) {
|
||||
return this._componentMetasMap.get(componentName)!;
|
||||
@ -324,7 +339,7 @@ export default class Designer {
|
||||
return this._lostComponentMetasMap.get(componentName)!;
|
||||
}
|
||||
|
||||
const meta = new ComponentMeta({
|
||||
const meta = new ComponentMeta(this, {
|
||||
componentName,
|
||||
...(generateMetadata ? generateMetadata() : null),
|
||||
});
|
||||
@ -337,7 +352,9 @@ export default class Designer {
|
||||
@computed get componentsMap(): { [key: string]: NpmInfo } {
|
||||
const maps: any = {};
|
||||
this._componentMetasMap.forEach((config, key) => {
|
||||
maps[key] = config.metadata.npm;
|
||||
if (config.npm) {
|
||||
maps[key] = config.npm;
|
||||
}
|
||||
});
|
||||
return maps;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user