feat: 加上 plugin-outline-pane

This commit is contained in:
力皓 2020-12-08 11:37:05 +08:00
parent e599acd2f0
commit 83c0772bc5
34 changed files with 3355 additions and 0 deletions

106
packages/plugin-outline-pane/.gitignore vendored Normal file
View 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

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

View File

@ -0,0 +1 @@
大纲面板

View File

@ -0,0 +1,9 @@
{
"plugins": [
"build-plugin-component",
"build-plugin-fusion",
["build-plugin-moment-locales", {
"locales": ["zh-cn"]
}]
]
}

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

View File

@ -0,0 +1 @@
大纲树

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

@ -0,0 +1,14 @@
{
"Initializing": "正在初始化",
"Hide": "隐藏",
"Show": "显示",
"Lock": "锁定",
"Unlock": "解锁",
"Expand": "展开",
"Collapse": "收起",
"Conditional": "条件式",
"Loop": "循环",
"Slots": "插槽",
"Slot for {prop}": "属性 {prop} 的插槽",
"Outline Tree": "大纲树"
}

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

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

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

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

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

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

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

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

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

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

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

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