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:
春希 2020-04-07 16:57:30 +08:00
commit abc602b4a1
1550 changed files with 116244 additions and 235783 deletions

View File

@ -4,3 +4,4 @@ build/
.*
~*
node_modules

3
.eslintrc Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "./node_modules/@ali/lowcode-config/.eslintrc"
}

16
.gitignore vendored
View File

@ -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

View File

@ -2,5 +2,7 @@
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true
"singleQuote": true,
"printWidth": 120,
"arrowParens": "always"
}

22
LICENSE Normal file
View 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.

View File

@ -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
View File

@ -0,0 +1,11 @@
{
"name": "lowcode-engine",
"assets": {
"type": "command",
"command": {
"cmd": [
"./scripts/deploy.sh $BUILD_DEST"
]
}
}
}

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

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

View 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"]
}

View File

@ -10,7 +10,7 @@
- 使用 `camelCase` 为属性或本地变量命名
- 不要为私有属性名添加 `_` 前缀
- 尽可能使用完整的单词拼写命名
- 文件夹命名统一使用小写
- 文件夹/文件命名统一使用小写 `get-custom-data.ts`
### 组件

View File

@ -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/*"
]
}
}

View File

@ -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"
}
}

View 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

View File

@ -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": [

View 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
View 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"
]
}

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

View 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"]
}
]
]
}

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

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -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>

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

View 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": "重置"
}]
}]
}]
}]
}

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

View 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: [],
};

View File

@ -0,0 +1,5 @@
/**
*
*/
export default {};

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

View 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();

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

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

View File

@ -0,0 +1,9 @@
.fusion-loading {
width: 48px;
height: 48px;
position: fixed;
top: 50%;
left: 50%;
margin-top: -24px;
margin-left: -24px;
}

View File

@ -0,0 +1,4 @@
import { Loading } from '@alifd/next';
import './index.scss';
export default () => <Loading tip="加载中..." className="fusion-loading" />;

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

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

View File

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

View File

@ -7,4 +7,4 @@ export default {
'zh-CN': zh_cn,
'zh-TW': zh_tw,
'ja-JP': ja_jp
};
};

View File

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

View File

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

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

View File

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

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

View File

@ -0,0 +1 @@
import './editor';

View File

@ -0,0 +1 @@
import './app';

View File

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "lib"
},
"include": [
"./src/"
]
}

View File

@ -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

View File

@ -1,6 +0,0 @@
.idea/
.vscode/
build/
.*
~*
node_modules

View File

@ -1,3 +0,0 @@
{
"extends": "./node_modules/@recore/config/.eslintrc"
}

View File

@ -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

View File

@ -1,6 +0,0 @@
{
"semi": true,
"singleQuote": true,
"printWidth": 120,
"trailingComma": "all"
}

View 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

View File

@ -1 +1,4 @@
编排模块
simulator/renderer 发 CDN

View File

@ -0,0 +1,7 @@
{
"plugins": [
[
"build-plugin-component"
]
]
}

File diff suppressed because it is too large Load Diff

View File

@ -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"
}
}

View File

@ -0,0 +1 @@
内置模拟器主进程

View File

@ -1,4 +1,4 @@
.lc-auxiliary {
.lc-bem-tools {
pointer-events: none;
position: absolute;
top: 0;

View File

@ -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>

View File

@ -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>
);
}
}

View File

@ -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;

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

View File

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

View File

@ -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;

View File

@ -0,0 +1,4 @@
import { createContext } from 'react';
import { BuiltinSimulatorHost } from './host';
export const SimulatorContext = createContext<BuiltinSimulatorHost>({} as any);

View File

@ -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!;

View File

@ -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) {

View File

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

View File

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

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

View File

@ -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;
});
}

View File

@ -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',

View File

@ -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;

View File

@ -1 +0,0 @@
主进程

View File

@ -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>
);
}
}

View File

@ -1 +0,0 @@
export * from './auxiliary';

View File

@ -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>
);
}
}

View File

@ -1,4 +0,0 @@
import { createContext } from 'react';
import { SimulatorHost } from './host';
export const SimulatorContext = createContext<SimulatorHost>({} as any);

View File

@ -1,5 +0,0 @@
import { SimulatorHostView } from './host/host-view';
export * from './host/host';
export * from './host/host-view';
export default SimulatorHostView;

View File

@ -1,4 +0,0 @@
// NOTE: 仅做类型标注,切勿做其它用途
import { SimulatorHost } from '../host';
export const host: SimulatorHost = (window as any).LCSimulatorHost;

View File

@ -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',
},
},
};

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

View File

@ -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) {

View File

@ -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);

View File

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

View File

@ -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>
);
}

View File

@ -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%;
}
}
}
}

View File

@ -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