mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-02-28 12:50:38 +00:00
feat: 加上 plugin-outline-pane
This commit is contained in:
parent
e599acd2f0
commit
83c0772bc5
106
packages/plugin-outline-pane/.gitignore
vendored
Normal file
106
packages/plugin-outline-pane/.gitignore
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
# project custom
|
||||
build
|
||||
dist
|
||||
packages/*/lib/
|
||||
packages/*/es/
|
||||
packages/*/dist/
|
||||
packages/*/output/
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
deploy-space/packages
|
||||
deploy-space/.env
|
||||
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
lib
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# mac config files
|
||||
.DS_Store
|
||||
|
||||
# codealike
|
||||
codealike.json
|
||||
665
packages/plugin-outline-pane/CHANGELOG.md
Normal file
665
packages/plugin-outline-pane/CHANGELOG.md
Normal file
@ -0,0 +1,665 @@
|
||||
# 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="1.0.21"></a>
|
||||
## [1.0.21](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.20...@ali/lowcode-plugin-outline-pane@1.0.21) (2020-11-16)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.20"></a>
|
||||
## [1.0.20](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.19...@ali/lowcode-plugin-outline-pane@1.0.20) (2020-11-10)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.19"></a>
|
||||
## [1.0.19](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.18...@ali/lowcode-plugin-outline-pane@1.0.19) (2020-11-10)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.18"></a>
|
||||
## [1.0.18](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.17...@ali/lowcode-plugin-outline-pane@1.0.18) (2020-11-05)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.17"></a>
|
||||
## [1.0.17](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.16...@ali/lowcode-plugin-outline-pane@1.0.17) (2020-11-05)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.16"></a>
|
||||
## [1.0.16](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.15...@ali/lowcode-plugin-outline-pane@1.0.16) (2020-11-05)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.15"></a>
|
||||
## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.14...@ali/lowcode-plugin-outline-pane@1.0.15) (2020-11-04)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.14"></a>
|
||||
## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.13...@ali/lowcode-plugin-outline-pane@1.0.14) (2020-11-04)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.13"></a>
|
||||
## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.12...@ali/lowcode-plugin-outline-pane@1.0.13) (2020-11-02)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.12"></a>
|
||||
## [1.0.12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.11...@ali/lowcode-plugin-outline-pane@1.0.12) (2020-10-20)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.11"></a>
|
||||
## [1.0.11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.10...@ali/lowcode-plugin-outline-pane@1.0.11) (2020-10-19)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.10"></a>
|
||||
## [1.0.10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.9...@ali/lowcode-plugin-outline-pane@1.0.10) (2020-09-29)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.9"></a>
|
||||
## [1.0.9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.8...@ali/lowcode-plugin-outline-pane@1.0.9) (2020-09-28)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.8"></a>
|
||||
## [1.0.8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.8-0...@ali/lowcode-plugin-outline-pane@1.0.8) (2020-09-28)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.8-0"></a>
|
||||
## [1.0.8-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.61...@ali/lowcode-plugin-outline-pane@1.0.8-0) (2020-09-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 合并master分支 ([bd2c6ad](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/bd2c6ad))
|
||||
* 兼容modal模式 ([1092ee9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/1092ee9))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.7-0"></a>
|
||||
## [1.0.7-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.6-0...@ali/lowcode-plugin-outline-pane@1.0.7-0) (2020-09-02)
|
||||
|
||||
|
||||
<a name="0.8.61"></a>
|
||||
## [0.8.61](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.60...@ali/lowcode-plugin-outline-pane@0.8.61) (2020-09-08)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.60"></a>
|
||||
## [0.8.60](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.59...@ali/lowcode-plugin-outline-pane@0.8.60) (2020-09-03)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.59"></a>
|
||||
## [0.8.59](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.58...@ali/lowcode-plugin-outline-pane@0.8.59) (2020-09-03)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.58"></a>
|
||||
## [0.8.58](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.57...@ali/lowcode-plugin-outline-pane@0.8.58) (2020-08-27)
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.6-0"></a>
|
||||
## [1.0.6-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.58...@ali/lowcode-plugin-outline-pane@1.0.6-0) (2020-09-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 合并master分支 ([bd2c6ad](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/bd2c6ad))
|
||||
* 兼容modal模式 ([1092ee9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/1092ee9))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.5-0"></a>
|
||||
## [1.0.5-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.4-0...@ali/lowcode-plugin-outline-pane@1.0.5-0) (2020-08-20)
|
||||
<a name="0.8.54"></a>
|
||||
## [0.8.54](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.53...@ali/lowcode-plugin-outline-pane@0.8.54) (2020-08-20)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.4-0"></a>
|
||||
## [1.0.4-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.3-0...@ali/lowcode-plugin-outline-pane@1.0.4-0) (2020-08-20)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.3-0"></a>
|
||||
## [1.0.3-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.2-0...@ali/lowcode-plugin-outline-pane@1.0.3-0) (2020-08-20)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.2-0"></a>
|
||||
## [1.0.2-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.1-0...@ali/lowcode-plugin-outline-pane@1.0.2-0) (2020-08-20)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.1-0"></a>
|
||||
## [1.0.1-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.53...@ali/lowcode-plugin-outline-pane@1.0.1-0) (2020-08-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 兼容modal模式 ([1092ee9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/1092ee9))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.0"></a>
|
||||
# [1.0.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.13.0...@ali/lowcode-plugin-outline-pane@1.0.0) (2020-08-17)
|
||||
<a name="0.8.53"></a>
|
||||
## [0.8.53](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.52...@ali/lowcode-plugin-outline-pane@0.8.53) (2020-08-19)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.52"></a>
|
||||
## [0.8.52](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.51...@ali/lowcode-plugin-outline-pane@0.8.52) (2020-08-19)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.51"></a>
|
||||
## [0.8.51](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.50...@ali/lowcode-plugin-outline-pane@0.8.51) (2020-08-19)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.50"></a>
|
||||
## [0.8.50](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.49...@ali/lowcode-plugin-outline-pane@0.8.50) (2020-08-17)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.49"></a>
|
||||
## [0.8.49](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.48...@ali/lowcode-plugin-outline-pane@0.8.49) (2020-08-14)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.13.0"></a>
|
||||
# [0.13.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.12.0...@ali/lowcode-plugin-outline-pane@0.13.0) (2020-08-17)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.12.0"></a>
|
||||
# [0.12.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.10.0...@ali/lowcode-plugin-outline-pane@0.12.0) (2020-08-17)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.11.0"></a>
|
||||
# [0.11.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.10.0...@ali/lowcode-plugin-outline-pane@0.11.0) (2020-08-17)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.10.0"></a>
|
||||
# [0.10.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.9.0...@ali/lowcode-plugin-outline-pane@0.10.0) (2020-08-16)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.9.0"></a>
|
||||
# [0.9.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.43...@ali/lowcode-plugin-outline-pane@0.9.0) (2020-08-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 兼容modal模式 ([1092ee9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/1092ee9))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.43"></a>
|
||||
## [0.8.43](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.42...@ali/lowcode-plugin-outline-pane@0.8.43) (2020-08-04)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.42"></a>
|
||||
## [0.8.42](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.40...@ali/lowcode-plugin-outline-pane@0.8.42) (2020-08-04)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.41"></a>
|
||||
## [0.8.41](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.40...@ali/lowcode-plugin-outline-pane@0.8.41) (2020-08-04)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.40"></a>
|
||||
## [0.8.40](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.39...@ali/lowcode-plugin-outline-pane@0.8.40) (2020-07-29)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.39"></a>
|
||||
## [0.8.39](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.38...@ali/lowcode-plugin-outline-pane@0.8.39) (2020-07-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 getPrototype is undefined ([95b3409](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/95b3409))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.38"></a>
|
||||
## [0.8.38](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.37...@ali/lowcode-plugin-outline-pane@0.8.38) (2020-07-23)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.37"></a>
|
||||
## [0.8.37](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.36...@ali/lowcode-plugin-outline-pane@0.8.37) (2020-07-22)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.36"></a>
|
||||
## [0.8.36](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.35...@ali/lowcode-plugin-outline-pane@0.8.36) (2020-07-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复导入的组件拖入画布报错 ([caf9915](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/caf9915))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.35"></a>
|
||||
## [0.8.35](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.34...@ali/lowcode-plugin-outline-pane@0.8.35) (2020-07-21)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.34"></a>
|
||||
## [0.8.34](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.33...@ali/lowcode-plugin-outline-pane@0.8.34) (2020-07-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* modal node locate ([9a72dd7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/9a72dd7))
|
||||
* 大纲树节点显示隐藏埋点 ([e91ab1f](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/e91ab1f))
|
||||
* 没有 modal node 时不显示模态视图 ([555824c](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/555824c))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 大纲树展开折叠埋点 ([d9828f2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/d9828f2))
|
||||
* 大纲树支持模态视图 ([3785e1c](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3785e1c))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.33"></a>
|
||||
## [0.8.33](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.32...@ali/lowcode-plugin-outline-pane@0.8.33) (2020-07-14)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.32"></a>
|
||||
## [0.8.32](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.31...@ali/lowcode-plugin-outline-pane@0.8.32) (2020-07-13)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.31"></a>
|
||||
## [0.8.31](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.30...@ali/lowcode-plugin-outline-pane@0.8.31) (2020-07-12)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.30"></a>
|
||||
## [0.8.30](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.29...@ali/lowcode-plugin-outline-pane@0.8.30) (2020-07-12)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.29"></a>
|
||||
## [0.8.29](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.27...@ali/lowcode-plugin-outline-pane@0.8.29) (2020-06-23)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.27"></a>
|
||||
## [0.8.27](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.26...@ali/lowcode-plugin-outline-pane@0.8.27) (2020-06-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 大纲树埋点 ([fa24821](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/fa24821))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.26"></a>
|
||||
## [0.8.26](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.25...@ali/lowcode-plugin-outline-pane@0.8.26) (2020-06-16)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.25"></a>
|
||||
## [0.8.25](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.24...@ali/lowcode-plugin-outline-pane@0.8.25) (2020-06-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.24"></a>
|
||||
## [0.8.24](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.23...@ali/lowcode-plugin-outline-pane@0.8.24) (2020-05-20)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.23"></a>
|
||||
## [0.8.23](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.22...@ali/lowcode-plugin-outline-pane@0.8.23) (2020-05-19)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.22"></a>
|
||||
## [0.8.22](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.21...@ali/lowcode-plugin-outline-pane@0.8.22) (2020-05-18)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.21"></a>
|
||||
## [0.8.21](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.20...@ali/lowcode-plugin-outline-pane@0.8.21) (2020-05-16)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.20"></a>
|
||||
## [0.8.20](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.19...@ali/lowcode-plugin-outline-pane@0.8.20) (2020-05-16)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.19"></a>
|
||||
## [0.8.19](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.18...@ali/lowcode-plugin-outline-pane@0.8.19) (2020-05-16)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.18"></a>
|
||||
## [0.8.18](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.17...@ali/lowcode-plugin-outline-pane@0.8.18) (2020-05-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.17"></a>
|
||||
## [0.8.17](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.16...@ali/lowcode-plugin-outline-pane@0.8.17) (2020-05-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.16"></a>
|
||||
## [0.8.16](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.15...@ali/lowcode-plugin-outline-pane@0.8.16) (2020-05-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.15"></a>
|
||||
## [0.8.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.14...@ali/lowcode-plugin-outline-pane@0.8.15) (2020-05-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* show value state ([bd49e50](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/bd49e50))
|
||||
* support plaintext liveediting ([ea62f12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/ea62f12))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.14"></a>
|
||||
## [0.8.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.13...@ali/lowcode-plugin-outline-pane@0.8.14) (2020-05-08)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.13"></a>
|
||||
## [0.8.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.12...@ali/lowcode-plugin-outline-pane@0.8.13) (2020-05-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* intl ([8a061ab](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/8a061ab))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.12"></a>
|
||||
## [0.8.12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.11...@ali/lowcode-plugin-outline-pane@0.8.12) (2020-04-27)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.11"></a>
|
||||
## [0.8.11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.10...@ali/lowcode-plugin-outline-pane@0.8.11) (2020-04-27)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.10"></a>
|
||||
## [0.8.10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.9...@ali/lowcode-plugin-outline-pane@0.8.10) (2020-04-27)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.9"></a>
|
||||
## [0.8.9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.8...@ali/lowcode-plugin-outline-pane@0.8.9) (2020-04-16)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.8"></a>
|
||||
## [0.8.8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.7...@ali/lowcode-plugin-outline-pane@0.8.8) (2020-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* plugin-designer ([2dfbcd4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2dfbcd4))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.7"></a>
|
||||
## [0.8.7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.6...@ali/lowcode-plugin-outline-pane@0.8.7) (2020-03-31)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.6"></a>
|
||||
## [0.8.6](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.5...@ali/lowcode-plugin-outline-pane@0.8.6) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.5"></a>
|
||||
## [0.8.5](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.4...@ali/lowcode-plugin-outline-pane@0.8.5) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.4"></a>
|
||||
## [0.8.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@0.8.3...@ali/lowcode-plugin-outline-pane@0.8.4) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="0.8.3"></a>
|
||||
## 0.8.3 (2020-03-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* double outline & ZH_EN support ([b379bd7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b379bd7))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.2"></a>
|
||||
## 0.8.2 (2020-03-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* double outline & ZH_EN support ([b379bd7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b379bd7))
|
||||
1
packages/plugin-outline-pane/README.md
Normal file
1
packages/plugin-outline-pane/README.md
Normal file
@ -0,0 +1 @@
|
||||
大纲面板
|
||||
9
packages/plugin-outline-pane/build.json
Normal file
9
packages/plugin-outline-pane/build.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"plugins": [
|
||||
"build-plugin-component",
|
||||
"build-plugin-fusion",
|
||||
["build-plugin-moment-locales", {
|
||||
"locales": ["zh-cn"]
|
||||
}]
|
||||
]
|
||||
}
|
||||
50
packages/plugin-outline-pane/package.json
Normal file
50
packages/plugin-outline-pane/package.json
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "@ali/lowcode-plugin-outline-pane",
|
||||
"version": "1.0.21",
|
||||
"description": "Outline pane for Ali lowCode engine",
|
||||
"files": [
|
||||
"es",
|
||||
"lib"
|
||||
],
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"scripts": {
|
||||
"build": "build-scripts build --skip-demo",
|
||||
"test": "ava",
|
||||
"test:snapshot": "ava --update-snapshots"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ali/lowcode-designer": "^1.0.22",
|
||||
"@ali/lowcode-editor-core": "^1.0.22",
|
||||
"@ali/lowcode-types": "^1.0.20",
|
||||
"@ali/lowcode-utils": "^1.0.21",
|
||||
"@alifd/next": "^1.19.16",
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16",
|
||||
"react-dom": "^16.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.18",
|
||||
"@types/classnames": "^2.2.7",
|
||||
"@types/node": "^13.7.1",
|
||||
"@types/react": "^16",
|
||||
"@types/react-dom": "^16",
|
||||
"build-plugin-component": "^0.2.10",
|
||||
"build-plugin-fusion": "^0.1.1",
|
||||
"build-plugin-moment-locales": "^0.1.0"
|
||||
},
|
||||
"ava": {
|
||||
"compileEnhancements": false,
|
||||
"snapshotDir": "test/fixtures/__snapshots__",
|
||||
"extensions": [
|
||||
"ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register"
|
||||
]
|
||||
},
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npm.alibaba-inc.com"
|
||||
}
|
||||
}
|
||||
1
packages/plugin-outline-pane/src/README.md
Normal file
1
packages/plugin-outline-pane/src/README.md
Normal file
@ -0,0 +1 @@
|
||||
大纲树
|
||||
55
packages/plugin-outline-pane/src/helper/dwell-timer.ts
Normal file
55
packages/plugin-outline-pane/src/helper/dwell-timer.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { ParentalNode, DropLocation, isLocationChildrenDetail, LocateEvent } from '@ali/lowcode-designer';
|
||||
|
||||
/**
|
||||
* 停留检查计时器
|
||||
*/
|
||||
export default class DwellTimer {
|
||||
private timer: number | undefined;
|
||||
|
||||
private previous?: ParentalNode;
|
||||
|
||||
private event?: LocateEvent;
|
||||
|
||||
private decide: (node: ParentalNode, event: LocateEvent) => void;
|
||||
|
||||
private timeout = 500;
|
||||
|
||||
constructor(decide: (node: ParentalNode, event: LocateEvent) => void, timeout = 500) {
|
||||
this.decide = decide;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
focus(node: ParentalNode, event: LocateEvent) {
|
||||
this.event = event;
|
||||
if (this.previous === node) {
|
||||
return;
|
||||
}
|
||||
this.reset();
|
||||
this.previous = node;
|
||||
this.timer = setTimeout(() => {
|
||||
this.previous && this.decide(this.previous, this.event!);
|
||||
this.reset();
|
||||
}, this.timeout) as any;
|
||||
}
|
||||
|
||||
tryFocus(loc?: DropLocation | null) {
|
||||
if (!loc || !isLocationChildrenDetail(loc.detail)) {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
if (loc.detail.focus?.type === 'node') {
|
||||
this.focus(loc.detail.focus.node, loc.event);
|
||||
} else {
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = undefined;
|
||||
}
|
||||
|
||||
this.previous = undefined;
|
||||
}
|
||||
}
|
||||
54
packages/plugin-outline-pane/src/helper/indent-track.ts
Normal file
54
packages/plugin-outline-pane/src/helper/indent-track.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { DropLocation, ParentalNode, isLocationChildrenDetail } from '@ali/lowcode-designer';
|
||||
|
||||
const IndentSensitive = 15;
|
||||
export class IndentTrack {
|
||||
private indentStart: number | null = null;
|
||||
|
||||
reset() {
|
||||
this.indentStart = null;
|
||||
}
|
||||
|
||||
getIndentParent(lastLoc: DropLocation, loc: DropLocation): [ParentalNode, number] | null {
|
||||
if (
|
||||
lastLoc.target !== loc.target ||
|
||||
!isLocationChildrenDetail(lastLoc.detail) ||
|
||||
!isLocationChildrenDetail(loc.detail) ||
|
||||
lastLoc.source !== loc.source ||
|
||||
lastLoc.detail.index !== loc.detail.index ||
|
||||
loc.detail.index == null
|
||||
) {
|
||||
this.indentStart = null;
|
||||
return null;
|
||||
}
|
||||
if (this.indentStart == null) {
|
||||
this.indentStart = lastLoc.event.globalX;
|
||||
}
|
||||
const delta = loc.event.globalX - this.indentStart;
|
||||
const indent = Math.floor(Math.abs(delta) / IndentSensitive);
|
||||
if (indent < 1) {
|
||||
return null;
|
||||
}
|
||||
this.indentStart = loc.event.globalX;
|
||||
const direction = delta < 0 ? 'left' : 'right';
|
||||
|
||||
let parent = loc.target;
|
||||
const { index } = loc.detail;
|
||||
|
||||
if (direction === 'left') {
|
||||
if (!parent.parent || index < parent.children.size || parent.isSlot()) {
|
||||
return null;
|
||||
}
|
||||
return [(parent as any).parent, parent.index + 1];
|
||||
} else {
|
||||
if (index === 0) {
|
||||
return null;
|
||||
}
|
||||
parent = parent.children.get(index - 1) as any;
|
||||
if (parent && parent.isContainer()) {
|
||||
return [parent, parent.children.size];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
11
packages/plugin-outline-pane/src/icons/arrow-right.tsx
Normal file
11
packages/plugin-outline-pane/src/icons/arrow-right.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconArrowRight(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M512.002047 771.904425c-10.152221 0.518816-20.442588-2.800789-28.202319-10.598382L77.902254 315.937602c-14.548344-14.618952-14.548344-38.318724 0-52.933583 14.544251-14.614859 38.118156-14.614859 52.662407 0l381.437385 418.531212L893.432269 263.004019c14.544251-14.614859 38.125319-14.614859 52.662407 0 14.552437 14.614859 14.552437 38.314631 0 52.933583L540.205389 761.307066C532.451798 769.103636 522.158361 772.424264 512.002047 771.904425z" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconArrowRight.displayName = 'IconArrowRight';
|
||||
11
packages/plugin-outline-pane/src/icons/cond.tsx
Normal file
11
packages/plugin-outline-pane/src/icons/cond.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconCond(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M479.552 276.544l296.896 2.752v75.712L960 249.024l-183.552-106.048v92.48h-271.36l-46.656-2.752-190.784 203.648 30.976 30.976 180.928-190.784z m296.896 484.928l-253.056-2.816-262.976-263.04H64v43.904h175.296l262.912 262.976 274.176 2.816v75.712L960 774.976l-183.616-105.984 0.064 92.48z" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconCond.displayName = 'IconCond';
|
||||
12
packages/plugin-outline-pane/src/icons/eye-close.tsx
Normal file
12
packages/plugin-outline-pane/src/icons/eye-close.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconEyeClose(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M512.7 700.9c-102.1 0-184.9-82.8-184.9-184.9 0-28.6 6.5-55.6 18-79.7l-93.7-93.7C138.9 418.1 65.2 514 65.2 514s200.4 260.7 447.6 260.7c50.2 0 98.6-10.8 143.6-27.9l-63.9-63.9c-24.2 11.5-51.2 18-79.8 18z" />
|
||||
<path d="M960.3 514S759.9 253.3 512.7 253.3c-49.5 0-97.2 10.5-141.7 27.2L243.5 153.1l-45.3 45.3 262.3 262.2c-13.1 13.3-21.2 31.5-21.2 51.6 0 40.6 32.9 73.4 73.4 73.4 20.1 0 38.4-8.1 51.6-21.2l260.9 260.8 45.3-45.3-95.6-95.6C887.2 609.1 960.3 514 960.3 514z m-376.7-20.9c-6.8-25.2-26.6-45.1-51.9-51.9L437.5 347c23-10.3 48.5-16 75.3-16 102.1 0 184.9 82.8 184.9 184.9 0 26.8-5.7 52.2-15.9 75.2l-98.2-98z" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconEyeClose.displayName = 'IconEyeClose';
|
||||
12
packages/plugin-outline-pane/src/icons/eye.tsx
Normal file
12
packages/plugin-outline-pane/src/icons/eye.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconEye(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M512 256c-163.8 0-291.4 97.6-448 256 134.8 135.4 248 256 448 256 199.8 0 346.8-152.8 448-253.2C856.4 397.2 709.6 256 512 256z m0 438.6c-98.8 0-179.2-82-179.2-182.6 0-100.8 80.4-182.6 179.2-182.6s179.2 82 179.2 182.6c0 100.8-80.4 182.6-179.2 182.6z" />
|
||||
<path d="M512 448c0-15.8 5.8-30.2 15.2-41.4-5-0.8-10-1.2-15.2-1.2-57.6 0-104.6 47.8-104.6 106.6s47 106.6 104.6 106.6 104.6-47.8 104.6-106.6c0-4.6-0.4-9.2-0.8-13.8-11 8.6-24.6 13.8-39.6 13.8-35.6 0-64.2-28.6-64.2-64z" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconEye.displayName = 'IconEye';
|
||||
11
packages/plugin-outline-pane/src/icons/lock.tsx
Normal file
11
packages/plugin-outline-pane/src/icons/lock.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconLock(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M832 464h-68V240c0-70.7-57.3-128-128-128H388c-70.7 0-128 57.3-128 128v224h-68c-17.7 0-32 14.3-32 32v384c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V496c0-17.7-14.3-32-32-32zM540 701v53c0 4.4-3.6 8-8 8h-40c-4.4 0-8-3.6-8-8v-53c-12.1-8.7-20-22.9-20-39 0-26.5 21.5-48 48-48s48 21.5 48 48c0 16.1-7.9 30.3-20 39z m152-237H332V240c0-30.9 25.1-56 56-56h248c30.9 0 56 25.1 56 56v224z" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconLock.displayName = 'IconLock';
|
||||
11
packages/plugin-outline-pane/src/icons/loop.tsx
Normal file
11
packages/plugin-outline-pane/src/icons/loop.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconLoop(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M60.235294 542.117647c0 132.879059 103.062588 240.941176 229.677176 240.941176l0 60.235294c-159.864471 0-289.912471-135.107765-289.912471-301.176471s130.048-301.176471 289.912471-301.176471l254.735059 0-99.147294-99.147294 42.586353-42.586353 171.911529 171.851294-171.851294 171.911529-42.646588-42.646588 99.207529-99.147294-254.795294 0c-126.614588 0-229.677176 108.062118-229.677176 240.941176zM734.087529 240.941176l0 60.235294c126.614588 0 229.677176 108.062118 229.677176 240.941176s-103.062588 240.941176-229.677176 240.941176l-254.795294 0 99.147294-99.147294-42.586353-42.586353-171.851294 171.851294 171.911529 171.911529 42.586353-42.586353-99.207529-99.207529 254.735059 0c159.924706 0 289.972706-135.107765 289.972706-301.176471s-130.048-301.176471-289.912471-301.176471z" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconLoop.displayName = 'IconLoop';
|
||||
12
packages/plugin-outline-pane/src/icons/outline.tsx
Normal file
12
packages/plugin-outline-pane/src/icons/outline.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconOutline(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M128 96h512a64 64 0 0 1 64 64v64a64 64 0 0 1-64 64H128a64 64 0 0 1-64-64V160a64 64 0 0 1 64-64z m32 64a32 32 0 1 0 0 64h448a32 32 0 0 0 0-64H160z m224 576h512a64 64 0 0 1 64 64v64a64 64 0 0 1-64 64H384a64 64 0 0 1-64-64v-64a64 64 0 0 1 64-64z m32 64a32 32 0 0 0 0 64h448a32 32 0 0 0 0-64H416z m-32-384h512a64 64 0 0 1 64 64v64a64 64 0 0 1-64 64H384a64 64 0 0 1-64-64v-64a64 64 0 0 1 64-64z m32 64a32 32 0 0 0 0 64h448a32 32 0 0 0 0-64H416z" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconOutline.displayName = 'IconOutline';
|
||||
|
||||
12
packages/plugin-outline-pane/src/icons/radio-active.tsx
Normal file
12
packages/plugin-outline-pane/src/icons/radio-active.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconRadioActive(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M512 1024A512 512 0 1 1 512 0a512 512 0 0 1 0 1024z m0-256a256 256 0 1 0 0-512 256 256 0 0 0 0 512z" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconRadioActive.displayName = 'IconRadioActive';
|
||||
|
||||
12
packages/plugin-outline-pane/src/icons/radio.tsx
Normal file
12
packages/plugin-outline-pane/src/icons/radio.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconRadio(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M512 1024A512 512 0 1 1 512 0a512 512 0 0 1 0 1024z m0-64A448 448 0 1 0 512 64a448 448 0 0 0 0 896z" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconRadio.displayName = 'IconRadio';
|
||||
|
||||
12
packages/plugin-outline-pane/src/icons/unlock.tsx
Normal file
12
packages/plugin-outline-pane/src/icons/unlock.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconUnlock(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M832 464H332V240c0-30.9 25.1-56 56-56h248c30.9 0 56 25.1 56 56v68c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-68c0-70.7-57.3-128-128-128H388c-70.7 0-128 57.3-128 128v224h-68c-17.7 0-32 14.3-32 32v384c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V496c0-17.7-14.3-32-32-32z m-40 376H232V536h560v304z" />
|
||||
<path d="M484 701v53c0 4.4 3.6 8 8 8h40c4.4 0 8-3.6 8-8v-53c12.1-8.7 20-22.9 20-39 0-26.5-21.5-48-48-48s-48 21.5-48 48c0 16.1 7.9 30.3 20 39z" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconUnlock.displayName = 'IconUnlock';
|
||||
16
packages/plugin-outline-pane/src/index.ts
Normal file
16
packages/plugin-outline-pane/src/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { OutlinePane } from './views/pane';
|
||||
import { OutlineBackupPane } from './views/backup-pane';
|
||||
import { IconOutline } from './icons/outline';
|
||||
import { intlNode } from './locale';
|
||||
import { getTreeMaster } from './tree-master';
|
||||
|
||||
export default {
|
||||
name: 'outline-pane',
|
||||
props: {
|
||||
icon: IconOutline,
|
||||
description: intlNode('Outline Tree'),
|
||||
},
|
||||
content: OutlinePane,
|
||||
};
|
||||
|
||||
export { OutlinePane, OutlineBackupPane, getTreeMaster };
|
||||
14
packages/plugin-outline-pane/src/locale/en-US.json
Normal file
14
packages/plugin-outline-pane/src/locale/en-US.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"Initializing": "Initializing",
|
||||
"Hide": "Hide",
|
||||
"Show": "Show",
|
||||
"Lock": "Lock",
|
||||
"Unlock": "Unlock",
|
||||
"Expand": "Expand",
|
||||
"Collapse": "Collapse",
|
||||
"Conditional": "Condition",
|
||||
"Loop": "Loop",
|
||||
"Slots": "Slots",
|
||||
"Slot for {prop}": "Slot for {prop}",
|
||||
"Outline Tree": "Outline Tree"
|
||||
}
|
||||
10
packages/plugin-outline-pane/src/locale/index.ts
Normal file
10
packages/plugin-outline-pane/src/locale/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { createIntl } from '@ali/lowcode-editor-core';
|
||||
import en_US from './en-US.json';
|
||||
import zh_CN from './zh-CN.json';
|
||||
|
||||
const { intl, intlNode, getLocale, setLocale } = createIntl({
|
||||
'en-US': en_US,
|
||||
'zh-CN': zh_CN,
|
||||
});
|
||||
|
||||
export { intl, intlNode, getLocale, setLocale };
|
||||
14
packages/plugin-outline-pane/src/locale/zh-CN.json
Normal file
14
packages/plugin-outline-pane/src/locale/zh-CN.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"Initializing": "正在初始化",
|
||||
"Hide": "隐藏",
|
||||
"Show": "显示",
|
||||
"Lock": "锁定",
|
||||
"Unlock": "解锁",
|
||||
"Expand": "展开",
|
||||
"Collapse": "收起",
|
||||
"Conditional": "条件式",
|
||||
"Loop": "循环",
|
||||
"Slots": "插槽",
|
||||
"Slot for {prop}": "属性 {prop} 的插槽",
|
||||
"Outline Tree": "大纲树"
|
||||
}
|
||||
667
packages/plugin-outline-pane/src/main.ts
Normal file
667
packages/plugin-outline-pane/src/main.ts
Normal file
@ -0,0 +1,667 @@
|
||||
import { computed, obx } from '@ali/lowcode-editor-core';
|
||||
import {
|
||||
Designer,
|
||||
ISensor,
|
||||
LocateEvent,
|
||||
isDragNodeObject,
|
||||
isDragAnyObject,
|
||||
DragObject,
|
||||
Scroller,
|
||||
ScrollTarget,
|
||||
IScrollable,
|
||||
DropLocation,
|
||||
isLocationChildrenDetail,
|
||||
LocationChildrenDetail,
|
||||
LocationDetailType,
|
||||
ParentalNode,
|
||||
contains,
|
||||
Node,
|
||||
} from '@ali/lowcode-designer';
|
||||
import TreeNode from './tree-node';
|
||||
import { IndentTrack } from './helper/indent-track';
|
||||
import DwellTimer from './helper/dwell-timer';
|
||||
import { uniqueId } from '@ali/lowcode-utils';
|
||||
import { Backup } from './views/backup-pane';
|
||||
import { IEditor } from '@ali/lowcode-types';
|
||||
import { ITreeBoard, TreeMaster, getTreeMaster } from './tree-master';
|
||||
|
||||
export class OutlineMain implements ISensor, ITreeBoard, IScrollable {
|
||||
private _designer?: Designer;
|
||||
|
||||
@obx.ref private _master?: TreeMaster;
|
||||
|
||||
get master() {
|
||||
return this._master;
|
||||
}
|
||||
|
||||
@computed get currentTree() {
|
||||
return this._master?.currentTree;
|
||||
}
|
||||
|
||||
readonly id = uniqueId('outline');
|
||||
|
||||
@obx.ref _visible = false;
|
||||
|
||||
get visible() {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
readonly editor: IEditor;
|
||||
|
||||
readonly at: string | symbol;
|
||||
|
||||
constructor(editor: IEditor, at: string | symbol) {
|
||||
this.editor = editor;
|
||||
this.at = at;
|
||||
let inited = false;
|
||||
const setup = async () => {
|
||||
if (inited) {
|
||||
return false;
|
||||
}
|
||||
inited = true;
|
||||
const designer = await editor.onceGot(Designer);
|
||||
this.setupDesigner(designer);
|
||||
};
|
||||
|
||||
if (at === Backup) {
|
||||
setup();
|
||||
} else {
|
||||
editor.on('skeleton.panel.show', (key: string) => {
|
||||
if (key === at) {
|
||||
setup();
|
||||
this._visible = true;
|
||||
}
|
||||
});
|
||||
editor.on('skeleton.panel.hide', (key: string) => {
|
||||
if (key === at) {
|
||||
this._visible = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ISensor
|
||||
*/
|
||||
fixEvent(e: LocateEvent): LocateEvent {
|
||||
if (e.fixed) {
|
||||
return e;
|
||||
}
|
||||
|
||||
const notMyEvent = e.originalEvent.view?.document !== document;
|
||||
|
||||
if (!e.target || notMyEvent) {
|
||||
e.target = document.elementFromPoint(e.canvasX!, e.canvasY!);
|
||||
}
|
||||
|
||||
// documentModel : 目标文档
|
||||
e.documentModel = this._designer?.currentDocument;
|
||||
|
||||
// 事件已订正
|
||||
e.fixed = true;
|
||||
return e;
|
||||
}
|
||||
|
||||
private indentTrack = new IndentTrack();
|
||||
|
||||
private dwell = new DwellTimer((target, event) => {
|
||||
const { document } = target;
|
||||
const { designer } = document;
|
||||
let index: any;
|
||||
let focus: any;
|
||||
let valid = true;
|
||||
if (target.hasSlots()) {
|
||||
index = null;
|
||||
focus = { type: 'slots' };
|
||||
} else {
|
||||
index = 0;
|
||||
valid = document.checkNesting(target, event.dragObject as any);
|
||||
}
|
||||
designer.createLocation({
|
||||
target,
|
||||
source: this.id,
|
||||
event,
|
||||
detail: {
|
||||
type: LocationDetailType.Children,
|
||||
index,
|
||||
focus,
|
||||
valid,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @see ISensor
|
||||
*/
|
||||
locate(e: LocateEvent): DropLocation | undefined | null {
|
||||
this.sensing = true;
|
||||
this.scroller?.scrolling(e);
|
||||
const { globalY, dragObject } = e;
|
||||
const { nodes } = dragObject;
|
||||
|
||||
const tree = this._master?.currentTree;
|
||||
if (!tree || !this._shell) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const operationalNodes = nodes?.filter((node: any) => {
|
||||
const onMoveHook = node.componentMeta?.getMetadata()?.experimental?.callbacks?.onMoveHook;
|
||||
const canMove = onMoveHook && typeof onMoveHook === 'function' ? onMoveHook() : true;
|
||||
|
||||
return canMove;
|
||||
});
|
||||
|
||||
if (!operationalNodes || operationalNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { document } = tree;
|
||||
const { designer } = document;
|
||||
const pos = getPosFromEvent(e, this._shell);
|
||||
const irect = this.getInsertionRect();
|
||||
const originLoc = document.dropLocation;
|
||||
|
||||
const componentMeta = e.dragObject.nodes ? e.dragObject.nodes[0].componentMeta : null;
|
||||
if (e.dragObject.type === 'node' && componentMeta && componentMeta.isModal) {
|
||||
return designer.createLocation({
|
||||
target: document.rootNode,
|
||||
detail: {
|
||||
type: LocationDetailType.Children,
|
||||
index: 0,
|
||||
valid: true,
|
||||
},
|
||||
source: this.id,
|
||||
event: e,
|
||||
});
|
||||
}
|
||||
|
||||
if (originLoc && ((pos && pos === 'unchanged') || (irect && globalY >= irect.top && globalY <= irect.bottom))) {
|
||||
const loc = originLoc.clone(e);
|
||||
const indented = this.indentTrack.getIndentParent(originLoc, loc);
|
||||
if (indented) {
|
||||
const [parent, index] = indented;
|
||||
if (checkRecursion(parent, dragObject)) {
|
||||
if (tree.getTreeNode(parent).expanded) {
|
||||
this.dwell.reset();
|
||||
return designer.createLocation({
|
||||
target: parent,
|
||||
source: this.id,
|
||||
event: e,
|
||||
detail: {
|
||||
type: LocationDetailType.Children,
|
||||
index,
|
||||
valid: document.checkNesting(parent, e.dragObject as any),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
(originLoc.detail as LocationChildrenDetail).focus = {
|
||||
type: 'node',
|
||||
node: parent,
|
||||
};
|
||||
// focus try expand go on
|
||||
this.dwell.focus(parent, e);
|
||||
} else {
|
||||
this.dwell.reset();
|
||||
}
|
||||
// FIXME: recreate new location
|
||||
} else if ((originLoc.detail as LocationChildrenDetail).near) {
|
||||
(originLoc.detail as LocationChildrenDetail).near = undefined;
|
||||
this.dwell.reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.indentTrack.reset();
|
||||
|
||||
if (pos && pos !== 'unchanged') {
|
||||
let treeNode = tree.getTreeNodeById(pos.nodeId);
|
||||
if (treeNode) {
|
||||
let { focusSlots } = pos;
|
||||
let { node } = treeNode;
|
||||
if (isDragNodeObject(dragObject)) {
|
||||
const newNodes = operationalNodes;
|
||||
let i = newNodes.length;
|
||||
let p: any = node;
|
||||
while (i-- > 0) {
|
||||
if (contains(newNodes[i], p)) {
|
||||
p = newNodes[i].parent;
|
||||
}
|
||||
}
|
||||
if (p !== node) {
|
||||
node = p || document.rootNode;
|
||||
treeNode = tree.getTreeNode(node);
|
||||
focusSlots = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (focusSlots) {
|
||||
this.dwell.reset();
|
||||
return designer.createLocation({
|
||||
target: node as ParentalNode,
|
||||
source: this.id,
|
||||
event: e,
|
||||
detail: {
|
||||
type: LocationDetailType.Children,
|
||||
index: null,
|
||||
valid: false,
|
||||
focus: { type: 'slots' },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!treeNode.isRoot()) {
|
||||
const loc = this.getNear(treeNode, e);
|
||||
this.dwell.tryFocus(loc);
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const loc = this.drillLocate(tree.root, e);
|
||||
this.dwell.tryFocus(loc);
|
||||
return loc;
|
||||
}
|
||||
|
||||
private getNear(treeNode: TreeNode, e: LocateEvent, index?: number, rect?: DOMRect) {
|
||||
const { document } = treeNode.tree;
|
||||
const { designer } = document;
|
||||
const { globalY, dragObject } = e;
|
||||
// TODO: check dragObject is anyData
|
||||
const { node, expanded } = treeNode;
|
||||
if (!rect) {
|
||||
rect = this.getTreeNodeRect(treeNode);
|
||||
if (!rect) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (index == null) {
|
||||
index = node.index;
|
||||
}
|
||||
|
||||
if (node.isSlot()) {
|
||||
// 是个插槽根节点
|
||||
if (!treeNode.isContainer() && !treeNode.hasSlots()) {
|
||||
return designer.createLocation({
|
||||
target: node.parent!,
|
||||
source: this.id,
|
||||
event: e,
|
||||
detail: {
|
||||
type: LocationDetailType.Children,
|
||||
index: null,
|
||||
near: { node, pos: 'replace' },
|
||||
valid: true, // TODO: future validation the slot limit
|
||||
},
|
||||
});
|
||||
}
|
||||
const loc1 = this.drillLocate(treeNode, e);
|
||||
if (loc1) {
|
||||
return loc1;
|
||||
}
|
||||
|
||||
return designer.createLocation({
|
||||
target: node.parent!,
|
||||
source: this.id,
|
||||
event: e,
|
||||
detail: {
|
||||
type: LocationDetailType.Children,
|
||||
index: null,
|
||||
valid: false,
|
||||
focus: { type: 'slots' },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let focusNode: Node | undefined;
|
||||
// focus
|
||||
if (!expanded && (treeNode.isContainer() || treeNode.hasSlots())) {
|
||||
focusNode = node;
|
||||
}
|
||||
|
||||
// before
|
||||
const titleRect = this.getTreeTitleRect(treeNode) || rect;
|
||||
if (globalY < titleRect.top + titleRect.height / 2) {
|
||||
return designer.createLocation({
|
||||
target: node.parent!,
|
||||
source: this.id,
|
||||
event: e,
|
||||
detail: {
|
||||
type: LocationDetailType.Children,
|
||||
index,
|
||||
valid: document.checkNesting(node.parent!, dragObject as any),
|
||||
near: { node, pos: 'before' },
|
||||
focus: checkRecursion(focusNode, dragObject) ? { type: 'node', node: focusNode } : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (globalY > titleRect.bottom) {
|
||||
focusNode = undefined;
|
||||
}
|
||||
|
||||
if (expanded) {
|
||||
// drill
|
||||
const loc = this.drillLocate(treeNode, e);
|
||||
if (loc) {
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
// after
|
||||
return designer.createLocation({
|
||||
target: node.parent!,
|
||||
source: this.id,
|
||||
event: e,
|
||||
detail: {
|
||||
type: LocationDetailType.Children,
|
||||
index: index + 1,
|
||||
valid: document.checkNesting(node.parent!, dragObject as any),
|
||||
near: { node, pos: 'after' },
|
||||
focus: checkRecursion(focusNode, dragObject) ? { type: 'node', node: focusNode } : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private drillLocate(treeNode: TreeNode, e: LocateEvent): DropLocation | null {
|
||||
const { document } = treeNode.tree;
|
||||
const { designer } = document;
|
||||
const { dragObject, globalY } = e;
|
||||
|
||||
if (!checkRecursion(treeNode.node, dragObject)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isDragAnyObject(dragObject)) {
|
||||
// TODO: future
|
||||
return null;
|
||||
}
|
||||
|
||||
const container = treeNode.node as ParentalNode;
|
||||
const detail: LocationChildrenDetail = {
|
||||
type: LocationDetailType.Children,
|
||||
};
|
||||
const locationData: any = {
|
||||
target: container,
|
||||
detail,
|
||||
source: this.id,
|
||||
event: e,
|
||||
};
|
||||
const isSlotContainer = treeNode.hasSlots();
|
||||
const isContainer = treeNode.isContainer();
|
||||
|
||||
if (container.isSlot() && !treeNode.expanded) {
|
||||
// 未展开,直接定位到内部第一个节点
|
||||
if (isSlotContainer) {
|
||||
detail.index = null;
|
||||
detail.focus = { type: 'slots' };
|
||||
detail.valid = false;
|
||||
} else {
|
||||
detail.index = 0;
|
||||
detail.valid = document.checkNesting(container, dragObject);
|
||||
}
|
||||
}
|
||||
|
||||
let items: TreeNode[] | null = null;
|
||||
let slotsRect: DOMRect | undefined;
|
||||
let focusSlots = false;
|
||||
// isSlotContainer
|
||||
if (isSlotContainer) {
|
||||
slotsRect = this.getTreeSlotsRect(treeNode);
|
||||
if (slotsRect) {
|
||||
if (globalY <= slotsRect.bottom) {
|
||||
focusSlots = true;
|
||||
items = treeNode.slots;
|
||||
} else if (!isContainer) {
|
||||
// 不在 slots 范围,又不是 container 的情况,高亮 slots 区
|
||||
detail.index = null;
|
||||
detail.focus = { type: 'slots' };
|
||||
detail.valid = false;
|
||||
return designer.createLocation(locationData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!items && isContainer) {
|
||||
items = treeNode.children;
|
||||
}
|
||||
|
||||
if (!items) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const l = items.length;
|
||||
let index = 0;
|
||||
let before = l < 1;
|
||||
let current: TreeNode | undefined;
|
||||
let currentIndex = index;
|
||||
for (; index < l; index++) {
|
||||
current = items[index];
|
||||
currentIndex = index;
|
||||
const rect = this.getTreeNodeRect(current);
|
||||
if (!rect) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// rect
|
||||
if (globalY < rect.top) {
|
||||
before = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (globalY > rect.bottom) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const loc = this.getNear(current, e, index, rect);
|
||||
if (loc) {
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
if (focusSlots) {
|
||||
detail.focus = { type: 'slots' };
|
||||
detail.valid = false;
|
||||
detail.index = null;
|
||||
} else {
|
||||
if (current) {
|
||||
detail.index = before ? currentIndex : currentIndex + 1;
|
||||
detail.near = { node: current.node, pos: before ? 'before' : 'after' };
|
||||
} else {
|
||||
detail.index = l;
|
||||
}
|
||||
detail.valid = document.checkNesting(container, dragObject);
|
||||
}
|
||||
|
||||
return designer.createLocation(locationData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ISensor
|
||||
*/
|
||||
isEnter(e: LocateEvent): boolean {
|
||||
if (!this._shell) {
|
||||
return false;
|
||||
}
|
||||
const rect = this._shell.getBoundingClientRect();
|
||||
return e.globalY >= rect.top && e.globalY <= rect.bottom && e.globalX >= rect.left && e.globalX <= rect.right;
|
||||
}
|
||||
|
||||
private tryScrollAgain: number | null = null;
|
||||
|
||||
/**
|
||||
* @see IScrollBoard
|
||||
*/
|
||||
scrollToNode(treeNode: TreeNode, detail?: any, tryTimes = 0) {
|
||||
if (tryTimes < 1 && this.tryScrollAgain) {
|
||||
(window as any).cancelIdleCallback(this.tryScrollAgain);
|
||||
this.tryScrollAgain = null;
|
||||
}
|
||||
if (this.sensing || !this.bounds || !this.scroller || !this.scrollTarget) {
|
||||
// is a active sensor
|
||||
return;
|
||||
}
|
||||
|
||||
let rect: ClientRect | undefined;
|
||||
if (detail && isLocationChildrenDetail(detail)) {
|
||||
rect = this.getInsertionRect();
|
||||
} else {
|
||||
rect = this.getTreeNodeRect(treeNode);
|
||||
}
|
||||
|
||||
if (!rect) {
|
||||
if (tryTimes < 3) {
|
||||
this.tryScrollAgain = (window as any).requestIdleCallback(() => this.scrollToNode(treeNode, detail, tryTimes + 1));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const { scrollHeight, top: scrollTop } = this.scrollTarget;
|
||||
const { height, top, bottom } = this.bounds;
|
||||
if (rect.top < top || rect.bottom > bottom) {
|
||||
const opt: any = {};
|
||||
opt.top = Math.min(rect.top + rect.height / 2 + scrollTop - top - height / 2, scrollHeight - height);
|
||||
if (rect.height >= height) {
|
||||
opt.top = Math.min(scrollTop + rect.top - top, opt.top);
|
||||
}
|
||||
this.scroller.scrollTo(opt);
|
||||
}
|
||||
// make tail scroll be sure
|
||||
if (tryTimes < 4) {
|
||||
this.tryScrollAgain = (window as any).requestIdleCallback(() => this.scrollToNode(treeNode, detail, 4));
|
||||
}
|
||||
}
|
||||
|
||||
private sensing = false;
|
||||
|
||||
/**
|
||||
* @see ISensor
|
||||
*/
|
||||
deactiveSensor() {
|
||||
this.sensing = false;
|
||||
this.scroller?.cancel();
|
||||
this.dwell.reset();
|
||||
this.indentTrack.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IScrollable
|
||||
*/
|
||||
get bounds(): DOMRect | null {
|
||||
if (!this._shell) {
|
||||
return null;
|
||||
}
|
||||
return this._shell.getBoundingClientRect();
|
||||
}
|
||||
|
||||
private _scrollTarget?: ScrollTarget;
|
||||
|
||||
/**
|
||||
* @see IScrollable
|
||||
*/
|
||||
get scrollTarget() {
|
||||
return this._scrollTarget;
|
||||
}
|
||||
|
||||
private scroller?: Scroller;
|
||||
|
||||
private setupDesigner(designer: Designer) {
|
||||
this._designer = designer;
|
||||
this._master = getTreeMaster(designer);
|
||||
this._master.addBoard(this);
|
||||
designer.dragon.addSensor(this);
|
||||
this.scroller = designer.createScroller(this);
|
||||
}
|
||||
|
||||
purge() {
|
||||
this._designer?.dragon.removeSensor(this);
|
||||
this._master?.removeBoard(this);
|
||||
// todo purge treeMaster if needed
|
||||
}
|
||||
|
||||
private _sensorAvailable = false;
|
||||
|
||||
/**
|
||||
* @see ISensor
|
||||
*/
|
||||
get sensorAvailable() {
|
||||
return this._sensorAvailable;
|
||||
}
|
||||
|
||||
private _shell: HTMLDivElement | null = null;
|
||||
|
||||
mount(shell: HTMLDivElement | null) {
|
||||
if (this._shell === shell) {
|
||||
return;
|
||||
}
|
||||
this._shell = shell;
|
||||
if (shell) {
|
||||
this._scrollTarget = new ScrollTarget(shell);
|
||||
this._sensorAvailable = true;
|
||||
} else {
|
||||
this._scrollTarget = undefined;
|
||||
this._sensorAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
private getInsertionRect(): DOMRect | undefined {
|
||||
if (!this._shell) {
|
||||
return undefined;
|
||||
}
|
||||
return this._shell.querySelector('.insertion')?.getBoundingClientRect();
|
||||
}
|
||||
|
||||
private getTreeNodeRect(treeNode: TreeNode): DOMRect | undefined {
|
||||
if (!this._shell) {
|
||||
return undefined;
|
||||
}
|
||||
return this._shell.querySelector(`.tree-node[data-id="${treeNode.id}"]`)?.getBoundingClientRect();
|
||||
}
|
||||
|
||||
private getTreeTitleRect(treeNode: TreeNode): DOMRect | undefined {
|
||||
if (!this._shell) {
|
||||
return undefined;
|
||||
}
|
||||
return this._shell.querySelector(`.tree-node-title[data-id="${treeNode.id}"]`)?.getBoundingClientRect();
|
||||
}
|
||||
|
||||
private getTreeSlotsRect(treeNode: TreeNode): DOMRect | undefined {
|
||||
if (!this._shell) {
|
||||
return undefined;
|
||||
}
|
||||
return this._shell.querySelector(`.tree-node-slots[data-id="${treeNode.id}"]`)?.getBoundingClientRect();
|
||||
}
|
||||
}
|
||||
|
||||
function checkRecursion(parent: Node | undefined | null, dragObject: DragObject): parent is ParentalNode {
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
if (isDragNodeObject(dragObject)) {
|
||||
const { nodes } = dragObject;
|
||||
if (nodes.some((node) => node.contains(parent))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function getPosFromEvent(
|
||||
{ target }: LocateEvent,
|
||||
stop: Element,
|
||||
): null | 'unchanged' | { nodeId: string; focusSlots: boolean } {
|
||||
if (!target || !stop.contains(target)) {
|
||||
return null;
|
||||
}
|
||||
if (target.matches('.insertion')) {
|
||||
return 'unchanged';
|
||||
}
|
||||
target = target.closest('[data-id]');
|
||||
if (!target || !stop.contains(target)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const nodeId = (target as HTMLDivElement).dataset.id!;
|
||||
return {
|
||||
focusSlots: target.matches('.tree-node-slots'),
|
||||
nodeId,
|
||||
};
|
||||
}
|
||||
122
packages/plugin-outline-pane/src/tree-master.ts
Normal file
122
packages/plugin-outline-pane/src/tree-master.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import { computed, obx } from '@ali/lowcode-editor-core';
|
||||
import { Designer, isLocationChildrenDetail } from '@ali/lowcode-designer';
|
||||
import TreeNode from './tree-node';
|
||||
import { Tree } from './tree';
|
||||
import { Backup } from './views/backup-pane';
|
||||
|
||||
export interface ITreeBoard {
|
||||
readonly visible: boolean;
|
||||
readonly at: string | symbol;
|
||||
scrollToNode(treeNode: TreeNode, detail?: any): void;
|
||||
}
|
||||
|
||||
export class TreeMaster {
|
||||
readonly designer: Designer;
|
||||
|
||||
constructor(designer: Designer) {
|
||||
this.designer = designer;
|
||||
let startTime: any;
|
||||
designer.dragon.onDragstart(() => {
|
||||
startTime = Date.now() / 1000;
|
||||
// needs?
|
||||
this.toVision();
|
||||
});
|
||||
designer.activeTracker.onChange(({ node, detail }) => {
|
||||
const tree = this.currentTree;
|
||||
if (!tree || node.document !== tree.document) {
|
||||
return;
|
||||
}
|
||||
|
||||
const treeNode = tree.getTreeNode(node);
|
||||
if (detail && isLocationChildrenDetail(detail)) {
|
||||
treeNode.expand(true);
|
||||
} else {
|
||||
treeNode.expandParents();
|
||||
}
|
||||
|
||||
this.boards.forEach((board) => {
|
||||
board.scrollToNode(treeNode, detail);
|
||||
});
|
||||
});
|
||||
designer.dragon.onDragend(() => {
|
||||
const endTime: any = Date.now() / 1000;
|
||||
const editor = designer?.editor;
|
||||
const nodes = designer.currentSelection?.getNodes();
|
||||
editor?.emit('outlinePane.drag', {
|
||||
selected: nodes
|
||||
?.map((n) => {
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
const npm = n?.componentMeta?.npm;
|
||||
return (
|
||||
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') || n?.componentMeta?.componentName
|
||||
);
|
||||
})
|
||||
.join('&'),
|
||||
time: (endTime - startTime).toFixed(2),
|
||||
});
|
||||
});
|
||||
designer.editor.on('designer.document.remove', ({ id }) => {
|
||||
this.treeMap.delete(id);
|
||||
});
|
||||
}
|
||||
|
||||
private toVision() {
|
||||
const tree = this.currentTree;
|
||||
if (tree) {
|
||||
tree.document.selection.getTopNodes().forEach((node) => {
|
||||
tree.getTreeNode(node).setExpanded(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@obx.val private boards = new Set<ITreeBoard>();
|
||||
|
||||
addBoard(board: ITreeBoard) {
|
||||
this.boards.add(board);
|
||||
}
|
||||
|
||||
removeBoard(board: ITreeBoard) {
|
||||
this.boards.delete(board);
|
||||
}
|
||||
|
||||
@computed hasVisibleTreeBoard() {
|
||||
for (const item of this.boards) {
|
||||
if (item.visible && item.at !== Backup) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
purge() {
|
||||
// todo others purge
|
||||
}
|
||||
|
||||
private treeMap = new Map<string, Tree>();
|
||||
|
||||
@computed get currentTree(): Tree | null {
|
||||
const doc = this.designer?.currentDocument;
|
||||
if (doc) {
|
||||
const { id } = doc;
|
||||
if (this.treeMap.has(id)) {
|
||||
return this.treeMap.get(id)!;
|
||||
}
|
||||
const tree = new Tree(doc);
|
||||
this.treeMap.set(id, tree);
|
||||
return tree;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const mastersMap = new Map<Designer, TreeMaster>();
|
||||
export function getTreeMaster(designer: Designer): TreeMaster {
|
||||
let master = mastersMap.get(designer);
|
||||
if (!master) {
|
||||
master = new TreeMaster(designer);
|
||||
mastersMap.set(designer, master);
|
||||
}
|
||||
return master;
|
||||
}
|
||||
235
packages/plugin-outline-pane/src/tree-node.ts
Normal file
235
packages/plugin-outline-pane/src/tree-node.ts
Normal file
@ -0,0 +1,235 @@
|
||||
import { TitleContent, isI18nData } from '@ali/lowcode-types';
|
||||
import { computed, obx, intl } from '@ali/lowcode-editor-core';
|
||||
import { Node, DocumentModel, isLocationChildrenDetail, LocationChildrenDetail, Designer } from '@ali/lowcode-designer';
|
||||
import { Tree } from './tree';
|
||||
|
||||
export default class TreeNode {
|
||||
get id(): string {
|
||||
return this.node.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可以展开
|
||||
*/
|
||||
@computed get expandable(): boolean {
|
||||
return this.hasChildren() || this.hasSlots() || this.dropDetail?.index != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入"线"位置信息
|
||||
*/
|
||||
@computed get dropDetail(): LocationChildrenDetail | undefined | null {
|
||||
const loc = this.node.document.dropLocation;
|
||||
return loc && this.isResponseDropping() && isLocationChildrenDetail(loc.detail) ? loc.detail : null;
|
||||
}
|
||||
|
||||
@computed get depth(): number {
|
||||
return this.node.zLevel;
|
||||
}
|
||||
|
||||
isRoot() {
|
||||
return this.tree.root === this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是响应投放区
|
||||
*/
|
||||
@computed isResponseDropping(): boolean {
|
||||
const loc = this.node.document.dropLocation;
|
||||
if (!loc) {
|
||||
return false;
|
||||
}
|
||||
return loc.target === this.node;
|
||||
}
|
||||
|
||||
@computed isFocusingNode(): boolean {
|
||||
const loc = this.node.document.dropLocation;
|
||||
if (!loc) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
isLocationChildrenDetail(loc.detail) && loc.detail.focus?.type === 'node' && loc.detail.focus.node === this.node
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认为折叠状态
|
||||
* 在初始化根节点时,设置为展开状态
|
||||
*/
|
||||
@obx.ref private _expanded = false;
|
||||
|
||||
get expanded(): boolean {
|
||||
return this.isRoot() || (this.expandable && this._expanded);
|
||||
}
|
||||
|
||||
setExpanded(value: boolean) {
|
||||
this._expanded = value;
|
||||
}
|
||||
|
||||
@computed get detecting() {
|
||||
return this.designer.detecting.current === this.node;
|
||||
}
|
||||
|
||||
@computed get hidden(): boolean {
|
||||
const cv = this.node.isConditionalVisible();
|
||||
if (cv == null) {
|
||||
return !this.node.getVisible();
|
||||
}
|
||||
return !cv;
|
||||
}
|
||||
|
||||
setHidden(flag: boolean) {
|
||||
if (this.node.conditionGroup) {
|
||||
return;
|
||||
}
|
||||
this.node.setVisible(!flag);
|
||||
}
|
||||
|
||||
@computed get locked(): boolean {
|
||||
return this.node.getExtraProp('locked', false)?.getValue() === true;
|
||||
}
|
||||
|
||||
setLocked(flag: boolean) {
|
||||
if (flag) {
|
||||
this.node.getExtraProp('locked', true)?.setValue(true);
|
||||
} else {
|
||||
this.node.getExtraProp('locked', false)?.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@computed get selected(): boolean {
|
||||
// TODO: check is dragging
|
||||
const { selection } = this.document;
|
||||
return selection.has(this.node.id);
|
||||
}
|
||||
|
||||
@computed get title(): TitleContent {
|
||||
return this.node.title;
|
||||
}
|
||||
|
||||
@computed get titleLabel() {
|
||||
let { title } = this;
|
||||
if (!title) {
|
||||
return '';
|
||||
}
|
||||
if ((title as any).label) {
|
||||
title = (title as any).label;
|
||||
}
|
||||
if (typeof title === 'string') {
|
||||
return title;
|
||||
}
|
||||
if (isI18nData(title)) {
|
||||
return intl(title) as string;
|
||||
}
|
||||
return this.node.componentName;
|
||||
}
|
||||
|
||||
setTitleLabel(label: string) {
|
||||
const origLabel = this.titleLabel;
|
||||
if (label === origLabel) {
|
||||
return;
|
||||
}
|
||||
if (label === '') {
|
||||
this.node.getExtraProp('title', false)?.remove();
|
||||
} else {
|
||||
this.node.getExtraProp('title', true)?.setValue(label);
|
||||
}
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return this.node.componentMeta.icon;
|
||||
}
|
||||
|
||||
@computed get parent() {
|
||||
const { parent } = this.node;
|
||||
if (parent) {
|
||||
return this.tree.getTreeNode(parent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@computed get slots(): TreeNode[] {
|
||||
// todo: shallowEqual
|
||||
return this.node.slots.map((node) => this.tree.getTreeNode(node));
|
||||
}
|
||||
|
||||
@computed get children(): TreeNode[] | null {
|
||||
return this.node.children?.map((node) => this.tree.getTreeNode(node)) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是容器,允许子节点拖入
|
||||
*/
|
||||
isContainer(): boolean {
|
||||
return this.node.isContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有"插槽"
|
||||
*/
|
||||
hasSlots(): boolean {
|
||||
return this.node.hasSlots();
|
||||
}
|
||||
|
||||
hasChildren(): boolean {
|
||||
return !!(this.isContainer() && this.node.children?.notEmpty());
|
||||
}
|
||||
|
||||
select(isMulti: boolean) {
|
||||
const { node } = this;
|
||||
|
||||
const { selection } = node.document;
|
||||
if (isMulti) {
|
||||
selection.add(node.id);
|
||||
} else {
|
||||
selection.select(node.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 展开节点,支持依次展开父节点
|
||||
*/
|
||||
expand(tryExpandParents = false) {
|
||||
// 这边不能直接使用 expanded,需要额外判断是否可以展开
|
||||
// 如果只使用 expanded,会漏掉不可以展开的情况,即在不可以展开的情况下,会触发展开
|
||||
if (this.expandable && !this._expanded) {
|
||||
this.setExpanded(true);
|
||||
}
|
||||
if (tryExpandParents) {
|
||||
this.expandParents();
|
||||
}
|
||||
}
|
||||
|
||||
expandParents() {
|
||||
let p = this.node.parent;
|
||||
while (p) {
|
||||
this.tree.getTreeNode(p).setExpanded(true);
|
||||
p = p.parent;
|
||||
}
|
||||
}
|
||||
|
||||
readonly designer: Designer;
|
||||
|
||||
readonly document: DocumentModel;
|
||||
|
||||
@obx.ref private _node: Node;
|
||||
|
||||
get node() {
|
||||
return this._node;
|
||||
}
|
||||
|
||||
readonly tree: Tree;
|
||||
|
||||
constructor(tree: Tree, node: Node) {
|
||||
this.tree = tree;
|
||||
this.document = node.document;
|
||||
this.designer = this.document.designer;
|
||||
this._node = node;
|
||||
}
|
||||
|
||||
setNode(node: Node) {
|
||||
if (this._node !== node) {
|
||||
this._node = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
packages/plugin-outline-pane/src/tree.ts
Normal file
34
packages/plugin-outline-pane/src/tree.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { DocumentModel, Node } from '@ali/lowcode-designer';
|
||||
import TreeNode from './tree-node';
|
||||
|
||||
export class Tree {
|
||||
private treeNodesMap = new Map<string, TreeNode>();
|
||||
|
||||
readonly root: TreeNode;
|
||||
|
||||
readonly id: string;
|
||||
|
||||
readonly document: DocumentModel;
|
||||
|
||||
constructor(document: DocumentModel) {
|
||||
this.document = document;
|
||||
this.root = this.getTreeNode(document.rootNode);
|
||||
this.id = document.id;
|
||||
}
|
||||
|
||||
getTreeNode(node: Node): TreeNode {
|
||||
if (this.treeNodesMap.has(node.id)) {
|
||||
const tnode = this.treeNodesMap.get(node.id)!;
|
||||
tnode.setNode(node);
|
||||
return tnode;
|
||||
}
|
||||
|
||||
const treeNode = new TreeNode(this, node);
|
||||
this.treeNodesMap.set(node.id, treeNode);
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
getTreeNodeById(id: string) {
|
||||
return this.treeNodesMap.get(id);
|
||||
}
|
||||
}
|
||||
18
packages/plugin-outline-pane/src/views/backup-pane.tsx
Normal file
18
packages/plugin-outline-pane/src/views/backup-pane.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { PureComponent } from 'react';
|
||||
import { PluginProps } from '@ali/lowcode-types';
|
||||
import { OutlinePane } from './pane';
|
||||
|
||||
export const Backup = Symbol.for('backup-outline');
|
||||
|
||||
export class OutlineBackupPane extends PureComponent<PluginProps> {
|
||||
render() {
|
||||
return (
|
||||
<OutlinePane
|
||||
editor={this.props.editor}
|
||||
config={{
|
||||
name: Backup,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
40
packages/plugin-outline-pane/src/views/pane.tsx
Normal file
40
packages/plugin-outline-pane/src/views/pane.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import React, { Component } from 'react';
|
||||
import { observer } from '@ali/lowcode-editor-core';
|
||||
import { intl } from '../locale';
|
||||
import { OutlineMain } from '../main';
|
||||
import TreeView from './tree';
|
||||
import './style.less';
|
||||
import { IEditor } from '@ali/lowcode-types';
|
||||
|
||||
@observer
|
||||
export class OutlinePane extends Component<{ config: any; editor: IEditor }> {
|
||||
private main = new OutlineMain(this.props.editor, this.props.config.name || this.props.config.pluginKey);
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.main.purge();
|
||||
}
|
||||
|
||||
render() {
|
||||
const tree = this.main.currentTree;
|
||||
|
||||
if (!tree) {
|
||||
return (
|
||||
<div className="lc-outline-pane">
|
||||
<p className="lc-outline-notice">{intl('Initializing')}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="lc-outline-pane">
|
||||
<div ref={(shell) => this.main.mount(shell)} className="lc-outline-tree-container">
|
||||
<TreeView key={tree.id} tree={tree} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
94
packages/plugin-outline-pane/src/views/root-tree-node.tsx
Normal file
94
packages/plugin-outline-pane/src/views/root-tree-node.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@ali/lowcode-editor-core';
|
||||
import TreeNode from '../tree-node';
|
||||
import TreeTitle from './tree-title';
|
||||
import TreeBranches from './tree-branches';
|
||||
import { ModalNodesManager } from '@ali/lowcode-designer';
|
||||
import { IconEyeClose } from '../icons/eye-close';
|
||||
|
||||
@observer
|
||||
class ModalTreeNodeView extends Component<{ treeNode: TreeNode }> {
|
||||
private modalNodesManager: ModalNodesManager;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
// 模态管理对象
|
||||
this.modalNodesManager = props.treeNode.document.modalNodesManager;
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
hideAllNodes() {
|
||||
this.modalNodesManager.hideModalNodes();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
const modalNodes = treeNode.children?.filter((item) => {
|
||||
return item.node.componentMeta.isModal;
|
||||
});
|
||||
if (!modalNodes || modalNodes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasVisibleModalNode = !!this.modalNodesManager.getVisibleModalNode();
|
||||
return (
|
||||
<div className="tree-node-modal">
|
||||
<div className="tree-node-modal-title">
|
||||
<span>模态视图层</span>
|
||||
<div
|
||||
className="tree-node-modal-title-visible-icon"
|
||||
onClick={this.hideAllNodes.bind(this)}
|
||||
>
|
||||
{hasVisibleModalNode ? <IconEyeClose /> : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="tree-pane-modal-content">
|
||||
<TreeBranches treeNode={treeNode} isModal />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class RootTreeNodeView extends Component<{ treeNode: TreeNode }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
const className = classNames('tree-node', {
|
||||
// 是否展开
|
||||
expanded: treeNode.expanded,
|
||||
// 是否悬停中
|
||||
detecting: treeNode.detecting,
|
||||
// 是否选中的
|
||||
selected: treeNode.selected,
|
||||
// 是否隐藏的
|
||||
hidden: treeNode.hidden,
|
||||
// 是否忽略的
|
||||
// ignored: treeNode.ignored,
|
||||
// 是否锁定的
|
||||
locked: treeNode.locked,
|
||||
// 是否投放响应
|
||||
dropping: treeNode.dropDetail?.index != null,
|
||||
'is-root': treeNode.isRoot(),
|
||||
'condition-flow': treeNode.node.conditionGroup != null,
|
||||
highlight: treeNode.isFocusingNode(),
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={className} data-id={treeNode.id}>
|
||||
<TreeTitle treeNode={treeNode} />
|
||||
<ModalTreeNodeView treeNode={treeNode} />
|
||||
<TreeBranches treeNode={treeNode} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
388
packages/plugin-outline-pane/src/views/style.less
Normal file
388
packages/plugin-outline-pane/src/views/style.less
Normal file
@ -0,0 +1,388 @@
|
||||
.lc-outline-pane {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
background-color: white;
|
||||
|
||||
> .lc-outline-tree-container {
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.lc-outline-tree {
|
||||
@treeNodeHeight: 30px;
|
||||
overflow: hidden;
|
||||
margin-bottom: @treeNodeHeight;
|
||||
user-select: none;
|
||||
|
||||
.tree-node-modal {
|
||||
margin: 5px;
|
||||
border: 1px solid rgba(31, 56, 88, 0.2);
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 4px 0 rgba(31, 56, 88, 0.15);
|
||||
|
||||
.tree-node-modal-title {
|
||||
position: relative;
|
||||
background: rgba(31, 56, 88, 0.04);
|
||||
padding: 0 10px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
border-bottom: 1px solid rgba(31, 56, 88, 0.2);
|
||||
|
||||
.tree-node-modal-title-visible-icon {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-pane-modal-content {
|
||||
& > .tree-node-branches::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-modal-radio, .tree-node-modal-radio-active {
|
||||
margin-right: 4px;
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
left: 6px;
|
||||
}
|
||||
.tree-node-modal-radio-active {
|
||||
color: #006cff;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-branches::before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
border-left: 1px solid transparent;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 6px;
|
||||
content: ' ';
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.tree-node-branches::before {
|
||||
border-left-color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
.insertion {
|
||||
pointer-events: all !important;
|
||||
border: 1px dashed var(--color-brand-light);
|
||||
height: @treeNodeHeight;
|
||||
box-sizing: border-box;
|
||||
transform: translateZ(0);
|
||||
transition: all 0.2s ease-in-out;
|
||||
&.invalid {
|
||||
border-color: red;
|
||||
background-color: rgba(240, 154, 154, 0.719);
|
||||
}
|
||||
}
|
||||
|
||||
.condition-group-container {
|
||||
border-bottom: 1px solid #7b605b;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
border-left: 0.5px solid #7b605b;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: ' ';
|
||||
z-index: 2;
|
||||
}
|
||||
>.condition-group-title {
|
||||
text-align: center;
|
||||
background-color: #7b605b;
|
||||
height: 14px;
|
||||
> .lc-title {
|
||||
font-size: 12px;
|
||||
transform: scale(0.8);
|
||||
transform-origin: top;
|
||||
color: white;
|
||||
text-shadow: 0px 0px 2px black;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tree-node-slots {
|
||||
border-bottom: 1px solid rgb(144, 94, 190);
|
||||
position: relative;
|
||||
&::before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
border-left: 0.5px solid rgb(144, 94, 190);
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: ' ';
|
||||
z-index: 2;
|
||||
}
|
||||
>.tree-node-slots-title {
|
||||
text-align: center;
|
||||
background-color: rgb(144, 94, 190);
|
||||
height: 14px;
|
||||
> .lc-title {
|
||||
font-size: 12px;
|
||||
transform: scale(0.8);
|
||||
transform-origin: top;
|
||||
color: white;
|
||||
text-shadow: 0px 0px 2px black;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
&.insertion-at-slots {
|
||||
padding-bottom: @treeNodeHeight;
|
||||
border-bottom-color: rgb(182, 55, 55);
|
||||
>.tree-node-slots-title {
|
||||
background-color: rgb(182, 55, 55);
|
||||
}
|
||||
&::before {
|
||||
border-left-color: rgb(182, 55, 55);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
.tree-node-expand-btn {
|
||||
width: 12px;
|
||||
line-height: 0;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: color 200ms ease;
|
||||
color: var(--color-icon-normal);
|
||||
&:hover {
|
||||
color: var(--color-icon-hover);
|
||||
}
|
||||
> svg {
|
||||
transform-origin: center;
|
||||
transform: rotate(-90deg);
|
||||
transition: transform 100ms ease;
|
||||
}
|
||||
margin-right: 4px;
|
||||
}
|
||||
.tree-node-expand-placeholder {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.tree-node-icon {
|
||||
transform: translateZ(0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 4px;
|
||||
color: var(--color-text);
|
||||
|
||||
& > svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
* {
|
||||
fill: var(--color-icon-normal, rgba(31, 56, 88, 0.4));
|
||||
}
|
||||
}
|
||||
& > img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
* {
|
||||
fill: var(--color-icon-normal, rgba(31, 56, 88, 0.4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-title {
|
||||
font-size: var(--font-size-text);
|
||||
cursor: pointer;
|
||||
background: var(--color-pane-background);
|
||||
border-bottom: 1px solid var(--color-line-normal, rgba(31, 56, 88, 0.1));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: @treeNodeHeight;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
transform: translateZ(0);
|
||||
padding-right: 5px;
|
||||
& > :first-child {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.tree-node-title-label {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
overflow: visible;
|
||||
margin-right: 5px;
|
||||
|
||||
.tree-node-title-input {
|
||||
flex: 1;
|
||||
border: 1px solid var(--color-brand-light);
|
||||
background-color: var(--color-pane-background);
|
||||
color: var(--color-text);
|
||||
line-height: 18px;
|
||||
padding: 2px;
|
||||
outline: none;
|
||||
margin-left: -3px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-hide-btn, .tree-node-lock-btn {
|
||||
opacity: 0;
|
||||
color: var(--color-text);
|
||||
line-height: 0;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 22px;
|
||||
&:hover {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.tree-node-hide-btn, .tree-node-lock-btn {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
html.lc-cursor-dragging & {
|
||||
// FIXME: only hide hover shows
|
||||
.tree-node-hide-btn, .tree-node-lock-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.editing {
|
||||
& > .tree-node-hide-btn, & >.tree-node-lock-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-tag {
|
||||
margin-left: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 0;
|
||||
&.cond {
|
||||
color: rgb(179, 52, 6);
|
||||
}
|
||||
&.loop {
|
||||
color: rgb(103, 187, 187);
|
||||
}
|
||||
&.slot {
|
||||
color: rgb(211, 90, 211);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-root {
|
||||
> .tree-node-title {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
& > .tree-node-title > .tree-node-expand-btn > svg {
|
||||
transform: rotate(0);
|
||||
}
|
||||
}
|
||||
|
||||
&.detecting > .tree-node-title {
|
||||
background: var(--color-block-background-light);
|
||||
}
|
||||
|
||||
// 选中节点处理
|
||||
&.selected {
|
||||
& > .tree-node-title {
|
||||
background: var(--color-block-background-shallow);
|
||||
}
|
||||
|
||||
& > .tree-node-branches::before {
|
||||
border-left-color: var(--color-brand-light);
|
||||
}
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
.tree-node-title-label {
|
||||
color: #9b9b9b;
|
||||
}
|
||||
& > .tree-node-title > .tree-node-hide-btn {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.tree-node-branches {
|
||||
.tree-node-hide-btn {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.condition-flow {
|
||||
& > .tree-node-title > .tree-node-hide-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
&.hidden > .tree-node-title > .tree-node-hide-btn {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.locked {
|
||||
& > .tree-node-title > .tree-node-lock-btn {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.tree-node-branches {
|
||||
.tree-node-lock-btn, .tree-node-hide-btn {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理拖入节点
|
||||
&.dropping {
|
||||
& > .tree-node-branches::before {
|
||||
border-left: 1px solid var(--color-brand);
|
||||
}
|
||||
& > .tree-node-title {
|
||||
.tree-node-expand-btn {
|
||||
color: var(--color-brand);
|
||||
}
|
||||
.tree-node-icon {
|
||||
color: var(--color-brand);
|
||||
}
|
||||
.tree-node-title-label > .lc-title {
|
||||
color: var(--color-brand);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.highlight {
|
||||
& > .tree-node-title {
|
||||
background: var(--color-block-background-shallow);
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-branches {
|
||||
padding-left: 12px;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
140
packages/plugin-outline-pane/src/views/tree-branches.tsx
Normal file
140
packages/plugin-outline-pane/src/views/tree-branches.tsx
Normal file
@ -0,0 +1,140 @@
|
||||
import { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer, Title } from '@ali/lowcode-editor-core';
|
||||
import { ExclusiveGroup } from '@ali/lowcode-designer';
|
||||
import TreeNode from '../tree-node';
|
||||
import TreeNodeView from './tree-node';
|
||||
import { intlNode } from '../locale';
|
||||
|
||||
@observer
|
||||
export default class TreeBranches extends Component<{
|
||||
treeNode: TreeNode;
|
||||
isModal?: boolean;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode, isModal } = this.props;
|
||||
const { expanded } = treeNode;
|
||||
|
||||
if (!expanded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tree-node-branches">
|
||||
{
|
||||
!isModal && <TreeNodeSlots treeNode={treeNode} />
|
||||
}
|
||||
<TreeNodeChildren treeNode={treeNode} isModal={isModal || false} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class TreeNodeChildren extends Component<{
|
||||
treeNode: TreeNode;
|
||||
isModal?: boolean;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode, isModal } = this.props;
|
||||
const children: any = [];
|
||||
let groupContents: any[] = [];
|
||||
let currentGrp: ExclusiveGroup;
|
||||
const endGroup = () => {
|
||||
if (groupContents.length > 0) {
|
||||
children.push(
|
||||
<div key={currentGrp.id} className="condition-group-container" data-id={currentGrp.firstNode.id}>
|
||||
<div className="condition-group-title">
|
||||
<Title title={currentGrp.title} />
|
||||
</div>
|
||||
{groupContents}
|
||||
</div>,
|
||||
);
|
||||
groupContents = [];
|
||||
}
|
||||
};
|
||||
const { dropDetail } = treeNode;
|
||||
const dropIndex = dropDetail?.index;
|
||||
const insertion = (
|
||||
<div
|
||||
key="insertion"
|
||||
className={classNames('insertion', {
|
||||
invalid: dropDetail?.valid === false,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
treeNode.children?.forEach((child, index) => {
|
||||
const childIsModal = child.node.componentMeta.isModal || false;
|
||||
if (isModal != childIsModal) {
|
||||
return;
|
||||
}
|
||||
const { conditionGroup } = child.node;
|
||||
if (conditionGroup !== currentGrp) {
|
||||
endGroup();
|
||||
}
|
||||
|
||||
if (conditionGroup) {
|
||||
currentGrp = conditionGroup;
|
||||
if (index === dropIndex) {
|
||||
if (groupContents.length > 0) {
|
||||
groupContents.push(insertion);
|
||||
} else {
|
||||
children.push(insertion);
|
||||
}
|
||||
}
|
||||
groupContents.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} />);
|
||||
} else {
|
||||
if (index === dropIndex) {
|
||||
children.push(insertion);
|
||||
}
|
||||
children.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} />);
|
||||
}
|
||||
});
|
||||
endGroup();
|
||||
const length = treeNode.children?.length || 0;
|
||||
if (dropIndex != null && dropIndex >= length) {
|
||||
children.push(insertion);
|
||||
}
|
||||
|
||||
return <div className="tree-node-children">{children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class TreeNodeSlots extends Component<{
|
||||
treeNode: TreeNode;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
if (!treeNode.hasSlots()) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={classNames('tree-node-slots', {
|
||||
'insertion-at-slots': treeNode.dropDetail?.focus?.type === 'slots',
|
||||
})}
|
||||
data-id={treeNode.id}
|
||||
>
|
||||
<div className="tree-node-slots-title">
|
||||
<Title title={{ type: 'i18n', intl: intlNode('Slots') }} />
|
||||
</div>
|
||||
{treeNode.slots.map(tnode => (
|
||||
<TreeNodeView key={tnode.id} treeNode={tnode} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
46
packages/plugin-outline-pane/src/views/tree-node.tsx
Normal file
46
packages/plugin-outline-pane/src/views/tree-node.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@ali/lowcode-editor-core';
|
||||
import TreeNode from '../tree-node';
|
||||
import TreeTitle from './tree-title';
|
||||
import TreeBranches from './tree-branches';
|
||||
|
||||
@observer
|
||||
export default class TreeNodeView extends Component<{
|
||||
treeNode: TreeNode;
|
||||
isModal?: boolean;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode, isModal } = this.props;
|
||||
const className = classNames('tree-node', {
|
||||
// 是否展开
|
||||
expanded: treeNode.expanded,
|
||||
// 是否悬停中
|
||||
detecting: treeNode.detecting,
|
||||
// 是否选中的
|
||||
selected: treeNode.selected,
|
||||
// 是否隐藏的
|
||||
hidden: treeNode.hidden,
|
||||
// 是否忽略的
|
||||
// ignored: treeNode.ignored,
|
||||
// 是否锁定的
|
||||
locked: treeNode.locked,
|
||||
// 是否投放响应
|
||||
dropping: treeNode.dropDetail?.index != null,
|
||||
'is-root': treeNode.isRoot(),
|
||||
'condition-flow': treeNode.node.conditionGroup != null,
|
||||
highlight: treeNode.isFocusingNode(),
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={className} data-id={treeNode.id}>
|
||||
<TreeTitle treeNode={treeNode} isModal={isModal} />
|
||||
<TreeBranches treeNode={treeNode} isModal={false} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
295
packages/plugin-outline-pane/src/views/tree-title.tsx
Normal file
295
packages/plugin-outline-pane/src/views/tree-title.tsx
Normal file
@ -0,0 +1,295 @@
|
||||
import { Component, KeyboardEvent, FocusEvent, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer, Title, Tip, globalContext, Editor } from '@ali/lowcode-editor-core';
|
||||
import { IconArrowRight } from '../icons/arrow-right';
|
||||
import { IconEyeClose } from '../icons/eye-close';
|
||||
import { intl, intlNode } from '../locale';
|
||||
import TreeNode from '../tree-node';
|
||||
import { IconEye } from '../icons/eye';
|
||||
import { IconCond } from '../icons/cond';
|
||||
import { IconLoop } from '../icons/loop';
|
||||
import { IconRadioActive } from '../icons/radio-active';
|
||||
import { IconRadio } from '../icons/radio';
|
||||
import { createIcon } from '@ali/lowcode-utils';
|
||||
|
||||
function emitOutlineEvent(type: string, treeNode: TreeNode, rest?: Record<string, unknown>) {
|
||||
const editor = globalContext.get(Editor);
|
||||
const node = treeNode?.node;
|
||||
const npm = node?.componentMeta?.npm;
|
||||
const selected =
|
||||
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') || node?.componentMeta?.componentName || '';
|
||||
editor?.emit(`outlinePane.${type}`, {
|
||||
selected,
|
||||
...rest,
|
||||
});
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class TreeTitle extends Component<{
|
||||
treeNode: TreeNode;
|
||||
isModal?: boolean;
|
||||
}> {
|
||||
state: {
|
||||
editing: boolean;
|
||||
} = {
|
||||
editing: false,
|
||||
};
|
||||
|
||||
private enableEdit = () => {
|
||||
this.setState({
|
||||
editing: true,
|
||||
});
|
||||
};
|
||||
|
||||
private cancelEdit() {
|
||||
this.setState({
|
||||
editing: false,
|
||||
});
|
||||
this.lastInput = undefined;
|
||||
}
|
||||
|
||||
private saveEdit = (e: FocusEvent<HTMLInputElement> | KeyboardEvent<HTMLInputElement>) => {
|
||||
const { treeNode } = this.props;
|
||||
const value = (e.target as HTMLInputElement).value || '';
|
||||
treeNode.setTitleLabel(value);
|
||||
emitOutlineEvent('rename', treeNode, { value });
|
||||
this.cancelEdit();
|
||||
};
|
||||
|
||||
private handleKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.keyCode === 13) {
|
||||
this.saveEdit(e);
|
||||
}
|
||||
if (e.keyCode === 27) {
|
||||
this.cancelEdit();
|
||||
}
|
||||
};
|
||||
|
||||
private lastInput?: HTMLInputElement;
|
||||
|
||||
private setCaret = (input: HTMLInputElement | null) => {
|
||||
if (!input || this.lastInput === input) {
|
||||
return;
|
||||
}
|
||||
input.focus();
|
||||
input.select();
|
||||
// 光标定位最后一个
|
||||
// input.selectionStart = input.selectionEnd;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { treeNode, isModal } = this.props;
|
||||
const { editing } = this.state;
|
||||
const isCNode = !treeNode.isRoot();
|
||||
const { node } = treeNode;
|
||||
const isNodeParent = node.isParental();
|
||||
let style: any;
|
||||
if (isCNode) {
|
||||
const { depth } = treeNode;
|
||||
const indent = depth * 12;
|
||||
style = {
|
||||
paddingLeft: indent + (isModal ? 12 : 0),
|
||||
marginLeft: -indent,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('tree-node-title', {
|
||||
editing,
|
||||
})}
|
||||
style={style}
|
||||
data-id={treeNode.id}
|
||||
onClick={() => {
|
||||
if (isModal) {
|
||||
node.document.modalNodesManager.setVisible(node);
|
||||
return;
|
||||
}
|
||||
if (node.conditionGroup) {
|
||||
node.setConditionalVisible();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{isModal && node.getVisible() && (
|
||||
<div onClick={() => {
|
||||
node.document.modalNodesManager.setInvisible(node);
|
||||
}}
|
||||
>
|
||||
<IconRadioActive className="tree-node-modal-radio-active" />
|
||||
</div>
|
||||
)}
|
||||
{isModal && !node.getVisible() && (
|
||||
<div onClick={() => {
|
||||
node.document.modalNodesManager.setVisible(node);
|
||||
}}
|
||||
>
|
||||
<IconRadio className="tree-node-modal-radio" />
|
||||
</div>
|
||||
)}
|
||||
{isCNode && <ExpandBtn treeNode={treeNode} />}
|
||||
<div className="tree-node-icon">{createIcon(treeNode.icon)}</div>
|
||||
<div className="tree-node-title-label" onDoubleClick={isNodeParent ? this.enableEdit : undefined}>
|
||||
{editing ? (
|
||||
<input
|
||||
className="tree-node-title-input"
|
||||
defaultValue={treeNode.titleLabel}
|
||||
onBlur={this.saveEdit}
|
||||
ref={this.setCaret}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
/>
|
||||
) : (
|
||||
<Fragment>
|
||||
<Title title={treeNode.title} />
|
||||
{node.slotFor && (
|
||||
<a className="tree-node-tag slot">
|
||||
{/* todo: click redirect to prop */}
|
||||
<Tip>{intlNode('Slot for {prop}', { prop: node.slotFor.key })}</Tip>
|
||||
</a>
|
||||
)}
|
||||
{node.hasLoop() && (
|
||||
<a className="tree-node-tag loop">
|
||||
{/* todo: click todo something */}
|
||||
<IconLoop />
|
||||
<Tip>{intlNode('Loop')}</Tip>
|
||||
</a>
|
||||
)}
|
||||
{node.hasCondition() && !node.conditionGroup && (
|
||||
<a className="tree-node-tag cond">
|
||||
{/* todo: click todo something */}
|
||||
<IconCond />
|
||||
<Tip>{intlNode('Conditional')}</Tip>
|
||||
</a>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
{isCNode && isNodeParent && !isModal && <HideBtn treeNode={treeNode} />}
|
||||
{/* isCNode && isNodeParent && <LockBtn treeNode={treeNode} /> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// @observer
|
||||
// class LockBtn extends Component<{ treeNode: TreeNode }> {
|
||||
// shouldComponentUpdate() {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// render() {
|
||||
// const { treeNode } = this.props;
|
||||
// return (
|
||||
// <div
|
||||
// className="tree-node-lock-btn"
|
||||
// onClick={(e) => {
|
||||
// e.stopPropagation();
|
||||
// treeNode.setLocked(!treeNode.locked);
|
||||
// }}
|
||||
// >
|
||||
// {treeNode.locked ? <IconLock /> : <IconUnlock />}
|
||||
// <Tip>{treeNode.locked ? intl('Unlock') : intl('Lock')}</Tip>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
@observer
|
||||
class HideBtn extends Component<{ treeNode: TreeNode }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
return (
|
||||
<div
|
||||
className="tree-node-hide-btn"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
emitOutlineEvent(treeNode.hidden ? 'show' : 'hide', treeNode);
|
||||
treeNode.setHidden(!treeNode.hidden);
|
||||
}}
|
||||
>
|
||||
{treeNode.hidden ? <IconEyeClose /> : <IconEye />}
|
||||
<Tip>{treeNode.hidden ? intl('Show') : intl('Hide')}</Tip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class ExpandBtn extends Component<{ treeNode: TreeNode }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
if (!treeNode.expandable) {
|
||||
return <i className="tree-node-expand-placeholder" />;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="tree-node-expand-btn"
|
||||
onClick={(e) => {
|
||||
if (treeNode.expanded) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
emitOutlineEvent(treeNode.expanded ? 'collapse' : 'expand', treeNode);
|
||||
treeNode.setExpanded(!treeNode.expanded);
|
||||
}}
|
||||
>
|
||||
<IconArrowRight size="small" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
interface Point {
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
}
|
||||
|
||||
function setCaret(point: Point) {
|
||||
debugger;
|
||||
const range = getRangeFromPoint(point);
|
||||
if (range) {
|
||||
selectRange(range);
|
||||
setTimeout(() => selectRange(range), 1);
|
||||
}
|
||||
}
|
||||
|
||||
function getRangeFromPoint(point: Point): Range | undefined {
|
||||
const x = point.clientX;
|
||||
const y = point.clientY;
|
||||
let range;
|
||||
let pos: CaretPosition | null = null;
|
||||
if (document.caretRangeFromPoint) {
|
||||
range = document.caretRangeFromPoint(x, y);
|
||||
} else if ((pos = document.caretPositionFromPoint(x, y))) {
|
||||
range = document.createRange();
|
||||
range.setStart(pos.offsetNode, pos.offset);
|
||||
range.collapse(true);
|
||||
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
function selectRange(range: Range) {
|
||||
const selection = document.getSelection();
|
||||
if (selection) {
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
function setCaretAfter(elem) {
|
||||
const range = document.createRange();
|
||||
const node = elem.lastChild;
|
||||
if (!node) return;
|
||||
range.setStartAfter(node);
|
||||
range.setEndAfter(node);
|
||||
selectRange(range);
|
||||
}
|
||||
*/
|
||||
165
packages/plugin-outline-pane/src/views/tree.tsx
Normal file
165
packages/plugin-outline-pane/src/views/tree.tsx
Normal file
@ -0,0 +1,165 @@
|
||||
import { Component, MouseEvent as ReactMouseEvent } from 'react';
|
||||
import { observer, Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||
import { isRootNode, Node, DragObjectType, isShaken } from '@ali/lowcode-designer';
|
||||
import { isFormEvent } from '@ali/lowcode-utils';
|
||||
import { Tree } from '../tree';
|
||||
import RootTreeNodeView from './root-tree-node';
|
||||
|
||||
function getTreeNodeIdByEvent(e: ReactMouseEvent, stop: Element): null | string {
|
||||
let target: Element | null = e.target as Element;
|
||||
if (!target || !stop.contains(target)) {
|
||||
return null;
|
||||
}
|
||||
target = target.closest('[data-id]');
|
||||
if (!target || !stop.contains(target)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (target as HTMLDivElement).dataset.id || null;
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class TreeView extends Component<{ tree: Tree }> {
|
||||
private shell: HTMLDivElement | null = null;
|
||||
|
||||
private hover(e: ReactMouseEvent) {
|
||||
const { tree } = this.props;
|
||||
|
||||
const doc = tree.document;
|
||||
const { detecting } = doc.designer;
|
||||
if (!detecting.enable) {
|
||||
return;
|
||||
}
|
||||
const node = this.getTreeNodeFromEvent(e)?.node;
|
||||
detecting.capture(node || null);
|
||||
}
|
||||
|
||||
private onClick = (e: ReactMouseEvent) => {
|
||||
if (this.ignoreUpSelected) {
|
||||
this.boostEvent = undefined;
|
||||
return;
|
||||
}
|
||||
if (this.boostEvent && isShaken(this.boostEvent, e.nativeEvent)) {
|
||||
this.boostEvent = undefined;
|
||||
return;
|
||||
}
|
||||
this.boostEvent = undefined;
|
||||
const treeNode = this.getTreeNodeFromEvent(e);
|
||||
if (!treeNode) {
|
||||
return;
|
||||
}
|
||||
const { node } = treeNode;
|
||||
const { designer } = treeNode;
|
||||
const doc = node.document;
|
||||
const { selection } = doc;
|
||||
const { id } = node;
|
||||
const isMulti = e.metaKey || e.ctrlKey || e.shiftKey;
|
||||
designer.activeTracker.track(node);
|
||||
if (isMulti && !isRootNode(node) && selection.has(id)) {
|
||||
if (!isFormEvent(e.nativeEvent)) {
|
||||
selection.remove(id);
|
||||
}
|
||||
} else {
|
||||
selection.select(id);
|
||||
const editor = globalContext.get(Editor);
|
||||
const selectedNode = designer.currentSelection?.getNodes()?.[0];
|
||||
const npm = selectedNode?.componentMeta?.npm;
|
||||
const selected =
|
||||
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
|
||||
selectedNode?.componentMeta?.componentName ||
|
||||
'';
|
||||
editor?.emit('outlinePane.select', {
|
||||
selected,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private onMouseOver = (e: ReactMouseEvent) => {
|
||||
this.hover(e);
|
||||
};
|
||||
|
||||
private getTreeNodeFromEvent(e: ReactMouseEvent) {
|
||||
if (!this.shell) {
|
||||
return;
|
||||
}
|
||||
const id = getTreeNodeIdByEvent(e, this.shell);
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { tree } = this.props;
|
||||
return tree.getTreeNodeById(id);
|
||||
}
|
||||
|
||||
private ignoreUpSelected = false;
|
||||
|
||||
private boostEvent?: MouseEvent;
|
||||
|
||||
private onMouseDown = (e: ReactMouseEvent) => {
|
||||
if (isFormEvent(e.nativeEvent)) {
|
||||
return;
|
||||
}
|
||||
const treeNode = this.getTreeNodeFromEvent(e);
|
||||
if (!treeNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { node } = treeNode;
|
||||
const { designer } = treeNode;
|
||||
const doc = node.document;
|
||||
const { selection } = doc;
|
||||
|
||||
// TODO: shift selection
|
||||
const isMulti = e.metaKey || e.ctrlKey || e.shiftKey;
|
||||
const isLeftButton = e.button === 0;
|
||||
|
||||
if (isLeftButton && !isRootNode(node)) {
|
||||
let nodes: Node[] = [node];
|
||||
this.ignoreUpSelected = false;
|
||||
if (isMulti) {
|
||||
// multi select mode, directily add
|
||||
if (!selection.has(node.id)) {
|
||||
designer.activeTracker.track(node);
|
||||
selection.add(node.id);
|
||||
this.ignoreUpSelected = true;
|
||||
}
|
||||
selection.remove(doc.rootNode.id);
|
||||
// 获得顶层 nodes
|
||||
nodes = selection.getTopNodes();
|
||||
} else if (selection.has(node.id)) {
|
||||
nodes = selection.getTopNodes();
|
||||
}
|
||||
this.boostEvent = e.nativeEvent;
|
||||
designer.dragon.boost(
|
||||
{
|
||||
type: DragObjectType.Node,
|
||||
nodes,
|
||||
},
|
||||
this.boostEvent,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
private onMouseLeave = () => {
|
||||
const { tree } = this.props;
|
||||
const doc = tree.document;
|
||||
doc.designer.detecting.leave(doc);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { tree } = this.props;
|
||||
const { root } = tree;
|
||||
return (
|
||||
<div
|
||||
className="lc-outline-tree"
|
||||
ref={(shell) => { this.shell = shell; }}
|
||||
onMouseDownCapture={this.onMouseDown}
|
||||
onMouseOver={this.onMouseOver}
|
||||
onClick={this.onClick}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
>
|
||||
<RootTreeNodeView key={root.id} treeNode={root} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user