mirror of
https://github.com/mathuo/dockview
synced 2025-05-07 20:18:26 +00:00
Compare commits
390 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1e63da807b | ||
|
68d7947dea | ||
|
093a034738 | ||
|
f9ca5b99d5 | ||
|
420466398d | ||
|
20b5c844f3 | ||
|
6d2f2f1012 | ||
|
3ddea55834 | ||
|
2f334c63c1 | ||
|
8334f9795c | ||
|
7ea3154946 | ||
|
1ef3dc06ce | ||
|
1ff74f2e65 | ||
|
a82aac09bc | ||
|
d1239a5ee5 | ||
|
22deb851dc | ||
|
87f257df1e | ||
|
a92a056981 | ||
|
c3e203f420 | ||
|
8bdd15161c | ||
|
8a3a7e5063 | ||
|
65d95f1271 | ||
|
3ec6627013 | ||
|
c0227d620c | ||
|
aad168da84 | ||
|
dfee4111a2 | ||
|
712f3f860d | ||
|
f545a0a5c7 | ||
|
e3d39fd2ed | ||
|
dca0eb0df7 | ||
|
8188fd9429 | ||
|
54a51ed10b | ||
|
1a11a4ba2c | ||
|
b6d83df1aa | ||
|
6444e4cb7d | ||
|
ca7bd0a86d | ||
|
dc828b7658 | ||
|
6e02e59fec | ||
|
f79bbe0a6a | ||
|
12448e006f | ||
|
5e380affe7 | ||
|
a44fc01f34 | ||
|
8e03e584a7 | ||
|
1ddcaefe80 | ||
|
d086dfd081 | ||
|
b231367e44 | ||
|
bba22fedd4 | ||
|
3ebcb8eaef | ||
|
39dd5f0759 | ||
|
e4f07dfbda | ||
|
d30263af67 | ||
|
54ae457ccd | ||
|
416039bc99 | ||
|
e8fdabba08 | ||
|
9373802115 | ||
|
759dcc7b05 | ||
|
f8faca731d | ||
|
d168356344 | ||
|
64e11a880c | ||
|
a306742508 | ||
|
bffd2dea89 | ||
|
ac92d59d0a | ||
|
21d94b654b | ||
|
1030bdb778 | ||
|
082b811d20 | ||
|
4eff83e9a0 | ||
|
090f2d26f8 | ||
|
e34fb43913 | ||
|
c0119d65c0 | ||
|
d3d57b62b2 | ||
|
96d6947aa6 | ||
|
5fb13dd8a2 | ||
|
cfe37766a9 | ||
|
7a6b2cb26d | ||
|
6511b8d936 | ||
|
5754ddc3f4 | ||
|
eec57c1cde | ||
|
3530c9896e | ||
|
d811ca6554 | ||
|
ab965ca726 | ||
|
b13c841416 | ||
|
773bf5a53c | ||
|
837fabac86 | ||
|
21caabce60 | ||
|
14b918b7ae | ||
|
f98be640ce | ||
|
cd1aa81d93 | ||
|
490fc05baf | ||
|
16373429a8 | ||
|
59fba25ade | ||
|
cdd0183fa9 | ||
|
98cbe377c2 | ||
|
d8da2f29c3 | ||
|
8ed0fb5586 | ||
|
a56f01215b | ||
|
e71d8b1d09 | ||
|
71b99541b3 | ||
|
0e3afcf83f | ||
|
1a85444b81 | ||
|
19a22c49c3 | ||
|
06f02ba411 | ||
|
0e9c648fa9 | ||
|
bb93c9e4c8 | ||
|
c6dcef537f | ||
|
3e409c4265 | ||
|
ca09ae537d | ||
|
e9985df262 | ||
|
dc6bfc67e3 | ||
|
5ca5ffac8d | ||
|
723272d6de | ||
|
6421a72653 | ||
|
0c1b8ce4d7 | ||
|
f40532f272 | ||
|
6b75b5093c | ||
|
c0cbe89835 | ||
|
b46a473cdf | ||
|
239c99ffe5 | ||
|
6678ae24e0 | ||
|
680eaca96c | ||
|
3d23a2d352 | ||
|
c7c1ae9238 | ||
|
c122bfd310 | ||
|
1a3c6ea7db | ||
|
c6865c691c | ||
|
872ec7cba9 | ||
|
2a3647db28 | ||
|
2f9af48146 | ||
|
04e4ea1a70 | ||
|
2d8d38b466 | ||
|
10fd5778dc | ||
|
cd91f4a4d3 | ||
|
148a05a201 | ||
|
8359596be0 | ||
|
cb45889aca | ||
|
5e116c80b4 | ||
|
20eb30ed1c | ||
|
5173922c69 | ||
|
0ca56eaa8a | ||
|
c14b66e5e5 | ||
|
cc5037d208 | ||
|
a53665adc5 | ||
|
d799b5bc89 | ||
|
fade057412 | ||
|
f6e7e4e390 | ||
|
d33babd522 | ||
|
7df9c510bd | ||
|
3479828237 | ||
|
b3ce3e0366 | ||
|
32fa1511d8 | ||
|
e1304ce694 | ||
|
bdf81fd5b5 | ||
|
cd7984cb2b | ||
|
491872ea75 | ||
|
296b49180e | ||
|
487d02bd90 | ||
|
4d01d4e0af | ||
|
8650de90aa | ||
|
e45d9e48e8 | ||
|
da750e89d7 | ||
|
81a904ccbc | ||
|
6016362c5c | ||
|
7fe2e43d43 | ||
|
56d4dc7c74 | ||
|
6fbe2b4233 | ||
|
f749372eb2 | ||
|
c216d70354 | ||
|
0d32d285d2 | ||
|
5d868e63ce | ||
|
7515b9b05d | ||
|
703418e27f | ||
|
53cdf42d84 | ||
|
140a61f401 | ||
|
ff0bf75632 | ||
|
dd918294d1 | ||
|
684b0593e7 | ||
|
13c4a65e18 | ||
|
755135c5ad | ||
|
15bf4bbd1f | ||
|
429177436e | ||
|
cf6891368e | ||
|
e2302372b1 | ||
|
f13f009155 | ||
|
c5025424c7 | ||
|
a66fdae8a3 | ||
|
c777ff463d | ||
|
04327b4613 | ||
|
a02241d1a7 | ||
|
615886e666 | ||
|
1b921864fc | ||
|
1ce96c8ab8 | ||
|
d651c2213e | ||
|
1d61a47e7c | ||
|
aeb0e38249 | ||
|
df3269d56e | ||
|
0233e8fa42 | ||
|
2bcd62412d | ||
|
32e759ef75 | ||
|
ede5a58ec9 | ||
|
56bdec846c | ||
|
25489bf48e | ||
|
9564319e43 | ||
|
461c30dc5b | ||
|
be628a5683 | ||
|
19f027a0db | ||
|
e46a586044 | ||
|
2f4150013b | ||
|
24cc974a68 | ||
|
a00875aae0 | ||
|
02d0ad8634 | ||
|
93b29e77c4 | ||
|
df0d12128b | ||
|
986132c602 | ||
|
fe39c475a2 | ||
|
c9e90168a4 | ||
|
1a9ee8c34e | ||
|
8a05aede4c | ||
|
83b01a9a66 | ||
|
7ebbd437a1 | ||
|
d8ae04e274 | ||
|
3f5c9743ad | ||
|
3d1fa25677 | ||
|
108bc49401 | ||
|
291e76afdb | ||
|
2dc4f81b20 | ||
|
ca6f46ac8e | ||
|
dad9eba474 | ||
|
adaeb16b98 | ||
|
ee7cf637bb | ||
|
e70755e4fc | ||
|
112dc2e2a9 | ||
|
5cf3cfb616 | ||
|
f6f248c63a | ||
|
4bf3fc4820 | ||
|
40c1a26ead | ||
|
3f3ed52007 | ||
|
b812cf9117 | ||
|
210ff38cfa | ||
|
3ffb5034c6 | ||
|
2657c79620 | ||
|
cd4cd0f0ce | ||
|
408db93917 | ||
|
f557e9080a | ||
|
e0744531ca | ||
|
3c680b251f | ||
|
c086cabd39 | ||
|
a202a49182 | ||
|
efd77413da | ||
|
0097da7fde | ||
|
8c7369ccf0 | ||
|
8015860bde | ||
|
14382aa0fd | ||
|
7ddb63383f | ||
|
ac56a1f250 | ||
|
12bb6f6d20 | ||
|
bc455265cd | ||
|
6881daa593 | ||
|
3c90e9d196 | ||
|
c55257ac56 | ||
|
1b7f29e800 | ||
|
693a3cd6ef | ||
|
d1d8c8083e | ||
|
fd29ea570f | ||
|
1acdd969a0 | ||
|
aeda5f4ca1 | ||
|
5df4c5cce1 | ||
|
62fee4e7ec | ||
|
b24470fa42 | ||
|
0c602afab5 | ||
|
ab5a11f13b | ||
|
fc7b9fb2e2 | ||
|
d519c239e3 | ||
|
2b36844110 | ||
|
e2e91834ff | ||
|
72f457ab9d | ||
|
1033b80783 | ||
|
7adabec088 | ||
|
4841d95573 | ||
|
2dc0dafa5a | ||
|
9d4f4cb534 | ||
|
05084030db | ||
|
4d71775804 | ||
|
77bccb3f69 | ||
|
affb8590dc | ||
|
6c39c448bb | ||
|
a6a2d048c7 | ||
|
464e255d56 | ||
|
0e8139217b | ||
|
1876511c70 | ||
|
56182aa60f | ||
|
bf35b265db | ||
|
520aa39724 | ||
|
59acd94bc6 | ||
|
619b0b3276 | ||
|
d686b2c2c7 | ||
|
479d4bb322 | ||
|
49b1c5a174 | ||
|
35b3ee2b0f | ||
|
a443bb5e41 | ||
|
a05d2af417 | ||
|
1efa5c0ef6 | ||
|
6bf0263797 | ||
|
ed3fd57d6a | ||
|
5a80b3a58d | ||
|
f022f3cb26 | ||
|
af5ce5bbfd | ||
|
ea5b94ad90 | ||
|
e5e9603221 | ||
|
cc18f5055c | ||
|
fafc18b419 | ||
|
bb312d871c | ||
|
49561d14cc | ||
|
1cd4251083 | ||
|
ee74785d7e | ||
|
eda3ea1210 | ||
|
f689e38e59 | ||
|
f765dd52fc | ||
|
04d54e1dc5 | ||
|
e93a008fe4 | ||
|
5b9dbdf57e | ||
|
0e77bdf4d7 | ||
|
0211148b66 | ||
|
973ecff0be | ||
|
bdf286103f | ||
|
e86155adf4 | ||
|
374bd2adff | ||
|
8618ed3aad | ||
|
aa39b55563 | ||
|
24e8c99dea | ||
|
eeab0251d2 | ||
|
2111e2293c | ||
|
bd28468bf5 | ||
|
05b1c27320 | ||
|
b326291f94 | ||
|
8a4380ec14 | ||
|
eb3cac8b1b | ||
|
0db4f59990 | ||
|
aed7b97bac | ||
|
9e4b3bbdd6 | ||
|
cd59248f34 | ||
|
876b107284 | ||
|
ea9dc13992 | ||
|
f20b5285da | ||
|
9b46c1e7d9 | ||
|
d7e1fd4bb2 | ||
|
ae88703a8b | ||
|
cf8f18dbbd | ||
|
b96ccf00ce | ||
|
5285baebdb | ||
|
3652505a08 | ||
|
c5f94906d4 | ||
|
e3fb689d27 | ||
|
93e5ecec4c | ||
|
82dbdb29c3 | ||
|
476b0a1d42 | ||
|
e7622f6c2b | ||
|
cd6604d28c | ||
|
cffd742b7b | ||
|
bbdb99dbb5 | ||
|
ce381f8ce9 | ||
|
1bd2bff8f8 | ||
|
943c2dc425 | ||
|
bc278f2beb | ||
|
b81afd45e8 | ||
|
77b8023b56 | ||
|
963607defe | ||
|
4a0b328622 | ||
|
5762faac9d | ||
|
7f750330b1 | ||
|
55d9ca31d4 | ||
|
fde6764377 | ||
|
9c6dae30ca | ||
|
acadeedaf6 | ||
|
fc84942b9b | ||
|
e3eb851ab7 | ||
|
84d02b9925 | ||
|
3fcbf6515e | ||
|
9306d9fcdc | ||
|
d29052e606 | ||
|
9ee2b821ff | ||
|
c4ab6ac562 | ||
|
35630e4f1c | ||
|
07b548c6b6 | ||
|
7eff8a8642 | ||
|
abaad37edd | ||
|
b68d8df9ac | ||
|
07d67b2246 | ||
|
0ae16cf444 | ||
|
26cd1cc1cc | ||
|
56457fe269 | ||
|
5d6055c4d2 |
@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/dockview-core",
|
"packages/dockview-core",
|
||||||
|
"packages/dockview-vue",
|
||||||
|
"packages/dockview-react",
|
||||||
"packages/dockview"
|
"packages/dockview"
|
||||||
],
|
],
|
||||||
"sandboxes": [
|
"sandboxes": [
|
||||||
|
4
.github/workflows/deploy-docs.yml
vendored
4
.github/workflows/deploy-docs.yml
vendored
@ -26,6 +26,10 @@ jobs:
|
|||||||
working-directory: packages/dockview-core
|
working-directory: packages/dockview-core
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
working-directory: packages/dockview
|
working-directory: packages/dockview
|
||||||
|
- run: npm run build
|
||||||
|
working-directory: packages/dockview-vue
|
||||||
|
- run: npm run build
|
||||||
|
working-directory: packages/dockview-react
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
working-directory: packages/docs
|
working-directory: packages/docs
|
||||||
- run: npm run docs
|
- run: npm run docs
|
||||||
|
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
|||||||
- run: npm run build
|
- run: npm run build
|
||||||
- run: npm run test:cov
|
- run: npm run test:cov
|
||||||
- name: SonarCloud Scan
|
- name: SonarCloud Scan
|
||||||
uses: SonarSource/sonarcloud-github-action@master
|
uses: sonarsource/sonarqube-scan-action@v5
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
12
.github/workflows/publish.yml
vendored
12
.github/workflows/publish.yml
vendored
@ -35,6 +35,12 @@ jobs:
|
|||||||
- name: Publish dockview
|
- name: Publish dockview
|
||||||
run: npm publish --provenance
|
run: npm publish --provenance
|
||||||
working-directory: packages/dockview
|
working-directory: packages/dockview
|
||||||
|
- name: Publish dockview-vue
|
||||||
|
run: npm publish --provenance
|
||||||
|
working-directory: packages/dockview-vue
|
||||||
|
- name: Publish dockview-react
|
||||||
|
run: npm publish --provenance
|
||||||
|
working-directory: packages/dockview-react
|
||||||
publish-experimental:
|
publish-experimental:
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -64,3 +70,9 @@ jobs:
|
|||||||
- name: Publish dockview
|
- name: Publish dockview
|
||||||
run: npm publish --provenance --tag experimental
|
run: npm publish --provenance --tag experimental
|
||||||
working-directory: packages/dockview
|
working-directory: packages/dockview
|
||||||
|
- name: Publish dockview-vue
|
||||||
|
run: npm publish --provenance --tag experimental
|
||||||
|
working-directory: packages/dockview-vue
|
||||||
|
- name: Publish dockview-react
|
||||||
|
run: npm publish --provenance --tag experimental
|
||||||
|
working-directory: packages/dockview-react
|
||||||
|
30
README.md
30
README.md
@ -1,18 +1,18 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>dockview</h1>
|
<h1>dockview</h1>
|
||||||
|
|
||||||
<p>Zero dependency layout manager supporting tabs, groups, grids and splitviews with ReactJS support written in TypeScript</p>
|
<p>Zero dependency layout manager supporting tabs, groups, grids and splitviews. Supports React, Vue and Vanilla TypeScript</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/dockview)
|
[](https://www.npmjs.com/package/dockview-core)
|
||||||
[](https://www.npmjs.com/package/dockview)
|
[](https://www.npmjs.com/package/dockview-core)
|
||||||
[](https://github.com/mathuo/dockview/actions?query=workflow%3ACI)
|
[](https://github.com/mathuo/dockview/actions?query=workflow%3ACI)
|
||||||
[](https://sonarcloud.io/summary/overall?id=mathuo_dockview)
|
[](https://sonarcloud.io/summary/overall?id=mathuo_dockview)
|
||||||
[](https://sonarcloud.io/summary/overall?id=mathuo_dockview)
|
[](https://sonarcloud.io/summary/overall?id=mathuo_dockview)
|
||||||
[](https://bundlephobia.com/result?p=dockview)
|
[](https://bundlephobia.com/result?p=dockview-core)
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
||||||
@ -35,24 +35,4 @@ Please see the website: https://dockview.dev
|
|||||||
- Transparent builds and Code Analysis
|
- Transparent builds and Code Analysis
|
||||||
- Security at mind - verifed publishing and builds through GitHub Actions
|
- Security at mind - verifed publishing and builds through GitHub Actions
|
||||||
|
|
||||||
Want to verify our builds? Go [here](https://www.npmjs.com/package/dockview#Provenance).
|
Want to verify our builds? Go [here](https://www.npmjs.com/package/dockview#user-content-provenance).
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview).
|
|
||||||
|
|
||||||
```
|
|
||||||
npm install --save dockview
|
|
||||||
```
|
|
||||||
|
|
||||||
Within your project you must import or reference the stylesheet at `dockview/dist/styles/dockview.css` and attach a theme.
|
|
||||||
|
|
||||||
```css
|
|
||||||
@import '~dockview/dist/styles/dockview.css';
|
|
||||||
```
|
|
||||||
|
|
||||||
You should also attach a dockview theme to an element containing your components. For example:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<body classname="dockview-theme-dark"></body>
|
|
||||||
```
|
|
||||||
|
8
SECURITY.md
Normal file
8
SECURITY.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Reporting a Vulnerability
|
||||||
|
|
||||||
|
- Dockview is an entirely open source project.
|
||||||
|
- All build and publication scripts use public Github Action files found [here](https://github.com/mathuo/dockview/tree/master/.github/workflows).
|
||||||
|
- All npm publications are verified through the use of [provenance statements](https://docs.npmjs.com/generating-provenance-statements/).
|
||||||
|
- All builds are scanned with SonarCube and outputs can be found [here](https://sonarcloud.io/summary/overall?id=mathuo_dockview).
|
||||||
|
|
||||||
|
If you believe you have found a security or vulnerability issue please send a complete example to github.mathuo@gmail.com where it will be investigated.
|
@ -2,7 +2,7 @@
|
|||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"version": "1.13.1",
|
"version": "4.2.5",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"command": {
|
"command": {
|
||||||
"publish": {
|
"publish": {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "lerna run build --scope '{dockview-core,dockview}'",
|
"build": "lerna run build --scope '{dockview-core,dockview,dockview-vue,dockview-react}'",
|
||||||
"clean": "lerna run clean",
|
"clean": "lerna run clean",
|
||||||
"docs": "typedoc",
|
"docs": "typedoc",
|
||||||
"generate-docs": "node scripts/docs.mjs",
|
"generate-docs": "node scripts/docs.mjs",
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
"jest-sonar-reporter": "^2.0.0",
|
"jest-sonar-reporter": "^2.0.0",
|
||||||
"jsdom": "^23.0.1",
|
"jsdom": "^23.0.1",
|
||||||
"lerna": "^8.0.1",
|
"lerna": "^8.2.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
@ -77,8 +77,5 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0"
|
"node": ">=18.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ag-grid-vue3": "^31.1.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dockview-angular",
|
"name": "dockview-angular",
|
||||||
"version": "1.13.1",
|
"version": "4.2.5",
|
||||||
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"splitview",
|
"splitview",
|
||||||
@ -54,6 +54,6 @@
|
|||||||
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
|
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dockview-core": "^1.13.1"
|
"dockview-core": "^4.2.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>dockview</h1>
|
<h1>dockview</h1>
|
||||||
|
|
||||||
<p>Zero dependency layout manager supporting tabs, groups, grids and splitviews written in TypeScript</p>
|
<p>Zero dependency layout manager supporting tabs, groups, grids and splitviews. Supports React, Vue and Vanilla TypeScript</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -36,23 +36,3 @@ Please see the website: https://dockview.dev
|
|||||||
- Security at mind - verifed publishing and builds through GitHub Actions
|
- Security at mind - verifed publishing and builds through GitHub Actions
|
||||||
|
|
||||||
Want to verify our builds? Go [here](https://www.npmjs.com/package/dockview#Provenance).
|
Want to verify our builds? Go [here](https://www.npmjs.com/package/dockview#Provenance).
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview-core).
|
|
||||||
|
|
||||||
```
|
|
||||||
npm install --save dockview-core
|
|
||||||
```
|
|
||||||
|
|
||||||
Within your project you must import or reference the stylesheet at `dockview-core/dist/styles/dockview.css` and attach a theme.
|
|
||||||
|
|
||||||
```css
|
|
||||||
@import '~dockview-core/dist/styles/dockview.css';
|
|
||||||
```
|
|
||||||
|
|
||||||
You should also attach a dockview theme to an element containing your components. For example:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<body classname="dockview-theme-dark"></body>
|
|
||||||
```
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dockview-core",
|
"name": "dockview-core",
|
||||||
"version": "1.13.1",
|
"version": "4.2.5",
|
||||||
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"splitview",
|
"splitview",
|
||||||
|
@ -1,23 +1,28 @@
|
|||||||
import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel';
|
import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel';
|
||||||
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
||||||
import {
|
import {
|
||||||
GroupPanelPartInitParameters,
|
TabPartInitParameters,
|
||||||
IContentRenderer,
|
IContentRenderer,
|
||||||
ITabRenderer,
|
ITabRenderer,
|
||||||
} from '../../dockview/types';
|
} from '../../dockview/types';
|
||||||
import { PanelUpdateEvent } from '../../panel/types';
|
import { PanelUpdateEvent } from '../../panel/types';
|
||||||
|
import { TabLocation } from '../../dockview/framework';
|
||||||
|
|
||||||
export class DockviewPanelModelMock implements IDockviewPanelModel {
|
export class DockviewPanelModelMock implements IDockviewPanelModel {
|
||||||
constructor(
|
constructor(
|
||||||
readonly contentComponent: string,
|
readonly contentComponent: string,
|
||||||
readonly content: IContentRenderer,
|
readonly content: IContentRenderer,
|
||||||
readonly tabComponent?: string,
|
readonly tabComponent: string,
|
||||||
readonly tab?: ITabRenderer
|
readonly tab: ITabRenderer
|
||||||
) {
|
) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
init(params: GroupPanelPartInitParameters): void {
|
createTabRenderer(tabLocation: TabLocation): ITabRenderer {
|
||||||
|
return this.tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
init(params: TabPartInitParameters): void {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
45
packages/dockview-core/src/__tests__/__mocks__/mockWindow.ts
Normal file
45
packages/dockview-core/src/__tests__/__mocks__/mockWindow.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
|
|
||||||
|
export function setupMockWindow() {
|
||||||
|
const listeners: Record<string, (() => void)[]> = {};
|
||||||
|
|
||||||
|
let width = 1000;
|
||||||
|
let height = 2000;
|
||||||
|
|
||||||
|
return fromPartial<Window>({
|
||||||
|
addEventListener: (type: string, listener: () => void) => {
|
||||||
|
if (!listeners[type]) {
|
||||||
|
listeners[type] = [];
|
||||||
|
}
|
||||||
|
listeners[type].push(listener);
|
||||||
|
if (type === 'load') {
|
||||||
|
listener();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeEventListener: (type: string, listener: () => void) => {
|
||||||
|
if (listeners[type]) {
|
||||||
|
const index = listeners[type].indexOf(listener);
|
||||||
|
if (index > -1) {
|
||||||
|
listeners[type].splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dispatchEvent: (event: Event) => {
|
||||||
|
const items = listeners[event.type];
|
||||||
|
if (!items) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
items.forEach((item) => item());
|
||||||
|
},
|
||||||
|
document: document,
|
||||||
|
close: () => {
|
||||||
|
listeners['beforeunload']?.forEach((f) => f());
|
||||||
|
},
|
||||||
|
get innerWidth() {
|
||||||
|
return width++;
|
||||||
|
},
|
||||||
|
get innerHeight() {
|
||||||
|
return height++;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
@ -44,3 +44,30 @@ export function createOffsetDragOverEvent(params: {
|
|||||||
export function exhaustMicrotaskQueue(): Promise<void> {
|
export function exhaustMicrotaskQueue(): Promise<void> {
|
||||||
return new Promise<void>((resolve) => resolve());
|
return new Promise<void>((resolve) => resolve());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const mockGetBoundingClientRect = ({
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
}: {
|
||||||
|
left: number;
|
||||||
|
top: number;
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
}) => {
|
||||||
|
const result = {
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
right: left + width,
|
||||||
|
bottom: top + height,
|
||||||
|
x: left,
|
||||||
|
y: top,
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
toJSON: () => result,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -9,7 +9,8 @@ describe('groupPanelApi', () => {
|
|||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const panelMock = jest.fn<DockviewPanel, []>(() => {
|
const panelMock = jest.fn<DockviewPanel, []>(() => {
|
||||||
@ -49,7 +50,8 @@ describe('groupPanelApi', () => {
|
|||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupViewPanel = new DockviewGroupPanel(
|
const groupViewPanel = new DockviewGroupPanel(
|
||||||
@ -81,7 +83,8 @@ describe('groupPanelApi', () => {
|
|||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupViewPanel = new DockviewGroupPanel(
|
const groupViewPanel = new DockviewGroupPanel(
|
||||||
|
@ -70,8 +70,8 @@ describe('abstractDragHandler', () => {
|
|||||||
expect(span.style.pointerEvents).toBeFalsy();
|
expect(span.style.pointerEvents).toBeFalsy();
|
||||||
|
|
||||||
fireEvent.dragEnd(element);
|
fireEvent.dragEnd(element);
|
||||||
expect(iframe.style.pointerEvents).toBe('auto');
|
expect(iframe.style.pointerEvents).toBe('');
|
||||||
expect(webview.style.pointerEvents).toBe('auto');
|
expect(webview.style.pointerEvents).toBe('');
|
||||||
expect(span.style.pointerEvents).toBeFalsy();
|
expect(span.style.pointerEvents).toBeFalsy();
|
||||||
|
|
||||||
handler.dispose();
|
handler.dispose();
|
||||||
@ -114,8 +114,8 @@ describe('abstractDragHandler', () => {
|
|||||||
expect(span.style.pointerEvents).toBeFalsy();
|
expect(span.style.pointerEvents).toBeFalsy();
|
||||||
|
|
||||||
handler.dispose();
|
handler.dispose();
|
||||||
expect(iframe.style.pointerEvents).toBe('auto');
|
expect(iframe.style.pointerEvents).toBe('');
|
||||||
expect(webview.style.pointerEvents).toBe('auto');
|
expect(webview.style.pointerEvents).toBe('');
|
||||||
expect(span.style.pointerEvents).toBeFalsy();
|
expect(span.style.pointerEvents).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -172,7 +172,7 @@ describe('abstractDragHandler', () => {
|
|||||||
const event = new Event('dragstart');
|
const event = new Event('dragstart');
|
||||||
const spy = jest.spyOn(event, 'preventDefault');
|
const spy = jest.spyOn(event, 'preventDefault');
|
||||||
fireEvent(element, event);
|
fireEvent(element, event);
|
||||||
expect(spy).toBeCalledTimes(0);
|
expect(spy).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
handler.dispose();
|
handler.dispose();
|
||||||
});
|
});
|
||||||
|
@ -16,10 +16,10 @@ describe('droptarget', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
element = document.createElement('div');
|
element = document.createElement('div');
|
||||||
|
|
||||||
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(element, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 200);
|
jest.spyOn(element, 'offsetWidth', 'get').mockImplementation(() => 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that dragover events are marked', () => {
|
test('that dragover events are marked', () => {
|
||||||
@ -53,7 +53,7 @@ describe('droptarget', () => {
|
|||||||
fireEvent.dragOver(element);
|
fireEvent.dragOver(element);
|
||||||
|
|
||||||
const target = element.querySelector(
|
const target = element.querySelector(
|
||||||
'.drop-target-dropzone'
|
'.dv-drop-target-dropzone'
|
||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
fireEvent.drop(target);
|
fireEvent.drop(target);
|
||||||
expect(position).toBe('center');
|
expect(position).toBe('center');
|
||||||
@ -61,7 +61,7 @@ describe('droptarget', () => {
|
|||||||
const event = new Event('dragover');
|
const event = new Event('dragover');
|
||||||
(event as any)['__dockview_droptarget_event_is_used__'] = true;
|
(event as any)['__dockview_droptarget_event_is_used__'] = true;
|
||||||
fireEvent(element, event);
|
fireEvent(element, event);
|
||||||
expect(element.querySelector('.drop-target-dropzone')).toBeNull();
|
expect(element.querySelector('.dv-drop-target-dropzone')).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('directionToPosition', () => {
|
test('directionToPosition', () => {
|
||||||
@ -102,7 +102,7 @@ describe('droptarget', () => {
|
|||||||
fireEvent.dragOver(element);
|
fireEvent.dragOver(element);
|
||||||
|
|
||||||
const target = element.querySelector(
|
const target = element.querySelector(
|
||||||
'.drop-target-dropzone'
|
'.dv-drop-target-dropzone'
|
||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
fireEvent.drop(target);
|
fireEvent.drop(target);
|
||||||
expect(position).toBe('center');
|
expect(position).toBe('center');
|
||||||
@ -124,7 +124,7 @@ describe('droptarget', () => {
|
|||||||
fireEvent.dragOver(element);
|
fireEvent.dragOver(element);
|
||||||
|
|
||||||
const target = element.querySelector(
|
const target = element.querySelector(
|
||||||
'.drop-target-dropzone'
|
'.dv-drop-target-dropzone'
|
||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
|
|
||||||
jest.spyOn(target, 'clientHeight', 'get').mockImplementation(() => 100);
|
jest.spyOn(target, 'clientHeight', 'get').mockImplementation(() => 100);
|
||||||
@ -155,12 +155,12 @@ describe('droptarget', () => {
|
|||||||
fireEvent.dragOver(element);
|
fireEvent.dragOver(element);
|
||||||
|
|
||||||
let viewQuery = element.querySelectorAll(
|
let viewQuery = element.querySelectorAll(
|
||||||
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
'.dv-drop-target > .dv-drop-target-dropzone > .dv-drop-target-selection'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
|
|
||||||
const target = element.querySelector(
|
const target = element.querySelector(
|
||||||
'.drop-target-dropzone'
|
'.dv-drop-target-dropzone'
|
||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
|
|
||||||
jest.spyOn(target, 'clientHeight', 'get').mockImplementation(() => 100);
|
jest.spyOn(target, 'clientHeight', 'get').mockImplementation(() => 100);
|
||||||
@ -187,13 +187,13 @@ describe('droptarget', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewQuery = element.querySelectorAll(
|
viewQuery = element.querySelectorAll(
|
||||||
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
'.dv-drop-target > .dv-drop-target-dropzone > .dv-drop-target-selection'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
expect(droptarget.state).toBe('left');
|
expect(droptarget.state).toBe('left');
|
||||||
check(
|
check(
|
||||||
element
|
element
|
||||||
.getElementsByClassName('drop-target-selection')
|
.getElementsByClassName('dv-drop-target-selection')
|
||||||
.item(0) as HTMLDivElement,
|
.item(0) as HTMLDivElement,
|
||||||
{
|
{
|
||||||
top: '0px',
|
top: '0px',
|
||||||
@ -209,13 +209,13 @@ describe('droptarget', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
viewQuery = element.querySelectorAll(
|
viewQuery = element.querySelectorAll(
|
||||||
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
'.dv-drop-target > .dv-drop-target-dropzone > .dv-drop-target-selection'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
expect(droptarget.state).toBe('top');
|
expect(droptarget.state).toBe('top');
|
||||||
check(
|
check(
|
||||||
element
|
element
|
||||||
.getElementsByClassName('drop-target-selection')
|
.getElementsByClassName('dv-drop-target-selection')
|
||||||
.item(0) as HTMLDivElement,
|
.item(0) as HTMLDivElement,
|
||||||
{
|
{
|
||||||
top: '0px',
|
top: '0px',
|
||||||
@ -231,13 +231,13 @@ describe('droptarget', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
viewQuery = element.querySelectorAll(
|
viewQuery = element.querySelectorAll(
|
||||||
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
'.dv-drop-target > .dv-drop-target-dropzone > .dv-drop-target-selection'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
expect(droptarget.state).toBe('bottom');
|
expect(droptarget.state).toBe('bottom');
|
||||||
check(
|
check(
|
||||||
element
|
element
|
||||||
.getElementsByClassName('drop-target-selection')
|
.getElementsByClassName('dv-drop-target-selection')
|
||||||
.item(0) as HTMLDivElement,
|
.item(0) as HTMLDivElement,
|
||||||
{
|
{
|
||||||
top: '50%',
|
top: '50%',
|
||||||
@ -253,13 +253,13 @@ describe('droptarget', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
viewQuery = element.querySelectorAll(
|
viewQuery = element.querySelectorAll(
|
||||||
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
'.dv-drop-target > .dv-drop-target-dropzone > .dv-drop-target-selection'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
expect(droptarget.state).toBe('right');
|
expect(droptarget.state).toBe('right');
|
||||||
check(
|
check(
|
||||||
element
|
element
|
||||||
.getElementsByClassName('drop-target-selection')
|
.getElementsByClassName('dv-drop-target-selection')
|
||||||
.item(0) as HTMLDivElement,
|
.item(0) as HTMLDivElement,
|
||||||
{
|
{
|
||||||
top: '0px',
|
top: '0px',
|
||||||
@ -276,14 +276,14 @@ describe('droptarget', () => {
|
|||||||
expect(
|
expect(
|
||||||
(
|
(
|
||||||
element
|
element
|
||||||
.getElementsByClassName('drop-target-selection')
|
.getElementsByClassName('dv-drop-target-selection')
|
||||||
.item(0) as HTMLDivElement
|
.item(0) as HTMLDivElement
|
||||||
).style.transform
|
).style.transform
|
||||||
).toBe('');
|
).toBe('');
|
||||||
|
|
||||||
fireEvent.dragLeave(target);
|
fireEvent.dragLeave(target);
|
||||||
expect(droptarget.state).toBe('center');
|
expect(droptarget.state).toBe('center');
|
||||||
viewQuery = element.querySelectorAll('.drop-target');
|
viewQuery = element.querySelectorAll('.dv-drop-target');
|
||||||
expect(viewQuery.length).toBe(0);
|
expect(viewQuery.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,156 +0,0 @@
|
|||||||
import { Overlay } from '../../dnd/overlay';
|
|
||||||
|
|
||||||
describe('overlay', () => {
|
|
||||||
test('toJSON', () => {
|
|
||||||
const container = document.createElement('div');
|
|
||||||
const content = document.createElement('div');
|
|
||||||
|
|
||||||
document.body.appendChild(container);
|
|
||||||
container.appendChild(content);
|
|
||||||
|
|
||||||
const cut = new Overlay({
|
|
||||||
height: 200,
|
|
||||||
width: 100,
|
|
||||||
left: 10,
|
|
||||||
top: 20,
|
|
||||||
minimumInViewportWidth: 0,
|
|
||||||
minimumInViewportHeight: 0,
|
|
||||||
container,
|
|
||||||
content,
|
|
||||||
});
|
|
||||||
|
|
||||||
jest.spyOn(
|
|
||||||
container.childNodes.item(0) as HTMLElement,
|
|
||||||
'getBoundingClientRect'
|
|
||||||
).mockImplementation(() => {
|
|
||||||
return { left: 80, top: 100, width: 40, height: 50 } as any;
|
|
||||||
});
|
|
||||||
jest.spyOn(container, 'getBoundingClientRect').mockImplementation(
|
|
||||||
() => {
|
|
||||||
return { left: 20, top: 30, width: 100, height: 100 } as any;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(cut.toJSON()).toEqual({
|
|
||||||
top: 70,
|
|
||||||
left: 60,
|
|
||||||
width: 40,
|
|
||||||
height: 50,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('that out-of-bounds dimensions are fixed', () => {
|
|
||||||
const container = document.createElement('div');
|
|
||||||
const content = document.createElement('div');
|
|
||||||
|
|
||||||
document.body.appendChild(container);
|
|
||||||
container.appendChild(content);
|
|
||||||
|
|
||||||
const cut = new Overlay({
|
|
||||||
height: 200,
|
|
||||||
width: 100,
|
|
||||||
left: -1000,
|
|
||||||
top: -1000,
|
|
||||||
minimumInViewportWidth: 0,
|
|
||||||
minimumInViewportHeight: 0,
|
|
||||||
container,
|
|
||||||
content,
|
|
||||||
});
|
|
||||||
|
|
||||||
jest.spyOn(
|
|
||||||
container.childNodes.item(0) as HTMLElement,
|
|
||||||
'getBoundingClientRect'
|
|
||||||
).mockImplementation(() => {
|
|
||||||
return { left: 80, top: 100, width: 40, height: 50 } as any;
|
|
||||||
});
|
|
||||||
jest.spyOn(container, 'getBoundingClientRect').mockImplementation(
|
|
||||||
() => {
|
|
||||||
return { left: 20, top: 30, width: 100, height: 100 } as any;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(cut.toJSON()).toEqual({
|
|
||||||
top: 70,
|
|
||||||
left: 60,
|
|
||||||
width: 40,
|
|
||||||
height: 50,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('setBounds', () => {
|
|
||||||
const container = document.createElement('div');
|
|
||||||
const content = document.createElement('div');
|
|
||||||
|
|
||||||
document.body.appendChild(container);
|
|
||||||
container.appendChild(content);
|
|
||||||
|
|
||||||
const cut = new Overlay({
|
|
||||||
height: 1000,
|
|
||||||
width: 1000,
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
minimumInViewportWidth: 0,
|
|
||||||
minimumInViewportHeight: 0,
|
|
||||||
container,
|
|
||||||
content,
|
|
||||||
});
|
|
||||||
|
|
||||||
const element: HTMLElement = container.querySelector(
|
|
||||||
'.dv-resize-container'
|
|
||||||
)!;
|
|
||||||
expect(element).toBeTruthy();
|
|
||||||
|
|
||||||
jest.spyOn(element, 'getBoundingClientRect').mockImplementation(() => {
|
|
||||||
return { left: 300, top: 400, width: 1000, height: 1000 } as any;
|
|
||||||
});
|
|
||||||
jest.spyOn(container, 'getBoundingClientRect').mockImplementation(
|
|
||||||
() => {
|
|
||||||
return { left: 0, top: 0, width: 1000, height: 1000 } as any;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
cut.setBounds({ height: 100, width: 200, left: 300, top: 400 });
|
|
||||||
|
|
||||||
expect(element.style.height).toBe('100px');
|
|
||||||
expect(element.style.width).toBe('200px');
|
|
||||||
expect(element.style.left).toBe('300px');
|
|
||||||
expect(element.style.top).toBe('400px');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('that the resize handles are added', () => {
|
|
||||||
const container = document.createElement('div');
|
|
||||||
const content = document.createElement('div');
|
|
||||||
|
|
||||||
const cut = new Overlay({
|
|
||||||
height: 500,
|
|
||||||
width: 500,
|
|
||||||
left: 100,
|
|
||||||
top: 200,
|
|
||||||
minimumInViewportWidth: 0,
|
|
||||||
minimumInViewportHeight: 0,
|
|
||||||
container,
|
|
||||||
content,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(container.querySelector('.dv-resize-handle-top')).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
container.querySelector('.dv-resize-handle-bottom')
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(container.querySelector('.dv-resize-handle-left')).toBeTruthy();
|
|
||||||
expect(container.querySelector('.dv-resize-handle-right')).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
container.querySelector('.dv-resize-handle-topleft')
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
container.querySelector('.dv-resize-handle-topright')
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
container.querySelector('.dv-resize-handle-bottomleft')
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
container.querySelector('.dv-resize-handle-bottomright')
|
|
||||||
).toBeTruthy();
|
|
||||||
|
|
||||||
cut.dispose();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,5 +1,4 @@
|
|||||||
import { fireEvent } from '@testing-library/dom';
|
import { fireEvent } from '@testing-library/dom';
|
||||||
import { Emitter, Event } from '../../../../events';
|
|
||||||
import { ContentContainer } from '../../../../dockview/components/panel/content';
|
import { ContentContainer } from '../../../../dockview/components/panel/content';
|
||||||
import {
|
import {
|
||||||
GroupPanelPartInitParameters,
|
GroupPanelPartInitParameters,
|
||||||
@ -10,9 +9,9 @@ import { PanelUpdateEvent } from '../../../../panel/types';
|
|||||||
import { IDockviewPanel } from '../../../../dockview/dockviewPanel';
|
import { IDockviewPanel } from '../../../../dockview/dockviewPanel';
|
||||||
import { IDockviewPanelModel } from '../../../../dockview/dockviewPanelModel';
|
import { IDockviewPanelModel } from '../../../../dockview/dockviewPanelModel';
|
||||||
import { DockviewComponent } from '../../../../dockview/dockviewComponent';
|
import { DockviewComponent } from '../../../../dockview/dockviewComponent';
|
||||||
import { OverlayRenderContainer } from '../../../../overlayRenderContainer';
|
|
||||||
import { fromPartial } from '@total-typescript/shoehorn';
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
import { DockviewGroupPanelModel } from '../../../../dockview/dockviewGroupPanelModel';
|
import { DockviewGroupPanelModel } from '../../../../dockview/dockviewGroupPanelModel';
|
||||||
|
import { OverlayRenderContainer } from '../../../../overlay/overlayRenderContainer';
|
||||||
|
|
||||||
class TestContentRenderer
|
class TestContentRenderer
|
||||||
extends CompositeDisposable
|
extends CompositeDisposable
|
||||||
@ -58,7 +57,8 @@ describe('contentContainer', () => {
|
|||||||
const disposable = new CompositeDisposable();
|
const disposable = new CompositeDisposable();
|
||||||
|
|
||||||
const overlayRenderContainer = new OverlayRenderContainer(
|
const overlayRenderContainer = new OverlayRenderContainer(
|
||||||
document.createElement('div')
|
document.createElement('div'),
|
||||||
|
fromPartial<DockviewComponent>({})
|
||||||
);
|
);
|
||||||
|
|
||||||
const cut = new ContentContainer(
|
const cut = new ContentContainer(
|
||||||
|
@ -8,6 +8,7 @@ import { DockviewGroupPanel } from '../../../dockview/dockviewGroupPanel';
|
|||||||
import { DockviewGroupPanelModel } from '../../../dockview/dockviewGroupPanelModel';
|
import { DockviewGroupPanelModel } from '../../../dockview/dockviewGroupPanelModel';
|
||||||
import { Tab } from '../../../dockview/components/tab/tab';
|
import { Tab } from '../../../dockview/components/tab/tab';
|
||||||
import { IDockviewPanel } from '../../../dockview/dockviewPanel';
|
import { IDockviewPanel } from '../../../dockview/dockviewPanel';
|
||||||
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
|
|
||||||
describe('tab', () => {
|
describe('tab', () => {
|
||||||
test('that empty tab has inactive-tab class', () => {
|
test('that empty tab has inactive-tab class', () => {
|
||||||
@ -20,7 +21,7 @@ describe('tab', () => {
|
|||||||
new groupMock()
|
new groupMock()
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(cut.element.className).toBe('tab inactive-tab');
|
expect(cut.element.className).toBe('dv-tab dv-inactive-tab');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that active tab has active-tab class', () => {
|
test('that active tab has active-tab class', () => {
|
||||||
@ -34,10 +35,10 @@ describe('tab', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
cut.setActive(true);
|
cut.setActive(true);
|
||||||
expect(cut.element.className).toBe('tab active-tab');
|
expect(cut.element.className).toBe('dv-tab dv-active-tab');
|
||||||
|
|
||||||
cut.setActive(false);
|
cut.setActive(false);
|
||||||
expect(cut.element.className).toBe('tab inactive-tab');
|
expect(cut.element.className).toBe('dv-tab dv-inactive-tab');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that an external event does not render a drop target and calls through to the group model', () => {
|
test('that an external event does not render a drop target and calls through to the group model', () => {
|
||||||
@ -46,15 +47,10 @@ describe('tab', () => {
|
|||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
|
||||||
() => {
|
|
||||||
return {
|
|
||||||
canDisplayOverlay: jest.fn(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const groupView = new groupviewMock() as DockviewGroupPanelModel;
|
const groupView = fromPartial<DockviewGroupPanelModel>({
|
||||||
|
canDisplayOverlay: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<Partial<DockviewGroupPanel>, []>(() => {
|
const groupPanelMock = jest.fn<Partial<DockviewGroupPanel>, []>(() => {
|
||||||
return {
|
return {
|
||||||
@ -72,38 +68,33 @@ describe('tab', () => {
|
|||||||
groupPanel
|
groupPanel
|
||||||
);
|
);
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(cut.element, 'clientWidth', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'offsetWidth', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.dragEnter(cut.element);
|
fireEvent.dragEnter(cut.element);
|
||||||
fireEvent.dragOver(cut.element);
|
fireEvent.dragOver(cut.element);
|
||||||
|
|
||||||
expect(groupView.canDisplayOverlay).toBeCalled();
|
expect(groupView.canDisplayOverlay).toHaveBeenCalled();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
cut.element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that if you drag over yourself no drop target is shown', () => {
|
test('that if you drag over yourself a drop target is shown', () => {
|
||||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||||
return {
|
return {
|
||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
|
||||||
() => {
|
|
||||||
return {
|
|
||||||
canDisplayOverlay: jest.fn(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const groupView = new groupviewMock() as DockviewGroupPanelModel;
|
const groupView = fromPartial<DockviewGroupPanelModel>({
|
||||||
|
canDisplayOverlay: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<Partial<DockviewGroupPanel>, []>(() => {
|
const groupPanelMock = jest.fn<Partial<DockviewGroupPanel>, []>(() => {
|
||||||
return {
|
return {
|
||||||
@ -121,10 +112,10 @@ describe('tab', () => {
|
|||||||
groupPanel
|
groupPanel
|
||||||
);
|
);
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(cut.element, 'clientWidth', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'offsetWidth', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -136,11 +127,11 @@ describe('tab', () => {
|
|||||||
fireEvent.dragEnter(cut.element);
|
fireEvent.dragEnter(cut.element);
|
||||||
fireEvent.dragOver(cut.element);
|
fireEvent.dragOver(cut.element);
|
||||||
|
|
||||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(0);
|
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
cut.element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that if you drag over another tab a drop target is shown', () => {
|
test('that if you drag over another tab a drop target is shown', () => {
|
||||||
@ -175,10 +166,10 @@ describe('tab', () => {
|
|||||||
groupPanel
|
groupPanel
|
||||||
);
|
);
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(cut.element, 'clientWidth', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'offsetWidth', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -193,7 +184,7 @@ describe('tab', () => {
|
|||||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(0);
|
expect(groupView.canDisplayOverlay).toBeCalledTimes(0);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
cut.element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(1);
|
).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -229,10 +220,10 @@ describe('tab', () => {
|
|||||||
groupPanel
|
groupPanel
|
||||||
);
|
);
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(cut.element, 'clientWidth', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'offsetWidth', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -253,7 +244,7 @@ describe('tab', () => {
|
|||||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(1);
|
expect(groupView.canDisplayOverlay).toBeCalledTimes(1);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
cut.element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -289,10 +280,10 @@ describe('tab', () => {
|
|||||||
groupPanel
|
groupPanel
|
||||||
);
|
);
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(cut.element, 'clientWidth', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'offsetWidth', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -313,7 +304,7 @@ describe('tab', () => {
|
|||||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(1);
|
expect(groupView.canDisplayOverlay).toBeCalledTimes(1);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
cut.element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
import { DockviewApi } from '../../../../api/component.api';
|
||||||
|
import { DockviewPanelApi, TitleEvent } from '../../../../api/dockviewPanelApi';
|
||||||
|
import { DefaultTab } from '../../../../dockview/components/tab/defaultTab';
|
||||||
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
|
import { Emitter } from '../../../../events';
|
||||||
|
import { fireEvent } from '@testing-library/dom';
|
||||||
|
|
||||||
|
describe('defaultTab', () => {
|
||||||
|
test('that title updates', () => {
|
||||||
|
const cut = new DefaultTab();
|
||||||
|
|
||||||
|
let el = cut.element.querySelector('.dv-default-tab-content');
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
expect(el!.textContent).toBe('');
|
||||||
|
|
||||||
|
const onDidTitleChange = new Emitter<TitleEvent>();
|
||||||
|
|
||||||
|
const api = fromPartial<DockviewPanelApi>({
|
||||||
|
onDidTitleChange: onDidTitleChange.event,
|
||||||
|
});
|
||||||
|
const containerApi = fromPartial<DockviewApi>({});
|
||||||
|
|
||||||
|
cut.init({
|
||||||
|
api,
|
||||||
|
containerApi,
|
||||||
|
params: {},
|
||||||
|
title: 'title_abc',
|
||||||
|
});
|
||||||
|
|
||||||
|
el = cut.element.querySelector('.dv-default-tab-content');
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
expect(el!.textContent).toBe('title_abc');
|
||||||
|
|
||||||
|
onDidTitleChange.fire({ title: 'title_def' });
|
||||||
|
|
||||||
|
expect(el!.textContent).toBe('title_def');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('that click closes tab', () => {
|
||||||
|
const cut = new DefaultTab();
|
||||||
|
|
||||||
|
const api = fromPartial<DockviewPanelApi>({
|
||||||
|
onDidTitleChange: jest.fn(),
|
||||||
|
close: jest.fn(),
|
||||||
|
});
|
||||||
|
const containerApi = fromPartial<DockviewApi>({});
|
||||||
|
|
||||||
|
cut.init({
|
||||||
|
api,
|
||||||
|
containerApi,
|
||||||
|
params: {},
|
||||||
|
title: 'title_abc',
|
||||||
|
});
|
||||||
|
|
||||||
|
let el = cut.element.querySelector('.dv-default-tab-action');
|
||||||
|
|
||||||
|
fireEvent.pointerDown(el!);
|
||||||
|
expect(api.close).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
|
fireEvent.click(el!);
|
||||||
|
expect(api.close).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,66 @@
|
|||||||
|
import { Tabs } from '../../../../dockview/components/titlebar/tabs';
|
||||||
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
|
import { DockviewGroupPanel } from '../../../../dockview/dockviewGroupPanel';
|
||||||
|
import { DockviewComponent } from '../../../../dockview/dockviewComponent';
|
||||||
|
|
||||||
|
describe('tabs', () => {
|
||||||
|
describe('disableCustomScrollbars', () => {
|
||||||
|
test('enabled by default', () => {
|
||||||
|
const cut = new Tabs(
|
||||||
|
fromPartial<DockviewGroupPanel>({}),
|
||||||
|
fromPartial<DockviewComponent>({
|
||||||
|
options: {},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
showTabsOverflowControl: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
cut.element.querySelectorAll(
|
||||||
|
'.dv-scrollable > .dv-tabs-container'
|
||||||
|
).length
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('enabled when disabled flag is false', () => {
|
||||||
|
const cut = new Tabs(
|
||||||
|
fromPartial<DockviewGroupPanel>({}),
|
||||||
|
fromPartial<DockviewComponent>({
|
||||||
|
options: {
|
||||||
|
scrollbars: 'custom',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
showTabsOverflowControl: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
cut.element.querySelectorAll(
|
||||||
|
'.dv-scrollable > .dv-tabs-container'
|
||||||
|
).length
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('disabled when disabled flag is true', () => {
|
||||||
|
const cut = new Tabs(
|
||||||
|
fromPartial<DockviewGroupPanel>({}),
|
||||||
|
fromPartial<DockviewComponent>({
|
||||||
|
options: {
|
||||||
|
scrollbars: 'native',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
showTabsOverflowControl: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
cut.element.querySelectorAll(
|
||||||
|
'.dv-scrollable > .dv-tabs-container'
|
||||||
|
).length
|
||||||
|
).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -10,13 +10,15 @@ import { fireEvent } from '@testing-library/dom';
|
|||||||
import { TestPanel } from '../../dockviewGroupPanelModel.spec';
|
import { TestPanel } from '../../dockviewGroupPanelModel.spec';
|
||||||
import { IDockviewPanel } from '../../../../dockview/dockviewPanel';
|
import { IDockviewPanel } from '../../../../dockview/dockviewPanel';
|
||||||
import { fromPartial } from '@total-typescript/shoehorn';
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
|
import { DockviewPanelApi } from '../../../../api/dockviewPanelApi';
|
||||||
|
|
||||||
describe('tabsContainer', () => {
|
describe('tabsContainer', () => {
|
||||||
test('that an external event does not render a drop target and calls through to the group mode', () => {
|
test('that an external event does not render a drop target and calls through to the group mode', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
||||||
@ -40,17 +42,17 @@ describe('tabsContainer', () => {
|
|||||||
const cut = new TabsContainer(accessor, groupPanel);
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
const emptySpace = cut.element
|
const emptySpace = cut.element
|
||||||
.getElementsByClassName('void-container')
|
.getElementsByClassName('dv-void-container')
|
||||||
.item(0);
|
.item(0) as HTMLElement;
|
||||||
|
|
||||||
if (!emptySpace!) {
|
if (!emptySpace!) {
|
||||||
fail('element not found');
|
fail('element not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
jest.spyOn(emptySpace!, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(emptySpace!, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(emptySpace!, 'clientWidth', 'get').mockImplementation(
|
jest.spyOn(emptySpace!, 'offsetWidth', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -60,7 +62,7 @@ describe('tabsContainer', () => {
|
|||||||
expect(groupView.canDisplayOverlay).toHaveBeenCalled();
|
expect(groupView.canDisplayOverlay).toHaveBeenCalled();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
cut.element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -69,18 +71,18 @@ describe('tabsContainer', () => {
|
|||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
const dropTargetContainer = document.createElement('div');
|
||||||
() => {
|
|
||||||
return {
|
|
||||||
canDisplayOverlay: jest.fn(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const groupView = new groupviewMock() as DockviewGroupPanelModel;
|
const groupView = fromPartial<DockviewGroupPanelModel>({
|
||||||
|
canDisplayOverlay: jest.fn(),
|
||||||
|
// dropTargetContainer: new DropTargetAnchorContainer(
|
||||||
|
// dropTargetContainer
|
||||||
|
// ),
|
||||||
|
});
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<Partial<DockviewGroupPanel>, []>(() => {
|
const groupPanelMock = jest.fn<Partial<DockviewGroupPanel>, []>(() => {
|
||||||
return {
|
return {
|
||||||
@ -95,17 +97,17 @@ describe('tabsContainer', () => {
|
|||||||
const cut = new TabsContainer(accessor, groupPanel);
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
const emptySpace = cut.element
|
const emptySpace = cut.element
|
||||||
.getElementsByClassName('void-container')
|
.getElementsByClassName('dv-void-container')
|
||||||
.item(0);
|
.item(0) as HTMLElement;
|
||||||
|
|
||||||
if (!emptySpace!) {
|
if (!emptySpace!) {
|
||||||
fail('element not found');
|
fail('element not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
jest.spyOn(emptySpace!, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(emptySpace!, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(emptySpace!, 'clientWidth', 'get').mockImplementation(
|
jest.spyOn(emptySpace!, 'offsetWidth', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -126,8 +128,12 @@ describe('tabsContainer', () => {
|
|||||||
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(0);
|
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
cut.element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(1);
|
).toBe(1);
|
||||||
|
// expect(
|
||||||
|
// dropTargetContainer.getElementsByClassName('dv-drop-target-anchor')
|
||||||
|
// .length
|
||||||
|
// ).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that dropping over the empty space should render a drop target', () => {
|
test('that dropping over the empty space should render a drop target', () => {
|
||||||
@ -135,7 +141,8 @@ describe('tabsContainer', () => {
|
|||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
||||||
@ -164,17 +171,17 @@ describe('tabsContainer', () => {
|
|||||||
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
||||||
|
|
||||||
const emptySpace = cut.element
|
const emptySpace = cut.element
|
||||||
.getElementsByClassName('void-container')
|
.getElementsByClassName('dv-void-container')
|
||||||
.item(0);
|
.item(0) as HTMLElement;
|
||||||
|
|
||||||
if (!emptySpace!) {
|
if (!emptySpace!) {
|
||||||
fail('element not found');
|
fail('element not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
jest.spyOn(emptySpace!, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(emptySpace!, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(emptySpace!, 'clientWidth', 'get').mockImplementation(
|
jest.spyOn(emptySpace!, 'offsetWidth', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -189,7 +196,7 @@ describe('tabsContainer', () => {
|
|||||||
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(0);
|
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
cut.element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(1);
|
).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -198,7 +205,8 @@ describe('tabsContainer', () => {
|
|||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
||||||
@ -227,17 +235,17 @@ describe('tabsContainer', () => {
|
|||||||
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
||||||
|
|
||||||
const emptySpace = cut.element
|
const emptySpace = cut.element
|
||||||
.getElementsByClassName('void-container')
|
.getElementsByClassName('dv-void-container')
|
||||||
.item(0);
|
.item(0) as HTMLElement;
|
||||||
|
|
||||||
if (!emptySpace!) {
|
if (!emptySpace!) {
|
||||||
fail('element not found');
|
fail('element not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
jest.spyOn(emptySpace!, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(emptySpace!, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(emptySpace!, 'clientWidth', 'get').mockImplementation(
|
jest.spyOn(emptySpace!, 'offsetWidth', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -252,7 +260,7 @@ describe('tabsContainer', () => {
|
|||||||
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(0);
|
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
cut.element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(1);
|
).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -261,7 +269,8 @@ describe('tabsContainer', () => {
|
|||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
||||||
@ -289,17 +298,17 @@ describe('tabsContainer', () => {
|
|||||||
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
||||||
|
|
||||||
const emptySpace = cut.element
|
const emptySpace = cut.element
|
||||||
.getElementsByClassName('void-container')
|
.getElementsByClassName('dv-void-container')
|
||||||
.item(0);
|
.item(0) as HTMLElement;
|
||||||
|
|
||||||
if (!emptySpace!) {
|
if (!emptySpace!) {
|
||||||
fail('element not found');
|
fail('element not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
jest.spyOn(emptySpace!, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(emptySpace!, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(emptySpace!, 'clientWidth', 'get').mockImplementation(
|
jest.spyOn(emptySpace!, 'offsetWidth', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -320,7 +329,7 @@ describe('tabsContainer', () => {
|
|||||||
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(1);
|
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
cut.element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -329,7 +338,8 @@ describe('tabsContainer', () => {
|
|||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||||
@ -341,7 +351,7 @@ describe('tabsContainer', () => {
|
|||||||
const cut = new TabsContainer(accessor, groupPanel);
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
let query = cut.element.querySelectorAll(
|
let query = cut.element.querySelectorAll(
|
||||||
'.tabs-and-actions-container > .left-actions-container'
|
'.dv-tabs-and-actions-container > .dv-left-actions-container'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(query.length).toBe(1);
|
expect(query.length).toBe(1);
|
||||||
@ -354,7 +364,7 @@ describe('tabsContainer', () => {
|
|||||||
cut.setLeftActionsElement(left);
|
cut.setLeftActionsElement(left);
|
||||||
|
|
||||||
query = cut.element.querySelectorAll(
|
query = cut.element.querySelectorAll(
|
||||||
'.tabs-and-actions-container > .left-actions-container'
|
'.dv-tabs-and-actions-container > .dv-left-actions-container'
|
||||||
);
|
);
|
||||||
expect(query.length).toBe(1);
|
expect(query.length).toBe(1);
|
||||||
expect(query[0].children.item(0)?.className).toBe(
|
expect(query[0].children.item(0)?.className).toBe(
|
||||||
@ -369,7 +379,7 @@ describe('tabsContainer', () => {
|
|||||||
cut.setLeftActionsElement(left2);
|
cut.setLeftActionsElement(left2);
|
||||||
|
|
||||||
query = cut.element.querySelectorAll(
|
query = cut.element.querySelectorAll(
|
||||||
'.tabs-and-actions-container > .left-actions-container'
|
'.dv-tabs-and-actions-container > .dv-left-actions-container'
|
||||||
);
|
);
|
||||||
expect(query.length).toBe(1);
|
expect(query.length).toBe(1);
|
||||||
expect(query[0].children.item(0)?.className).toBe(
|
expect(query[0].children.item(0)?.className).toBe(
|
||||||
@ -381,7 +391,7 @@ describe('tabsContainer', () => {
|
|||||||
|
|
||||||
cut.setLeftActionsElement(undefined);
|
cut.setLeftActionsElement(undefined);
|
||||||
query = cut.element.querySelectorAll(
|
query = cut.element.querySelectorAll(
|
||||||
'.tabs-and-actions-container > .left-actions-container'
|
'.dv-tabs-and-actions-container > .dv-left-actions-container'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(query.length).toBe(1);
|
expect(query.length).toBe(1);
|
||||||
@ -393,7 +403,8 @@ describe('tabsContainer', () => {
|
|||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||||
@ -405,7 +416,7 @@ describe('tabsContainer', () => {
|
|||||||
const cut = new TabsContainer(accessor, groupPanel);
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
let query = cut.element.querySelectorAll(
|
let query = cut.element.querySelectorAll(
|
||||||
'.tabs-and-actions-container > .right-actions-container'
|
'.dv-tabs-and-actions-container > .dv-right-actions-container'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(query.length).toBe(1);
|
expect(query.length).toBe(1);
|
||||||
@ -418,7 +429,7 @@ describe('tabsContainer', () => {
|
|||||||
cut.setRightActionsElement(right);
|
cut.setRightActionsElement(right);
|
||||||
|
|
||||||
query = cut.element.querySelectorAll(
|
query = cut.element.querySelectorAll(
|
||||||
'.tabs-and-actions-container > .right-actions-container'
|
'.dv-tabs-and-actions-container > .dv-right-actions-container'
|
||||||
);
|
);
|
||||||
expect(query.length).toBe(1);
|
expect(query.length).toBe(1);
|
||||||
expect(query[0].children.item(0)?.className).toBe(
|
expect(query[0].children.item(0)?.className).toBe(
|
||||||
@ -433,7 +444,7 @@ describe('tabsContainer', () => {
|
|||||||
cut.setRightActionsElement(right2);
|
cut.setRightActionsElement(right2);
|
||||||
|
|
||||||
query = cut.element.querySelectorAll(
|
query = cut.element.querySelectorAll(
|
||||||
'.tabs-and-actions-container > .right-actions-container'
|
'.dv-tabs-and-actions-container > .dv-right-actions-container'
|
||||||
);
|
);
|
||||||
expect(query.length).toBe(1);
|
expect(query.length).toBe(1);
|
||||||
expect(query[0].children.item(0)?.className).toBe(
|
expect(query[0].children.item(0)?.className).toBe(
|
||||||
@ -445,7 +456,7 @@ describe('tabsContainer', () => {
|
|||||||
|
|
||||||
cut.setRightActionsElement(undefined);
|
cut.setRightActionsElement(undefined);
|
||||||
query = cut.element.querySelectorAll(
|
query = cut.element.querySelectorAll(
|
||||||
'.tabs-and-actions-container > .right-actions-container'
|
'.dv-tabs-and-actions-container > .dv-right-actions-container'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(query.length).toBe(1);
|
expect(query.length).toBe(1);
|
||||||
@ -454,11 +465,13 @@ describe('tabsContainer', () => {
|
|||||||
|
|
||||||
test('that a tab will become floating when clicked if not floating and shift is selected', () => {
|
test('that a tab will become floating when clicked if not floating and shift is selected', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
element: document.createElement('div'),
|
element: document.createElement('div'),
|
||||||
addFloatingGroup: jest.fn(),
|
addFloatingGroup: jest.fn(),
|
||||||
|
doSetGroupActive: jest.fn(),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||||
@ -471,7 +484,7 @@ describe('tabsContainer', () => {
|
|||||||
|
|
||||||
const cut = new TabsContainer(accessor, groupPanel);
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
const container = cut.element.querySelector('.void-container')!;
|
const container = cut.element.querySelector('.dv-void-container')!;
|
||||||
expect(container).toBeTruthy();
|
expect(container).toBeTruthy();
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'getBoundingClientRect').mockImplementation(
|
jest.spyOn(cut.element, 'getBoundingClientRect').mockImplementation(
|
||||||
@ -486,22 +499,20 @@ describe('tabsContainer', () => {
|
|||||||
return { top: 10, left: 20, width: 0, height: 0 } as any;
|
return { top: 10, left: 20, width: 0, height: 0 } as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
const event = new KeyboardEvent('mousedown', { shiftKey: true });
|
const event = new KeyboardEvent('pointerdown', { shiftKey: true });
|
||||||
const eventPreventDefaultSpy = jest.spyOn(event, 'preventDefault');
|
const eventPreventDefaultSpy = jest.spyOn(event, 'preventDefault');
|
||||||
fireEvent(container, event);
|
fireEvent(container, event);
|
||||||
|
|
||||||
expect(accessor.addFloatingGroup).toHaveBeenCalledWith(
|
expect(accessor.doSetGroupActive).toHaveBeenCalledWith(groupPanel);
|
||||||
groupPanel,
|
expect(accessor.addFloatingGroup).toHaveBeenCalledWith(groupPanel, {
|
||||||
{
|
|
||||||
x: 100,
|
x: 100,
|
||||||
y: 60,
|
y: 60,
|
||||||
},
|
inDragMode: true,
|
||||||
{ inDragMode: true }
|
});
|
||||||
);
|
|
||||||
expect(accessor.addFloatingGroup).toHaveBeenCalledTimes(1);
|
expect(accessor.addFloatingGroup).toHaveBeenCalledTimes(1);
|
||||||
expect(eventPreventDefaultSpy).toHaveBeenCalledTimes(1);
|
expect(eventPreventDefaultSpy).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
const event2 = new KeyboardEvent('mousedown', { shiftKey: false });
|
const event2 = new KeyboardEvent('pointerdown', { shiftKey: false });
|
||||||
const eventPreventDefaultSpy2 = jest.spyOn(event2, 'preventDefault');
|
const eventPreventDefaultSpy2 = jest.spyOn(event2, 'preventDefault');
|
||||||
fireEvent(container, event2);
|
fireEvent(container, event2);
|
||||||
|
|
||||||
@ -511,11 +522,13 @@ describe('tabsContainer', () => {
|
|||||||
|
|
||||||
test('that a tab that is already floating cannot be floated again', () => {
|
test('that a tab that is already floating cannot be floated again', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
element: document.createElement('div'),
|
element: document.createElement('div'),
|
||||||
addFloatingGroup: jest.fn(),
|
addFloatingGroup: jest.fn(),
|
||||||
|
doSetGroupActive: jest.fn(),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||||
@ -528,7 +541,7 @@ describe('tabsContainer', () => {
|
|||||||
|
|
||||||
const cut = new TabsContainer(accessor, groupPanel);
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
const container = cut.element.querySelector('.void-container')!;
|
const container = cut.element.querySelector('.dv-void-container')!;
|
||||||
expect(container).toBeTruthy();
|
expect(container).toBeTruthy();
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'getBoundingClientRect').mockImplementation(
|
jest.spyOn(cut.element, 'getBoundingClientRect').mockImplementation(
|
||||||
@ -543,14 +556,15 @@ describe('tabsContainer', () => {
|
|||||||
return { top: 10, left: 20, width: 0, height: 0 } as any;
|
return { top: 10, left: 20, width: 0, height: 0 } as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
const event = new KeyboardEvent('mousedown', { shiftKey: true });
|
const event = new KeyboardEvent('pointerdown', { shiftKey: true });
|
||||||
const eventPreventDefaultSpy = jest.spyOn(event, 'preventDefault');
|
const eventPreventDefaultSpy = jest.spyOn(event, 'preventDefault');
|
||||||
fireEvent(container, event);
|
fireEvent(container, event);
|
||||||
|
|
||||||
|
expect(accessor.doSetGroupActive).toHaveBeenCalledWith(groupPanel);
|
||||||
expect(accessor.addFloatingGroup).toHaveBeenCalledTimes(0);
|
expect(accessor.addFloatingGroup).toHaveBeenCalledTimes(0);
|
||||||
expect(eventPreventDefaultSpy).toHaveBeenCalledTimes(0);
|
expect(eventPreventDefaultSpy).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
const event2 = new KeyboardEvent('mousedown', { shiftKey: false });
|
const event2 = new KeyboardEvent('pointerdown', { shiftKey: false });
|
||||||
const eventPreventDefaultSpy2 = jest.spyOn(event2, 'preventDefault');
|
const eventPreventDefaultSpy2 = jest.spyOn(event2, 'preventDefault');
|
||||||
fireEvent(container, event2);
|
fireEvent(container, event2);
|
||||||
|
|
||||||
@ -560,12 +574,13 @@ describe('tabsContainer', () => {
|
|||||||
|
|
||||||
test('that selecting a tab with shift down will move that tab into a new floating group', () => {
|
test('that selecting a tab with shift down will move that tab into a new floating group', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
element: document.createElement('div'),
|
element: document.createElement('div'),
|
||||||
addFloatingGroup: jest.fn(),
|
addFloatingGroup: jest.fn(),
|
||||||
getGroupPanel: jest.fn(),
|
getGroupPanel: jest.fn(),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||||
@ -595,10 +610,10 @@ describe('tabsContainer', () => {
|
|||||||
const panel = createPanel('test_id');
|
const panel = createPanel('test_id');
|
||||||
cut.openPanel(panel);
|
cut.openPanel(panel);
|
||||||
|
|
||||||
const el = cut.element.querySelector('.tab')!;
|
const el = cut.element.querySelector('.dv-tab')!;
|
||||||
expect(el).toBeTruthy();
|
expect(el).toBeTruthy();
|
||||||
|
|
||||||
const event = new KeyboardEvent('mousedown', { shiftKey: true });
|
const event = new KeyboardEvent('pointerdown', { shiftKey: true });
|
||||||
const preventDefaultSpy = jest.spyOn(event, 'preventDefault');
|
const preventDefaultSpy = jest.spyOn(event, 'preventDefault');
|
||||||
fireEvent(el, event);
|
fireEvent(el, event);
|
||||||
|
|
||||||
@ -616,12 +631,13 @@ describe('tabsContainer', () => {
|
|||||||
|
|
||||||
test('pre header actions', () => {
|
test('pre header actions', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
element: document.createElement('div'),
|
element: document.createElement('div'),
|
||||||
addFloatingGroup: jest.fn(),
|
addFloatingGroup: jest.fn(),
|
||||||
getGroupPanel: jest.fn(),
|
getGroupPanel: jest.fn(),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||||
@ -654,14 +670,14 @@ describe('tabsContainer', () => {
|
|||||||
const panel = new panelMock('test_id');
|
const panel = new panelMock('test_id');
|
||||||
cut.openPanel(panel);
|
cut.openPanel(panel);
|
||||||
|
|
||||||
let result = cut.element.querySelector('.pre-actions-container');
|
let result = cut.element.querySelector('.dv-pre-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(0);
|
expect(result!.childNodes.length).toBe(0);
|
||||||
|
|
||||||
const actions = document.createElement('div');
|
const actions = document.createElement('div');
|
||||||
cut.setPrefixActionsElement(actions);
|
cut.setPrefixActionsElement(actions);
|
||||||
|
|
||||||
result = cut.element.querySelector('.pre-actions-container');
|
result = cut.element.querySelector('.dv-pre-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(1);
|
expect(result!.childNodes.length).toBe(1);
|
||||||
expect(result!.childNodes.item(0)).toBe(actions);
|
expect(result!.childNodes.item(0)).toBe(actions);
|
||||||
@ -669,26 +685,27 @@ describe('tabsContainer', () => {
|
|||||||
const updatedActions = document.createElement('div');
|
const updatedActions = document.createElement('div');
|
||||||
cut.setPrefixActionsElement(updatedActions);
|
cut.setPrefixActionsElement(updatedActions);
|
||||||
|
|
||||||
result = cut.element.querySelector('.pre-actions-container');
|
result = cut.element.querySelector('.dv-pre-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(1);
|
expect(result!.childNodes.length).toBe(1);
|
||||||
expect(result!.childNodes.item(0)).toBe(updatedActions);
|
expect(result!.childNodes.item(0)).toBe(updatedActions);
|
||||||
|
|
||||||
cut.setPrefixActionsElement(undefined);
|
cut.setPrefixActionsElement(undefined);
|
||||||
|
|
||||||
result = cut.element.querySelector('.pre-actions-container');
|
result = cut.element.querySelector('.dv-pre-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(0);
|
expect(result!.childNodes.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('left header actions', () => {
|
test('left header actions', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
element: document.createElement('div'),
|
element: document.createElement('div'),
|
||||||
addFloatingGroup: jest.fn(),
|
addFloatingGroup: jest.fn(),
|
||||||
getGroupPanel: jest.fn(),
|
getGroupPanel: jest.fn(),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||||
@ -721,14 +738,14 @@ describe('tabsContainer', () => {
|
|||||||
const panel = new panelMock('test_id');
|
const panel = new panelMock('test_id');
|
||||||
cut.openPanel(panel);
|
cut.openPanel(panel);
|
||||||
|
|
||||||
let result = cut.element.querySelector('.left-actions-container');
|
let result = cut.element.querySelector('.dv-left-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(0);
|
expect(result!.childNodes.length).toBe(0);
|
||||||
|
|
||||||
const actions = document.createElement('div');
|
const actions = document.createElement('div');
|
||||||
cut.setLeftActionsElement(actions);
|
cut.setLeftActionsElement(actions);
|
||||||
|
|
||||||
result = cut.element.querySelector('.left-actions-container');
|
result = cut.element.querySelector('.dv-left-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(1);
|
expect(result!.childNodes.length).toBe(1);
|
||||||
expect(result!.childNodes.item(0)).toBe(actions);
|
expect(result!.childNodes.item(0)).toBe(actions);
|
||||||
@ -736,26 +753,27 @@ describe('tabsContainer', () => {
|
|||||||
const updatedActions = document.createElement('div');
|
const updatedActions = document.createElement('div');
|
||||||
cut.setLeftActionsElement(updatedActions);
|
cut.setLeftActionsElement(updatedActions);
|
||||||
|
|
||||||
result = cut.element.querySelector('.left-actions-container');
|
result = cut.element.querySelector('.dv-left-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(1);
|
expect(result!.childNodes.length).toBe(1);
|
||||||
expect(result!.childNodes.item(0)).toBe(updatedActions);
|
expect(result!.childNodes.item(0)).toBe(updatedActions);
|
||||||
|
|
||||||
cut.setLeftActionsElement(undefined);
|
cut.setLeftActionsElement(undefined);
|
||||||
|
|
||||||
result = cut.element.querySelector('.left-actions-container');
|
result = cut.element.querySelector('.dv-left-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(0);
|
expect(result!.childNodes.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('right header actions', () => {
|
test('right header actions', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
element: document.createElement('div'),
|
element: document.createElement('div'),
|
||||||
addFloatingGroup: jest.fn(),
|
addFloatingGroup: jest.fn(),
|
||||||
getGroupPanel: jest.fn(),
|
getGroupPanel: jest.fn(),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||||
@ -788,14 +806,14 @@ describe('tabsContainer', () => {
|
|||||||
const panel = new panelMock('test_id');
|
const panel = new panelMock('test_id');
|
||||||
cut.openPanel(panel);
|
cut.openPanel(panel);
|
||||||
|
|
||||||
let result = cut.element.querySelector('.right-actions-container');
|
let result = cut.element.querySelector('.dv-right-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(0);
|
expect(result!.childNodes.length).toBe(0);
|
||||||
|
|
||||||
const actions = document.createElement('div');
|
const actions = document.createElement('div');
|
||||||
cut.setRightActionsElement(actions);
|
cut.setRightActionsElement(actions);
|
||||||
|
|
||||||
result = cut.element.querySelector('.right-actions-container');
|
result = cut.element.querySelector('.dv-right-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(1);
|
expect(result!.childNodes.length).toBe(1);
|
||||||
expect(result!.childNodes.item(0)).toBe(actions);
|
expect(result!.childNodes.item(0)).toBe(actions);
|
||||||
@ -803,15 +821,47 @@ describe('tabsContainer', () => {
|
|||||||
const updatedActions = document.createElement('div');
|
const updatedActions = document.createElement('div');
|
||||||
cut.setRightActionsElement(updatedActions);
|
cut.setRightActionsElement(updatedActions);
|
||||||
|
|
||||||
result = cut.element.querySelector('.right-actions-container');
|
result = cut.element.querySelector('.dv-right-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(1);
|
expect(result!.childNodes.length).toBe(1);
|
||||||
expect(result!.childNodes.item(0)).toBe(updatedActions);
|
expect(result!.childNodes.item(0)).toBe(updatedActions);
|
||||||
|
|
||||||
cut.setRightActionsElement(undefined);
|
cut.setRightActionsElement(undefined);
|
||||||
|
|
||||||
result = cut.element.querySelector('.right-actions-container');
|
result = cut.element.querySelector('.dv-right-actions-container');
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
expect(result!.childNodes.length).toBe(0);
|
expect(result!.childNodes.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('class dv-single-tab is present when only one tab exists`', () => {
|
||||||
|
const cut = new TabsContainer(
|
||||||
|
fromPartial<DockviewComponent>({
|
||||||
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
|
}),
|
||||||
|
fromPartial<DockviewGroupPanel>({})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(cut.element.classList.contains('dv-single-tab')).toBeFalsy();
|
||||||
|
|
||||||
|
const panel1 = new TestPanel(
|
||||||
|
'panel_1',
|
||||||
|
fromPartial<DockviewPanelApi>({})
|
||||||
|
);
|
||||||
|
cut.openPanel(panel1);
|
||||||
|
expect(cut.element.classList.contains('dv-single-tab')).toBeTruthy();
|
||||||
|
|
||||||
|
const panel2 = new TestPanel(
|
||||||
|
'panel_2',
|
||||||
|
fromPartial<DockviewPanelApi>({})
|
||||||
|
);
|
||||||
|
cut.openPanel(panel2);
|
||||||
|
expect(cut.element.classList.contains('dv-single-tab')).toBeFalsy();
|
||||||
|
|
||||||
|
cut.closePanel(panel1);
|
||||||
|
expect(cut.element.classList.contains('dv-single-tab')).toBeTruthy();
|
||||||
|
|
||||||
|
cut.closePanel(panel2);
|
||||||
|
expect(cut.element.classList.contains('dv-single-tab')).toBeFalsy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
import { VoidContainer } from '../../../../dockview/components/titlebar/voidContainer';
|
||||||
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
|
import { DockviewComponent } from '../../../../dockview/dockviewComponent';
|
||||||
|
import { DockviewGroupPanel } from '../../../../dockview/dockviewGroupPanel';
|
||||||
|
import { fireEvent } from '@testing-library/dom';
|
||||||
|
|
||||||
|
describe('voidContainer', () => {
|
||||||
|
test('that `pointerDown` triggers activation', () => {
|
||||||
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
|
doSetGroupActive: jest.fn(),
|
||||||
|
});
|
||||||
|
const group = fromPartial<DockviewGroupPanel>({});
|
||||||
|
const cut = new VoidContainer(accessor, group);
|
||||||
|
|
||||||
|
expect(accessor.doSetGroupActive).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
fireEvent.pointerDown(cut.element);
|
||||||
|
expect(accessor.doSetGroupActive).toHaveBeenCalledWith(group);
|
||||||
|
});
|
||||||
|
});
|
@ -1,27 +0,0 @@
|
|||||||
import { DockviewApi } from '../../../../api/component.api';
|
|
||||||
import { Watermark } from '../../../../dockview/components/watermark/watermark';
|
|
||||||
|
|
||||||
describe('watermark', () => {
|
|
||||||
test('that the group is closed when the close button is clicked', () => {
|
|
||||||
const cut = new Watermark();
|
|
||||||
|
|
||||||
const mockApi = jest.fn<Partial<DockviewApi>, any[]>(() => {
|
|
||||||
return {
|
|
||||||
removeGroup: jest.fn(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const api = <DockviewApi>new mockApi();
|
|
||||||
const group = jest.fn() as any;
|
|
||||||
|
|
||||||
cut.init({ containerApi: api });
|
|
||||||
cut.updateParentGroup(group, true);
|
|
||||||
|
|
||||||
const closeEl = cut.element.querySelector('.close-action')!;
|
|
||||||
|
|
||||||
expect(closeEl).toBeTruthy();
|
|
||||||
|
|
||||||
closeEl.dispatchEvent(new Event('click'));
|
|
||||||
|
|
||||||
expect(api.removeGroup).toHaveBeenCalledWith(group);
|
|
||||||
});
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,190 @@
|
|||||||
|
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
||||||
|
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
||||||
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
|
import { GroupOptions } from '../../dockview/dockviewGroupPanelModel';
|
||||||
|
import { DockviewPanel, IDockviewPanel } from '../../dockview/dockviewPanel';
|
||||||
|
import { DockviewPanelModelMock } from '../__mocks__/mockDockviewPanelModel';
|
||||||
|
import { IContentRenderer, ITabRenderer } from '../../dockview/types';
|
||||||
|
import { OverlayRenderContainer } from '../../overlay/overlayRenderContainer';
|
||||||
|
import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel';
|
||||||
|
import { ContentContainer } from '../../dockview/components/panel/content';
|
||||||
|
|
||||||
|
describe('dockviewGroupPanel', () => {
|
||||||
|
test('default minimum/maximium width/height', () => {
|
||||||
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
|
onDidActivePanelChange: jest.fn(),
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
|
});
|
||||||
|
const options = fromPartial<GroupOptions>({});
|
||||||
|
const cut = new DockviewGroupPanel(accessor, 'test_id', options);
|
||||||
|
|
||||||
|
expect(cut.minimumWidth).toBe(100);
|
||||||
|
expect(cut.minimumHeight).toBe(100);
|
||||||
|
expect(cut.maximumHeight).toBe(Number.MAX_SAFE_INTEGER);
|
||||||
|
expect(cut.maximumWidth).toBe(Number.MAX_SAFE_INTEGER);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('that onDidActivePanelChange is configured at inline', () => {
|
||||||
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
|
onDidActivePanelChange: jest.fn(),
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
options: {},
|
||||||
|
api: {},
|
||||||
|
renderer: 'always',
|
||||||
|
overlayRenderContainer: {
|
||||||
|
attach: jest.fn(),
|
||||||
|
detatch: jest.fn(),
|
||||||
|
},
|
||||||
|
doSetGroupActive: jest.fn(),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
|
});
|
||||||
|
const options = fromPartial<GroupOptions>({});
|
||||||
|
|
||||||
|
const cut = new DockviewGroupPanel(accessor, 'test_id', options);
|
||||||
|
|
||||||
|
let counter = 0;
|
||||||
|
|
||||||
|
cut.api.onDidActivePanelChange((event) => {
|
||||||
|
counter++;
|
||||||
|
});
|
||||||
|
|
||||||
|
cut.model.openPanel(
|
||||||
|
fromPartial<IDockviewPanel>({
|
||||||
|
updateParentGroup: jest.fn(),
|
||||||
|
view: {
|
||||||
|
tab: { element: document.createElement('div') },
|
||||||
|
content: new ContentContainer(accessor, cut.model),
|
||||||
|
},
|
||||||
|
api: {
|
||||||
|
renderer: 'onlyWhenVisible',
|
||||||
|
onDidTitleChange: jest.fn(),
|
||||||
|
onDidParametersChange: jest.fn(),
|
||||||
|
},
|
||||||
|
layout: jest.fn(),
|
||||||
|
runEvents: jest.fn(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(counter).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('group constraints', () => {
|
||||||
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
|
onDidActivePanelChange: jest.fn(),
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
doSetGroupActive: jest.fn(),
|
||||||
|
overlayRenderContainer: fromPartial<OverlayRenderContainer>({
|
||||||
|
attach: jest.fn(),
|
||||||
|
detatch: jest.fn(),
|
||||||
|
}),
|
||||||
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
|
});
|
||||||
|
const options = fromPartial<GroupOptions>({});
|
||||||
|
const cut = new DockviewGroupPanel(accessor, 'test_id', options);
|
||||||
|
|
||||||
|
cut.api.setConstraints({
|
||||||
|
minimumHeight: 10,
|
||||||
|
maximumHeight: 100,
|
||||||
|
minimumWidth: 20,
|
||||||
|
maximumWidth: 200,
|
||||||
|
});
|
||||||
|
|
||||||
|
// initial constraints
|
||||||
|
|
||||||
|
expect(cut.minimumWidth).toBe(20);
|
||||||
|
expect(cut.minimumHeight).toBe(10);
|
||||||
|
expect(cut.maximumHeight).toBe(100);
|
||||||
|
expect(cut.maximumWidth).toBe(200);
|
||||||
|
|
||||||
|
const panelModel = new DockviewPanelModelMock(
|
||||||
|
'content_component',
|
||||||
|
fromPartial<IContentRenderer>({
|
||||||
|
element: document.createElement('div'),
|
||||||
|
}),
|
||||||
|
'tab_component',
|
||||||
|
fromPartial<ITabRenderer>({
|
||||||
|
element: document.createElement('div'),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const panel = new DockviewPanel(
|
||||||
|
'panel_id',
|
||||||
|
'component_id',
|
||||||
|
undefined,
|
||||||
|
accessor,
|
||||||
|
accessor.api,
|
||||||
|
cut,
|
||||||
|
panelModel,
|
||||||
|
{
|
||||||
|
renderer: 'onlyWhenVisible',
|
||||||
|
minimumWidth: 21,
|
||||||
|
minimumHeight: 11,
|
||||||
|
maximumHeight: 101,
|
||||||
|
maximumWidth: 201,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
cut.model.openPanel(panel);
|
||||||
|
|
||||||
|
// active panel constraints
|
||||||
|
|
||||||
|
expect(cut.minimumWidth).toBe(21);
|
||||||
|
expect(cut.minimumHeight).toBe(11);
|
||||||
|
expect(cut.maximumHeight).toBe(101);
|
||||||
|
expect(cut.maximumWidth).toBe(201);
|
||||||
|
|
||||||
|
const panel2 = new DockviewPanel(
|
||||||
|
'panel_id',
|
||||||
|
'component_id',
|
||||||
|
undefined,
|
||||||
|
accessor,
|
||||||
|
accessor.api,
|
||||||
|
cut,
|
||||||
|
panelModel,
|
||||||
|
{
|
||||||
|
renderer: 'onlyWhenVisible',
|
||||||
|
minimumWidth: 22,
|
||||||
|
minimumHeight: 12,
|
||||||
|
maximumHeight: 102,
|
||||||
|
maximumWidth: 202,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
cut.model.openPanel(panel2);
|
||||||
|
|
||||||
|
// active panel constraints
|
||||||
|
|
||||||
|
expect(cut.minimumWidth).toBe(22);
|
||||||
|
expect(cut.minimumHeight).toBe(12);
|
||||||
|
expect(cut.maximumHeight).toBe(102);
|
||||||
|
expect(cut.maximumWidth).toBe(202);
|
||||||
|
|
||||||
|
const panel3 = new DockviewPanel(
|
||||||
|
'panel_id',
|
||||||
|
'component_id',
|
||||||
|
undefined,
|
||||||
|
accessor,
|
||||||
|
accessor.api,
|
||||||
|
cut,
|
||||||
|
panelModel,
|
||||||
|
{
|
||||||
|
renderer: 'onlyWhenVisible',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
cut.model.openPanel(panel3);
|
||||||
|
|
||||||
|
// active panel without specified constraints so falls back to group constraints
|
||||||
|
|
||||||
|
expect(cut.minimumWidth).toBe(20);
|
||||||
|
expect(cut.minimumHeight).toBe(10);
|
||||||
|
expect(cut.maximumHeight).toBe(100);
|
||||||
|
expect(cut.maximumWidth).toBe(200);
|
||||||
|
});
|
||||||
|
});
|
@ -9,40 +9,22 @@ import {
|
|||||||
} from '../../dockview/types';
|
} from '../../dockview/types';
|
||||||
import { PanelUpdateEvent, Parameters } from '../../panel/types';
|
import { PanelUpdateEvent, Parameters } from '../../panel/types';
|
||||||
import {
|
import {
|
||||||
DockviewGroupLocation,
|
|
||||||
DockviewGroupPanelModel,
|
DockviewGroupPanelModel,
|
||||||
GroupOptions,
|
GroupOptions,
|
||||||
} from '../../dockview/dockviewGroupPanelModel';
|
} from '../../dockview/dockviewGroupPanelModel';
|
||||||
import { fireEvent } from '@testing-library/dom';
|
import { fireEvent } from '@testing-library/dom';
|
||||||
import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer';
|
import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer';
|
||||||
import { CompositeDisposable } from '../../lifecycle';
|
import { CompositeDisposable } from '../../lifecycle';
|
||||||
import {
|
import { DockviewPanelApi } from '../../api/dockviewPanelApi';
|
||||||
ActiveGroupEvent,
|
|
||||||
DockviewPanelApi,
|
|
||||||
GroupChangedEvent,
|
|
||||||
RendererChangedEvent,
|
|
||||||
} from '../../api/dockviewPanelApi';
|
|
||||||
import { IDockviewPanel } from '../../dockview/dockviewPanel';
|
import { IDockviewPanel } from '../../dockview/dockviewPanel';
|
||||||
import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel';
|
import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel';
|
||||||
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
||||||
import { WatermarkRendererInitParameters } from '../../dockview/types';
|
import { WatermarkRendererInitParameters } from '../../dockview/types';
|
||||||
import { createOffsetDragOverEvent } from '../__test_utils__/utils';
|
import { createOffsetDragOverEvent } from '../__test_utils__/utils';
|
||||||
import {
|
import { OverlayRenderContainer } from '../../overlay/overlayRenderContainer';
|
||||||
DockviewPanelRenderer,
|
import { Emitter } from '../../events';
|
||||||
OverlayRenderContainer,
|
|
||||||
} from '../../overlayRenderContainer';
|
|
||||||
import { DockviewGroupPanelFloatingChangeEvent } from '../../api/dockviewGroupPanelApi';
|
|
||||||
import { SizeEvent } from '../../api/gridviewPanelApi';
|
|
||||||
import {
|
|
||||||
PanelDimensionChangeEvent,
|
|
||||||
FocusEvent,
|
|
||||||
VisibilityEvent,
|
|
||||||
ActiveEvent,
|
|
||||||
WillFocusEvent,
|
|
||||||
} from '../../api/panelApi';
|
|
||||||
import { Position } from '../../dnd/droptarget';
|
|
||||||
import { Emitter, Event } from '../../events';
|
|
||||||
import { fromPartial } from '@total-typescript/shoehorn';
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
|
import { TabLocation } from '../../dockview/framework';
|
||||||
|
|
||||||
enum GroupChangeKind2 {
|
enum GroupChangeKind2 {
|
||||||
ADD_PANEL,
|
ADD_PANEL,
|
||||||
@ -55,12 +37,16 @@ class TestModel implements IDockviewPanelModel {
|
|||||||
readonly contentComponent: string;
|
readonly contentComponent: string;
|
||||||
readonly tab: ITabRenderer;
|
readonly tab: ITabRenderer;
|
||||||
|
|
||||||
constructor(id: string) {
|
constructor(readonly id: string) {
|
||||||
this.content = new TestHeaderPart(id);
|
this.content = new TestHeaderPart(id);
|
||||||
this.contentComponent = id;
|
this.contentComponent = id;
|
||||||
this.tab = new TestContentPart(id);
|
this.tab = new TestContentPart(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createTabRenderer(tabLocation: TabLocation): ITabRenderer {
|
||||||
|
return new TestHeaderPart(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
update(event: PanelUpdateEvent): void {
|
update(event: PanelUpdateEvent): void {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
@ -116,10 +102,6 @@ class Watermark implements IWatermarkRenderer {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParentGroup() {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
@ -276,7 +258,7 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
dockview = fromPartial<DockviewComponent>({
|
dockview = fromPartial<DockviewComponent>({
|
||||||
options: { parentElement: document.createElement('div') },
|
options: {},
|
||||||
createWatermarkComponent: () => new Watermark(),
|
createWatermarkComponent: () => new Watermark(),
|
||||||
doSetGroupActive: jest.fn(),
|
doSetGroupActive: jest.fn(),
|
||||||
id: 'dockview-1',
|
id: 'dockview-1',
|
||||||
@ -285,8 +267,10 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
onDidAddPanel: () => ({ dispose: jest.fn() }),
|
onDidAddPanel: () => ({ dispose: jest.fn() }),
|
||||||
onDidRemovePanel: () => ({ dispose: jest.fn() }),
|
onDidRemovePanel: () => ({ dispose: jest.fn() }),
|
||||||
overlayRenderContainer: new OverlayRenderContainer(
|
overlayRenderContainer: new OverlayRenderContainer(
|
||||||
document.createElement('div')
|
document.createElement('div'),
|
||||||
|
fromPartial<DockviewComponent>({})
|
||||||
),
|
),
|
||||||
|
onDidOptionsChange: () => ({ dispose: jest.fn() }),
|
||||||
});
|
});
|
||||||
|
|
||||||
groupview = new DockviewGroupPanel(dockview, 'groupview-1', options);
|
groupview = new DockviewGroupPanel(dockview, 'groupview-1', options);
|
||||||
@ -495,12 +479,12 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
|
|
||||||
test('default', () => {
|
test('default', () => {
|
||||||
let viewQuery = groupview.element.querySelectorAll(
|
let viewQuery = groupview.element.querySelectorAll(
|
||||||
'.groupview > .tabs-and-actions-container'
|
'.dv-groupview > .dv-tabs-and-actions-container'
|
||||||
);
|
);
|
||||||
expect(viewQuery).toBeTruthy();
|
expect(viewQuery).toBeTruthy();
|
||||||
|
|
||||||
viewQuery = groupview.element.querySelectorAll(
|
viewQuery = groupview.element.querySelectorAll(
|
||||||
'.groupview > .content-container'
|
'.dv-groupview > .dv-content-container'
|
||||||
);
|
);
|
||||||
expect(viewQuery).toBeTruthy();
|
expect(viewQuery).toBeTruthy();
|
||||||
});
|
});
|
||||||
@ -516,19 +500,18 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
|
|
||||||
groupview.model.closeAllPanels();
|
groupview.model.closeAllPanels();
|
||||||
|
|
||||||
expect(removePanelMock).toBeCalledWith(panel1);
|
expect(removePanelMock).toHaveBeenCalledWith(panel1, undefined);
|
||||||
expect(removePanelMock).toBeCalledWith(panel2);
|
expect(removePanelMock).toHaveBeenCalledWith(panel2, undefined);
|
||||||
expect(removePanelMock).toBeCalledWith(panel3);
|
expect(removePanelMock).toHaveBeenCalledWith(panel3, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('closeAllPanels with no panels', () => {
|
test('closeAllPanels with no panels', () => {
|
||||||
groupview.model.closeAllPanels();
|
groupview.model.closeAllPanels();
|
||||||
expect(removeGroupMock).toBeCalledWith(groupview);
|
expect(removeGroupMock).toHaveBeenCalledWith(groupview);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that group is set on panel during onDidAddPanel event', () => {
|
test('that group is set on panel during onDidAddPanel event', () => {
|
||||||
const cut = new DockviewComponent({
|
const cut = new DockviewComponent(document.createElement('div'), {
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
createComponent(options) {
|
createComponent(options) {
|
||||||
switch (options.name) {
|
switch (options.name) {
|
||||||
case 'component':
|
case 'component':
|
||||||
@ -548,8 +531,9 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('toJSON() default', () => {
|
test('toJSON() default', () => {
|
||||||
const dockviewComponent = new DockviewComponent({
|
const dockviewComponent = new DockviewComponent(
|
||||||
parentElement: document.createElement('div'),
|
document.createElement('div'),
|
||||||
|
{
|
||||||
createComponent(options) {
|
createComponent(options) {
|
||||||
switch (options.name) {
|
switch (options.name) {
|
||||||
case 'component':
|
case 'component':
|
||||||
@ -558,7 +542,8 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
throw new Error(`unsupported`);
|
throw new Error(`unsupported`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const cut = new DockviewGroupPanelModel(
|
const cut = new DockviewGroupPanelModel(
|
||||||
document.createElement('div'),
|
document.createElement('div'),
|
||||||
@ -576,8 +561,9 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('toJSON() locked and hideHeader', () => {
|
test('toJSON() locked and hideHeader', () => {
|
||||||
const dockviewComponent = new DockviewComponent({
|
const dockviewComponent = new DockviewComponent(
|
||||||
parentElement: document.createElement('div'),
|
document.createElement('div'),
|
||||||
|
{
|
||||||
createComponent(options) {
|
createComponent(options) {
|
||||||
switch (options.name) {
|
switch (options.name) {
|
||||||
case 'component':
|
case 'component':
|
||||||
@ -586,7 +572,8 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
throw new Error(`unsupported`);
|
throw new Error(`unsupported`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const cut = new DockviewGroupPanelModel(
|
const cut = new DockviewGroupPanelModel(
|
||||||
document.createElement('div'),
|
document.createElement('div'),
|
||||||
@ -609,8 +596,9 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("that openPanel with skipSetActive doesn't set panel to active", () => {
|
test("that openPanel with skipSetActive doesn't set panel to active", () => {
|
||||||
const dockviewComponent = new DockviewComponent({
|
const dockviewComponent = new DockviewComponent(
|
||||||
parentElement: document.createElement('div'),
|
document.createElement('div'),
|
||||||
|
{
|
||||||
createComponent(options) {
|
createComponent(options) {
|
||||||
switch (options.name) {
|
switch (options.name) {
|
||||||
case 'component':
|
case 'component':
|
||||||
@ -619,7 +607,8 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
throw new Error(`unsupported`);
|
throw new Error(`unsupported`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const groupviewContainer = document.createElement('div');
|
const groupviewContainer = document.createElement('div');
|
||||||
const cut = new DockviewGroupPanelModel(
|
const cut = new DockviewGroupPanelModel(
|
||||||
@ -630,7 +619,7 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
null as any
|
null as any
|
||||||
);
|
);
|
||||||
const contentContainer = groupviewContainer
|
const contentContainer = groupviewContainer
|
||||||
.getElementsByClassName('content-container')
|
.getElementsByClassName('dv-content-container')
|
||||||
.item(0)!.childNodes;
|
.item(0)!.childNodes;
|
||||||
|
|
||||||
const panel1 = new TestPanel('id_1', panelApi);
|
const panel1 = new TestPanel('id_1', panelApi);
|
||||||
@ -659,12 +648,11 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
test('that should not show drop target is external event', () => {
|
test('that should not show drop target is external event', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
options: {
|
options: {},
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
},
|
|
||||||
getPanel: jest.fn(),
|
getPanel: jest.fn(),
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
||||||
@ -702,13 +690,13 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const element = container
|
const element = container
|
||||||
.getElementsByClassName('content-container')
|
.getElementsByClassName('dv-content-container')
|
||||||
.item(0)!;
|
.item(0)! as HTMLElement;
|
||||||
|
|
||||||
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(element, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 100);
|
jest.spyOn(element, 'offsetWidth', 'get').mockImplementation(() => 100);
|
||||||
|
|
||||||
fireEvent.dragEnter(element);
|
fireEvent.dragEnter(element);
|
||||||
fireEvent.dragOver(element);
|
fireEvent.dragOver(element);
|
||||||
@ -716,19 +704,18 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
expect(counter).toBe(1);
|
expect(counter).toBe(1);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
element.getElementsByClassName('drop-target-dropzone').length
|
element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that the .locked behaviour is as', () => {
|
test('that the .locked behaviour is as', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
options: {
|
options: {},
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
},
|
|
||||||
getPanel: jest.fn(),
|
getPanel: jest.fn(),
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
||||||
@ -764,13 +751,13 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const element = container
|
const element = container
|
||||||
.getElementsByClassName('content-container')
|
.getElementsByClassName('dv-content-container')
|
||||||
.item(0)!;
|
.item(0)! as HTMLElement;
|
||||||
|
|
||||||
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(element, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 100);
|
jest.spyOn(element, 'offsetWidth', 'get').mockImplementation(() => 100);
|
||||||
|
|
||||||
function run(value: number) {
|
function run(value: number) {
|
||||||
fireEvent.dragEnter(element);
|
fireEvent.dragEnter(element);
|
||||||
@ -784,7 +771,7 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
cut.locked = false;
|
cut.locked = false;
|
||||||
run(10);
|
run(10);
|
||||||
expect(
|
expect(
|
||||||
element.getElementsByClassName('drop-target-dropzone').length
|
element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(1);
|
).toBe(1);
|
||||||
fireEvent.dragEnd(element);
|
fireEvent.dragEnd(element);
|
||||||
|
|
||||||
@ -792,7 +779,7 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
cut.locked = 'no-drop-target';
|
cut.locked = 'no-drop-target';
|
||||||
run(10);
|
run(10);
|
||||||
expect(
|
expect(
|
||||||
element.getElementsByClassName('drop-target-dropzone').length
|
element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
fireEvent.dragEnd(element);
|
fireEvent.dragEnd(element);
|
||||||
|
|
||||||
@ -800,7 +787,7 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
cut.locked = true;
|
cut.locked = true;
|
||||||
run(10);
|
run(10);
|
||||||
expect(
|
expect(
|
||||||
element.getElementsByClassName('drop-target-dropzone').length
|
element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(1);
|
).toBe(1);
|
||||||
fireEvent.dragEnd(element);
|
fireEvent.dragEnd(element);
|
||||||
|
|
||||||
@ -808,35 +795,29 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
cut.locked = true;
|
cut.locked = true;
|
||||||
run(25);
|
run(25);
|
||||||
expect(
|
expect(
|
||||||
element.getElementsByClassName('drop-target-dropzone').length
|
element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
fireEvent.dragEnd(element);
|
fireEvent.dragEnd(element);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that should not show drop target if dropping on self', () => {
|
test('that should show drop target if dropping on self', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
options: {
|
options: {},
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
},
|
|
||||||
getPanel: jest.fn(),
|
getPanel: jest.fn(),
|
||||||
doSetGroupActive: jest.fn(),
|
doSetGroupActive: jest.fn(),
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
overlayRenderContainer: new OverlayRenderContainer(
|
overlayRenderContainer: new OverlayRenderContainer(
|
||||||
document.createElement('div')
|
document.createElement('div'),
|
||||||
|
fromPartial<DockviewComponent>({})
|
||||||
),
|
),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
const groupView = fromPartial<DockviewGroupPanelModel>({
|
||||||
() => {
|
|
||||||
return {
|
|
||||||
canDisplayOverlay: jest.fn(),
|
canDisplayOverlay: jest.fn(),
|
||||||
};
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const groupView = new groupviewMock() as DockviewGroupPanelModel;
|
|
||||||
|
|
||||||
const groupPanelMock = jest.fn<Partial<DockviewGroupPanel>, []>(() => {
|
const groupPanelMock = jest.fn<Partial<DockviewGroupPanel>, []>(() => {
|
||||||
return {
|
return {
|
||||||
@ -863,13 +844,13 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
cut.openPanel(new TestPanel('panel1', panelApi));
|
cut.openPanel(new TestPanel('panel1', panelApi));
|
||||||
|
|
||||||
const element = container
|
const element = container
|
||||||
.getElementsByClassName('content-container')
|
.getElementsByClassName('dv-content-container')
|
||||||
.item(0)!;
|
.item(0)! as HTMLElement;
|
||||||
|
|
||||||
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(element, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 100);
|
jest.spyOn(element, 'offsetWidth', 'get').mockImplementation(() => 100);
|
||||||
|
|
||||||
LocalSelectionTransfer.getInstance().setData(
|
LocalSelectionTransfer.getInstance().setData(
|
||||||
[new PanelTransfer('testcomponentid', 'groupviewid', 'panel1')],
|
[new PanelTransfer('testcomponentid', 'groupviewid', 'panel1')],
|
||||||
@ -882,23 +863,23 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
expect(counter).toBe(0);
|
expect(counter).toBe(0);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
element.getElementsByClassName('drop-target-dropzone').length
|
element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that should not allow drop when dropping on self for same component id', () => {
|
test('that should allow drop when dropping on self for same component id', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
options: {
|
options: {},
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
},
|
|
||||||
getPanel: jest.fn(),
|
getPanel: jest.fn(),
|
||||||
doSetGroupActive: jest.fn(),
|
doSetGroupActive: jest.fn(),
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
overlayRenderContainer: new OverlayRenderContainer(
|
overlayRenderContainer: new OverlayRenderContainer(
|
||||||
document.createElement('div')
|
document.createElement('div'),
|
||||||
|
fromPartial<DockviewComponent>({})
|
||||||
),
|
),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
||||||
@ -937,13 +918,13 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
cut.openPanel(new TestPanel('panel2', panelApi));
|
cut.openPanel(new TestPanel('panel2', panelApi));
|
||||||
|
|
||||||
const element = container
|
const element = container
|
||||||
.getElementsByClassName('content-container')
|
.getElementsByClassName('dv-content-container')
|
||||||
.item(0)!;
|
.item(0) as HTMLElement;
|
||||||
|
|
||||||
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(element, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 100);
|
jest.spyOn(element, 'offsetWidth', 'get').mockImplementation(() => 100);
|
||||||
|
|
||||||
LocalSelectionTransfer.getInstance().setData(
|
LocalSelectionTransfer.getInstance().setData(
|
||||||
[new PanelTransfer('testcomponentid', 'groupviewid', 'panel1')],
|
[new PanelTransfer('testcomponentid', 'groupviewid', 'panel1')],
|
||||||
@ -956,23 +937,23 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
expect(counter).toBe(0);
|
expect(counter).toBe(0);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
element.getElementsByClassName('drop-target-dropzone').length
|
element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that should not allow drop when not dropping for different component id', () => {
|
test('that should not allow drop when not dropping for different component id', () => {
|
||||||
const accessor = fromPartial<DockviewComponent>({
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
options: {
|
options: {},
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
},
|
|
||||||
getPanel: jest.fn(),
|
getPanel: jest.fn(),
|
||||||
doSetGroupActive: jest.fn(),
|
doSetGroupActive: jest.fn(),
|
||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
overlayRenderContainer: new OverlayRenderContainer(
|
overlayRenderContainer: new OverlayRenderContainer(
|
||||||
document.createElement('div')
|
document.createElement('div'),
|
||||||
|
fromPartial<DockviewComponent>({})
|
||||||
),
|
),
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
||||||
@ -1011,13 +992,13 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
cut.openPanel(new TestPanel('panel2', panelApi));
|
cut.openPanel(new TestPanel('panel2', panelApi));
|
||||||
|
|
||||||
const element = container
|
const element = container
|
||||||
.getElementsByClassName('content-container')
|
.getElementsByClassName('dv-content-container')
|
||||||
.item(0)!;
|
.item(0) as HTMLElement;
|
||||||
|
|
||||||
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(element, 'offsetHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
);
|
);
|
||||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 100);
|
jest.spyOn(element, 'offsetWidth', 'get').mockImplementation(() => 100);
|
||||||
|
|
||||||
LocalSelectionTransfer.getInstance().setData(
|
LocalSelectionTransfer.getInstance().setData(
|
||||||
[new PanelTransfer('anothercomponentid', 'groupviewid', 'panel1')],
|
[new PanelTransfer('anothercomponentid', 'groupviewid', 'panel1')],
|
||||||
@ -1030,7 +1011,7 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
expect(counter).toBe(1);
|
expect(counter).toBe(1);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
element.getElementsByClassName('drop-target-dropzone').length
|
element.getElementsByClassName('dv-drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1115,7 +1096,7 @@ describe('dockviewGroupPanelModel', () => {
|
|||||||
container.getElementsByClassName('watermark-test-container').length
|
container.getElementsByClassName('watermark-test-container').length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
expect(
|
expect(
|
||||||
container.getElementsByClassName('tabs-and-actions-container')
|
container.getElementsByClassName('dv-tabs-and-actions-container')
|
||||||
.length
|
.length
|
||||||
).toBe(1);
|
).toBe(1);
|
||||||
|
|
||||||
|
@ -7,24 +7,8 @@ import { fromPartial } from '@total-typescript/shoehorn';
|
|||||||
|
|
||||||
describe('dockviewPanel', () => {
|
describe('dockviewPanel', () => {
|
||||||
test('update title', () => {
|
test('update title', () => {
|
||||||
const dockviewApiMock = jest.fn<DockviewApi, []>(() => {
|
const api = fromPartial<DockviewApi>({});
|
||||||
return {
|
const accessor = fromPartial<DockviewComponent>({});
|
||||||
onDidActiveChange: jest.fn(),
|
|
||||||
} as any;
|
|
||||||
});
|
|
||||||
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
|
||||||
return {} as any;
|
|
||||||
});
|
|
||||||
|
|
||||||
const panelModelMock = jest.fn<Partial<IDockviewPanelModel>, []>(() => {
|
|
||||||
return {
|
|
||||||
update: jest.fn(),
|
|
||||||
init: jest.fn(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const api = new dockviewApiMock();
|
|
||||||
const accessor = new accessorMock();
|
|
||||||
const group = fromPartial<DockviewGroupPanel>({
|
const group = fromPartial<DockviewGroupPanel>({
|
||||||
api: {
|
api: {
|
||||||
onDidVisibilityChange: jest.fn(),
|
onDidVisibilityChange: jest.fn(),
|
||||||
@ -32,7 +16,11 @@ describe('dockviewPanel', () => {
|
|||||||
onDidActiveChange: jest.fn(),
|
onDidActiveChange: jest.fn(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const model = <IDockviewPanelModel>new panelModelMock();
|
const model = fromPartial<IDockviewPanelModel>({
|
||||||
|
update: jest.fn(),
|
||||||
|
init: jest.fn(),
|
||||||
|
dispose: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
const cut = new DockviewPanel(
|
const cut = new DockviewPanel(
|
||||||
'fake-id',
|
'fake-id',
|
||||||
@ -67,23 +55,8 @@ describe('dockviewPanel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('that .setTitle updates the title', () => {
|
test('that .setTitle updates the title', () => {
|
||||||
const dockviewApiMock = jest.fn<DockviewApi, []>(() => {
|
const api = fromPartial<DockviewApi>({});
|
||||||
return {
|
const accessor = fromPartial<DockviewComponent>({});
|
||||||
onDidActiveChange: jest.fn(),
|
|
||||||
} as any;
|
|
||||||
});
|
|
||||||
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
|
||||||
return {} as any;
|
|
||||||
});
|
|
||||||
const panelModelMock = jest.fn<Partial<IDockviewPanelModel>, []>(() => {
|
|
||||||
return {
|
|
||||||
update: jest.fn(),
|
|
||||||
init: jest.fn(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const api = new dockviewApiMock();
|
|
||||||
const accessor = new accessorMock();
|
|
||||||
const group = fromPartial<DockviewGroupPanel>({
|
const group = fromPartial<DockviewGroupPanel>({
|
||||||
api: {
|
api: {
|
||||||
onDidVisibilityChange: jest.fn(),
|
onDidVisibilityChange: jest.fn(),
|
||||||
@ -91,7 +64,10 @@ describe('dockviewPanel', () => {
|
|||||||
onDidActiveChange: jest.fn(),
|
onDidActiveChange: jest.fn(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const model = <IDockviewPanelModel>new panelModelMock();
|
const model = fromPartial<IDockviewPanelModel>({
|
||||||
|
update: jest.fn(),
|
||||||
|
init: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
const cut = new DockviewPanel(
|
const cut = new DockviewPanel(
|
||||||
'fake-id',
|
'fake-id',
|
||||||
@ -117,22 +93,8 @@ describe('dockviewPanel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('dispose cleanup', () => {
|
test('dispose cleanup', () => {
|
||||||
const dockviewApiMock = jest.fn<DockviewApi, []>(() => {
|
const api = fromPartial<DockviewApi>({});
|
||||||
return {} as any;
|
const accessor = fromPartial<DockviewComponent>({});
|
||||||
});
|
|
||||||
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
|
||||||
return {} as any;
|
|
||||||
});
|
|
||||||
const panelModelMock = jest.fn<Partial<IDockviewPanelModel>, []>(() => {
|
|
||||||
return {
|
|
||||||
update: jest.fn(),
|
|
||||||
init: jest.fn(),
|
|
||||||
dispose: jest.fn(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const api = new dockviewApiMock();
|
|
||||||
const accessor = new accessorMock();
|
|
||||||
const group = fromPartial<DockviewGroupPanel>({
|
const group = fromPartial<DockviewGroupPanel>({
|
||||||
api: {
|
api: {
|
||||||
onDidVisibilityChange: jest
|
onDidVisibilityChange: jest
|
||||||
@ -146,7 +108,11 @@ describe('dockviewPanel', () => {
|
|||||||
.mockReturnValue({ dispose: jest.fn() }),
|
.mockReturnValue({ dispose: jest.fn() }),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const model = <IDockviewPanelModel>new panelModelMock();
|
const model = fromPartial<IDockviewPanelModel>({
|
||||||
|
update: jest.fn(),
|
||||||
|
init: jest.fn(),
|
||||||
|
dispose: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
const cut = new DockviewPanel(
|
const cut = new DockviewPanel(
|
||||||
'fake-id',
|
'fake-id',
|
||||||
@ -169,22 +135,8 @@ describe('dockviewPanel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('get params', () => {
|
test('get params', () => {
|
||||||
const dockviewApiMock = jest.fn<DockviewApi, []>(() => {
|
const api = fromPartial<DockviewApi>({});
|
||||||
return {} as any;
|
const accessor = fromPartial<DockviewComponent>({});
|
||||||
});
|
|
||||||
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
|
||||||
return {} as any;
|
|
||||||
});
|
|
||||||
const panelModelMock = jest.fn<Partial<IDockviewPanelModel>, []>(() => {
|
|
||||||
return {
|
|
||||||
update: jest.fn(),
|
|
||||||
init: jest.fn(),
|
|
||||||
dispose: jest.fn(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const api = new dockviewApiMock();
|
|
||||||
const accessor = new accessorMock();
|
|
||||||
const group = fromPartial<DockviewGroupPanel>({
|
const group = fromPartial<DockviewGroupPanel>({
|
||||||
api: {
|
api: {
|
||||||
onDidVisibilityChange: jest.fn(),
|
onDidVisibilityChange: jest.fn(),
|
||||||
@ -192,7 +144,11 @@ describe('dockviewPanel', () => {
|
|||||||
onDidActiveChange: jest.fn(),
|
onDidActiveChange: jest.fn(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const model = <IDockviewPanelModel>new panelModelMock();
|
const model = fromPartial<IDockviewPanelModel>({
|
||||||
|
update: jest.fn(),
|
||||||
|
init: jest.fn(),
|
||||||
|
dispose: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
const cut = new DockviewPanel(
|
const cut = new DockviewPanel(
|
||||||
'fake-id',
|
'fake-id',
|
||||||
@ -215,22 +171,8 @@ describe('dockviewPanel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('setSize propagates to underlying group', () => {
|
test('setSize propagates to underlying group', () => {
|
||||||
const dockviewApiMock = jest.fn<DockviewApi, []>(() => {
|
const api = fromPartial<DockviewApi>({});
|
||||||
return {} as any;
|
const accessor = fromPartial<DockviewComponent>({});
|
||||||
});
|
|
||||||
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
|
||||||
return {} as any;
|
|
||||||
});
|
|
||||||
const panelModelMock = jest.fn<Partial<IDockviewPanelModel>, []>(() => {
|
|
||||||
return {
|
|
||||||
update: jest.fn(),
|
|
||||||
init: jest.fn(),
|
|
||||||
dispose: jest.fn(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const api = new dockviewApiMock();
|
|
||||||
const accessor = new accessorMock();
|
|
||||||
const group = fromPartial<DockviewGroupPanel>({
|
const group = fromPartial<DockviewGroupPanel>({
|
||||||
api: {
|
api: {
|
||||||
onDidVisibilityChange: jest.fn(),
|
onDidVisibilityChange: jest.fn(),
|
||||||
@ -239,7 +181,11 @@ describe('dockviewPanel', () => {
|
|||||||
setSize: jest.fn(),
|
setSize: jest.fn(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const model = <IDockviewPanelModel>new panelModelMock();
|
const model = fromPartial<IDockviewPanelModel>({
|
||||||
|
update: jest.fn(),
|
||||||
|
init: jest.fn(),
|
||||||
|
dispose: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
const cut = new DockviewPanel(
|
const cut = new DockviewPanel(
|
||||||
'fake-id',
|
'fake-id',
|
||||||
@ -256,27 +202,16 @@ describe('dockviewPanel', () => {
|
|||||||
|
|
||||||
cut.api.setSize({ height: 123, width: 456 });
|
cut.api.setSize({ height: 123, width: 456 });
|
||||||
|
|
||||||
expect(group.api.setSize).toBeCalledWith({ height: 123, width: 456 });
|
expect(group.api.setSize).toHaveBeenCalledWith({
|
||||||
expect(group.api.setSize).toBeCalledTimes(1);
|
height: 123,
|
||||||
|
width: 456,
|
||||||
|
});
|
||||||
|
expect(group.api.setSize).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('updateParameter', () => {
|
test('updateParameter', () => {
|
||||||
const dockviewApiMock = jest.fn<DockviewApi, []>(() => {
|
const api = fromPartial<DockviewApi>({});
|
||||||
return {} as any;
|
const accessor = fromPartial<DockviewComponent>({});
|
||||||
});
|
|
||||||
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
|
||||||
return {} as any;
|
|
||||||
});
|
|
||||||
const panelModelMock = jest.fn<Partial<IDockviewPanelModel>, []>(() => {
|
|
||||||
return {
|
|
||||||
update: jest.fn(),
|
|
||||||
init: jest.fn(),
|
|
||||||
dispose: jest.fn(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const api = new dockviewApiMock();
|
|
||||||
const accessor = new accessorMock();
|
|
||||||
const group = fromPartial<DockviewGroupPanel>({
|
const group = fromPartial<DockviewGroupPanel>({
|
||||||
api: {
|
api: {
|
||||||
onDidVisibilityChange: jest.fn(),
|
onDidVisibilityChange: jest.fn(),
|
||||||
@ -284,7 +219,11 @@ describe('dockviewPanel', () => {
|
|||||||
onDidActiveChange: jest.fn(),
|
onDidActiveChange: jest.fn(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const model = <IDockviewPanelModel>new panelModelMock();
|
const model = fromPartial<IDockviewPanelModel>({
|
||||||
|
update: jest.fn(),
|
||||||
|
init: jest.fn(),
|
||||||
|
dispose: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
const cut = new DockviewPanel(
|
const cut = new DockviewPanel(
|
||||||
'fake-id',
|
'fake-id',
|
||||||
@ -305,6 +244,9 @@ describe('dockviewPanel', () => {
|
|||||||
// update 'a' and add 'c'
|
// update 'a' and add 'c'
|
||||||
cut.update({ params: { a: '-1', c: '3' } });
|
cut.update({ params: { a: '-1', c: '3' } });
|
||||||
expect(cut.params).toEqual({ a: '-1', b: '2', c: '3' });
|
expect(cut.params).toEqual({ a: '-1', b: '2', c: '3' });
|
||||||
|
expect(model.update).toHaveBeenCalledWith({
|
||||||
|
params: { a: '-1', b: '2', c: '3' },
|
||||||
|
});
|
||||||
|
|
||||||
cut.update({ params: { d: '4', e: '5', f: '6' } });
|
cut.update({ params: { d: '4', e: '5', f: '6' } });
|
||||||
expect(cut.params).toEqual({
|
expect(cut.params).toEqual({
|
||||||
@ -315,6 +257,9 @@ describe('dockviewPanel', () => {
|
|||||||
e: '5',
|
e: '5',
|
||||||
f: '6',
|
f: '6',
|
||||||
});
|
});
|
||||||
|
expect(model.update).toHaveBeenCalledWith({
|
||||||
|
params: { a: '-1', b: '2', c: '3', d: '4', e: '5', f: '6' },
|
||||||
|
});
|
||||||
|
|
||||||
cut.update({
|
cut.update({
|
||||||
params: {
|
params: {
|
||||||
@ -335,5 +280,8 @@ describe('dockviewPanel', () => {
|
|||||||
g: '',
|
g: '',
|
||||||
h: null,
|
h: null,
|
||||||
});
|
});
|
||||||
|
expect(model.update).toHaveBeenCalledWith({
|
||||||
|
params: { a: '-1', b: '2', c: '3', d: '', e: null, g: '', h: null },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -30,7 +30,6 @@ describe('dockviewGroupPanel', () => {
|
|||||||
|
|
||||||
accessorMock = fromPartial<DockviewComponent>({
|
accessorMock = fromPartial<DockviewComponent>({
|
||||||
options: {
|
options: {
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
createComponent(options) {
|
createComponent(options) {
|
||||||
switch (options.name) {
|
switch (options.name) {
|
||||||
case 'contentComponent':
|
case 'contentComponent':
|
||||||
@ -84,7 +83,6 @@ describe('dockviewGroupPanel', () => {
|
|||||||
test('that the default tab is created', () => {
|
test('that the default tab is created', () => {
|
||||||
accessorMock = fromPartial<DockviewComponent>({
|
accessorMock = fromPartial<DockviewComponent>({
|
||||||
options: {
|
options: {
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
createComponent(options) {
|
createComponent(options) {
|
||||||
switch (options.name) {
|
switch (options.name) {
|
||||||
case 'contentComponent':
|
case 'contentComponent':
|
||||||
@ -117,7 +115,6 @@ describe('dockviewGroupPanel', () => {
|
|||||||
test('that the provided default tab is chosen when no implementation is provided', () => {
|
test('that the provided default tab is chosen when no implementation is provided', () => {
|
||||||
accessorMock = fromPartial<DockviewComponent>({
|
accessorMock = fromPartial<DockviewComponent>({
|
||||||
options: {
|
options: {
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
defaultTabComponent: 'tabComponent',
|
defaultTabComponent: 'tabComponent',
|
||||||
createComponent(options) {
|
createComponent(options) {
|
||||||
switch (options.name) {
|
switch (options.name) {
|
||||||
@ -150,7 +147,6 @@ describe('dockviewGroupPanel', () => {
|
|||||||
test('that is library default tab instance is created when no alternative exists', () => {
|
test('that is library default tab instance is created when no alternative exists', () => {
|
||||||
accessorMock = fromPartial<DockviewComponent>({
|
accessorMock = fromPartial<DockviewComponent>({
|
||||||
options: {
|
options: {
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
createComponent(options) {
|
createComponent(options) {
|
||||||
switch (options.name) {
|
switch (options.name) {
|
||||||
case 'contentComponent':
|
case 'contentComponent':
|
||||||
@ -174,7 +170,6 @@ describe('dockviewGroupPanel', () => {
|
|||||||
test('that the default content is created', () => {
|
test('that the default content is created', () => {
|
||||||
accessorMock = fromPartial<DockviewComponent>({
|
accessorMock = fromPartial<DockviewComponent>({
|
||||||
options: {
|
options: {
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
createComponent(options) {
|
createComponent(options) {
|
||||||
switch (options.name) {
|
switch (options.name) {
|
||||||
case 'contentComponent':
|
case 'contentComponent':
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
disableIframePointEvents,
|
||||||
isInDocument,
|
isInDocument,
|
||||||
quasiDefaultPrevented,
|
quasiDefaultPrevented,
|
||||||
quasiPreventDefault,
|
quasiPreventDefault,
|
||||||
@ -45,4 +46,38 @@ describe('dom', () => {
|
|||||||
|
|
||||||
expect(isInDocument(el2)).toBeTruthy();
|
expect(isInDocument(el2)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('disableIframePointEvents', () => {
|
||||||
|
const el1 = document.createElement('iframe');
|
||||||
|
const el2 = document.createElement('iframe');
|
||||||
|
const el3 = document.createElement('webview');
|
||||||
|
const el4 = document.createElement('webview');
|
||||||
|
|
||||||
|
document.body.appendChild(el1);
|
||||||
|
document.body.appendChild(el2);
|
||||||
|
document.body.appendChild(el3);
|
||||||
|
document.body.appendChild(el4);
|
||||||
|
|
||||||
|
el1.style.pointerEvents = 'inherit';
|
||||||
|
el3.style.pointerEvents = 'inherit';
|
||||||
|
|
||||||
|
expect(el1.style.pointerEvents).toBe('inherit');
|
||||||
|
expect(el2.style.pointerEvents).toBe('');
|
||||||
|
expect(el3.style.pointerEvents).toBe('inherit');
|
||||||
|
expect(el4.style.pointerEvents).toBe('');
|
||||||
|
|
||||||
|
const f = disableIframePointEvents();
|
||||||
|
|
||||||
|
expect(el1.style.pointerEvents).toBe('none');
|
||||||
|
expect(el2.style.pointerEvents).toBe('none');
|
||||||
|
expect(el3.style.pointerEvents).toBe('none');
|
||||||
|
expect(el4.style.pointerEvents).toBe('none');
|
||||||
|
|
||||||
|
f.release();
|
||||||
|
|
||||||
|
expect(el1.style.pointerEvents).toBe('inherit');
|
||||||
|
expect(el2.style.pointerEvents).toBe('');
|
||||||
|
expect(el3.style.pointerEvents).toBe('inherit');
|
||||||
|
expect(el4.style.pointerEvents).toBe('');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,6 @@ import {
|
|||||||
Emitter,
|
Emitter,
|
||||||
Event,
|
Event,
|
||||||
addDisposableListener,
|
addDisposableListener,
|
||||||
addDisposableWindowListener,
|
|
||||||
} from '../events';
|
} from '../events';
|
||||||
|
|
||||||
describe('events', () => {
|
describe('events', () => {
|
||||||
@ -143,73 +142,6 @@ describe('events', () => {
|
|||||||
expect(value).toBe(3);
|
expect(value).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('addDisposableWindowListener with capture options', () => {
|
|
||||||
const element = {
|
|
||||||
addEventListener: jest.fn(),
|
|
||||||
removeEventListener: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const handler = jest.fn();
|
|
||||||
|
|
||||||
const disposable = addDisposableWindowListener(
|
|
||||||
element as any,
|
|
||||||
'mousedown',
|
|
||||||
handler,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(element.addEventListener).toBeCalledTimes(1);
|
|
||||||
expect(element.addEventListener).toHaveBeenCalledWith(
|
|
||||||
'mousedown',
|
|
||||||
handler,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
expect(element.removeEventListener).toBeCalledTimes(0);
|
|
||||||
|
|
||||||
disposable.dispose();
|
|
||||||
|
|
||||||
expect(element.addEventListener).toBeCalledTimes(1);
|
|
||||||
expect(element.removeEventListener).toBeCalledTimes(1);
|
|
||||||
expect(element.removeEventListener).toBeCalledWith(
|
|
||||||
'mousedown',
|
|
||||||
handler,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('addDisposableWindowListener without capture options', () => {
|
|
||||||
const element = {
|
|
||||||
addEventListener: jest.fn(),
|
|
||||||
removeEventListener: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const handler = jest.fn();
|
|
||||||
|
|
||||||
const disposable = addDisposableWindowListener(
|
|
||||||
element as any,
|
|
||||||
'mousedown',
|
|
||||||
handler
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(element.addEventListener).toBeCalledTimes(1);
|
|
||||||
expect(element.addEventListener).toHaveBeenCalledWith(
|
|
||||||
'mousedown',
|
|
||||||
handler,
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
expect(element.removeEventListener).toBeCalledTimes(0);
|
|
||||||
|
|
||||||
disposable.dispose();
|
|
||||||
|
|
||||||
expect(element.addEventListener).toBeCalledTimes(1);
|
|
||||||
expect(element.removeEventListener).toBeCalledTimes(1);
|
|
||||||
expect(element.removeEventListener).toBeCalledWith(
|
|
||||||
'mousedown',
|
|
||||||
handler,
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('addDisposableListener with capture options', () => {
|
it('addDisposableListener with capture options', () => {
|
||||||
const element = {
|
const element = {
|
||||||
addEventListener: jest.fn(),
|
addEventListener: jest.fn(),
|
||||||
@ -220,14 +152,14 @@ describe('events', () => {
|
|||||||
|
|
||||||
const disposable = addDisposableListener(
|
const disposable = addDisposableListener(
|
||||||
element as any,
|
element as any,
|
||||||
'mousedown',
|
'pointerdown',
|
||||||
handler,
|
handler,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(element.addEventListener).toBeCalledTimes(1);
|
expect(element.addEventListener).toBeCalledTimes(1);
|
||||||
expect(element.addEventListener).toHaveBeenCalledWith(
|
expect(element.addEventListener).toHaveBeenCalledWith(
|
||||||
'mousedown',
|
'pointerdown',
|
||||||
handler,
|
handler,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@ -238,7 +170,7 @@ describe('events', () => {
|
|||||||
expect(element.addEventListener).toBeCalledTimes(1);
|
expect(element.addEventListener).toBeCalledTimes(1);
|
||||||
expect(element.removeEventListener).toBeCalledTimes(1);
|
expect(element.removeEventListener).toBeCalledTimes(1);
|
||||||
expect(element.removeEventListener).toBeCalledWith(
|
expect(element.removeEventListener).toBeCalledWith(
|
||||||
'mousedown',
|
'pointerdown',
|
||||||
handler,
|
handler,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@ -254,13 +186,13 @@ describe('events', () => {
|
|||||||
|
|
||||||
const disposable = addDisposableListener(
|
const disposable = addDisposableListener(
|
||||||
element as any,
|
element as any,
|
||||||
'mousedown',
|
'pointerdown',
|
||||||
handler
|
handler
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(element.addEventListener).toBeCalledTimes(1);
|
expect(element.addEventListener).toBeCalledTimes(1);
|
||||||
expect(element.addEventListener).toHaveBeenCalledWith(
|
expect(element.addEventListener).toHaveBeenCalledWith(
|
||||||
'mousedown',
|
'pointerdown',
|
||||||
handler,
|
handler,
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
@ -271,7 +203,74 @@ describe('events', () => {
|
|||||||
expect(element.addEventListener).toBeCalledTimes(1);
|
expect(element.addEventListener).toBeCalledTimes(1);
|
||||||
expect(element.removeEventListener).toBeCalledTimes(1);
|
expect(element.removeEventListener).toBeCalledTimes(1);
|
||||||
expect(element.removeEventListener).toBeCalledWith(
|
expect(element.removeEventListener).toBeCalledWith(
|
||||||
'mousedown',
|
'pointerdown',
|
||||||
|
handler,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('addDisposableListener with capture options', () => {
|
||||||
|
const element = {
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
removeEventListener: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const handler = jest.fn();
|
||||||
|
|
||||||
|
const disposable = addDisposableListener(
|
||||||
|
element as any,
|
||||||
|
'pointerdown',
|
||||||
|
handler,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(element.addEventListener).toBeCalledTimes(1);
|
||||||
|
expect(element.addEventListener).toHaveBeenCalledWith(
|
||||||
|
'pointerdown',
|
||||||
|
handler,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
expect(element.removeEventListener).toBeCalledTimes(0);
|
||||||
|
|
||||||
|
disposable.dispose();
|
||||||
|
|
||||||
|
expect(element.addEventListener).toBeCalledTimes(1);
|
||||||
|
expect(element.removeEventListener).toBeCalledTimes(1);
|
||||||
|
expect(element.removeEventListener).toBeCalledWith(
|
||||||
|
'pointerdown',
|
||||||
|
handler,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('addDisposableListener without capture options', () => {
|
||||||
|
const element = {
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
removeEventListener: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const handler = jest.fn();
|
||||||
|
|
||||||
|
const disposable = addDisposableListener(
|
||||||
|
element as any,
|
||||||
|
'pointerdown',
|
||||||
|
handler
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(element.addEventListener).toBeCalledTimes(1);
|
||||||
|
expect(element.addEventListener).toHaveBeenCalledWith(
|
||||||
|
'pointerdown',
|
||||||
|
handler,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
expect(element.removeEventListener).toBeCalledTimes(0);
|
||||||
|
|
||||||
|
disposable.dispose();
|
||||||
|
|
||||||
|
expect(element.addEventListener).toBeCalledTimes(1);
|
||||||
|
expect(element.removeEventListener).toBeCalledTimes(1);
|
||||||
|
expect(element.removeEventListener).toBeCalledWith(
|
||||||
|
'pointerdown',
|
||||||
handler,
|
handler,
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
@ -17,13 +17,9 @@ class TestPanel implements IGridPanelView {
|
|||||||
_onDidChange = new Emitter<IViewSize | undefined>();
|
_onDidChange = new Emitter<IViewSize | undefined>();
|
||||||
readonly onDidChange = this._onDidChange.event;
|
readonly onDidChange = this._onDidChange.event;
|
||||||
|
|
||||||
get isActive(): boolean {
|
isVisible: boolean = true;
|
||||||
return true;
|
isActive: boolean = true;
|
||||||
}
|
params: Parameters = {};
|
||||||
|
|
||||||
get params(): Parameters {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly id: string,
|
public readonly id: string,
|
||||||
@ -70,8 +66,10 @@ class TestPanel implements IGridPanelView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ClassUnderTest extends BaseGrid<TestPanel> {
|
class ClassUnderTest extends BaseGrid<TestPanel> {
|
||||||
constructor(options: BaseGridOptions) {
|
readonly gridview = this.gridview;
|
||||||
super(options);
|
|
||||||
|
constructor(parentElement: HTMLElement, options: BaseGridOptions) {
|
||||||
|
super(parentElement, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
doRemoveGroup(
|
doRemoveGroup(
|
||||||
@ -107,9 +105,47 @@ class ClassUnderTest extends BaseGrid<TestPanel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('baseComponentGridview', () => {
|
describe('baseComponentGridview', () => {
|
||||||
|
test('that the container is not removed when grid is disposed', () => {
|
||||||
|
const root = document.createElement('div');
|
||||||
|
const container = document.createElement('div');
|
||||||
|
root.appendChild(container);
|
||||||
|
|
||||||
|
const cut = new ClassUnderTest(container, {
|
||||||
|
orientation: Orientation.HORIZONTAL,
|
||||||
|
proportionalLayout: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
cut.dispose();
|
||||||
|
|
||||||
|
expect(container.parentElement).toBe(root);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('that .layout(...) force flag works', () => {
|
||||||
|
const cut = new ClassUnderTest(document.createElement('div'), {
|
||||||
|
orientation: Orientation.HORIZONTAL,
|
||||||
|
proportionalLayout: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const spy = jest.spyOn(cut.gridview, 'layout');
|
||||||
|
|
||||||
|
cut.layout(100, 100);
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
cut.layout(100, 100, false);
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
cut.layout(100, 100, true);
|
||||||
|
expect(spy).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
cut.layout(150, 150, false);
|
||||||
|
expect(spy).toHaveBeenCalledTimes(3);
|
||||||
|
|
||||||
|
cut.layout(150, 150, true);
|
||||||
|
expect(spy).toHaveBeenCalledTimes(4);
|
||||||
|
});
|
||||||
|
|
||||||
test('can add group', () => {
|
test('can add group', () => {
|
||||||
const cut = new ClassUnderTest({
|
const cut = new ClassUnderTest(document.createElement('div'), {
|
||||||
parentElement: document.createElement('div'),
|
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
});
|
});
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
IGridView,
|
IGridView,
|
||||||
IViewSize,
|
IViewSize,
|
||||||
SerializedGridview,
|
SerializedGridview,
|
||||||
|
getGridLocation,
|
||||||
orthogonal,
|
orthogonal,
|
||||||
} from '../../gridview/gridview';
|
} from '../../gridview/gridview';
|
||||||
import { Orientation, Sizing } from '../../splitview/splitview';
|
import { Orientation, Sizing } from '../../splitview/splitview';
|
||||||
@ -18,7 +19,7 @@ class MockGridview implements IGridView {
|
|||||||
IViewSize | undefined
|
IViewSize | undefined
|
||||||
>().event;
|
>().event;
|
||||||
element: HTMLElement = document.createElement('div');
|
element: HTMLElement = document.createElement('div');
|
||||||
|
isVisible: boolean = true;
|
||||||
width: number = 0;
|
width: number = 0;
|
||||||
height: number = 0;
|
height: number = 0;
|
||||||
|
|
||||||
@ -1105,4 +1106,102 @@ describe('gridview', () => {
|
|||||||
|
|
||||||
expect(gridview.hasMaximizedView()).toBeFalsy();
|
expect(gridview.hasMaximizedView()).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('visibility check', () => {
|
||||||
|
const gridview = new Gridview(
|
||||||
|
true,
|
||||||
|
{ separatorBorder: '' },
|
||||||
|
Orientation.HORIZONTAL
|
||||||
|
);
|
||||||
|
gridview.layout(1000, 1000);
|
||||||
|
|
||||||
|
const view1 = new MockGridview('1');
|
||||||
|
const view2 = new MockGridview('2');
|
||||||
|
const view3 = new MockGridview('3');
|
||||||
|
const view4 = new MockGridview('4');
|
||||||
|
const view5 = new MockGridview('5');
|
||||||
|
const view6 = new MockGridview('6');
|
||||||
|
|
||||||
|
gridview.addView(view1, Sizing.Distribute, [0]);
|
||||||
|
gridview.addView(view2, Sizing.Distribute, [1]);
|
||||||
|
gridview.addView(view3, Sizing.Distribute, [1, 1]);
|
||||||
|
gridview.addView(view4, Sizing.Distribute, [1, 1, 0]);
|
||||||
|
gridview.addView(view5, Sizing.Distribute, [1, 1, 0, 0]);
|
||||||
|
gridview.addView(view6, Sizing.Distribute, [1, 1, 0, 0, 0]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _____________________________________________
|
||||||
|
* | | |
|
||||||
|
* | | 2 |
|
||||||
|
* | | |
|
||||||
|
* | 1 |_______________________|
|
||||||
|
* | | | 4 |
|
||||||
|
* | | 3 |_____________|
|
||||||
|
* | | | 5 | 6 |
|
||||||
|
* |_____________________|_________|______|______|
|
||||||
|
*/
|
||||||
|
|
||||||
|
function assertVisibility(visibility: boolean[]) {
|
||||||
|
expect(gridview.isViewVisible(getGridLocation(view1.element))).toBe(
|
||||||
|
visibility[0]
|
||||||
|
);
|
||||||
|
expect(gridview.isViewVisible(getGridLocation(view2.element))).toBe(
|
||||||
|
visibility[1]
|
||||||
|
);
|
||||||
|
expect(gridview.isViewVisible(getGridLocation(view3.element))).toBe(
|
||||||
|
visibility[2]
|
||||||
|
);
|
||||||
|
expect(gridview.isViewVisible(getGridLocation(view4.element))).toBe(
|
||||||
|
visibility[3]
|
||||||
|
);
|
||||||
|
expect(gridview.isViewVisible(getGridLocation(view5.element))).toBe(
|
||||||
|
visibility[4]
|
||||||
|
);
|
||||||
|
expect(gridview.isViewVisible(getGridLocation(view6.element))).toBe(
|
||||||
|
visibility[5]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide each view one by one
|
||||||
|
|
||||||
|
assertVisibility([true, true, true, true, true, true]);
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view5.element), false);
|
||||||
|
assertVisibility([true, true, true, true, false, true]);
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view4.element), false);
|
||||||
|
assertVisibility([true, true, true, false, false, true]);
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view1.element), false);
|
||||||
|
assertVisibility([false, true, true, false, false, true]);
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view2.element), false);
|
||||||
|
assertVisibility([false, false, true, false, false, true]);
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view3.element), false);
|
||||||
|
assertVisibility([false, false, false, false, false, true]);
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view6.element), false);
|
||||||
|
assertVisibility([false, false, false, false, false, false]);
|
||||||
|
|
||||||
|
// un-hide each view one by one
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view1.element), true);
|
||||||
|
assertVisibility([true, false, false, false, false, false]);
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view5.element), true);
|
||||||
|
assertVisibility([true, false, false, false, true, false]);
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view6.element), true);
|
||||||
|
assertVisibility([true, false, false, false, true, true]);
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view2.element), true);
|
||||||
|
assertVisibility([true, true, false, false, true, true]);
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view3.element), true);
|
||||||
|
assertVisibility([true, true, true, false, true, true]);
|
||||||
|
|
||||||
|
gridview.setViewVisible(getGridLocation(view4.element), true);
|
||||||
|
assertVisibility([true, true, true, true, true, true]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -32,12 +32,40 @@ describe('gridview', () => {
|
|||||||
container = document.createElement('div');
|
container = document.createElement('div');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('added views are visible by default', () => {
|
test('update className', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
className: 'test-a test-b',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(gridview.element.className).toBe('test-a test-b');
|
||||||
|
|
||||||
|
gridview.updateOptions({ className: 'test-b test-c' });
|
||||||
|
|
||||||
|
expect(gridview.element.className).toBe('test-b test-c');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('added views are visible by default', () => {
|
||||||
|
const gridview = new GridviewComponent(container, {
|
||||||
|
proportionalLayout: false,
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -53,11 +81,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('remove panel', () => {
|
test('remove panel', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -84,11 +118,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('active panel', () => {
|
test('active panel', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -145,13 +185,21 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('deserialize and serialize a layout', () => {
|
test('deserialize and serialize a layout', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll('.dv-grid-view').length).toBe(1);
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
gridview.fromJSON({
|
gridview.fromJSON({
|
||||||
grid: {
|
grid: {
|
||||||
@ -196,6 +244,9 @@ describe('gridview', () => {
|
|||||||
},
|
},
|
||||||
activePanel: 'panel_1',
|
activePanel: 'panel_1',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll('.dv-grid-view').length).toBe(1);
|
||||||
|
|
||||||
gridview.layout(800, 400, true);
|
gridview.layout(800, 400, true);
|
||||||
|
|
||||||
const panel1 = gridview.getPanel('panel_1')!;
|
const panel1 = gridview.getPanel('panel_1')!;
|
||||||
@ -273,11 +324,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('toJSON shouldnt fire any layout events', () => {
|
test('toJSON shouldnt fire any layout events', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(1000, 1000);
|
gridview.layout(1000, 1000);
|
||||||
@ -310,11 +367,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('gridview events', () => {
|
test('gridview events', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -434,11 +497,17 @@ describe('gridview', () => {
|
|||||||
test('dispose of gridviewComponent', () => {
|
test('dispose of gridviewComponent', () => {
|
||||||
expect(container.childNodes.length).toBe(0);
|
expect(container.childNodes.length).toBe(0);
|
||||||
|
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -460,15 +529,21 @@ describe('gridview', () => {
|
|||||||
|
|
||||||
gridview.dispose();
|
gridview.dispose();
|
||||||
|
|
||||||
expect(container.childNodes.length).toBe(0);
|
expect(container.children.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('#1/VERTICAL', () => {
|
test('#1/VERTICAL', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -523,11 +598,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('#2/HORIZONTAL', () => {
|
test('#2/HORIZONTAL', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -582,11 +663,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('#3/HORIZONTAL', () => {
|
test('#3/HORIZONTAL', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -659,11 +746,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('#4/HORIZONTAL', () => {
|
test('#4/HORIZONTAL', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -754,11 +847,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('#5/VERTICAL', () => {
|
test('#5/VERTICAL', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -849,11 +948,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('#5/VERTICAL/proportional/false', () => {
|
test('#5/VERTICAL/proportional/false', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -944,11 +1049,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('#6/VERTICAL', () => {
|
test('#6/VERTICAL', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -1069,11 +1180,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('#7/VERTICAL layout first', () => {
|
test('#7/VERTICAL layout first', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -1194,11 +1311,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('#8/VERTICAL layout after', () => {
|
test('#8/VERTICAL layout after', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -1321,11 +1444,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('#9/HORIZONTAL', () => {
|
test('#9/HORIZONTAL', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -1446,11 +1575,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('#9/HORIZONTAL/proportional/false', () => {
|
test('#9/HORIZONTAL/proportional/false', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(800, 400);
|
gridview.layout(800, 400);
|
||||||
@ -1571,11 +1706,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('#10/HORIZONTAL scale x:1.5 y:2', () => {
|
test('#10/HORIZONTAL scale x:1.5 y:2', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.fromJSON({
|
gridview.fromJSON({
|
||||||
@ -1699,11 +1840,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('panel is disposed of when component is disposed', () => {
|
test('panel is disposed of when component is disposed', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(1000, 1000);
|
gridview.layout(1000, 1000);
|
||||||
@ -1730,11 +1877,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('panel is disposed of when removed', () => {
|
test('panel is disposed of when removed', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
gridview.layout(1000, 1000);
|
gridview.layout(1000, 1000);
|
||||||
|
|
||||||
@ -1760,11 +1913,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('panel is disposed of when fromJSON is called', () => {
|
test('panel is disposed of when fromJSON is called', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: false,
|
proportionalLayout: false,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
gridview.layout(1000, 1000);
|
gridview.layout(1000, 1000);
|
||||||
|
|
||||||
@ -1799,11 +1958,17 @@ describe('gridview', () => {
|
|||||||
test('fromJSON events should still fire', () => {
|
test('fromJSON events should still fire', () => {
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let addGroup: GridviewPanel[] = [];
|
let addGroup: GridviewPanel[] = [];
|
||||||
@ -1922,11 +2087,17 @@ describe('gridview', () => {
|
|||||||
test('that fromJSON layouts are resized to the current dimensions', async () => {
|
test('that fromJSON layouts are resized to the current dimensions', async () => {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
|
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(1600, 800);
|
gridview.layout(1600, 800);
|
||||||
@ -2049,11 +2220,17 @@ describe('gridview', () => {
|
|||||||
test('that a deep HORIZONTAL layout with fromJSON dimensions identical to the current dimensions loads', async () => {
|
test('that a deep HORIZONTAL layout with fromJSON dimensions identical to the current dimensions loads', async () => {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
|
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(6000, 5000);
|
gridview.layout(6000, 5000);
|
||||||
@ -2325,11 +2502,17 @@ describe('gridview', () => {
|
|||||||
test('that a deep VERTICAL layout with fromJSON dimensions identical to the current dimensions loads', async () => {
|
test('that a deep VERTICAL layout with fromJSON dimensions identical to the current dimensions loads', async () => {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
|
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gridview.layout(5000, 6000);
|
gridview.layout(5000, 6000);
|
||||||
@ -2599,14 +2782,20 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('that loading a corrupt layout throws an error and leaves a clean gridview behind', () => {
|
test('that loading a corrupt layout throws an error and leaves a clean gridview behind', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error(`unsupported panel '${options.name}'`);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let el = gridview.element.querySelector('.view-container');
|
let el = gridview.element.querySelector('.dv-view-container');
|
||||||
expect(el).toBeTruthy();
|
expect(el).toBeTruthy();
|
||||||
expect(el!.childNodes.length).toBe(0);
|
expect(el!.childNodes.length).toBe(0);
|
||||||
|
|
||||||
@ -2667,34 +2856,44 @@ describe('gridview', () => {
|
|||||||
},
|
},
|
||||||
activePanel: 'panel_1',
|
activePanel: 'panel_1',
|
||||||
});
|
});
|
||||||
}).toThrow(
|
}).toThrow("unsupported panel 'somethingBad'");
|
||||||
"Cannot create 'panel_1', no component 'somethingBad' provided"
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(gridview.groups.length).toBe(0);
|
expect(gridview.groups.length).toBe(0);
|
||||||
|
|
||||||
el = gridview.element.querySelector('.view-container');
|
el = gridview.element.querySelector('.dv-view-container');
|
||||||
expect(el).toBeTruthy();
|
expect(el).toBeTruthy();
|
||||||
expect(el!.childNodes.length).toBe(0);
|
expect(el!.childNodes.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that disableAutoResizing is false by default', () => {
|
test('that disableAutoResizing is false by default', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(gridview.disableResizing).toBeFalsy();
|
expect(gridview.disableResizing).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that disableAutoResizing can be enabled', () => {
|
test('that disableAutoResizing can be enabled', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
disableAutoResizing: true,
|
disableAutoResizing: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2702,11 +2901,17 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('that setVisible toggles visiblity', () => {
|
test('that setVisible toggles visiblity', () => {
|
||||||
const gridview = new GridviewComponent({
|
const gridview = new GridviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
proportionalLayout: true,
|
proportionalLayout: true,
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: { default: TestGridview },
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestGridview(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
disableAutoResizing: true,
|
disableAutoResizing: true,
|
||||||
});
|
});
|
||||||
gridview.layout(1000, 1000);
|
gridview.layout(1000, 1000);
|
||||||
|
@ -8,6 +8,7 @@ describe('gridviewPanel', () => {
|
|||||||
onDidAddPanel: jest.fn(),
|
onDidAddPanel: jest.fn(),
|
||||||
onDidRemovePanel: jest.fn(),
|
onDidRemovePanel: jest.fn(),
|
||||||
options: {},
|
options: {},
|
||||||
|
onDidOptionsChange: jest.fn(),
|
||||||
} as any;
|
} as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,10 +8,8 @@ describe('math', () => {
|
|||||||
expect(clamp(55, 40, 50)).toBe(50);
|
expect(clamp(55, 40, 50)).toBe(50);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if min > max', () => {
|
it('if min > max return min', () => {
|
||||||
expect(() => clamp(55, 50, 40)).toThrow(
|
expect(clamp(55, 50, 40)).toBe(50);
|
||||||
'50 > 40 is an invalid condition'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
418
packages/dockview-core/src/__tests__/overlay/overlay.spec.ts
Normal file
418
packages/dockview-core/src/__tests__/overlay/overlay.spec.ts
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
import { Overlay } from '../../overlay/overlay';
|
||||||
|
import { mockGetBoundingClientRect } from '../__test_utils__/utils';
|
||||||
|
|
||||||
|
describe('overlay', () => {
|
||||||
|
test('toJSON, top left', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
const content = document.createElement('div');
|
||||||
|
|
||||||
|
document.body.appendChild(container);
|
||||||
|
container.appendChild(content);
|
||||||
|
|
||||||
|
const cut = new Overlay({
|
||||||
|
height: 200,
|
||||||
|
width: 100,
|
||||||
|
left: 10,
|
||||||
|
top: 20,
|
||||||
|
minimumInViewportWidth: 0,
|
||||||
|
minimumInViewportHeight: 0,
|
||||||
|
container,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.spyOn(
|
||||||
|
container.childNodes.item(0) as HTMLElement,
|
||||||
|
'getBoundingClientRect'
|
||||||
|
).mockImplementation(() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 80,
|
||||||
|
top: 100,
|
||||||
|
width: 40,
|
||||||
|
height: 50,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
jest.spyOn(container, 'getBoundingClientRect').mockImplementation(
|
||||||
|
() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 20,
|
||||||
|
top: 30,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
cut.setBounds();
|
||||||
|
|
||||||
|
expect(cut.toJSON()).toEqual({
|
||||||
|
top: 70,
|
||||||
|
left: 60,
|
||||||
|
width: 40,
|
||||||
|
height: 50,
|
||||||
|
});
|
||||||
|
|
||||||
|
cut.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toJSON, bottom right', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
const content = document.createElement('div');
|
||||||
|
|
||||||
|
document.body.appendChild(container);
|
||||||
|
container.appendChild(content);
|
||||||
|
|
||||||
|
const cut = new Overlay({
|
||||||
|
height: 200,
|
||||||
|
width: 100,
|
||||||
|
right: 10,
|
||||||
|
bottom: 20,
|
||||||
|
minimumInViewportWidth: 0,
|
||||||
|
minimumInViewportHeight: 0,
|
||||||
|
container,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.spyOn(
|
||||||
|
container.childNodes.item(0) as HTMLElement,
|
||||||
|
'getBoundingClientRect'
|
||||||
|
).mockImplementation(() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 80,
|
||||||
|
top: 100,
|
||||||
|
width: 40,
|
||||||
|
height: 50,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
jest.spyOn(container, 'getBoundingClientRect').mockImplementation(
|
||||||
|
() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 20,
|
||||||
|
top: 30,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
cut.setBounds();
|
||||||
|
|
||||||
|
expect(cut.toJSON()).toEqual({
|
||||||
|
bottom: -20,
|
||||||
|
right: 0,
|
||||||
|
width: 40,
|
||||||
|
height: 50,
|
||||||
|
});
|
||||||
|
|
||||||
|
cut.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('that out-of-bounds dimensions are fixed, top left', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
const content = document.createElement('div');
|
||||||
|
|
||||||
|
document.body.appendChild(container);
|
||||||
|
container.appendChild(content);
|
||||||
|
|
||||||
|
const cut = new Overlay({
|
||||||
|
height: 200,
|
||||||
|
width: 100,
|
||||||
|
left: -1000,
|
||||||
|
top: -1000,
|
||||||
|
minimumInViewportWidth: 0,
|
||||||
|
minimumInViewportHeight: 0,
|
||||||
|
container,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.spyOn(
|
||||||
|
container.childNodes.item(0) as HTMLElement,
|
||||||
|
'getBoundingClientRect'
|
||||||
|
).mockImplementation(() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 80,
|
||||||
|
top: 100,
|
||||||
|
width: 40,
|
||||||
|
height: 50,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
jest.spyOn(container, 'getBoundingClientRect').mockImplementation(
|
||||||
|
() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 20,
|
||||||
|
top: 30,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
cut.setBounds();
|
||||||
|
|
||||||
|
expect(cut.toJSON()).toEqual({
|
||||||
|
top: 70,
|
||||||
|
left: 60,
|
||||||
|
width: 40,
|
||||||
|
height: 50,
|
||||||
|
});
|
||||||
|
|
||||||
|
cut.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('that out-of-bounds dimensions are fixed, bottom right', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
const content = document.createElement('div');
|
||||||
|
|
||||||
|
document.body.appendChild(container);
|
||||||
|
container.appendChild(content);
|
||||||
|
|
||||||
|
const cut = new Overlay({
|
||||||
|
height: 200,
|
||||||
|
width: 100,
|
||||||
|
bottom: -1000,
|
||||||
|
right: -1000,
|
||||||
|
minimumInViewportWidth: 0,
|
||||||
|
minimumInViewportHeight: 0,
|
||||||
|
container,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.spyOn(
|
||||||
|
container.childNodes.item(0) as HTMLElement,
|
||||||
|
'getBoundingClientRect'
|
||||||
|
).mockImplementation(() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 80,
|
||||||
|
top: 100,
|
||||||
|
width: 40,
|
||||||
|
height: 50,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
jest.spyOn(container, 'getBoundingClientRect').mockImplementation(
|
||||||
|
() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 20,
|
||||||
|
top: 30,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
cut.setBounds();
|
||||||
|
|
||||||
|
expect(cut.toJSON()).toEqual({
|
||||||
|
bottom: -20,
|
||||||
|
right: 0,
|
||||||
|
width: 40,
|
||||||
|
height: 50,
|
||||||
|
});
|
||||||
|
|
||||||
|
cut.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setBounds, top left', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
const content = document.createElement('div');
|
||||||
|
|
||||||
|
document.body.appendChild(container);
|
||||||
|
container.appendChild(content);
|
||||||
|
|
||||||
|
const cut = new Overlay({
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
minimumInViewportWidth: 0,
|
||||||
|
minimumInViewportHeight: 0,
|
||||||
|
container,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
|
||||||
|
const element: HTMLElement = container.querySelector(
|
||||||
|
'.dv-resize-container'
|
||||||
|
)!;
|
||||||
|
expect(element).toBeTruthy();
|
||||||
|
|
||||||
|
jest.spyOn(element, 'getBoundingClientRect').mockImplementation(() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 300,
|
||||||
|
top: 400,
|
||||||
|
width: 200,
|
||||||
|
height: 100,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
jest.spyOn(container, 'getBoundingClientRect').mockImplementation(
|
||||||
|
() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: 1000,
|
||||||
|
height: 1000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
cut.setBounds({ height: 100, width: 200, left: 300, top: 400 });
|
||||||
|
|
||||||
|
expect(element.style.height).toBe('100px');
|
||||||
|
expect(element.style.width).toBe('200px');
|
||||||
|
expect(element.style.left).toBe('300px');
|
||||||
|
expect(element.style.top).toBe('400px');
|
||||||
|
|
||||||
|
cut.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setBounds, bottom right', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
const content = document.createElement('div');
|
||||||
|
|
||||||
|
document.body.appendChild(container);
|
||||||
|
container.appendChild(content);
|
||||||
|
|
||||||
|
const cut = new Overlay({
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
minimumInViewportWidth: 0,
|
||||||
|
minimumInViewportHeight: 0,
|
||||||
|
container,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
|
||||||
|
const element: HTMLElement = container.querySelector(
|
||||||
|
'.dv-resize-container'
|
||||||
|
)!;
|
||||||
|
expect(element).toBeTruthy();
|
||||||
|
|
||||||
|
jest.spyOn(element, 'getBoundingClientRect').mockImplementation(() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 500,
|
||||||
|
top: 500,
|
||||||
|
width: 200,
|
||||||
|
height: 100,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
jest.spyOn(container, 'getBoundingClientRect').mockImplementation(
|
||||||
|
() => {
|
||||||
|
return mockGetBoundingClientRect({
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: 1000,
|
||||||
|
height: 1000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
cut.setBounds({ height: 100, width: 200, right: 300, bottom: 400 });
|
||||||
|
|
||||||
|
expect(element.style.height).toBe('100px');
|
||||||
|
expect(element.style.width).toBe('200px');
|
||||||
|
expect(element.style.right).toBe('300px');
|
||||||
|
expect(element.style.bottom).toBe('400px');
|
||||||
|
|
||||||
|
cut.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('that the resize handles are added', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
const content = document.createElement('div');
|
||||||
|
|
||||||
|
const cut = new Overlay({
|
||||||
|
height: 500,
|
||||||
|
width: 500,
|
||||||
|
left: 100,
|
||||||
|
top: 200,
|
||||||
|
minimumInViewportWidth: 0,
|
||||||
|
minimumInViewportHeight: 0,
|
||||||
|
container,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelector('.dv-resize-handle-top')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
container.querySelector('.dv-resize-handle-bottom')
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(container.querySelector('.dv-resize-handle-left')).toBeTruthy();
|
||||||
|
expect(container.querySelector('.dv-resize-handle-right')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
container.querySelector('.dv-resize-handle-topleft')
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
container.querySelector('.dv-resize-handle-topright')
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
container.querySelector('.dv-resize-handle-bottomleft')
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
container.querySelector('.dv-resize-handle-bottomright')
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
cut.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('aria-level attributes and corresponding z-index', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
const content = document.createElement('div');
|
||||||
|
|
||||||
|
const createOverlay = () =>
|
||||||
|
new Overlay({
|
||||||
|
height: 500,
|
||||||
|
width: 500,
|
||||||
|
left: 100,
|
||||||
|
top: 200,
|
||||||
|
minimumInViewportWidth: 0,
|
||||||
|
minimumInViewportHeight: 0,
|
||||||
|
container,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
|
||||||
|
const overlay1 = createOverlay();
|
||||||
|
|
||||||
|
const zIndexValue = (delta: number) =>
|
||||||
|
`calc(var(--dv-overlay-z-index, 999) + ${delta})`;
|
||||||
|
|
||||||
|
expect(overlay1.element.getAttribute('aria-level')).toBe('0');
|
||||||
|
expect(overlay1.element.style.zIndex).toBe(zIndexValue(0));
|
||||||
|
|
||||||
|
const overlay2 = createOverlay();
|
||||||
|
const overlay3 = createOverlay();
|
||||||
|
|
||||||
|
expect(overlay1.element.getAttribute('aria-level')).toBe('0');
|
||||||
|
expect(overlay2.element.getAttribute('aria-level')).toBe('1');
|
||||||
|
expect(overlay3.element.getAttribute('aria-level')).toBe('2');
|
||||||
|
expect(overlay1.element.style.zIndex).toBe(zIndexValue(0));
|
||||||
|
expect(overlay2.element.style.zIndex).toBe(zIndexValue(2));
|
||||||
|
expect(overlay3.element.style.zIndex).toBe(zIndexValue(4));
|
||||||
|
|
||||||
|
overlay2.bringToFront();
|
||||||
|
|
||||||
|
expect(overlay1.element.getAttribute('aria-level')).toBe('0');
|
||||||
|
expect(overlay2.element.getAttribute('aria-level')).toBe('2');
|
||||||
|
expect(overlay3.element.getAttribute('aria-level')).toBe('1');
|
||||||
|
expect(overlay1.element.style.zIndex).toBe(zIndexValue(0));
|
||||||
|
expect(overlay2.element.style.zIndex).toBe(zIndexValue(4));
|
||||||
|
expect(overlay3.element.style.zIndex).toBe(zIndexValue(2));
|
||||||
|
|
||||||
|
overlay1.bringToFront();
|
||||||
|
|
||||||
|
expect(overlay1.element.getAttribute('aria-level')).toBe('2');
|
||||||
|
expect(overlay2.element.getAttribute('aria-level')).toBe('1');
|
||||||
|
expect(overlay3.element.getAttribute('aria-level')).toBe('0');
|
||||||
|
expect(overlay1.element.style.zIndex).toBe(zIndexValue(4));
|
||||||
|
expect(overlay2.element.style.zIndex).toBe(zIndexValue(2));
|
||||||
|
expect(overlay3.element.style.zIndex).toBe(zIndexValue(0));
|
||||||
|
|
||||||
|
overlay2.dispose();
|
||||||
|
|
||||||
|
expect(overlay1.element.getAttribute('aria-level')).toBe('1');
|
||||||
|
expect(overlay3.element.getAttribute('aria-level')).toBe('0');
|
||||||
|
expect(overlay1.element.style.zIndex).toBe(zIndexValue(2));
|
||||||
|
expect(overlay3.element.style.zIndex).toBe(zIndexValue(0));
|
||||||
|
|
||||||
|
overlay1.dispose();
|
||||||
|
|
||||||
|
expect(overlay3.element.getAttribute('aria-level')).toBe('0');
|
||||||
|
expect(overlay3.element.style.zIndex).toBe(zIndexValue(0));
|
||||||
|
});
|
||||||
|
});
|
@ -1,14 +1,18 @@
|
|||||||
import { Droptarget } from '../dnd/droptarget';
|
import { Droptarget } from '../../dnd/droptarget';
|
||||||
import { IDockviewPanel } from '../dockview/dockviewPanel';
|
import { IDockviewPanel } from '../../dockview/dockviewPanel';
|
||||||
import { Emitter } from '../events';
|
import { Emitter } from '../../events';
|
||||||
import { IRenderable, OverlayRenderContainer } from '../overlayRenderContainer';
|
import {
|
||||||
|
IRenderable,
|
||||||
|
OverlayRenderContainer,
|
||||||
|
} from '../../overlay/overlayRenderContainer';
|
||||||
import { fromPartial } from '@total-typescript/shoehorn';
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
import { Writable, exhaustMicrotaskQueue } from './__test_utils__/utils';
|
import { Writable, exhaustMicrotaskQueue } from '../__test_utils__/utils';
|
||||||
|
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
||||||
|
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
||||||
|
|
||||||
describe('overlayRenderContainer', () => {
|
describe('overlayRenderContainer', () => {
|
||||||
let referenceContainer: IRenderable;
|
let referenceContainer: IRenderable;
|
||||||
let parentContainer: HTMLElement;
|
let parentContainer: HTMLElement;
|
||||||
let cut: OverlayRenderContainer;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
parentContainer = document.createElement('div');
|
parentContainer = document.createElement('div');
|
||||||
@ -17,22 +21,28 @@ describe('overlayRenderContainer', () => {
|
|||||||
element: document.createElement('div'),
|
element: document.createElement('div'),
|
||||||
dropTarget: fromPartial<Droptarget>({}),
|
dropTarget: fromPartial<Droptarget>({}),
|
||||||
};
|
};
|
||||||
|
|
||||||
cut = new OverlayRenderContainer(parentContainer);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that attach(...) and detach(...) mutate the DOM as expected', () => {
|
test('that attach(...) and detach(...) mutate the DOM as expected', () => {
|
||||||
|
const cut = new OverlayRenderContainer(
|
||||||
|
parentContainer,
|
||||||
|
fromPartial<DockviewComponent>({})
|
||||||
|
);
|
||||||
|
|
||||||
const panelContentEl = document.createElement('div');
|
const panelContentEl = document.createElement('div');
|
||||||
|
|
||||||
const onDidVisibilityChange = new Emitter<any>();
|
const onDidVisibilityChange = new Emitter<any>();
|
||||||
const onDidDimensionsChange = new Emitter<any>();
|
const onDidDimensionsChange = new Emitter<any>();
|
||||||
|
const onDidLocationChange = new Emitter<any>();
|
||||||
|
|
||||||
const panel = fromPartial<IDockviewPanel>({
|
const panel = fromPartial<IDockviewPanel>({
|
||||||
api: {
|
api: {
|
||||||
id: 'test_panel_id',
|
id: 'test_panel_id',
|
||||||
onDidVisibilityChange: onDidVisibilityChange.event,
|
onDidVisibilityChange: onDidVisibilityChange.event,
|
||||||
onDidDimensionsChange: onDidDimensionsChange.event,
|
onDidDimensionsChange: onDidDimensionsChange.event,
|
||||||
|
onDidLocationChange: onDidLocationChange.event,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
|
location: { type: 'grid' },
|
||||||
},
|
},
|
||||||
view: {
|
view: {
|
||||||
content: {
|
content: {
|
||||||
@ -58,17 +68,25 @@ describe('overlayRenderContainer', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('add a view that is not currently in the DOM', async () => {
|
test('add a view that is not currently in the DOM', async () => {
|
||||||
|
const cut = new OverlayRenderContainer(
|
||||||
|
parentContainer,
|
||||||
|
fromPartial<DockviewComponent>({})
|
||||||
|
);
|
||||||
|
|
||||||
const panelContentEl = document.createElement('div');
|
const panelContentEl = document.createElement('div');
|
||||||
|
|
||||||
const onDidVisibilityChange = new Emitter<any>();
|
const onDidVisibilityChange = new Emitter<any>();
|
||||||
const onDidDimensionsChange = new Emitter<any>();
|
const onDidDimensionsChange = new Emitter<any>();
|
||||||
|
const onDidLocationChange = new Emitter<any>();
|
||||||
|
|
||||||
const panel = fromPartial<IDockviewPanel>({
|
const panel = fromPartial<IDockviewPanel>({
|
||||||
api: {
|
api: {
|
||||||
id: 'test_panel_id',
|
id: 'test_panel_id',
|
||||||
onDidVisibilityChange: onDidVisibilityChange.event,
|
onDidVisibilityChange: onDidVisibilityChange.event,
|
||||||
onDidDimensionsChange: onDidDimensionsChange.event,
|
onDidDimensionsChange: onDidDimensionsChange.event,
|
||||||
|
onDidLocationChange: onDidLocationChange.event,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
|
location: { type: 'grid' },
|
||||||
},
|
},
|
||||||
view: {
|
view: {
|
||||||
content: {
|
content: {
|
||||||
@ -186,4 +204,62 @@ describe('overlayRenderContainer', () => {
|
|||||||
referenceContainer.element.getBoundingClientRect
|
referenceContainer.element.getBoundingClientRect
|
||||||
).toHaveBeenCalledTimes(3);
|
).toHaveBeenCalledTimes(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('related z-index from `aria-level` set on floating panels', async () => {
|
||||||
|
const group = fromPartial<DockviewGroupPanel>({});
|
||||||
|
|
||||||
|
const element = document.createElement('div');
|
||||||
|
element.setAttribute('aria-level', '2');
|
||||||
|
const spy = jest.spyOn(element, 'getAttribute');
|
||||||
|
|
||||||
|
const accessor = fromPartial<DockviewComponent>({
|
||||||
|
floatingGroups: [
|
||||||
|
{
|
||||||
|
group,
|
||||||
|
overlay: {
|
||||||
|
element,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const cut = new OverlayRenderContainer(parentContainer, accessor);
|
||||||
|
|
||||||
|
const panelContentEl = document.createElement('div');
|
||||||
|
|
||||||
|
const onDidVisibilityChange = new Emitter<any>();
|
||||||
|
const onDidDimensionsChange = new Emitter<any>();
|
||||||
|
const onDidLocationChange = new Emitter<any>();
|
||||||
|
|
||||||
|
const panel = fromPartial<IDockviewPanel>({
|
||||||
|
api: {
|
||||||
|
id: 'test_panel_id',
|
||||||
|
onDidVisibilityChange: onDidVisibilityChange.event,
|
||||||
|
onDidDimensionsChange: onDidDimensionsChange.event,
|
||||||
|
onDidLocationChange: onDidLocationChange.event,
|
||||||
|
isVisible: true,
|
||||||
|
group,
|
||||||
|
location: { type: 'floating' },
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
content: {
|
||||||
|
element: panelContentEl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
api: {
|
||||||
|
location: { type: 'floating' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
cut.attach({ panel, referenceContainer });
|
||||||
|
|
||||||
|
await exhaustMicrotaskQueue();
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith('aria-level');
|
||||||
|
expect(panelContentEl.parentElement!.style.zIndex).toBe(
|
||||||
|
'calc(var(--dv-overlay-z-index, 999) + 5)'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
@ -1,102 +0,0 @@
|
|||||||
import { createComponent } from '../../panel/componentFactory';
|
|
||||||
|
|
||||||
describe('componentFactory', () => {
|
|
||||||
describe('createComponent', () => {
|
|
||||||
test('valid component and framework component', () => {
|
|
||||||
const mock = jest.fn();
|
|
||||||
const mock2 = jest.fn();
|
|
||||||
|
|
||||||
expect(() =>
|
|
||||||
createComponent(
|
|
||||||
'id-1',
|
|
||||||
'component-1',
|
|
||||||
{ 'component-1': mock },
|
|
||||||
{ 'component-1': mock2 }
|
|
||||||
)
|
|
||||||
).toThrow(
|
|
||||||
"Cannot create 'id-1'. component 'component-1' registered as both a component and frameworkComponent"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('valid framework component but no factory', () => {
|
|
||||||
const mock = jest.fn();
|
|
||||||
|
|
||||||
expect(() =>
|
|
||||||
createComponent(
|
|
||||||
'id-1',
|
|
||||||
'component-1',
|
|
||||||
{},
|
|
||||||
{ 'component-1': mock }
|
|
||||||
)
|
|
||||||
).toThrow(
|
|
||||||
"Cannot create 'id-1' for framework component 'component-1'. you must register a frameworkPanelWrapper to use framework components"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('valid framework component', () => {
|
|
||||||
const component = jest.fn();
|
|
||||||
const createComponentFn = jest
|
|
||||||
.fn()
|
|
||||||
.mockImplementation(() => component);
|
|
||||||
const frameworkComponent = jest.fn();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
createComponent(
|
|
||||||
'id-1',
|
|
||||||
'component-1',
|
|
||||||
{},
|
|
||||||
{ 'component-1': frameworkComponent },
|
|
||||||
{
|
|
||||||
createComponent: createComponentFn,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
).toBe(component);
|
|
||||||
|
|
||||||
expect(createComponentFn).toHaveBeenCalledWith(
|
|
||||||
'id-1',
|
|
||||||
'component-1',
|
|
||||||
frameworkComponent
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('no valid component with fallback', () => {
|
|
||||||
const mock = jest.fn();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
createComponent(
|
|
||||||
'id-1',
|
|
||||||
'component-1',
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
createComponent: () => null,
|
|
||||||
},
|
|
||||||
() => mock
|
|
||||||
)
|
|
||||||
).toBe(mock);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('no valid component', () => {
|
|
||||||
expect(() =>
|
|
||||||
createComponent('id-1', 'component-1', {}, {})
|
|
||||||
).toThrow(
|
|
||||||
"Cannot create 'id-1', no component 'component-1' provided"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('valid component', () => {
|
|
||||||
const component = jest.fn();
|
|
||||||
|
|
||||||
const componentResult = createComponent(
|
|
||||||
'id-1',
|
|
||||||
'component-1',
|
|
||||||
{ 'component-1': component },
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(component).toHaveBeenCalled();
|
|
||||||
|
|
||||||
expect(componentResult instanceof component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,14 +1,10 @@
|
|||||||
import { CompositeDisposable } from '../../lifecycle';
|
import { CompositeDisposable } from '../../lifecycle';
|
||||||
import { Paneview } from '../../paneview/paneview';
|
import { Paneview } from '../../paneview/paneview';
|
||||||
import {
|
import { IPanePart, PaneviewPanel } from '../../paneview/paneviewPanel';
|
||||||
IPaneBodyPart,
|
|
||||||
IPaneHeaderPart,
|
|
||||||
PaneviewPanel,
|
|
||||||
} from '../../paneview/paneviewPanel';
|
|
||||||
import { Orientation } from '../../splitview/splitview';
|
import { Orientation } from '../../splitview/splitview';
|
||||||
|
|
||||||
class TestPanel extends PaneviewPanel {
|
class TestPanel extends PaneviewPanel {
|
||||||
protected getBodyComponent(): IPaneBodyPart {
|
protected getBodyComponent(): IPanePart {
|
||||||
return {
|
return {
|
||||||
element: document.createElement('div'),
|
element: document.createElement('div'),
|
||||||
update: () => {
|
update: () => {
|
||||||
@ -23,7 +19,7 @@ class TestPanel extends PaneviewPanel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getHeaderComponent(): IPaneHeaderPart {
|
protected getHeaderComponent(): IPanePart {
|
||||||
return {
|
return {
|
||||||
element: document.createElement('div'),
|
element: document.createElement('div'),
|
||||||
update: () => {
|
update: () => {
|
||||||
@ -60,22 +56,28 @@ describe('paneview', () => {
|
|||||||
paneview.onDidRemoveView((view) => removed.push(view))
|
paneview.onDidRemoveView((view) => removed.push(view))
|
||||||
);
|
);
|
||||||
|
|
||||||
const view1 = new TestPanel(
|
const view1 = new TestPanel({
|
||||||
'id',
|
id: 'id',
|
||||||
'component',
|
component: 'component',
|
||||||
'headerComponent',
|
headerComponent: 'headerComponent',
|
||||||
Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
true,
|
isExpanded: true,
|
||||||
true
|
isHeaderVisible: true,
|
||||||
);
|
headerSize: 22,
|
||||||
const view2 = new TestPanel(
|
minimumBodySize: 0,
|
||||||
'id2',
|
maximumBodySize: Number.MAX_SAFE_INTEGER,
|
||||||
'component',
|
});
|
||||||
'headerComponent',
|
const view2 = new TestPanel({
|
||||||
Orientation.VERTICAL,
|
id: 'id2',
|
||||||
true,
|
component: 'component',
|
||||||
true
|
headerComponent: 'headerComponent',
|
||||||
);
|
orientation: Orientation.VERTICAL,
|
||||||
|
isExpanded: true,
|
||||||
|
isHeaderVisible: true,
|
||||||
|
headerSize: 22,
|
||||||
|
minimumBodySize: 0,
|
||||||
|
maximumBodySize: Number.MAX_SAFE_INTEGER,
|
||||||
|
});
|
||||||
|
|
||||||
expect(added.length).toBe(0);
|
expect(added.length).toBe(0);
|
||||||
expect(removed.length).toBe(0);
|
expect(removed.length).toBe(0);
|
||||||
@ -110,22 +112,28 @@ describe('paneview', () => {
|
|||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
});
|
});
|
||||||
|
|
||||||
const view1 = new TestPanel(
|
const view1 = new TestPanel({
|
||||||
'id',
|
id: 'id',
|
||||||
'component',
|
component: 'component',
|
||||||
'headerComponent',
|
headerComponent: 'headerComponent',
|
||||||
Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
true,
|
isExpanded: true,
|
||||||
true
|
isHeaderVisible: true,
|
||||||
);
|
headerSize: 22,
|
||||||
const view2 = new TestPanel(
|
minimumBodySize: 0,
|
||||||
'id2',
|
maximumBodySize: Number.MAX_SAFE_INTEGER,
|
||||||
'component',
|
});
|
||||||
'headerComponent',
|
const view2 = new TestPanel({
|
||||||
Orientation.VERTICAL,
|
id: 'id2',
|
||||||
true,
|
component: 'component',
|
||||||
true
|
headerComponent: 'headerComponent',
|
||||||
);
|
orientation: Orientation.VERTICAL,
|
||||||
|
isExpanded: true,
|
||||||
|
isHeaderVisible: true,
|
||||||
|
headerSize: 22,
|
||||||
|
minimumBodySize: 0,
|
||||||
|
maximumBodySize: Number.MAX_SAFE_INTEGER,
|
||||||
|
});
|
||||||
|
|
||||||
paneview.addPane(view1);
|
paneview.addPane(view1);
|
||||||
paneview.addPane(view2);
|
paneview.addPane(view2);
|
||||||
|
@ -4,19 +4,28 @@ import { PanelUpdateEvent } from '../../panel/types';
|
|||||||
import { PaneviewComponent } from '../../paneview/paneviewComponent';
|
import { PaneviewComponent } from '../../paneview/paneviewComponent';
|
||||||
import {
|
import {
|
||||||
PaneviewPanel,
|
PaneviewPanel,
|
||||||
IPaneBodyPart,
|
IPanePart,
|
||||||
IPaneHeaderPart,
|
|
||||||
PanePanelComponentInitParameter,
|
PanePanelComponentInitParameter,
|
||||||
} from '../../paneview/paneviewPanel';
|
} from '../../paneview/paneviewPanel';
|
||||||
import { Orientation } from '../../splitview/splitview';
|
import { Orientation } from '../../splitview/splitview';
|
||||||
|
|
||||||
class TestPanel extends PaneviewPanel {
|
class TestPanel extends PaneviewPanel {
|
||||||
constructor(id: string, component: string) {
|
constructor(id: string, component: string) {
|
||||||
super(id, component, 'header', Orientation.VERTICAL, false, true);
|
super({
|
||||||
|
id,
|
||||||
|
component,
|
||||||
|
headerComponent: 'header',
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
isExpanded: false,
|
||||||
|
isHeaderVisible: true,
|
||||||
|
headerSize: 22,
|
||||||
|
minimumBodySize: 0,
|
||||||
|
maximumBodySize: Number.MAX_SAFE_INTEGER,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getHeaderComponent() {
|
getHeaderComponent() {
|
||||||
return new (class Header implements IPaneHeaderPart {
|
return new (class Header implements IPanePart {
|
||||||
private _element: HTMLElement = document.createElement('div');
|
private _element: HTMLElement = document.createElement('div');
|
||||||
|
|
||||||
get element() {
|
get element() {
|
||||||
@ -38,7 +47,7 @@ class TestPanel extends PaneviewPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getBodyComponent() {
|
getBodyComponent() {
|
||||||
return new (class Header implements IPaneBodyPart {
|
return new (class Header implements IPanePart {
|
||||||
private _element: HTMLElement = document.createElement('div');
|
private _element: HTMLElement = document.createElement('div');
|
||||||
|
|
||||||
get element() {
|
get element() {
|
||||||
@ -60,7 +69,7 @@ class TestPanel extends PaneviewPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('componentPaneview', () => {
|
describe('paneviewComponent', () => {
|
||||||
let container: HTMLElement;
|
let container: HTMLElement;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -68,13 +77,39 @@ describe('componentPaneview', () => {
|
|||||||
container.className = 'container';
|
container.className = 'container';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('that the container is not removed when grid is disposed', () => {
|
||||||
|
const root = document.createElement('div');
|
||||||
|
const container = document.createElement('div');
|
||||||
|
root.appendChild(container);
|
||||||
|
|
||||||
|
const paneview = new PaneviewComponent(container, {
|
||||||
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
paneview.dispose();
|
||||||
|
|
||||||
|
expect(container.parentElement).toBe(root);
|
||||||
|
expect(container.children.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
test('vertical panels', () => {
|
test('vertical panels', () => {
|
||||||
const disposables = new CompositeDisposable();
|
const disposables = new CompositeDisposable();
|
||||||
|
|
||||||
const paneview = new PaneviewComponent({
|
const paneview = new PaneviewComponent(container, {
|
||||||
parentElement: container,
|
createComponent: (options) => {
|
||||||
components: {
|
switch (options.name) {
|
||||||
testPanel: TestPanel,
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -82,12 +117,12 @@ describe('componentPaneview', () => {
|
|||||||
|
|
||||||
paneview.addPanel({
|
paneview.addPanel({
|
||||||
id: 'panel1',
|
id: 'panel1',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 1',
|
title: 'Panel 1',
|
||||||
});
|
});
|
||||||
paneview.addPanel({
|
paneview.addPanel({
|
||||||
id: 'panel2',
|
id: 'panel2',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel2',
|
title: 'Panel2',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -144,13 +179,19 @@ describe('componentPaneview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('serialization', () => {
|
test('serialization', () => {
|
||||||
const paneview = new PaneviewComponent({
|
const paneview = new PaneviewComponent(container, {
|
||||||
parentElement: container,
|
createComponent: (options) => {
|
||||||
components: {
|
switch (options.name) {
|
||||||
testPanel: TestPanel,
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll('.dv-pane-container').length).toBe(1);
|
||||||
|
|
||||||
paneview.fromJSON({
|
paneview.fromJSON({
|
||||||
size: 6,
|
size: 6,
|
||||||
views: [
|
views: [
|
||||||
@ -158,7 +199,7 @@ describe('componentPaneview', () => {
|
|||||||
size: 1,
|
size: 1,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel1',
|
id: 'panel1',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 1',
|
title: 'Panel 1',
|
||||||
},
|
},
|
||||||
expanded: true,
|
expanded: true,
|
||||||
@ -167,7 +208,7 @@ describe('componentPaneview', () => {
|
|||||||
size: 2,
|
size: 2,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel2',
|
id: 'panel2',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 2',
|
title: 'Panel 2',
|
||||||
},
|
},
|
||||||
expanded: false,
|
expanded: false,
|
||||||
@ -176,13 +217,15 @@ describe('componentPaneview', () => {
|
|||||||
size: 3,
|
size: 3,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel3',
|
id: 'panel3',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 3',
|
title: 'Panel 3',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll('.dv-pane-container').length).toBe(1);
|
||||||
|
|
||||||
paneview.layout(400, 800);
|
paneview.layout(400, 800);
|
||||||
|
|
||||||
const panel1 = paneview.getPanel('panel1');
|
const panel1 = paneview.getPanel('panel1');
|
||||||
@ -222,53 +265,57 @@ describe('componentPaneview', () => {
|
|||||||
size: 756,
|
size: 756,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel1',
|
id: 'panel1',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 1',
|
title: 'Panel 1',
|
||||||
},
|
},
|
||||||
expanded: true,
|
expanded: true,
|
||||||
minimumSize: 100,
|
headerSize: 22,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 22,
|
size: 22,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel2',
|
id: 'panel2',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 2',
|
title: 'Panel 2',
|
||||||
},
|
},
|
||||||
expanded: false,
|
expanded: false,
|
||||||
minimumSize: 100,
|
headerSize: 22,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 22,
|
size: 22,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel3',
|
id: 'panel3',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 3',
|
title: 'Panel 3',
|
||||||
},
|
},
|
||||||
expanded: false,
|
expanded: false,
|
||||||
minimumSize: 100,
|
headerSize: 22,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('toJSON shouldnt fire any layout events', () => {
|
test('toJSON shouldnt fire any layout events', () => {
|
||||||
const paneview = new PaneviewComponent({
|
const paneview = new PaneviewComponent(container, {
|
||||||
parentElement: container,
|
createComponent: (options) => {
|
||||||
components: {
|
switch (options.name) {
|
||||||
testPanel: TestPanel,
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
paneview.layout(1000, 1000);
|
paneview.layout(1000, 1000);
|
||||||
|
|
||||||
paneview.addPanel({
|
paneview.addPanel({
|
||||||
id: 'panel1',
|
id: 'panel1',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 1',
|
title: 'Panel 1',
|
||||||
});
|
});
|
||||||
paneview.addPanel({
|
paneview.addPanel({
|
||||||
id: 'panel2',
|
id: 'panel2',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 2',
|
title: 'Panel 2',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -282,41 +329,15 @@ describe('componentPaneview', () => {
|
|||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('dispose of paneviewComponent', () => {
|
|
||||||
expect(container.childNodes.length).toBe(0);
|
|
||||||
|
|
||||||
const paneview = new PaneviewComponent({
|
|
||||||
parentElement: container,
|
|
||||||
components: {
|
|
||||||
testPanel: TestPanel,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
paneview.layout(1000, 1000);
|
|
||||||
|
|
||||||
paneview.addPanel({
|
|
||||||
id: 'panel1',
|
|
||||||
component: 'testPanel',
|
|
||||||
title: 'Panel 1',
|
|
||||||
});
|
|
||||||
paneview.addPanel({
|
|
||||||
id: 'panel2',
|
|
||||||
component: 'testPanel',
|
|
||||||
title: 'Panel 2',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(container.childNodes.length).toBeGreaterThan(0);
|
|
||||||
|
|
||||||
paneview.dispose();
|
|
||||||
|
|
||||||
expect(container.childNodes.length).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('panel is disposed of when component is disposed', () => {
|
test('panel is disposed of when component is disposed', () => {
|
||||||
const paneview = new PaneviewComponent({
|
const paneview = new PaneviewComponent(container, {
|
||||||
parentElement: container,
|
createComponent: (options) => {
|
||||||
components: {
|
switch (options.name) {
|
||||||
testPanel: TestPanel,
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -324,12 +345,12 @@ describe('componentPaneview', () => {
|
|||||||
|
|
||||||
paneview.addPanel({
|
paneview.addPanel({
|
||||||
id: 'panel1',
|
id: 'panel1',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 1',
|
title: 'Panel 1',
|
||||||
});
|
});
|
||||||
paneview.addPanel({
|
paneview.addPanel({
|
||||||
id: 'panel2',
|
id: 'panel2',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 2',
|
title: 'Panel 2',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -346,10 +367,14 @@ describe('componentPaneview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('panel is disposed of when removed', () => {
|
test('panel is disposed of when removed', () => {
|
||||||
const paneview = new PaneviewComponent({
|
const paneview = new PaneviewComponent(container, {
|
||||||
parentElement: container,
|
createComponent: (options) => {
|
||||||
components: {
|
switch (options.name) {
|
||||||
testPanel: TestPanel,
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -357,12 +382,12 @@ describe('componentPaneview', () => {
|
|||||||
|
|
||||||
paneview.addPanel({
|
paneview.addPanel({
|
||||||
id: 'panel1',
|
id: 'panel1',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 1',
|
title: 'Panel 1',
|
||||||
});
|
});
|
||||||
paneview.addPanel({
|
paneview.addPanel({
|
||||||
id: 'panel2',
|
id: 'panel2',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 2',
|
title: 'Panel 2',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -379,10 +404,14 @@ describe('componentPaneview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('panel is disposed of when fromJSON is called', () => {
|
test('panel is disposed of when fromJSON is called', () => {
|
||||||
const paneview = new PaneviewComponent({
|
const paneview = new PaneviewComponent(container, {
|
||||||
parentElement: container,
|
createComponent: (options) => {
|
||||||
components: {
|
switch (options.name) {
|
||||||
testPanel: TestPanel,
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -390,12 +419,12 @@ describe('componentPaneview', () => {
|
|||||||
|
|
||||||
paneview.addPanel({
|
paneview.addPanel({
|
||||||
id: 'panel1',
|
id: 'panel1',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 1',
|
title: 'Panel 1',
|
||||||
});
|
});
|
||||||
paneview.addPanel({
|
paneview.addPanel({
|
||||||
id: 'panel2',
|
id: 'panel2',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 2',
|
title: 'Panel 2',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -412,10 +441,14 @@ describe('componentPaneview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('that fromJSON layouts are resized to the current dimensions', async () => {
|
test('that fromJSON layouts are resized to the current dimensions', async () => {
|
||||||
const paneview = new PaneviewComponent({
|
const paneview = new PaneviewComponent(container, {
|
||||||
parentElement: container,
|
createComponent: (options) => {
|
||||||
components: {
|
switch (options.name) {
|
||||||
testPanel: TestPanel,
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -428,16 +461,17 @@ describe('componentPaneview', () => {
|
|||||||
size: 1,
|
size: 1,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel1',
|
id: 'panel1',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 1',
|
title: 'Panel 1',
|
||||||
},
|
},
|
||||||
|
minimumSize: 100,
|
||||||
expanded: true,
|
expanded: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 2,
|
size: 2,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel2',
|
id: 'panel2',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 2',
|
title: 'Panel 2',
|
||||||
},
|
},
|
||||||
expanded: true,
|
expanded: true,
|
||||||
@ -446,7 +480,7 @@ describe('componentPaneview', () => {
|
|||||||
size: 3,
|
size: 3,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel3',
|
id: 'panel3',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 3',
|
title: 'Panel 3',
|
||||||
},
|
},
|
||||||
expanded: true,
|
expanded: true,
|
||||||
@ -462,41 +496,46 @@ describe('componentPaneview', () => {
|
|||||||
size: 122,
|
size: 122,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel1',
|
id: 'panel1',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 1',
|
title: 'Panel 1',
|
||||||
},
|
},
|
||||||
expanded: true,
|
expanded: true,
|
||||||
minimumSize: 100,
|
minimumSize: 100,
|
||||||
|
headerSize: 22,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 122,
|
size: 22,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel2',
|
id: 'panel2',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 2',
|
title: 'Panel 2',
|
||||||
},
|
},
|
||||||
expanded: true,
|
expanded: true,
|
||||||
minimumSize: 100,
|
headerSize: 22,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 356,
|
size: 456,
|
||||||
data: {
|
data: {
|
||||||
id: 'panel3',
|
id: 'panel3',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
title: 'Panel 3',
|
title: 'Panel 3',
|
||||||
},
|
},
|
||||||
expanded: true,
|
expanded: true,
|
||||||
minimumSize: 100,
|
headerSize: 22,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that disableAutoResizing is false by default', () => {
|
test('that disableAutoResizing is false by default', () => {
|
||||||
const paneview = new PaneviewComponent({
|
const paneview = new PaneviewComponent(container, {
|
||||||
parentElement: container,
|
createComponent: (options) => {
|
||||||
components: {
|
switch (options.name) {
|
||||||
testPanel: TestPanel,
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -504,10 +543,14 @@ describe('componentPaneview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('that disableAutoResizing can be enabled', () => {
|
test('that disableAutoResizing can be enabled', () => {
|
||||||
const paneview = new PaneviewComponent({
|
const paneview = new PaneviewComponent(container, {
|
||||||
parentElement: container,
|
createComponent: (options) => {
|
||||||
components: {
|
switch (options.name) {
|
||||||
testPanel: TestPanel,
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disableAutoResizing: true,
|
disableAutoResizing: true,
|
||||||
});
|
});
|
||||||
@ -516,10 +559,14 @@ describe('componentPaneview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('that setVisible toggles visiblity', () => {
|
test('that setVisible toggles visiblity', () => {
|
||||||
const paneview = new PaneviewComponent({
|
const paneview = new PaneviewComponent(container, {
|
||||||
parentElement: container,
|
createComponent: (options) => {
|
||||||
components: {
|
switch (options.name) {
|
||||||
default: TestPanel,
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disableAutoResizing: true,
|
disableAutoResizing: true,
|
||||||
});
|
});
|
||||||
@ -548,4 +595,25 @@ describe('componentPaneview', () => {
|
|||||||
expect(panel1.api.isVisible).toBeTruthy();
|
expect(panel1.api.isVisible).toBeTruthy();
|
||||||
expect(panel2.api.isVisible).toBeTruthy();
|
expect(panel2.api.isVisible).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('update className', () => {
|
||||||
|
const paneview = new PaneviewComponent(container, {
|
||||||
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
disableAutoResizing: true,
|
||||||
|
className: 'test-a test-b',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(paneview.element.className).toBe('test-a test-b');
|
||||||
|
|
||||||
|
paneview.updateOptions({ className: 'test-b test-c' });
|
||||||
|
|
||||||
|
expect(paneview.element.className).toBe('test-b test-c');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -96,7 +96,7 @@ describe('splitview', () => {
|
|||||||
expect(splitview.orientation).toBe(Orientation.HORIZONTAL);
|
expect(splitview.orientation).toBe(Orientation.HORIZONTAL);
|
||||||
|
|
||||||
const viewQuery = container.querySelectorAll(
|
const viewQuery = container.querySelectorAll(
|
||||||
'.split-view-container horizontal'
|
'.dv-split-view-container dv-horizontal'
|
||||||
);
|
);
|
||||||
expect(viewQuery).toBeTruthy();
|
expect(viewQuery).toBeTruthy();
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ describe('splitview', () => {
|
|||||||
expect(splitview.orientation).toBe(Orientation.VERTICAL);
|
expect(splitview.orientation).toBe(Orientation.VERTICAL);
|
||||||
|
|
||||||
const viewQuery = container.querySelectorAll(
|
const viewQuery = container.querySelectorAll(
|
||||||
'.split-view-container vertical'
|
'.dv-split-view-container dv-vertical'
|
||||||
);
|
);
|
||||||
expect(viewQuery).toBeTruthy();
|
expect(viewQuery).toBeTruthy();
|
||||||
|
|
||||||
@ -128,48 +128,48 @@ describe('splitview', () => {
|
|||||||
splitview.addView(new Testview(50, 50));
|
splitview.addView(new Testview(50, 50));
|
||||||
|
|
||||||
let viewQuery = container.querySelectorAll(
|
let viewQuery = container.querySelectorAll(
|
||||||
'.split-view-container > .view-container > .view'
|
'.dv-split-view-container > .dv-view-container > .dv-view'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(3);
|
expect(viewQuery.length).toBe(3);
|
||||||
|
|
||||||
let sashQuery = container.querySelectorAll(
|
let sashQuery = container.querySelectorAll(
|
||||||
'.split-view-container > .sash-container > .sash'
|
'.dv-split-view-container > .dv-sash-container > .dv-sash'
|
||||||
);
|
);
|
||||||
expect(sashQuery.length).toBe(2);
|
expect(sashQuery.length).toBe(2);
|
||||||
|
|
||||||
splitview.removeView(2);
|
splitview.removeView(2);
|
||||||
|
|
||||||
viewQuery = container.querySelectorAll(
|
viewQuery = container.querySelectorAll(
|
||||||
'.split-view-container > .view-container > .view'
|
'.dv-split-view-container > .dv-view-container > .dv-view'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(2);
|
expect(viewQuery.length).toBe(2);
|
||||||
|
|
||||||
sashQuery = container.querySelectorAll(
|
sashQuery = container.querySelectorAll(
|
||||||
'.split-view-container > .sash-container > .sash'
|
'.dv-split-view-container > .dv-sash-container > .dv-sash'
|
||||||
);
|
);
|
||||||
expect(sashQuery.length).toBe(1);
|
expect(sashQuery.length).toBe(1);
|
||||||
|
|
||||||
splitview.removeView(0);
|
splitview.removeView(0);
|
||||||
|
|
||||||
viewQuery = container.querySelectorAll(
|
viewQuery = container.querySelectorAll(
|
||||||
'.split-view-container > .view-container > .view'
|
'.dv-split-view-container > .dv-view-container > .dv-view'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
|
|
||||||
sashQuery = container.querySelectorAll(
|
sashQuery = container.querySelectorAll(
|
||||||
'.split-view-container > .sash-container > .sash'
|
'.dv-split-view-container > .dv-sash-container > .dv-sash'
|
||||||
);
|
);
|
||||||
expect(sashQuery.length).toBe(0);
|
expect(sashQuery.length).toBe(0);
|
||||||
|
|
||||||
splitview.removeView(0);
|
splitview.removeView(0);
|
||||||
|
|
||||||
viewQuery = container.querySelectorAll(
|
viewQuery = container.querySelectorAll(
|
||||||
'.split-view-container > .view-container > .view'
|
'.dv-split-view-container > .dv-view-container > .dv-view'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(0);
|
expect(viewQuery.length).toBe(0);
|
||||||
|
|
||||||
sashQuery = container.querySelectorAll(
|
sashQuery = container.querySelectorAll(
|
||||||
'.split-view-container > .sash-container > .sash'
|
'.dv-split-view-container > .dv-sash-container > .dv-sash'
|
||||||
);
|
);
|
||||||
expect(sashQuery.length).toBe(0);
|
expect(sashQuery.length).toBe(0);
|
||||||
|
|
||||||
@ -188,14 +188,14 @@ describe('splitview', () => {
|
|||||||
splitview.addView(view2);
|
splitview.addView(view2);
|
||||||
|
|
||||||
let viewQuery = container.querySelectorAll(
|
let viewQuery = container.querySelectorAll(
|
||||||
'.split-view-container > .view-container > .view.visible'
|
'.dv-split-view-container > .dv-view-container > .dv-view.visible'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(2);
|
expect(viewQuery.length).toBe(2);
|
||||||
|
|
||||||
splitview.setViewVisible(1, false);
|
splitview.setViewVisible(1, false);
|
||||||
|
|
||||||
viewQuery = container.querySelectorAll(
|
viewQuery = container.querySelectorAll(
|
||||||
'.split-view-container > .view-container > .view.visible'
|
'.dv-split-view-container > .dv-view-container > .dv-view.visible'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
|
|
||||||
@ -619,7 +619,7 @@ describe('splitview', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const sashElement = container
|
const sashElement = container
|
||||||
.getElementsByClassName('sash')
|
.getElementsByClassName('dv-sash')
|
||||||
.item(0) as HTMLElement;
|
.item(0) as HTMLElement;
|
||||||
|
|
||||||
// validate the expected state before drag
|
// validate the expected state before drag
|
||||||
@ -772,4 +772,130 @@ describe('splitview', () => {
|
|||||||
view1.fireChangeEvent({ size: 300 });
|
view1.fireChangeEvent({ size: 300 });
|
||||||
expect([view1.size, view2.size, view3.size]).toEqual([300, 300, 300]);
|
expect([view1.size, view2.size, view3.size]).toEqual([300, 300, 300]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('that margins are applied to view sizing', () => {
|
||||||
|
const splitview = new Splitview(container, {
|
||||||
|
orientation: Orientation.HORIZONTAL,
|
||||||
|
proportionalLayout: false,
|
||||||
|
margin: 24,
|
||||||
|
});
|
||||||
|
splitview.layout(924, 500);
|
||||||
|
|
||||||
|
const view1 = new Testview(0, 1000);
|
||||||
|
const view2 = new Testview(0, 1000);
|
||||||
|
const view3 = new Testview(0, 1000);
|
||||||
|
const view4 = new Testview(0, 1000);
|
||||||
|
|
||||||
|
splitview.addView(view1);
|
||||||
|
expect([view1.size]).toEqual([924]);
|
||||||
|
|
||||||
|
splitview.addView(view2);
|
||||||
|
expect([view1.size, view2.size]).toEqual([450, 450]); // 450 + 24 + 450 = 924
|
||||||
|
|
||||||
|
splitview.addView(view3);
|
||||||
|
expect([view1.size, view2.size, view3.size]).toEqual([292, 292, 292]); // 292 + 24 + 292 + 24 + 292 = 924
|
||||||
|
|
||||||
|
splitview.addView(view4);
|
||||||
|
expect([view1.size, view2.size, view3.size, view4.size]).toEqual([
|
||||||
|
213, 213, 213, 213,
|
||||||
|
]); // 213 + 24 + 213 + 24 + 213 + 24 + 213 = 924
|
||||||
|
|
||||||
|
let viewQuery = Array.from(
|
||||||
|
container
|
||||||
|
.querySelectorAll(
|
||||||
|
'.dv-split-view-container > .dv-view-container > .dv-view'
|
||||||
|
)
|
||||||
|
.entries()
|
||||||
|
)
|
||||||
|
.map(([i, e]) => e as HTMLElement)
|
||||||
|
.map((e) => ({
|
||||||
|
left: e.style.left,
|
||||||
|
top: e.style.top,
|
||||||
|
height: e.style.height,
|
||||||
|
width: e.style.width,
|
||||||
|
}));
|
||||||
|
|
||||||
|
let sashQuery = Array.from(
|
||||||
|
container
|
||||||
|
.querySelectorAll(
|
||||||
|
'.dv-split-view-container > .dv-sash-container > .dv-sash'
|
||||||
|
)
|
||||||
|
.entries()
|
||||||
|
)
|
||||||
|
.map(([i, e]) => e as HTMLElement)
|
||||||
|
.map((e) => ({
|
||||||
|
left: e.style.left,
|
||||||
|
top: e.style.top,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// check HTMLElement positions since these are the ones that really matter
|
||||||
|
|
||||||
|
expect(viewQuery).toEqual([
|
||||||
|
{ left: '0px', top: '', width: '213px', height: '' },
|
||||||
|
// 213 + 24 = 237
|
||||||
|
{ left: '237px', top: '', width: '213px', height: '' },
|
||||||
|
// 237 + 213 + 24 = 474
|
||||||
|
{ left: '474px', top: '', width: '213px', height: '' },
|
||||||
|
// 474 + 213 + 24 = 474
|
||||||
|
{ left: '711px', top: '', width: '213px', height: '' },
|
||||||
|
// 711 + 213 = 924
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 924 / 4 = 231 view size
|
||||||
|
// 231 - (24*3/4) = 213 margin adjusted view size
|
||||||
|
// 213 - 4/2 + 24/2 = 223
|
||||||
|
expect(sashQuery).toEqual([
|
||||||
|
// 213 - 4/2 + 24/2 = 223
|
||||||
|
{ left: '223px', top: '0px' },
|
||||||
|
// 213 + 24 + 213 = 450
|
||||||
|
// 450 - 4/2 + 24/2 = 460
|
||||||
|
{ left: '460px', top: '0px' },
|
||||||
|
// 213 + 24 + 213 + 24 + 213 = 687
|
||||||
|
// 687 - 4/2 + 24/2 = 697
|
||||||
|
{ left: '697px', top: '0px' },
|
||||||
|
]);
|
||||||
|
|
||||||
|
splitview.setViewVisible(0, false);
|
||||||
|
|
||||||
|
viewQuery = Array.from(
|
||||||
|
container
|
||||||
|
.querySelectorAll(
|
||||||
|
'.dv-split-view-container > .dv-view-container > .dv-view'
|
||||||
|
)
|
||||||
|
.entries()
|
||||||
|
)
|
||||||
|
.map(([i, e]) => e as HTMLElement)
|
||||||
|
.map((e) => ({
|
||||||
|
left: e.style.left,
|
||||||
|
top: e.style.top,
|
||||||
|
height: e.style.height,
|
||||||
|
width: e.style.width,
|
||||||
|
}));
|
||||||
|
|
||||||
|
sashQuery = Array.from(
|
||||||
|
container
|
||||||
|
.querySelectorAll(
|
||||||
|
'.dv-split-view-container > .dv-sash-container > .dv-sash'
|
||||||
|
)
|
||||||
|
.entries()
|
||||||
|
)
|
||||||
|
.map(([i, e]) => e as HTMLElement)
|
||||||
|
.map((e) => ({
|
||||||
|
left: e.style.left,
|
||||||
|
top: e.style.top,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(viewQuery).toEqual([
|
||||||
|
{ left: '0px', top: '', width: '0px', height: '' },
|
||||||
|
{ left: '0px', top: '', width: '215px', height: '' },
|
||||||
|
{ left: '239px', top: '', width: '215px', height: '' },
|
||||||
|
{ left: '478px', top: '', width: '446px', height: '' },
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(sashQuery).toEqual([
|
||||||
|
{ left: '0px', top: '0px' },
|
||||||
|
{ left: '225px', top: '0px' },
|
||||||
|
{ left: '464px', top: '0px' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -26,25 +26,52 @@ describe('componentSplitview', () => {
|
|||||||
container.className = 'container';
|
container.className = 'container';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('that the container is not removed when grid is disposed', () => {
|
||||||
|
const root = document.createElement('div');
|
||||||
|
const container = document.createElement('div');
|
||||||
|
root.appendChild(container);
|
||||||
|
|
||||||
|
const splitview = new SplitviewComponent(container, {
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
splitview.dispose();
|
||||||
|
|
||||||
|
expect(container.parentElement).toBe(root);
|
||||||
|
expect(container.children.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
test('event leakage', () => {
|
test('event leakage', () => {
|
||||||
Emitter.setLeakageMonitorEnabled(true);
|
Emitter.setLeakageMonitorEnabled(true);
|
||||||
|
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
splitview.layout(600, 400);
|
splitview.layout(600, 400);
|
||||||
|
|
||||||
const panel1 = splitview.addPanel({
|
const panel1 = splitview.addPanel({
|
||||||
id: 'panel1',
|
id: 'panel1',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
});
|
});
|
||||||
const panel2 = splitview.addPanel({
|
const panel2 = splitview.addPanel({
|
||||||
id: 'panel2',
|
id: 'panel2',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
splitview.movePanel(0, 1);
|
splitview.movePanel(0, 1);
|
||||||
@ -66,18 +93,22 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('remove panel', () => {
|
test('remove panel', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
splitview.layout(600, 400);
|
splitview.layout(600, 400);
|
||||||
|
|
||||||
splitview.addPanel({ id: 'panel1', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel1', component: 'default' });
|
||||||
splitview.addPanel({ id: 'panel2', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel2', component: 'default' });
|
||||||
splitview.addPanel({ id: 'panel3', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel3', component: 'default' });
|
||||||
|
|
||||||
const panel1 = splitview.getPanel('panel1')!;
|
const panel1 = splitview.getPanel('panel1')!;
|
||||||
const panel2 = splitview.getPanel('panel2')!;
|
const panel2 = splitview.getPanel('panel2')!;
|
||||||
@ -102,11 +133,15 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('horizontal dimensions', () => {
|
test('horizontal dimensions', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
splitview.layout(600, 400);
|
splitview.layout(600, 400);
|
||||||
@ -116,11 +151,15 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('vertical dimensions', () => {
|
test('vertical dimensions', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
splitview.layout(600, 400);
|
splitview.layout(600, 400);
|
||||||
@ -130,18 +169,22 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('api resize', () => {
|
test('api resize', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
splitview.layout(400, 600);
|
splitview.layout(400, 600);
|
||||||
splitview.addPanel({ id: 'panel1', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel1', component: 'default' });
|
||||||
splitview.addPanel({ id: 'panel2', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel2', component: 'default' });
|
||||||
splitview.addPanel({ id: 'panel3', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel3', component: 'default' });
|
||||||
|
|
||||||
const panel1 = splitview.getPanel('panel1')!;
|
const panel1 = splitview.getPanel('panel1')!;
|
||||||
const panel2 = splitview.getPanel('panel2')!;
|
const panel2 = splitview.getPanel('panel2')!;
|
||||||
@ -183,16 +226,20 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('api', () => {
|
test('api', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
splitview.layout(600, 400);
|
splitview.layout(600, 400);
|
||||||
splitview.addPanel({ id: 'panel1', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel1', component: 'default' });
|
||||||
|
|
||||||
const panel1 = splitview.getPanel('panel1');
|
const panel1 = splitview.getPanel('panel1');
|
||||||
|
|
||||||
@ -203,7 +250,7 @@ describe('componentSplitview', () => {
|
|||||||
// expect(panel1?.api.isFocused).toBeFalsy();
|
// expect(panel1?.api.isFocused).toBeFalsy();
|
||||||
expect(panel1!.api.isVisible).toBeTruthy();
|
expect(panel1!.api.isVisible).toBeTruthy();
|
||||||
|
|
||||||
splitview.addPanel({ id: 'panel2', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel2', component: 'default' });
|
||||||
|
|
||||||
const panel2 = splitview.getPanel('panel2');
|
const panel2 = splitview.getPanel('panel2');
|
||||||
|
|
||||||
@ -225,18 +272,22 @@ describe('componentSplitview', () => {
|
|||||||
test('vertical panels', () => {
|
test('vertical panels', () => {
|
||||||
const disposables = new CompositeDisposable();
|
const disposables = new CompositeDisposable();
|
||||||
|
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
splitview.layout(300, 200);
|
splitview.layout(300, 200);
|
||||||
|
|
||||||
splitview.addPanel({ id: 'panel1', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel1', component: 'default' });
|
||||||
splitview.addPanel({ id: 'panel2', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel2', component: 'default' });
|
||||||
|
|
||||||
const panel1 = splitview.getPanel('panel1') as SplitviewPanel;
|
const panel1 = splitview.getPanel('panel1') as SplitviewPanel;
|
||||||
const panel2 = splitview.getPanel('panel2') as SplitviewPanel;
|
const panel2 = splitview.getPanel('panel2') as SplitviewPanel;
|
||||||
@ -277,18 +328,22 @@ describe('componentSplitview', () => {
|
|||||||
test('horizontal panels', () => {
|
test('horizontal panels', () => {
|
||||||
const disposables = new CompositeDisposable();
|
const disposables = new CompositeDisposable();
|
||||||
|
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
splitview.layout(300, 200);
|
splitview.layout(300, 200);
|
||||||
|
|
||||||
splitview.addPanel({ id: 'panel1', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel1', component: 'default' });
|
||||||
splitview.addPanel({ id: 'panel2', component: 'testPanel' });
|
splitview.addPanel({ id: 'panel2', component: 'default' });
|
||||||
|
|
||||||
const panel1 = splitview.getPanel('panel1') as SplitviewPanel;
|
const panel1 = splitview.getPanel('panel1') as SplitviewPanel;
|
||||||
const panel2 = splitview.getPanel('panel2') as SplitviewPanel;
|
const panel2 = splitview.getPanel('panel2') as SplitviewPanel;
|
||||||
@ -327,51 +382,63 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('serialization', () => {
|
test('serialization', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
splitview.layout(400, 6);
|
splitview.layout(400, 6);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.querySelectorAll('.dv-split-view-container').length
|
||||||
|
).toBe(1);
|
||||||
|
|
||||||
splitview.fromJSON({
|
splitview.fromJSON({
|
||||||
views: [
|
views: [
|
||||||
{
|
{
|
||||||
size: 1,
|
size: 1,
|
||||||
data: { id: 'panel1', component: 'testPanel' },
|
data: { id: 'panel1', component: 'default' },
|
||||||
snap: false,
|
snap: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 2,
|
size: 2,
|
||||||
data: { id: 'panel2', component: 'testPanel' },
|
data: { id: 'panel2', component: 'default' },
|
||||||
snap: true,
|
snap: true,
|
||||||
},
|
},
|
||||||
{ size: 3, data: { id: 'panel3', component: 'testPanel' } },
|
{ size: 3, data: { id: 'panel3', component: 'default' } },
|
||||||
],
|
],
|
||||||
size: 6,
|
size: 6,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
activeView: 'panel1',
|
activeView: 'panel1',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.querySelectorAll('.dv-split-view-container').length
|
||||||
|
).toBe(1);
|
||||||
|
|
||||||
expect(splitview.length).toBe(3);
|
expect(splitview.length).toBe(3);
|
||||||
|
|
||||||
expect(JSON.parse(JSON.stringify(splitview.toJSON()))).toEqual({
|
expect(JSON.parse(JSON.stringify(splitview.toJSON()))).toEqual({
|
||||||
views: [
|
views: [
|
||||||
{
|
{
|
||||||
size: 1,
|
size: 1,
|
||||||
data: { id: 'panel1', component: 'testPanel' },
|
data: { id: 'panel1', component: 'default' },
|
||||||
snap: false,
|
snap: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 2,
|
size: 2,
|
||||||
data: { id: 'panel2', component: 'testPanel' },
|
data: { id: 'panel2', component: 'default' },
|
||||||
snap: true,
|
snap: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 3,
|
size: 3,
|
||||||
data: { id: 'panel3', component: 'testPanel' },
|
data: { id: 'panel3', component: 'default' },
|
||||||
snap: false,
|
snap: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -382,11 +449,15 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('toJSON shouldnt fire any layout events', () => {
|
test('toJSON shouldnt fire any layout events', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -394,11 +465,11 @@ describe('componentSplitview', () => {
|
|||||||
|
|
||||||
splitview.addPanel({
|
splitview.addPanel({
|
||||||
id: 'panel1',
|
id: 'panel1',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
});
|
});
|
||||||
splitview.addPanel({
|
splitview.addPanel({
|
||||||
id: 'panel2',
|
id: 'panel2',
|
||||||
component: 'testPanel',
|
component: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
const disposable = splitview.onDidLayoutChange(() => {
|
const disposable = splitview.onDidLayoutChange(() => {
|
||||||
@ -411,41 +482,16 @@ describe('componentSplitview', () => {
|
|||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('dispose of splitviewComponent', () => {
|
|
||||||
expect(container.childNodes.length).toBe(0);
|
|
||||||
|
|
||||||
const splitview = new SplitviewComponent({
|
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.HORIZONTAL,
|
|
||||||
components: {
|
|
||||||
testPanel: TestPanel,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
splitview.layout(1000, 1000);
|
|
||||||
|
|
||||||
splitview.addPanel({
|
|
||||||
id: 'panel1',
|
|
||||||
component: 'testPanel',
|
|
||||||
});
|
|
||||||
splitview.addPanel({
|
|
||||||
id: 'panel2',
|
|
||||||
component: 'testPanel',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(container.childNodes.length).toBeGreaterThan(0);
|
|
||||||
|
|
||||||
splitview.dispose();
|
|
||||||
|
|
||||||
expect(container.childNodes.length).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('panel is disposed of when component is disposed', () => {
|
test('panel is disposed of when component is disposed', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
default: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -473,11 +519,15 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('panel is disposed of when removed', () => {
|
test('panel is disposed of when removed', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
default: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -505,11 +555,15 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('panel is disposed of when fromJSON is called', () => {
|
test('panel is disposed of when fromJSON is called', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
default: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -541,11 +595,15 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('that fromJSON layouts are resized to the current dimensions', async () => {
|
test('that fromJSON layouts are resized to the current dimensions', async () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
splitview.layout(400, 600);
|
splitview.layout(400, 600);
|
||||||
@ -554,15 +612,15 @@ describe('componentSplitview', () => {
|
|||||||
views: [
|
views: [
|
||||||
{
|
{
|
||||||
size: 1,
|
size: 1,
|
||||||
data: { id: 'panel1', component: 'testPanel' },
|
data: { id: 'panel1', component: 'default' },
|
||||||
snap: false,
|
snap: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 2,
|
size: 2,
|
||||||
data: { id: 'panel2', component: 'testPanel' },
|
data: { id: 'panel2', component: 'default' },
|
||||||
snap: true,
|
snap: true,
|
||||||
},
|
},
|
||||||
{ size: 3, data: { id: 'panel3', component: 'testPanel' } },
|
{ size: 3, data: { id: 'panel3', component: 'default' } },
|
||||||
],
|
],
|
||||||
size: 6,
|
size: 6,
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
@ -573,17 +631,17 @@ describe('componentSplitview', () => {
|
|||||||
views: [
|
views: [
|
||||||
{
|
{
|
||||||
size: 100,
|
size: 100,
|
||||||
data: { id: 'panel1', component: 'testPanel' },
|
data: { id: 'panel1', component: 'default' },
|
||||||
snap: false,
|
snap: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 200,
|
size: 200,
|
||||||
data: { id: 'panel2', component: 'testPanel' },
|
data: { id: 'panel2', component: 'default' },
|
||||||
snap: true,
|
snap: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 300,
|
size: 300,
|
||||||
data: { id: 'panel3', component: 'testPanel' },
|
data: { id: 'panel3', component: 'default' },
|
||||||
snap: false,
|
snap: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -594,11 +652,15 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('that disableAutoResizing is false by default', () => {
|
test('that disableAutoResizing is false by default', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -606,11 +668,15 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('that disableAutoResizing can be enabled', () => {
|
test('that disableAutoResizing can be enabled', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.VERTICAL,
|
orientation: Orientation.VERTICAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
testPanel: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disableAutoResizing: true,
|
disableAutoResizing: true,
|
||||||
});
|
});
|
||||||
@ -619,11 +685,15 @@ describe('componentSplitview', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('that setVisible toggles visiblity', () => {
|
test('that setVisible toggles visiblity', () => {
|
||||||
const splitview = new SplitviewComponent({
|
const splitview = new SplitviewComponent(container, {
|
||||||
parentElement: container,
|
|
||||||
orientation: Orientation.HORIZONTAL,
|
orientation: Orientation.HORIZONTAL,
|
||||||
components: {
|
createComponent: (options) => {
|
||||||
default: TestPanel,
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -649,4 +719,25 @@ describe('componentSplitview', () => {
|
|||||||
expect(panel1.api.isVisible).toBeTruthy();
|
expect(panel1.api.isVisible).toBeTruthy();
|
||||||
expect(panel2.api.isVisible).toBeTruthy();
|
expect(panel2.api.isVisible).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('update className', () => {
|
||||||
|
const splitview = new SplitviewComponent(container, {
|
||||||
|
orientation: Orientation.HORIZONTAL,
|
||||||
|
createComponent: (options) => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new TestPanel(options.id, options.name);
|
||||||
|
default:
|
||||||
|
throw new Error('unsupported');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
className: 'test-a test-b',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(splitview.element.className).toBe('test-a test-b');
|
||||||
|
|
||||||
|
splitview.updateOptions({ className: 'test-b test-c' });
|
||||||
|
|
||||||
|
expect(splitview.element.className).toBe('test-b test-c');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
|
DockviewMaximizedGroupChanged,
|
||||||
|
FloatingGroupOptions,
|
||||||
IDockviewComponent,
|
IDockviewComponent,
|
||||||
|
MovePanelEvent,
|
||||||
|
PopoutGroupChangePositionEvent,
|
||||||
|
PopoutGroupChangeSizeEvent,
|
||||||
SerializedDockview,
|
SerializedDockview,
|
||||||
} from '../dockview/dockviewComponent';
|
} from '../dockview/dockviewComponent';
|
||||||
import {
|
import {
|
||||||
AddGroupOptions,
|
AddGroupOptions,
|
||||||
AddPanelOptions,
|
AddPanelOptions,
|
||||||
|
DockviewComponentOptions,
|
||||||
DockviewDndOverlayEvent,
|
DockviewDndOverlayEvent,
|
||||||
MovementOptions,
|
MovementOptions,
|
||||||
} from '../dockview/options';
|
} from '../dockview/options';
|
||||||
@ -27,7 +33,6 @@ import {
|
|||||||
AddSplitviewComponentOptions,
|
AddSplitviewComponentOptions,
|
||||||
ISplitviewComponent,
|
ISplitviewComponent,
|
||||||
SerializedSplitview,
|
SerializedSplitview,
|
||||||
SplitviewComponentUpdateOptions,
|
|
||||||
} from '../splitview/splitviewComponent';
|
} from '../splitview/splitviewComponent';
|
||||||
import { IView, Orientation, Sizing } from '../splitview/splitview';
|
import { IView, Orientation, Sizing } from '../splitview/splitview';
|
||||||
import { ISplitviewPanel } from '../splitview/splitviewPanel';
|
import { ISplitviewPanel } from '../splitview/splitviewPanel';
|
||||||
@ -35,9 +40,9 @@ import {
|
|||||||
DockviewGroupPanel,
|
DockviewGroupPanel,
|
||||||
IDockviewGroupPanel,
|
IDockviewGroupPanel,
|
||||||
} from '../dockview/dockviewGroupPanel';
|
} from '../dockview/dockviewGroupPanel';
|
||||||
import { Emitter, Event } from '../events';
|
import { Event } from '../events';
|
||||||
import { IDockviewPanel } from '../dockview/dockviewPanel';
|
import { IDockviewPanel } from '../dockview/dockviewPanel';
|
||||||
import { PaneviewDropEvent } from '../paneview/draggablePaneviewPanel';
|
import { PaneviewDidDropEvent } from '../paneview/draggablePaneviewPanel';
|
||||||
import {
|
import {
|
||||||
GroupDragEvent,
|
GroupDragEvent,
|
||||||
TabDragEvent,
|
TabDragEvent,
|
||||||
@ -48,6 +53,12 @@ import {
|
|||||||
DockviewWillDropEvent,
|
DockviewWillDropEvent,
|
||||||
WillShowOverlayLocationEvent,
|
WillShowOverlayLocationEvent,
|
||||||
} from '../dockview/dockviewGroupPanelModel';
|
} from '../dockview/dockviewGroupPanelModel';
|
||||||
|
import {
|
||||||
|
PaneviewComponentOptions,
|
||||||
|
PaneviewDndOverlayEvent,
|
||||||
|
} from '../paneview/options';
|
||||||
|
import { SplitviewComponentOptions } from '../splitview/options';
|
||||||
|
import { GridviewComponentOptions } from '../gridview/options';
|
||||||
|
|
||||||
export interface CommonApi<T = any> {
|
export interface CommonApi<T = any> {
|
||||||
readonly height: number;
|
readonly height: number;
|
||||||
@ -59,6 +70,7 @@ export interface CommonApi<T = any> {
|
|||||||
fromJSON(data: T): void;
|
fromJSON(data: T): void;
|
||||||
toJSON(): T;
|
toJSON(): T;
|
||||||
clear(): void;
|
clear(): void;
|
||||||
|
dispose(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SplitviewApi implements CommonApi<SerializedSplitview> {
|
export class SplitviewApi implements CommonApi<SerializedSplitview> {
|
||||||
@ -141,13 +153,6 @@ export class SplitviewApi implements CommonApi<SerializedSplitview> {
|
|||||||
|
|
||||||
constructor(private readonly component: ISplitviewComponent) {}
|
constructor(private readonly component: ISplitviewComponent) {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update configuratable options.
|
|
||||||
*/
|
|
||||||
updateOptions(options: SplitviewComponentUpdateOptions): void {
|
|
||||||
this.component.updateOptions(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an existing panel and optionally provide a `Sizing` method
|
* Removes an existing panel and optionally provide a `Sizing` method
|
||||||
* for the subsequent resize.
|
* for the subsequent resize.
|
||||||
@ -211,6 +216,20 @@ export class SplitviewApi implements CommonApi<SerializedSplitview> {
|
|||||||
clear(): void {
|
clear(): void {
|
||||||
this.component.clear();
|
this.component.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update configuratable options.
|
||||||
|
*/
|
||||||
|
updateOptions(options: Partial<SplitviewComponentOptions>): void {
|
||||||
|
this.component.updateOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release resources and teardown component. Do not call when using framework versions of dockview.
|
||||||
|
*/
|
||||||
|
dispose(): void {
|
||||||
|
this.component.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PaneviewApi implements CommonApi<SerializedPaneview> {
|
export class PaneviewApi implements CommonApi<SerializedPaneview> {
|
||||||
@ -280,19 +299,12 @@ export class PaneviewApi implements CommonApi<SerializedPaneview> {
|
|||||||
/**
|
/**
|
||||||
* Invoked when a Drag'n'Drop event occurs that the component was unable to handle. Exposed for custom Drag'n'Drop functionality.
|
* Invoked when a Drag'n'Drop event occurs that the component was unable to handle. Exposed for custom Drag'n'Drop functionality.
|
||||||
*/
|
*/
|
||||||
get onDidDrop(): Event<PaneviewDropEvent> {
|
get onDidDrop(): Event<PaneviewDidDropEvent> {
|
||||||
const emitter = new Emitter<PaneviewDropEvent>();
|
return this.component.onDidDrop;
|
||||||
|
}
|
||||||
|
|
||||||
const disposable = this.component.onDidDrop((e) => {
|
get onUnhandledDragOverEvent(): Event<PaneviewDndOverlayEvent> {
|
||||||
emitter.fire({ ...e, api: this });
|
return this.component.onUnhandledDragOverEvent;
|
||||||
});
|
|
||||||
|
|
||||||
emitter.dispose = () => {
|
|
||||||
disposable.dispose();
|
|
||||||
emitter.dispose();
|
|
||||||
};
|
|
||||||
|
|
||||||
return emitter.event;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private readonly component: IPaneviewComponent) {}
|
constructor(private readonly component: IPaneviewComponent) {}
|
||||||
@ -361,6 +373,20 @@ export class PaneviewApi implements CommonApi<SerializedPaneview> {
|
|||||||
clear(): void {
|
clear(): void {
|
||||||
this.component.clear();
|
this.component.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update configuratable options.
|
||||||
|
*/
|
||||||
|
updateOptions(options: Partial<PaneviewComponentOptions>): void {
|
||||||
|
this.component.updateOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release resources and teardown component. Do not call when using framework versions of dockview.
|
||||||
|
*/
|
||||||
|
dispose(): void {
|
||||||
|
this.component.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GridviewApi implements CommonApi<SerializedGridviewComponent> {
|
export class GridviewApi implements CommonApi<SerializedGridviewComponent> {
|
||||||
@ -528,6 +554,17 @@ export class GridviewApi implements CommonApi<SerializedGridviewComponent> {
|
|||||||
clear(): void {
|
clear(): void {
|
||||||
this.component.clear();
|
this.component.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateOptions(options: Partial<GridviewComponentOptions>) {
|
||||||
|
this.component.updateOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release resources and teardown component. Do not call when using framework versions of dockview.
|
||||||
|
*/
|
||||||
|
dispose(): void {
|
||||||
|
this.component.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DockviewApi implements CommonApi<SerializedDockview> {
|
export class DockviewApi implements CommonApi<SerializedDockview> {
|
||||||
@ -636,6 +673,10 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
|||||||
return this.component.onDidRemovePanel;
|
return this.component.onDidRemovePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get onDidMovePanel(): Event<MovePanelEvent> {
|
||||||
|
return this.component.onDidMovePanel;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked after a layout is deserialzied using the `fromJSON` method.
|
* Invoked after a layout is deserialzied using the `fromJSON` method.
|
||||||
*/
|
*/
|
||||||
@ -700,6 +741,14 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
|||||||
return this.component.onUnhandledDragOverEvent;
|
return this.component.onUnhandledDragOverEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get onDidPopoutGroupSizeChange(): Event<PopoutGroupChangeSizeEvent> {
|
||||||
|
return this.component.onDidPopoutGroupSizeChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
get onDidPopoutGroupPositionChange(): Event<PopoutGroupChangePositionEvent> {
|
||||||
|
return this.component.onDidPopoutGroupPositionChange;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All panel objects.
|
* All panel objects.
|
||||||
*/
|
*/
|
||||||
@ -800,9 +849,9 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
|||||||
*/
|
*/
|
||||||
addFloatingGroup(
|
addFloatingGroup(
|
||||||
item: IDockviewPanel | DockviewGroupPanel,
|
item: IDockviewPanel | DockviewGroupPanel,
|
||||||
coord?: { x: number; y: number }
|
options?: FloatingGroupOptions
|
||||||
): void {
|
): void {
|
||||||
return this.component.addFloatingGroup(item, coord);
|
return this.component.addFloatingGroup(item, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -852,7 +901,7 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
|||||||
this.component.exitMaximizedGroup();
|
this.component.exitMaximizedGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
get onDidMaximizedGroupChange(): Event<void> {
|
get onDidMaximizedGroupChange(): Event<DockviewMaximizedGroupChanged> {
|
||||||
return this.component.onDidMaximizedGroupChange;
|
return this.component.onDidMaximizedGroupChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,7 +916,18 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
|||||||
onDidOpen?: (event: { id: string; window: Window }) => void;
|
onDidOpen?: (event: { id: string; window: Window }) => void;
|
||||||
onWillClose?: (event: { id: string; window: Window }) => void;
|
onWillClose?: (event: { id: string; window: Window }) => void;
|
||||||
}
|
}
|
||||||
): Promise<void> {
|
): Promise<boolean> {
|
||||||
return this.component.addPopoutGroup(item, options);
|
return this.component.addPopoutGroup(item, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateOptions(options: Partial<DockviewComponentOptions>) {
|
||||||
|
this.component.updateOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release resources and teardown component. Do not call when using framework versions of dockview.
|
||||||
|
*/
|
||||||
|
dispose(): void {
|
||||||
|
this.component.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,17 @@ import {
|
|||||||
DockviewGroupLocation,
|
DockviewGroupLocation,
|
||||||
} from '../dockview/dockviewGroupPanelModel';
|
} from '../dockview/dockviewGroupPanelModel';
|
||||||
import { Emitter, Event } from '../events';
|
import { Emitter, Event } from '../events';
|
||||||
import { MutableDisposable } from '../lifecycle';
|
|
||||||
import { GridviewPanelApi, GridviewPanelApiImpl } from './gridviewPanelApi';
|
import { GridviewPanelApi, GridviewPanelApiImpl } from './gridviewPanelApi';
|
||||||
|
|
||||||
|
export interface DockviewGroupMoveParams {
|
||||||
|
group?: DockviewGroupPanel;
|
||||||
|
position?: Position;
|
||||||
|
/**
|
||||||
|
* The index to place the panel within a group, only applicable if the placement is within an existing group
|
||||||
|
*/
|
||||||
|
index?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DockviewGroupPanelApi extends GridviewPanelApi {
|
export interface DockviewGroupPanelApi extends GridviewPanelApi {
|
||||||
readonly onDidLocationChange: Event<DockviewGroupPanelFloatingChangeEvent>;
|
readonly onDidLocationChange: Event<DockviewGroupPanelFloatingChangeEvent>;
|
||||||
readonly onDidActivePanelChange: Event<DockviewGroupChangeEvent>;
|
readonly onDidActivePanelChange: Event<DockviewGroupChangeEvent>;
|
||||||
@ -17,7 +25,7 @@ export interface DockviewGroupPanelApi extends GridviewPanelApi {
|
|||||||
* If you require the Window object
|
* If you require the Window object
|
||||||
*/
|
*/
|
||||||
getWindow(): Window;
|
getWindow(): Window;
|
||||||
moveTo(options: { group?: DockviewGroupPanel; position?: Position }): void;
|
moveTo(options: DockviewGroupMoveParams): void;
|
||||||
maximize(): void;
|
maximize(): void;
|
||||||
isMaximized(): boolean;
|
isMaximized(): boolean;
|
||||||
exitMaximized(): void;
|
exitMaximized(): void;
|
||||||
@ -28,12 +36,10 @@ export interface DockviewGroupPanelFloatingChangeEvent {
|
|||||||
readonly location: DockviewGroupLocation;
|
readonly location: DockviewGroupLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO find a better way to initialize and avoid needing null checks
|
const NOT_INITIALIZED_MESSAGE =
|
||||||
const NOT_INITIALIZED_MESSAGE = 'DockviewGroupPanelApiImpl not initialized';
|
'dockview: DockviewGroupPanelApiImpl not initialized';
|
||||||
|
|
||||||
export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
|
export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
|
||||||
private readonly _mutableDisposable = new MutableDisposable();
|
|
||||||
|
|
||||||
private _group: DockviewGroupPanel | undefined;
|
private _group: DockviewGroupPanel | undefined;
|
||||||
|
|
||||||
readonly _onDidLocationChange =
|
readonly _onDidLocationChange =
|
||||||
@ -41,8 +47,7 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
|
|||||||
readonly onDidLocationChange: Event<DockviewGroupPanelFloatingChangeEvent> =
|
readonly onDidLocationChange: Event<DockviewGroupPanelFloatingChangeEvent> =
|
||||||
this._onDidLocationChange.event;
|
this._onDidLocationChange.event;
|
||||||
|
|
||||||
private readonly _onDidActivePanelChange =
|
readonly _onDidActivePanelChange = new Emitter<DockviewGroupChangeEvent>();
|
||||||
new Emitter<DockviewGroupChangeEvent>();
|
|
||||||
readonly onDidActivePanelChange = this._onDidActivePanelChange.event;
|
readonly onDidActivePanelChange = this._onDidActivePanelChange.event;
|
||||||
|
|
||||||
get location(): DockviewGroupLocation {
|
get location(): DockviewGroupLocation {
|
||||||
@ -57,8 +62,7 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
|
|||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this._onDidLocationChange,
|
this._onDidLocationChange,
|
||||||
this._onDidActivePanelChange,
|
this._onDidActivePanelChange
|
||||||
this._mutableDisposable
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +79,7 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
|
|||||||
: window;
|
: window;
|
||||||
}
|
}
|
||||||
|
|
||||||
moveTo(options: { group?: DockviewGroupPanel; position?: Position }): void {
|
moveTo(options: DockviewGroupMoveParams): void {
|
||||||
if (!this._group) {
|
if (!this._group) {
|
||||||
throw new Error(NOT_INITIALIZED_MESSAGE);
|
throw new Error(NOT_INITIALIZED_MESSAGE);
|
||||||
}
|
}
|
||||||
@ -94,6 +98,7 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
|
|||||||
position: options.group
|
position: options.group
|
||||||
? options.position ?? 'center'
|
? options.position ?? 'center'
|
||||||
: 'center',
|
: 'center',
|
||||||
|
index: options.index,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -131,19 +136,5 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
|
|||||||
|
|
||||||
initialize(group: DockviewGroupPanel): void {
|
initialize(group: DockviewGroupPanel): void {
|
||||||
this._group = group;
|
this._group = group;
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Annoying initialization order caveat
|
|
||||||
*
|
|
||||||
* Due to the order on initialization we know that the model isn't defined until later in the same stack-frame of setup.
|
|
||||||
* By queuing a microtask we can ensure the setup is completed within the same stack-frame, but after everything else has
|
|
||||||
* finished ensuring the `model` is defined.
|
|
||||||
*/
|
|
||||||
queueMicrotask(() => {
|
|
||||||
this._mutableDisposable.value =
|
|
||||||
this._group!.model.onDidActivePanelChange((event) => {
|
|
||||||
this._onDidActivePanelChange.fire(event);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,11 @@ import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel';
|
|||||||
import { CompositeDisposable, MutableDisposable } from '../lifecycle';
|
import { CompositeDisposable, MutableDisposable } from '../lifecycle';
|
||||||
import { DockviewPanel } from '../dockview/dockviewPanel';
|
import { DockviewPanel } from '../dockview/dockviewPanel';
|
||||||
import { DockviewComponent } from '../dockview/dockviewComponent';
|
import { DockviewComponent } from '../dockview/dockviewComponent';
|
||||||
import { Position } from '../dnd/droptarget';
|
import { DockviewPanelRenderer } from '../overlay/overlayRenderContainer';
|
||||||
import { DockviewPanelRenderer } from '../overlayRenderContainer';
|
import {
|
||||||
import { DockviewGroupPanelFloatingChangeEvent } from './dockviewGroupPanelApi';
|
DockviewGroupMoveParams,
|
||||||
|
DockviewGroupPanelFloatingChangeEvent,
|
||||||
|
} from './dockviewGroupPanelApi';
|
||||||
import { DockviewGroupLocation } from '../dockview/dockviewGroupPanelModel';
|
import { DockviewGroupLocation } from '../dockview/dockviewGroupPanelModel';
|
||||||
|
|
||||||
export interface TitleEvent {
|
export interface TitleEvent {
|
||||||
@ -25,6 +27,8 @@ export interface GroupChangedEvent {
|
|||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DockviewPanelMoveParams = DockviewGroupMoveParams;
|
||||||
|
|
||||||
export interface DockviewPanelApi
|
export interface DockviewPanelApi
|
||||||
extends Omit<
|
extends Omit<
|
||||||
GridviewPanelApi,
|
GridviewPanelApi,
|
||||||
@ -50,11 +54,7 @@ export interface DockviewPanelApi
|
|||||||
close(): void;
|
close(): void;
|
||||||
setTitle(title: string): void;
|
setTitle(title: string): void;
|
||||||
setRenderer(renderer: DockviewPanelRenderer): void;
|
setRenderer(renderer: DockviewPanelRenderer): void;
|
||||||
moveTo(options: {
|
moveTo(options: DockviewPanelMoveParams): void;
|
||||||
group: DockviewGroupPanel;
|
|
||||||
position?: Position;
|
|
||||||
index?: number;
|
|
||||||
}): void;
|
|
||||||
maximize(): void;
|
maximize(): void;
|
||||||
isMaximized(): boolean;
|
isMaximized(): boolean;
|
||||||
exitMaximized(): void;
|
exitMaximized(): void;
|
||||||
@ -69,7 +69,7 @@ export class DockviewPanelApiImpl
|
|||||||
implements DockviewPanelApi
|
implements DockviewPanelApi
|
||||||
{
|
{
|
||||||
private _group: DockviewGroupPanel;
|
private _group: DockviewGroupPanel;
|
||||||
private _tabComponent: string | undefined;
|
private readonly _tabComponent: string | undefined;
|
||||||
|
|
||||||
readonly _onDidTitleChange = new Emitter<TitleEvent>();
|
readonly _onDidTitleChange = new Emitter<TitleEvent>();
|
||||||
readonly onDidTitleChange = this._onDidTitleChange.event;
|
readonly onDidTitleChange = this._onDidTitleChange.event;
|
||||||
@ -131,7 +131,7 @@ export class DockviewPanelApiImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private panel: DockviewPanel,
|
private readonly panel: DockviewPanel,
|
||||||
group: DockviewGroupPanel,
|
group: DockviewGroupPanel,
|
||||||
private readonly accessor: DockviewComponent,
|
private readonly accessor: DockviewComponent,
|
||||||
component: string,
|
component: string,
|
||||||
@ -160,16 +160,14 @@ export class DockviewPanelApiImpl
|
|||||||
return this.group.api.getWindow();
|
return this.group.api.getWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
moveTo(options: {
|
moveTo(options: DockviewPanelMoveParams): void {
|
||||||
group: DockviewGroupPanel;
|
|
||||||
position?: Position;
|
|
||||||
index?: number;
|
|
||||||
}): void {
|
|
||||||
this.accessor.moveGroupOrPanel({
|
this.accessor.moveGroupOrPanel({
|
||||||
from: { groupId: this._group.id, panelId: this.panel.id },
|
from: { groupId: this._group.id, panelId: this.panel.id },
|
||||||
to: {
|
to: {
|
||||||
group: options.group,
|
group: options.group ?? this._group,
|
||||||
position: options.position ?? 'center',
|
position: options.group
|
||||||
|
? options.position ?? 'center'
|
||||||
|
: 'center',
|
||||||
index: options.index,
|
index: options.index,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -204,13 +202,14 @@ export class DockviewPanelApiImpl
|
|||||||
|
|
||||||
this.groupEventsDisposable.value = new CompositeDisposable(
|
this.groupEventsDisposable.value = new CompositeDisposable(
|
||||||
this.group.api.onDidVisibilityChange((event) => {
|
this.group.api.onDidVisibilityChange((event) => {
|
||||||
if (!event.isVisible && this.isVisible) {
|
const hasBecomeHidden = !event.isVisible && this.isVisible;
|
||||||
this._onDidVisibilityChange.fire(event);
|
const hasBecomeVisible = event.isVisible && !this.isVisible;
|
||||||
} else if (
|
|
||||||
event.isVisible &&
|
const isActivePanel = this.group.model.isPanelActive(
|
||||||
!this.isVisible &&
|
this.panel
|
||||||
this.group.model.isPanelActive(this.panel)
|
);
|
||||||
) {
|
|
||||||
|
if (hasBecomeHidden || (hasBecomeVisible && isActivePanel)) {
|
||||||
this._onDidVisibilityChange.fire(event);
|
this._onDidVisibilityChange.fire(event);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
46
packages/dockview-core/src/api/entryPoints.ts
Normal file
46
packages/dockview-core/src/api/entryPoints.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import {
|
||||||
|
DockviewApi,
|
||||||
|
GridviewApi,
|
||||||
|
PaneviewApi,
|
||||||
|
SplitviewApi,
|
||||||
|
} from '../api/component.api';
|
||||||
|
import { DockviewComponent } from '../dockview/dockviewComponent';
|
||||||
|
import { DockviewComponentOptions } from '../dockview/options';
|
||||||
|
import { GridviewComponent } from '../gridview/gridviewComponent';
|
||||||
|
import { GridviewComponentOptions } from '../gridview/options';
|
||||||
|
import { PaneviewComponentOptions } from '../paneview/options';
|
||||||
|
import { PaneviewComponent } from '../paneview/paneviewComponent';
|
||||||
|
import { SplitviewComponentOptions } from '../splitview/options';
|
||||||
|
import { SplitviewComponent } from '../splitview/splitviewComponent';
|
||||||
|
|
||||||
|
export function createDockview(
|
||||||
|
element: HTMLElement,
|
||||||
|
options: DockviewComponentOptions
|
||||||
|
): DockviewApi {
|
||||||
|
const component = new DockviewComponent(element, options);
|
||||||
|
return component.api;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSplitview(
|
||||||
|
element: HTMLElement,
|
||||||
|
options: SplitviewComponentOptions
|
||||||
|
): SplitviewApi {
|
||||||
|
const component = new SplitviewComponent(element, options);
|
||||||
|
return new SplitviewApi(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createGridview(
|
||||||
|
element: HTMLElement,
|
||||||
|
options: GridviewComponentOptions
|
||||||
|
): GridviewApi {
|
||||||
|
const component = new GridviewComponent(element, options);
|
||||||
|
return new GridviewApi(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPaneview(
|
||||||
|
element: HTMLElement,
|
||||||
|
options: PaneviewComponentOptions
|
||||||
|
): PaneviewApi {
|
||||||
|
const component = new PaneviewComponent(element, options);
|
||||||
|
return new PaneviewApi(component);
|
||||||
|
}
|
@ -1,3 +1,3 @@
|
|||||||
export const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100;
|
export const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100;
|
||||||
|
|
||||||
export const DEFAULT_FLOATING_GROUP_POSITION = { left: 100, top: 100 };
|
export const DEFAULT_FLOATING_GROUP_POSITION = { left: 100, top: 100, width: 300, height: 300 };
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { getElementsByTagName } from '../dom';
|
import { disableIframePointEvents } from '../dom';
|
||||||
import { addDisposableListener, Emitter } from '../events';
|
import { addDisposableListener, Emitter } from '../events';
|
||||||
import {
|
import {
|
||||||
CompositeDisposable,
|
CompositeDisposable,
|
||||||
@ -40,23 +40,14 @@ export abstract class DragHandler extends CompositeDisposable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const iframes = [
|
const iframes = disableIframePointEvents();
|
||||||
...getElementsByTagName('iframe'),
|
|
||||||
...getElementsByTagName('webview'),
|
|
||||||
];
|
|
||||||
|
|
||||||
this.pointerEventsDisposable.value = {
|
this.pointerEventsDisposable.value = {
|
||||||
dispose: () => {
|
dispose: () => {
|
||||||
for (const iframe of iframes) {
|
iframes.release();
|
||||||
iframe.style.pointerEvents = 'auto';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const iframe of iframes) {
|
|
||||||
iframe.style.pointerEvents = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.el.classList.add('dv-dragged');
|
this.el.classList.add('dv-dragged');
|
||||||
setTimeout(() => this.el.classList.remove('dv-dragged'), 0);
|
setTimeout(() => this.el.classList.remove('dv-dragged'), 0);
|
||||||
|
|
||||||
@ -76,18 +67,17 @@ export abstract class DragHandler extends CompositeDisposable {
|
|||||||
* For example: in react-dnd if dataTransfer.types is not set then the dragStart event will be cancelled
|
* For example: in react-dnd if dataTransfer.types is not set then the dragStart event will be cancelled
|
||||||
* through .preventDefault(). Since this is applied globally to all drag events this would break dockviews
|
* through .preventDefault(). Since this is applied globally to all drag events this would break dockviews
|
||||||
* dnd logic. You can see the code at
|
* dnd logic. You can see the code at
|
||||||
* https://github.com/react-dnd/react-dnd/blob/main/packages/backend-html5/src/HTML5BackendImpl.ts#L542
|
P * https://github.com/react-dnd/react-dnd/blob/main/packages/backend-html5/src/HTML5BackendImpl.ts#L542
|
||||||
*/
|
*/
|
||||||
event.dataTransfer.setData(
|
event.dataTransfer.setData('text/plain', '');
|
||||||
'text/plain',
|
|
||||||
'__dockview_internal_drag_event__'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
addDisposableListener(this.el, 'dragend', () => {
|
addDisposableListener(this.el, 'dragend', () => {
|
||||||
this.pointerEventsDisposable.dispose();
|
this.pointerEventsDisposable.dispose();
|
||||||
this.dataDisposable.dispose();
|
setTimeout(() => {
|
||||||
|
this.dataDisposable.dispose(); // allow the data to be read by other handlers before disposing
|
||||||
|
}, 0);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
class TransferObject {}
|
class TransferObject {
|
||||||
|
// intentionally empty class
|
||||||
|
}
|
||||||
|
|
||||||
export class PanelTransfer extends TransferObject {
|
export class PanelTransfer extends TransferObject {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -13,8 +13,8 @@ export class DragAndDropObserver extends CompositeDisposable {
|
|||||||
private target: EventTarget | null = null;
|
private target: EventTarget | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private element: HTMLElement,
|
private readonly element: HTMLElement,
|
||||||
private callbacks: IDragAndDropObserverCallbacks
|
private readonly callbacks: IDragAndDropObserverCallbacks
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
.dv-drop-target-container {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 9999;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
overflow: hidden;
|
||||||
|
--dv-transition-duration: 300ms;
|
||||||
|
|
||||||
|
.dv-drop-target-anchor {
|
||||||
|
position: relative;
|
||||||
|
border: var(--dv-drag-over-border);
|
||||||
|
transition: opacity var(--dv-transition-duration) ease-in,
|
||||||
|
top var(--dv-transition-duration) ease-out,
|
||||||
|
left var(--dv-transition-duration) ease-out,
|
||||||
|
width var(--dv-transition-duration) ease-out,
|
||||||
|
height var(--dv-transition-duration) ease-out;
|
||||||
|
background-color: var(--dv-drag-over-background-color);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
102
packages/dockview-core/src/dnd/dropTargetAnchorContainer.ts
Normal file
102
packages/dockview-core/src/dnd/dropTargetAnchorContainer.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { CompositeDisposable, Disposable } from '../lifecycle';
|
||||||
|
import { DropTargetTargetModel } from './droptarget';
|
||||||
|
|
||||||
|
export class DropTargetAnchorContainer extends CompositeDisposable {
|
||||||
|
private _model:
|
||||||
|
| { root: HTMLElement; overlay: HTMLElement; changed: boolean }
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
private _outline: HTMLElement | undefined;
|
||||||
|
|
||||||
|
private _disabled = false;
|
||||||
|
|
||||||
|
get disabled(): boolean {
|
||||||
|
return this._disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(value: boolean) {
|
||||||
|
if (this.disabled === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._disabled = value;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
this.model?.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get model(): DropTargetTargetModel | undefined {
|
||||||
|
if (this.disabled) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
clear: () => {
|
||||||
|
if (this._model) {
|
||||||
|
this._model.root.parentElement?.removeChild(
|
||||||
|
this._model.root
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this._model = undefined;
|
||||||
|
},
|
||||||
|
exists: () => {
|
||||||
|
return !!this._model;
|
||||||
|
},
|
||||||
|
getElements: (event?: DragEvent, outline?: HTMLElement) => {
|
||||||
|
const changed = this._outline !== outline;
|
||||||
|
this._outline = outline;
|
||||||
|
|
||||||
|
if (this._model) {
|
||||||
|
this._model.changed = changed;
|
||||||
|
return this._model;
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = this.createContainer();
|
||||||
|
const anchor = this.createAnchor();
|
||||||
|
|
||||||
|
this._model = { root: container, overlay: anchor, changed };
|
||||||
|
|
||||||
|
container.appendChild(anchor);
|
||||||
|
this.element.appendChild(container);
|
||||||
|
|
||||||
|
if (event?.target instanceof HTMLElement) {
|
||||||
|
const targetBox = event.target.getBoundingClientRect();
|
||||||
|
const box = this.element.getBoundingClientRect();
|
||||||
|
|
||||||
|
anchor.style.left = `${targetBox.left - box.left}px`;
|
||||||
|
anchor.style.top = `${targetBox.top - box.top}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._model;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(readonly element: HTMLElement, options: { disabled: boolean }) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._disabled = options.disabled;
|
||||||
|
|
||||||
|
this.addDisposables(
|
||||||
|
Disposable.from(() => {
|
||||||
|
this.model?.clear();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createContainer(): HTMLElement {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.className = 'dv-drop-target-container';
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createAnchor(): HTMLElement {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.className = 'dv-drop-target-anchor';
|
||||||
|
el.style.visibility = 'hidden';
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
.drop-target {
|
.dv-drop-target {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
--dv-transition-duration: 70ms;
|
||||||
|
|
||||||
> .drop-target-dropzone {
|
> .dv-drop-target-dropzone {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
@ -10,15 +11,18 @@
|
|||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
> .drop-target-selection {
|
> .dv-drop-target-selection {
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
border: var(--dv-drag-over-border);
|
||||||
background-color: var(--dv-drag-over-background-color);
|
background-color: var(--dv-drag-over-background-color);
|
||||||
transition: top 70ms ease-out, left 70ms ease-out,
|
transition: top var(--dv-transition-duration) ease-out,
|
||||||
width 70ms ease-out, height 70ms ease-out,
|
left var(--dv-transition-duration) ease-out,
|
||||||
opacity 0.15s ease-out;
|
width var(--dv-transition-duration) ease-out,
|
||||||
|
height var(--dv-transition-duration) ease-out,
|
||||||
|
opacity var(--dv-transition-duration) ease-out;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
|
@ -93,10 +93,26 @@ const DEFAULT_SIZE: MeasuredValue = {
|
|||||||
const SMALL_WIDTH_BOUNDARY = 100;
|
const SMALL_WIDTH_BOUNDARY = 100;
|
||||||
const SMALL_HEIGHT_BOUNDARY = 100;
|
const SMALL_HEIGHT_BOUNDARY = 100;
|
||||||
|
|
||||||
|
export interface DropTargetTargetModel {
|
||||||
|
getElements(
|
||||||
|
event?: DragEvent,
|
||||||
|
outline?: HTMLElement
|
||||||
|
): {
|
||||||
|
root: HTMLElement;
|
||||||
|
overlay: HTMLElement;
|
||||||
|
changed: boolean;
|
||||||
|
};
|
||||||
|
exists(): boolean;
|
||||||
|
clear(): void;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DroptargetOptions {
|
export interface DroptargetOptions {
|
||||||
canDisplayOverlay: CanDisplayOverlay;
|
canDisplayOverlay: CanDisplayOverlay;
|
||||||
acceptedTargetZones: Position[];
|
acceptedTargetZones: Position[];
|
||||||
overlayModel?: DroptargetOverlayModel;
|
overlayModel?: DroptargetOverlayModel;
|
||||||
|
getOverrideTarget?: () => DropTargetTargetModel | undefined;
|
||||||
|
className?: string;
|
||||||
|
getOverlayOutline?: () => HTMLElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Droptarget extends CompositeDisposable {
|
export class Droptarget extends CompositeDisposable {
|
||||||
@ -116,6 +132,18 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
|
|
||||||
private static USED_EVENT_ID = '__dockview_droptarget_event_is_used__';
|
private static USED_EVENT_ID = '__dockview_droptarget_event_is_used__';
|
||||||
|
|
||||||
|
private static ACTUAL_TARGET: Droptarget | undefined;
|
||||||
|
|
||||||
|
private _disabled: boolean;
|
||||||
|
|
||||||
|
get disabled(): boolean {
|
||||||
|
return this._disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(value: boolean) {
|
||||||
|
this._disabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
get state(): Position | undefined {
|
get state(): Position | undefined {
|
||||||
return this._state;
|
return this._state;
|
||||||
}
|
}
|
||||||
@ -126,21 +154,35 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this._disabled = false;
|
||||||
|
|
||||||
// use a set to take advantage of #<set>.has
|
// use a set to take advantage of #<set>.has
|
||||||
this._acceptedTargetZonesSet = new Set(
|
this._acceptedTargetZonesSet = new Set(
|
||||||
this.options.acceptedTargetZones
|
this.options.acceptedTargetZones
|
||||||
);
|
);
|
||||||
|
|
||||||
this.dnd = new DragAndDropObserver(this.element, {
|
this.dnd = new DragAndDropObserver(this.element, {
|
||||||
onDragEnter: () => undefined,
|
onDragEnter: () => {
|
||||||
|
this.options.getOverrideTarget?.()?.getElements();
|
||||||
|
},
|
||||||
onDragOver: (e) => {
|
onDragOver: (e) => {
|
||||||
|
Droptarget.ACTUAL_TARGET = this;
|
||||||
|
|
||||||
|
const overrideTraget = this.options.getOverrideTarget?.();
|
||||||
|
|
||||||
if (this._acceptedTargetZonesSet.size === 0) {
|
if (this._acceptedTargetZonesSet.size === 0) {
|
||||||
|
if (overrideTraget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.removeDropTarget();
|
this.removeDropTarget();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const width = this.element.clientWidth;
|
const target =
|
||||||
const height = this.element.clientHeight;
|
this.options.getOverlayOutline?.() ?? this.element;
|
||||||
|
|
||||||
|
const width = target.offsetWidth;
|
||||||
|
const height = target.offsetHeight;
|
||||||
|
|
||||||
if (width === 0 || height === 0) {
|
if (width === 0 || height === 0) {
|
||||||
return; // avoid div!0
|
return; // avoid div!0
|
||||||
@ -149,8 +191,8 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
const rect = (
|
const rect = (
|
||||||
e.currentTarget as HTMLElement
|
e.currentTarget as HTMLElement
|
||||||
).getBoundingClientRect();
|
).getBoundingClientRect();
|
||||||
const x = e.clientX - rect.left;
|
const x = (e.clientX ?? 0) - rect.left;
|
||||||
const y = e.clientY - rect.top;
|
const y = (e.clientY ?? 0) - rect.top;
|
||||||
|
|
||||||
const quadrant = this.calculateQuadrant(
|
const quadrant = this.calculateQuadrant(
|
||||||
this._acceptedTargetZonesSet,
|
this._acceptedTargetZonesSet,
|
||||||
@ -172,6 +214,9 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.options.canDisplayOverlay(e, quadrant)) {
|
if (!this.options.canDisplayOverlay(e, quadrant)) {
|
||||||
|
if (overrideTraget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.removeDropTarget();
|
this.removeDropTarget();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -194,16 +239,26 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
|
|
||||||
this.markAsUsed(e);
|
this.markAsUsed(e);
|
||||||
|
|
||||||
if (!this.targetElement) {
|
if (overrideTraget) {
|
||||||
|
//
|
||||||
|
} else if (!this.targetElement) {
|
||||||
this.targetElement = document.createElement('div');
|
this.targetElement = document.createElement('div');
|
||||||
this.targetElement.className = 'drop-target-dropzone';
|
this.targetElement.className = 'dv-drop-target-dropzone';
|
||||||
this.overlayElement = document.createElement('div');
|
this.overlayElement = document.createElement('div');
|
||||||
this.overlayElement.className = 'drop-target-selection';
|
this.overlayElement.className = 'dv-drop-target-selection';
|
||||||
this._state = 'center';
|
this._state = 'center';
|
||||||
this.targetElement.appendChild(this.overlayElement);
|
this.targetElement.appendChild(this.overlayElement);
|
||||||
|
|
||||||
this.element.classList.add('drop-target');
|
target.classList.add('dv-drop-target');
|
||||||
this.element.append(this.targetElement);
|
target.append(this.targetElement);
|
||||||
|
|
||||||
|
// this.overlayElement.style.opacity = '0';
|
||||||
|
|
||||||
|
// requestAnimationFrame(() => {
|
||||||
|
// if (this.overlayElement) {
|
||||||
|
// this.overlayElement.style.opacity = '';
|
||||||
|
// }
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toggleClasses(quadrant, width, height);
|
this.toggleClasses(quadrant, width, height);
|
||||||
@ -211,10 +266,32 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
this._state = quadrant;
|
this._state = quadrant;
|
||||||
},
|
},
|
||||||
onDragLeave: () => {
|
onDragLeave: () => {
|
||||||
|
const target = this.options.getOverrideTarget?.();
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.removeDropTarget();
|
this.removeDropTarget();
|
||||||
},
|
},
|
||||||
onDragEnd: () => {
|
onDragEnd: (e) => {
|
||||||
|
const target = this.options.getOverrideTarget?.();
|
||||||
|
|
||||||
|
if (target && Droptarget.ACTUAL_TARGET === this) {
|
||||||
|
if (this._state) {
|
||||||
|
// only stop the propagation of the event if we are dealing with it
|
||||||
|
// which is only when the target has state
|
||||||
|
e.stopPropagation();
|
||||||
|
this._onDrop.fire({
|
||||||
|
position: this._state,
|
||||||
|
nativeEvent: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.removeDropTarget();
|
this.removeDropTarget();
|
||||||
|
|
||||||
|
target?.clear();
|
||||||
},
|
},
|
||||||
onDrop: (e) => {
|
onDrop: (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -223,6 +300,8 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
|
|
||||||
this.removeDropTarget();
|
this.removeDropTarget();
|
||||||
|
|
||||||
|
this.options.getOverrideTarget?.()?.clear();
|
||||||
|
|
||||||
if (state) {
|
if (state) {
|
||||||
// only stop the propagation of the event if we are dealing with it
|
// only stop the propagation of the event if we are dealing with it
|
||||||
// which is only when the target has state
|
// which is only when the target has state
|
||||||
@ -268,7 +347,9 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
width: number,
|
width: number,
|
||||||
height: number
|
height: number
|
||||||
): void {
|
): void {
|
||||||
if (!this.overlayElement) {
|
const target = this.options.getOverrideTarget?.();
|
||||||
|
|
||||||
|
if (!target && !this.overlayElement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,6 +381,103 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
const outlineEl =
|
||||||
|
this.options.getOverlayOutline?.() ?? this.element;
|
||||||
|
const elBox = outlineEl.getBoundingClientRect();
|
||||||
|
|
||||||
|
const ta = target.getElements(undefined, outlineEl);
|
||||||
|
const el = ta.root;
|
||||||
|
const overlay = ta.overlay;
|
||||||
|
|
||||||
|
const bigbox = el.getBoundingClientRect();
|
||||||
|
|
||||||
|
const rootTop = elBox.top - bigbox.top;
|
||||||
|
const rootLeft = elBox.left - bigbox.left;
|
||||||
|
|
||||||
|
const box = {
|
||||||
|
top: rootTop,
|
||||||
|
left: rootLeft,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rightClass) {
|
||||||
|
box.left = rootLeft + width * (1 - size);
|
||||||
|
box.width = width * size;
|
||||||
|
} else if (leftClass) {
|
||||||
|
box.width = width * size;
|
||||||
|
} else if (topClass) {
|
||||||
|
box.height = height * size;
|
||||||
|
} else if (bottomClass) {
|
||||||
|
box.top = rootTop + height * (1 - size);
|
||||||
|
box.height = height * size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSmallX && isLeft) {
|
||||||
|
box.width = 4;
|
||||||
|
}
|
||||||
|
if (isSmallX && isRight) {
|
||||||
|
box.left = rootLeft + width - 4;
|
||||||
|
box.width = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const topPx = `${Math.round(box.top)}px`;
|
||||||
|
const leftPx = `${Math.round(box.left)}px`;
|
||||||
|
const widthPx = `${Math.round(box.width)}px`;
|
||||||
|
const heightPx = `${Math.round(box.height)}px`;
|
||||||
|
|
||||||
|
if (
|
||||||
|
overlay.style.top === topPx &&
|
||||||
|
overlay.style.left === leftPx &&
|
||||||
|
overlay.style.width === widthPx &&
|
||||||
|
overlay.style.height === heightPx
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay.style.top = topPx;
|
||||||
|
overlay.style.left = leftPx;
|
||||||
|
overlay.style.width = widthPx;
|
||||||
|
overlay.style.height = heightPx;
|
||||||
|
overlay.style.visibility = 'visible';
|
||||||
|
|
||||||
|
overlay.className = `dv-drop-target-anchor${
|
||||||
|
this.options.className ? ` ${this.options.className}` : ''
|
||||||
|
}`;
|
||||||
|
|
||||||
|
toggleClass(overlay, 'dv-drop-target-left', isLeft);
|
||||||
|
toggleClass(overlay, 'dv-drop-target-right', isRight);
|
||||||
|
toggleClass(overlay, 'dv-drop-target-top', isTop);
|
||||||
|
toggleClass(overlay, 'dv-drop-target-bottom', isBottom);
|
||||||
|
toggleClass(
|
||||||
|
overlay,
|
||||||
|
'dv-drop-target-center',
|
||||||
|
quadrant === 'center'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ta.changed) {
|
||||||
|
toggleClass(
|
||||||
|
overlay,
|
||||||
|
'dv-drop-target-anchor-container-changed',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
setTimeout(() => {
|
||||||
|
toggleClass(
|
||||||
|
overlay,
|
||||||
|
'dv-drop-target-anchor-container-changed',
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.overlayElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const box = { top: '0px', left: '0px', width: '100%', height: '100%' };
|
const box = { top: '0px', left: '0px', width: '100%', height: '100%' };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -396,10 +574,12 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
private removeDropTarget(): void {
|
private removeDropTarget(): void {
|
||||||
if (this.targetElement) {
|
if (this.targetElement) {
|
||||||
this._state = undefined;
|
this._state = undefined;
|
||||||
this.element.removeChild(this.targetElement);
|
this.targetElement.parentElement?.classList.remove(
|
||||||
|
'dv-drop-target'
|
||||||
|
);
|
||||||
|
this.targetElement.remove();
|
||||||
this.targetElement = undefined;
|
this.targetElement = undefined;
|
||||||
this.overlayElement = undefined;
|
this.overlayElement = undefined;
|
||||||
this.element.classList.remove('drop-target');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,17 @@ import { addClasses, removeClasses } from '../dom';
|
|||||||
|
|
||||||
export function addGhostImage(
|
export function addGhostImage(
|
||||||
dataTransfer: DataTransfer,
|
dataTransfer: DataTransfer,
|
||||||
ghostElement: HTMLElement
|
ghostElement: HTMLElement,
|
||||||
|
options?: { x?: number; y?: number }
|
||||||
): void {
|
): void {
|
||||||
// class dockview provides to force ghost image to be drawn on a different layer and prevent weird rendering issues
|
// class dockview provides to force ghost image to be drawn on a different layer and prevent weird rendering issues
|
||||||
addClasses(ghostElement, 'dv-dragged');
|
addClasses(ghostElement, 'dv-dragged');
|
||||||
|
|
||||||
|
// move the element off-screen initially otherwise it may in some cases be rendered at (0,0) momentarily
|
||||||
|
ghostElement.style.top = '-9999px';
|
||||||
|
|
||||||
document.body.appendChild(ghostElement);
|
document.body.appendChild(ghostElement);
|
||||||
dataTransfer.setDragImage(ghostElement, 0, 0);
|
dataTransfer.setDragImage(ghostElement, options?.x ?? 0, options?.y ?? 0);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
removeClasses(ghostElement, 'dv-dragged');
|
removeClasses(ghostElement, 'dv-dragged');
|
||||||
|
@ -21,7 +21,7 @@ export class GroupDragHandler extends DragHandler {
|
|||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
addDisposableListener(
|
addDisposableListener(
|
||||||
element,
|
element,
|
||||||
'mousedown',
|
'pointerdown',
|
||||||
(e) => {
|
(e) => {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
/**
|
/**
|
||||||
@ -72,9 +72,11 @@ export class GroupDragHandler extends DragHandler {
|
|||||||
ghostElement.style.lineHeight = '20px';
|
ghostElement.style.lineHeight = '20px';
|
||||||
ghostElement.style.borderRadius = '12px';
|
ghostElement.style.borderRadius = '12px';
|
||||||
ghostElement.style.position = 'absolute';
|
ghostElement.style.position = 'absolute';
|
||||||
|
ghostElement.style.pointerEvents = 'none';
|
||||||
|
ghostElement.style.top = '-9999px';
|
||||||
ghostElement.textContent = `Multiple Panels (${this.group.size})`;
|
ghostElement.textContent = `Multiple Panels (${this.group.size})`;
|
||||||
|
|
||||||
addGhostImage(dataTransfer, ghostElement);
|
addGhostImage(dataTransfer, ghostElement, { y: -10, x: 30 });
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -28,9 +28,9 @@ export class ContentContainer
|
|||||||
extends CompositeDisposable
|
extends CompositeDisposable
|
||||||
implements IContentContainer
|
implements IContentContainer
|
||||||
{
|
{
|
||||||
private _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
private panel: IDockviewPanel | undefined;
|
private panel: IDockviewPanel | undefined;
|
||||||
private disposable = new MutableDisposable();
|
private readonly disposable = new MutableDisposable();
|
||||||
|
|
||||||
private readonly _onDidFocus = new Emitter<void>();
|
private readonly _onDidFocus = new Emitter<void>();
|
||||||
readonly onDidFocus: Event<void> = this._onDidFocus.event;
|
readonly onDidFocus: Event<void> = this._onDidFocus.event;
|
||||||
@ -50,12 +50,20 @@ export class ContentContainer
|
|||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._element = document.createElement('div');
|
this._element = document.createElement('div');
|
||||||
this._element.className = 'content-container';
|
this._element.className = 'dv-content-container';
|
||||||
this._element.tabIndex = -1;
|
this._element.tabIndex = -1;
|
||||||
|
|
||||||
this.addDisposables(this._onDidFocus, this._onDidBlur);
|
this.addDisposables(this._onDidFocus, this._onDidBlur);
|
||||||
|
|
||||||
|
const target = group.dropTargetContainer;
|
||||||
|
|
||||||
this.dropTarget = new Droptarget(this.element, {
|
this.dropTarget = new Droptarget(this.element, {
|
||||||
|
getOverlayOutline: () => {
|
||||||
|
return accessor.options.theme?.dndPanelOverlay === 'group'
|
||||||
|
? this.element.parentElement
|
||||||
|
: null;
|
||||||
|
},
|
||||||
|
className: 'dv-drop-target-content',
|
||||||
acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
|
acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
|
||||||
canDisplayOverlay: (event, position) => {
|
canDisplayOverlay: (event, position) => {
|
||||||
if (
|
if (
|
||||||
@ -76,26 +84,12 @@ export class ContentContainer
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data && data.viewId === this.accessor.id) {
|
if (data && data.viewId === this.accessor.id) {
|
||||||
if (data.groupId === this.group.id) {
|
return true;
|
||||||
if (position === 'center') {
|
|
||||||
// don't allow to drop on self for center position
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (data.panelId === null) {
|
|
||||||
// don't allow group move to drop anywhere on self
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupHasOnePanelAndIsActiveDragElement =
|
|
||||||
this.group.panels.length === 1 &&
|
|
||||||
data.groupId === this.group.id;
|
|
||||||
|
|
||||||
return !groupHasOnePanelAndIsActiveDragElement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.group.canDisplayOverlay(event, position, 'content');
|
return this.group.canDisplayOverlay(event, position, 'content');
|
||||||
},
|
},
|
||||||
|
getOverrideTarget: target ? () => target.model : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addDisposables(this.dropTarget);
|
this.addDisposables(this.dropTarget);
|
||||||
@ -154,6 +148,10 @@ export class ContentContainer
|
|||||||
referenceContainer: this,
|
referenceContainer: this,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(
|
||||||
|
`dockview: invalid renderer type '${panel.api.renderer}'`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doRender) {
|
if (doRender) {
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
import { shiftAbsoluteElementIntoView } from '../../dom';
|
||||||
|
import { addDisposableListener } from '../../events';
|
||||||
|
import {
|
||||||
|
CompositeDisposable,
|
||||||
|
Disposable,
|
||||||
|
MutableDisposable,
|
||||||
|
} from '../../lifecycle';
|
||||||
|
|
||||||
|
export class PopupService extends CompositeDisposable {
|
||||||
|
private readonly _element: HTMLElement;
|
||||||
|
private _active: HTMLElement | null = null;
|
||||||
|
private readonly _activeDisposable = new MutableDisposable();
|
||||||
|
|
||||||
|
constructor(private readonly root: HTMLElement) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._element = document.createElement('div');
|
||||||
|
this._element.className = 'dv-popover-anchor';
|
||||||
|
this._element.style.position = 'relative';
|
||||||
|
|
||||||
|
this.root.prepend(this._element);
|
||||||
|
|
||||||
|
this.addDisposables(
|
||||||
|
Disposable.from(() => {
|
||||||
|
this.close();
|
||||||
|
}),
|
||||||
|
this._activeDisposable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
openPopover(
|
||||||
|
element: HTMLElement,
|
||||||
|
position: { x: number; y: number; zIndex?: string }
|
||||||
|
): void {
|
||||||
|
this.close();
|
||||||
|
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.style.position = 'absolute';
|
||||||
|
wrapper.style.zIndex = position.zIndex ?? 'var(--dv-overlay-z-index)';
|
||||||
|
wrapper.appendChild(element);
|
||||||
|
|
||||||
|
const anchorBox = this._element.getBoundingClientRect();
|
||||||
|
const offsetX = anchorBox.left;
|
||||||
|
const offsetY = anchorBox.top;
|
||||||
|
|
||||||
|
wrapper.style.top = `${position.y - offsetY}px`;
|
||||||
|
wrapper.style.left = `${position.x - offsetX}px`;
|
||||||
|
|
||||||
|
this._element.appendChild(wrapper);
|
||||||
|
|
||||||
|
this._active = wrapper;
|
||||||
|
|
||||||
|
this._activeDisposable.value = new CompositeDisposable(
|
||||||
|
addDisposableListener(window, 'pointerdown', (event) => {
|
||||||
|
const target = event.target;
|
||||||
|
|
||||||
|
if (!(target instanceof HTMLElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let el: HTMLElement | null = target;
|
||||||
|
|
||||||
|
while (el && el !== wrapper) {
|
||||||
|
el = el?.parentElement ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el) {
|
||||||
|
return; // clicked within popover
|
||||||
|
}
|
||||||
|
|
||||||
|
this.close();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
shiftAbsoluteElementIntoView(wrapper, this.root);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
if (this._active) {
|
||||||
|
this._active.remove();
|
||||||
|
this._activeDisposable.dispose();
|
||||||
|
this._active = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@
|
|||||||
); /* forces tab to be drawn on a separate layer (see https://github.com/microsoft/vscode/issues/18733) */
|
); /* forces tab to be drawn on a separate layer (see https://github.com/microsoft/vscode/issues/18733) */
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab {
|
.dv-tab {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
&:focus-within,
|
&:focus-within,
|
||||||
@ -33,7 +33,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active-tab {
|
&.dv-active-tab {
|
||||||
.dv-default-tab {
|
.dv-default-tab {
|
||||||
.dv-default-tab-action {
|
.dv-default-tab-action {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
@ -41,7 +41,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.inactive-tab {
|
&.dv-inactive-tab {
|
||||||
.dv-default-tab {
|
.dv-default-tab {
|
||||||
.dv-default-tab-action {
|
.dv-default-tab-action {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
@ -58,15 +58,13 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
min-width: 80px;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0px 8px;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: elipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
.dv-default-tab-content {
|
.dv-default-tab-content {
|
||||||
padding: 0px 8px;
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dv-default-tab-action {
|
.dv-default-tab-action {
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
import { CompositeDisposable } from '../../../lifecycle';
|
import { CompositeDisposable } from '../../../lifecycle';
|
||||||
import { ITabRenderer, GroupPanelPartInitParameters } from '../../types';
|
import { ITabRenderer, GroupPanelPartInitParameters } from '../../types';
|
||||||
import { addDisposableListener } from '../../../events';
|
import { addDisposableListener } from '../../../events';
|
||||||
import { PanelUpdateEvent } from '../../../panel/types';
|
|
||||||
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
|
|
||||||
import { createCloseButton } from '../../../svg';
|
import { createCloseButton } from '../../../svg';
|
||||||
|
|
||||||
export class DefaultTab extends CompositeDisposable implements ITabRenderer {
|
export class DefaultTab extends CompositeDisposable implements ITabRenderer {
|
||||||
private _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
private _content: HTMLElement;
|
private readonly _content: HTMLElement;
|
||||||
private action: HTMLElement;
|
private readonly action: HTMLElement;
|
||||||
//
|
private _title: string | undefined;
|
||||||
private params: GroupPanelPartInitParameters = {} as any;
|
|
||||||
|
|
||||||
get element(): HTMLElement {
|
get element(): HTMLElement {
|
||||||
return this._element;
|
return this._element;
|
||||||
@ -21,7 +18,7 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer {
|
|||||||
|
|
||||||
this._element = document.createElement('div');
|
this._element = document.createElement('div');
|
||||||
this._element.className = 'dv-default-tab';
|
this._element.className = 'dv-default-tab';
|
||||||
//
|
|
||||||
this._content = document.createElement('div');
|
this._content = document.createElement('div');
|
||||||
this._content.className = 'dv-default-tab-content';
|
this._content.className = 'dv-default-tab-content';
|
||||||
|
|
||||||
@ -29,53 +26,39 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer {
|
|||||||
this.action.className = 'dv-default-tab-action';
|
this.action.className = 'dv-default-tab-action';
|
||||||
this.action.appendChild(createCloseButton());
|
this.action.appendChild(createCloseButton());
|
||||||
|
|
||||||
//
|
|
||||||
this._element.appendChild(this._content);
|
this._element.appendChild(this._content);
|
||||||
this._element.appendChild(this.action);
|
this._element.appendChild(this.action);
|
||||||
//
|
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
init(params: GroupPanelPartInitParameters): void {
|
||||||
|
this._title = params.title;
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
addDisposableListener(this.action, 'mousedown', (ev) => {
|
params.api.onDidTitleChange((event) => {
|
||||||
|
this._title = event.title;
|
||||||
|
this.render();
|
||||||
|
}),
|
||||||
|
addDisposableListener(this.action, 'pointerdown', (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
}),
|
||||||
|
addDisposableListener(this.action, 'click', (ev) => {
|
||||||
|
if (ev.defaultPrevented) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.preventDefault();
|
||||||
|
params.api.close();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(event: PanelUpdateEvent): void {
|
|
||||||
this.params = { ...this.params, ...event.params };
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
focus(): void {
|
|
||||||
//noop
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(params: GroupPanelPartInitParameters): void {
|
|
||||||
this.params = params;
|
|
||||||
this._content.textContent = params.title;
|
|
||||||
|
|
||||||
addDisposableListener(this.action, 'click', (ev) => {
|
|
||||||
ev.preventDefault(); //
|
|
||||||
this.params.api.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onGroupChange(_group: DockviewGroupPanel): void {
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
onPanelVisibleChange(_isPanelVisible: boolean): void {
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
public layout(_width: number, _height: number): void {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
private render(): void {
|
private render(): void {
|
||||||
if (this._content.textContent !== this.params.title) {
|
if (this._content.textContent !== this._title) {
|
||||||
this._content.textContent = this.params.title;
|
this._content.textContent = this._title ?? '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,11 @@ import { DockviewGroupPanel } from '../../dockviewGroupPanel';
|
|||||||
import {
|
import {
|
||||||
DroptargetEvent,
|
DroptargetEvent,
|
||||||
Droptarget,
|
Droptarget,
|
||||||
Position,
|
|
||||||
WillShowOverlayEvent,
|
WillShowOverlayEvent,
|
||||||
} from '../../../dnd/droptarget';
|
} from '../../../dnd/droptarget';
|
||||||
import { DragHandler } from '../../../dnd/abstractDragHandler';
|
import { DragHandler } from '../../../dnd/abstractDragHandler';
|
||||||
import { IDockviewPanel } from '../../dockviewPanel';
|
import { IDockviewPanel } from '../../dockviewPanel';
|
||||||
|
import { addGhostImage } from '../../../dnd/ghost';
|
||||||
|
|
||||||
class TabDragHandler extends DragHandler {
|
class TabDragHandler extends DragHandler {
|
||||||
private readonly panelTransfer =
|
private readonly panelTransfer =
|
||||||
@ -50,8 +50,8 @@ export class Tab extends CompositeDisposable {
|
|||||||
private readonly dropTarget: Droptarget;
|
private readonly dropTarget: Droptarget;
|
||||||
private content: ITabRenderer | undefined = undefined;
|
private content: ITabRenderer | undefined = undefined;
|
||||||
|
|
||||||
private readonly _onChanged = new Emitter<MouseEvent>();
|
private readonly _onPointDown = new Emitter<MouseEvent>();
|
||||||
readonly onChanged: Event<MouseEvent> = this._onChanged.event;
|
readonly onPointerDown: Event<MouseEvent> = this._onPointDown.event;
|
||||||
|
|
||||||
private readonly _onDropped = new Emitter<DroptargetEvent>();
|
private readonly _onDropped = new Emitter<DroptargetEvent>();
|
||||||
readonly onDrop: Event<DroptargetEvent> = this._onDropped.event;
|
readonly onDrop: Event<DroptargetEvent> = this._onDropped.event;
|
||||||
@ -73,11 +73,11 @@ export class Tab extends CompositeDisposable {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this._element = document.createElement('div');
|
this._element = document.createElement('div');
|
||||||
this._element.className = 'tab';
|
this._element.className = 'dv-tab';
|
||||||
this._element.tabIndex = 0;
|
this._element.tabIndex = 0;
|
||||||
this._element.draggable = true;
|
this._element.draggable = true;
|
||||||
|
|
||||||
toggleClass(this.element, 'inactive-tab', true);
|
toggleClass(this.element, 'dv-inactive-tab', true);
|
||||||
|
|
||||||
const dragHandler = new TabDragHandler(
|
const dragHandler = new TabDragHandler(
|
||||||
this._element,
|
this._element,
|
||||||
@ -87,7 +87,8 @@ export class Tab extends CompositeDisposable {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.dropTarget = new Droptarget(this._element, {
|
this.dropTarget = new Droptarget(this._element, {
|
||||||
acceptedTargetZones: ['center'],
|
acceptedTargetZones: ['left', 'right'],
|
||||||
|
overlayModel: { activationSize: { value: 50, type: 'percentage' } },
|
||||||
canDisplayOverlay: (event, position) => {
|
canDisplayOverlay: (event, position) => {
|
||||||
if (this.group.locked) {
|
if (this.group.locked) {
|
||||||
return false;
|
return false;
|
||||||
@ -96,15 +97,7 @@ export class Tab extends CompositeDisposable {
|
|||||||
const data = getPanelData();
|
const data = getPanelData();
|
||||||
|
|
||||||
if (data && this.accessor.id === data.viewId) {
|
if (data && this.accessor.id === data.viewId) {
|
||||||
if (
|
return true;
|
||||||
data.panelId === null &&
|
|
||||||
data.groupId === this.group.id
|
|
||||||
) {
|
|
||||||
// don't allow group move to drop on self
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.panel.id !== data.panelId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.group.model.canDisplayOverlay(
|
return this.group.model.canDisplayOverlay(
|
||||||
@ -113,24 +106,38 @@ export class Tab extends CompositeDisposable {
|
|||||||
'tab'
|
'tab'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
getOverrideTarget: () => group.model.dropTargetContainer?.model,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
|
this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this._onChanged,
|
this._onPointDown,
|
||||||
this._onDropped,
|
this._onDropped,
|
||||||
this._onDragStart,
|
this._onDragStart,
|
||||||
dragHandler.onDragStart((event) => {
|
dragHandler.onDragStart((event) => {
|
||||||
|
if (event.dataTransfer) {
|
||||||
|
const style = getComputedStyle(this.element);
|
||||||
|
const newNode = this.element.cloneNode(true) as HTMLElement;
|
||||||
|
Array.from(style).forEach((key) =>
|
||||||
|
newNode.style.setProperty(
|
||||||
|
key,
|
||||||
|
style.getPropertyValue(key),
|
||||||
|
style.getPropertyPriority(key)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
newNode.style.position = 'absolute';
|
||||||
|
|
||||||
|
addGhostImage(event.dataTransfer, newNode, {
|
||||||
|
y: -10,
|
||||||
|
x: 30,
|
||||||
|
});
|
||||||
|
}
|
||||||
this._onDragStart.fire(event);
|
this._onDragStart.fire(event);
|
||||||
}),
|
}),
|
||||||
dragHandler,
|
dragHandler,
|
||||||
addDisposableListener(this._element, 'mousedown', (event) => {
|
addDisposableListener(this._element, 'pointerdown', (event) => {
|
||||||
if (event.defaultPrevented) {
|
this._onPointDown.fire(event);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._onChanged.fire(event);
|
|
||||||
}),
|
}),
|
||||||
this.dropTarget.onDrop((event) => {
|
this.dropTarget.onDrop((event) => {
|
||||||
this._onDropped.fire(event);
|
this._onDropped.fire(event);
|
||||||
@ -140,8 +147,8 @@ export class Tab extends CompositeDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setActive(isActive: boolean): void {
|
public setActive(isActive: boolean): void {
|
||||||
toggleClass(this.element, 'active-tab', isActive);
|
toggleClass(this.element, 'dv-active-tab', isActive);
|
||||||
toggleClass(this.element, 'inactive-tab', !isActive);
|
toggleClass(this.element, 'dv-inactive-tab', !isActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setContent(part: ITabRenderer): void {
|
public setContent(part: ITabRenderer): void {
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
.dv-tabs-overflow-dropdown-default {
|
||||||
|
height: 100%;
|
||||||
|
color: var(--dv-activegroup-hiddenpanel-tab-color);
|
||||||
|
|
||||||
|
margin: var(--dv-tab-margin);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
padding-left: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
> svg {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { createChevronRightButton } from '../../../svg';
|
||||||
|
|
||||||
|
export type DropdownElement = {
|
||||||
|
element: HTMLElement;
|
||||||
|
update: (params: { tabs: number }) => void;
|
||||||
|
dispose?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createDropdownElementHandle(): DropdownElement {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.className = 'dv-tabs-overflow-dropdown-default';
|
||||||
|
|
||||||
|
const text = document.createElement('span');
|
||||||
|
text.textContent = ``;
|
||||||
|
const icon = createChevronRightButton();
|
||||||
|
el.appendChild(icon);
|
||||||
|
el.appendChild(text);
|
||||||
|
|
||||||
|
return {
|
||||||
|
element: el,
|
||||||
|
update: (params: { tabs: number }) => {
|
||||||
|
text.textContent = `${params.tabs}`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
.dv-tabs-container {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
scrollbar-width: thin; // firefox
|
||||||
|
|
||||||
|
&.dv-horizontal {
|
||||||
|
.dv-tab {
|
||||||
|
&:not(:first-child)::before {
|
||||||
|
content: ' ';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 5;
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: var(--dv-tab-divider-color);
|
||||||
|
width: 1px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
height: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Track */
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle */
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--dv-tabs-container-scrollbar-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dv-scrollable {
|
||||||
|
> .dv-tabs-container {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dv-tab {
|
||||||
|
-webkit-user-drag: element;
|
||||||
|
outline: none;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: var(--dv-tab-font-size);
|
||||||
|
margin: var(--dv-tab-margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dv-tabs-overflow-container {
|
||||||
|
flex-direction: column;
|
||||||
|
height: unset;
|
||||||
|
border: 1px solid var(--dv-tab-divider-color);
|
||||||
|
background-color: var(--dv-group-view-background-color);
|
||||||
|
|
||||||
|
.dv-tab {
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid var(--dv-tab-divider-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dv-active-tab {
|
||||||
|
background-color: var(
|
||||||
|
--dv-activegroup-visiblepanel-tab-background-color
|
||||||
|
);
|
||||||
|
color: var(--dv-activegroup-visiblepanel-tab-color);
|
||||||
|
}
|
||||||
|
.dv-inactive-tab {
|
||||||
|
background-color: var(
|
||||||
|
--dv-activegroup-hiddenpanel-tab-background-color
|
||||||
|
);
|
||||||
|
color: var(--dv-activegroup-hiddenpanel-tab-color);
|
||||||
|
}
|
||||||
|
}
|
301
packages/dockview-core/src/dockview/components/titlebar/tabs.ts
Normal file
301
packages/dockview-core/src/dockview/components/titlebar/tabs.ts
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
import { getPanelData } from '../../../dnd/dataTransfer';
|
||||||
|
import {
|
||||||
|
isChildEntirelyVisibleWithinParent,
|
||||||
|
OverflowObserver,
|
||||||
|
} from '../../../dom';
|
||||||
|
import { addDisposableListener, Emitter, Event } from '../../../events';
|
||||||
|
import {
|
||||||
|
CompositeDisposable,
|
||||||
|
Disposable,
|
||||||
|
IValueDisposable,
|
||||||
|
MutableDisposable,
|
||||||
|
} from '../../../lifecycle';
|
||||||
|
import { Scrollbar } from '../../../scrollbar';
|
||||||
|
import { DockviewComponent } from '../../dockviewComponent';
|
||||||
|
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
|
||||||
|
import { WillShowOverlayLocationEvent } from '../../dockviewGroupPanelModel';
|
||||||
|
import { DockviewPanel, IDockviewPanel } from '../../dockviewPanel';
|
||||||
|
import { Tab } from '../tab/tab';
|
||||||
|
import { TabDragEvent, TabDropIndexEvent } from './tabsContainer';
|
||||||
|
|
||||||
|
export class Tabs extends CompositeDisposable {
|
||||||
|
private readonly _element: HTMLElement;
|
||||||
|
private readonly _tabsList: HTMLElement;
|
||||||
|
private readonly _observerDisposable = new MutableDisposable();
|
||||||
|
|
||||||
|
private _tabs: IValueDisposable<Tab>[] = [];
|
||||||
|
private selectedIndex = -1;
|
||||||
|
private _showTabsOverflowControl = false;
|
||||||
|
|
||||||
|
private readonly _onTabDragStart = new Emitter<TabDragEvent>();
|
||||||
|
readonly onTabDragStart: Event<TabDragEvent> = this._onTabDragStart.event;
|
||||||
|
|
||||||
|
private readonly _onDrop = new Emitter<TabDropIndexEvent>();
|
||||||
|
readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event;
|
||||||
|
|
||||||
|
private readonly _onWillShowOverlay =
|
||||||
|
new Emitter<WillShowOverlayLocationEvent>();
|
||||||
|
readonly onWillShowOverlay: Event<WillShowOverlayLocationEvent> =
|
||||||
|
this._onWillShowOverlay.event;
|
||||||
|
|
||||||
|
private readonly _onOverflowTabsChange = new Emitter<{
|
||||||
|
tabs: string[];
|
||||||
|
reset: boolean;
|
||||||
|
}>();
|
||||||
|
readonly onOverflowTabsChange = this._onOverflowTabsChange.event;
|
||||||
|
|
||||||
|
get showTabsOverflowControl(): boolean {
|
||||||
|
return this._showTabsOverflowControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
set showTabsOverflowControl(value: boolean) {
|
||||||
|
if (this._showTabsOverflowControl == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._showTabsOverflowControl = value;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
const observer = new OverflowObserver(this._tabsList);
|
||||||
|
|
||||||
|
this._observerDisposable.value = new CompositeDisposable(
|
||||||
|
observer,
|
||||||
|
observer.onDidChange((event) => {
|
||||||
|
const hasOverflow = event.hasScrollX || event.hasScrollY;
|
||||||
|
this.toggleDropdown({ reset: !hasOverflow });
|
||||||
|
}),
|
||||||
|
addDisposableListener(this._tabsList, 'scroll', () => {
|
||||||
|
this.toggleDropdown({ reset: false });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get element(): HTMLElement {
|
||||||
|
return this._element;
|
||||||
|
}
|
||||||
|
|
||||||
|
get panels(): string[] {
|
||||||
|
return this._tabs.map((_) => _.value.panel.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
get size(): number {
|
||||||
|
return this._tabs.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
get tabs(): Tab[] {
|
||||||
|
return this._tabs.map((_) => _.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly group: DockviewGroupPanel,
|
||||||
|
private readonly accessor: DockviewComponent,
|
||||||
|
options: {
|
||||||
|
showTabsOverflowControl: boolean;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._tabsList = document.createElement('div');
|
||||||
|
this._tabsList.className = 'dv-tabs-container dv-horizontal';
|
||||||
|
|
||||||
|
this.showTabsOverflowControl = options.showTabsOverflowControl;
|
||||||
|
|
||||||
|
if (accessor.options.scrollbars === 'native') {
|
||||||
|
this._element = this._tabsList;
|
||||||
|
} else {
|
||||||
|
const scrollbar = new Scrollbar(this._tabsList);
|
||||||
|
this._element = scrollbar.element;
|
||||||
|
this.addDisposables(scrollbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addDisposables(
|
||||||
|
this._onOverflowTabsChange,
|
||||||
|
this._observerDisposable,
|
||||||
|
this._onWillShowOverlay,
|
||||||
|
this._onDrop,
|
||||||
|
this._onTabDragStart,
|
||||||
|
addDisposableListener(this.element, 'pointerdown', (event) => {
|
||||||
|
if (event.defaultPrevented) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLeftClick = event.button === 0;
|
||||||
|
|
||||||
|
if (isLeftClick) {
|
||||||
|
this.accessor.doSetGroupActive(this.group);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Disposable.from(() => {
|
||||||
|
for (const { value, disposable } of this._tabs) {
|
||||||
|
disposable.dispose();
|
||||||
|
value.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tabs = [];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
indexOf(id: string): number {
|
||||||
|
return this._tabs.findIndex((tab) => tab.value.panel.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
isActive(tab: Tab): boolean {
|
||||||
|
return (
|
||||||
|
this.selectedIndex > -1 &&
|
||||||
|
this._tabs[this.selectedIndex].value === tab
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setActivePanel(panel: IDockviewPanel): void {
|
||||||
|
let runningWidth = 0;
|
||||||
|
|
||||||
|
for (const tab of this._tabs) {
|
||||||
|
const isActivePanel = panel.id === tab.value.panel.id;
|
||||||
|
tab.value.setActive(isActivePanel);
|
||||||
|
|
||||||
|
if (isActivePanel) {
|
||||||
|
const element = tab.value.element;
|
||||||
|
const parentElement = element.parentElement!;
|
||||||
|
|
||||||
|
if (
|
||||||
|
runningWidth < parentElement.scrollLeft ||
|
||||||
|
runningWidth + element.clientWidth >
|
||||||
|
parentElement.scrollLeft + parentElement.clientWidth
|
||||||
|
) {
|
||||||
|
parentElement.scrollLeft = runningWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runningWidth += tab.value.element.clientWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openPanel(panel: IDockviewPanel, index: number = this._tabs.length): void {
|
||||||
|
if (this._tabs.find((tab) => tab.value.panel.id === panel.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tab = new Tab(panel, this.accessor, this.group);
|
||||||
|
tab.setContent(panel.view.tab);
|
||||||
|
|
||||||
|
const disposable = new CompositeDisposable(
|
||||||
|
tab.onDragStart((event) => {
|
||||||
|
this._onTabDragStart.fire({ nativeEvent: event, panel });
|
||||||
|
}),
|
||||||
|
tab.onPointerDown((event) => {
|
||||||
|
if (event.defaultPrevented) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isFloatingGroupsEnabled =
|
||||||
|
!this.accessor.options.disableFloatingGroups;
|
||||||
|
|
||||||
|
const isFloatingWithOnePanel =
|
||||||
|
this.group.api.location.type === 'floating' &&
|
||||||
|
this.size === 1;
|
||||||
|
|
||||||
|
if (
|
||||||
|
isFloatingGroupsEnabled &&
|
||||||
|
!isFloatingWithOnePanel &&
|
||||||
|
event.shiftKey
|
||||||
|
) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const panel = this.accessor.getGroupPanel(tab.panel.id);
|
||||||
|
|
||||||
|
const { top, left } = tab.element.getBoundingClientRect();
|
||||||
|
const { top: rootTop, left: rootLeft } =
|
||||||
|
this.accessor.element.getBoundingClientRect();
|
||||||
|
|
||||||
|
this.accessor.addFloatingGroup(panel as DockviewPanel, {
|
||||||
|
x: left - rootLeft,
|
||||||
|
y: top - rootTop,
|
||||||
|
inDragMode: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (event.button) {
|
||||||
|
case 0: // left click or touch
|
||||||
|
if (this.group.activePanel !== panel) {
|
||||||
|
this.group.model.openPanel(panel);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
tab.onDrop((event) => {
|
||||||
|
this._onDrop.fire({
|
||||||
|
event: event.nativeEvent,
|
||||||
|
index: this._tabs.findIndex((x) => x.value === tab),
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
tab.onWillShowOverlay((event) => {
|
||||||
|
this._onWillShowOverlay.fire(
|
||||||
|
new WillShowOverlayLocationEvent(event, {
|
||||||
|
kind: 'tab',
|
||||||
|
panel: this.group.activePanel,
|
||||||
|
api: this.accessor.api,
|
||||||
|
group: this.group,
|
||||||
|
getData: getPanelData,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const value: IValueDisposable<Tab> = { value: tab, disposable };
|
||||||
|
|
||||||
|
this.addTab(value, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id: string): void {
|
||||||
|
const index = this.indexOf(id);
|
||||||
|
const tabToRemove = this._tabs.splice(index, 1)[0];
|
||||||
|
|
||||||
|
const { value, disposable } = tabToRemove;
|
||||||
|
|
||||||
|
disposable.dispose();
|
||||||
|
value.dispose();
|
||||||
|
value.element.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
private addTab(
|
||||||
|
tab: IValueDisposable<Tab>,
|
||||||
|
index: number = this._tabs.length
|
||||||
|
): void {
|
||||||
|
if (index < 0 || index > this._tabs.length) {
|
||||||
|
throw new Error('invalid location');
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tabsList.insertBefore(
|
||||||
|
tab.value.element,
|
||||||
|
this._tabsList.children[index]
|
||||||
|
);
|
||||||
|
|
||||||
|
this._tabs = [
|
||||||
|
...this._tabs.slice(0, index),
|
||||||
|
tab,
|
||||||
|
...this._tabs.slice(index),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (this.selectedIndex < 0) {
|
||||||
|
this.selectedIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private toggleDropdown(options: { reset: boolean }): void {
|
||||||
|
const tabs = options.reset
|
||||||
|
? []
|
||||||
|
: this._tabs
|
||||||
|
.filter(
|
||||||
|
(tab) =>
|
||||||
|
!isChildEntirelyVisibleWithinParent(
|
||||||
|
tab.value.element,
|
||||||
|
this._tabsList
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map((x) => x.value.panel.id);
|
||||||
|
|
||||||
|
this._onOverflowTabsChange.fire({ tabs, reset: options.reset });
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
.tabs-and-actions-container {
|
.dv-tabs-and-actions-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: var(--dv-tabs-and-actions-container-background-color);
|
background-color: var(--dv-tabs-and-actions-container-background-color);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@ -6,70 +6,32 @@
|
|||||||
height: var(--dv-tabs-and-actions-container-height);
|
height: var(--dv-tabs-and-actions-container-height);
|
||||||
font-size: var(--dv-tabs-and-actions-container-font-size);
|
font-size: var(--dv-tabs-and-actions-container-font-size);
|
||||||
|
|
||||||
&.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.dv-single-tab.dv-full-width-single-tab {
|
&.dv-single-tab.dv-full-width-single-tab {
|
||||||
.tabs-container {
|
.dv-scrollable {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dv-tabs-container {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|
||||||
.tab {
|
.dv-tab {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
padding: 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.void-container {
|
.dv-void-container {
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.void-container {
|
.dv-void-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container {
|
.dv-right-actions-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow-x: overlay;
|
|
||||||
overflow-y: hidden;
|
|
||||||
|
|
||||||
scrollbar-width: thin; // firefox
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
height: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Track */
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle */
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background: var(--dv-tabs-container-scrollbar-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
-webkit-user-drag: element;
|
|
||||||
outline: none;
|
|
||||||
min-width: 75px;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&:not(:first-child)::before {
|
|
||||||
content: ' ';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 5;
|
|
||||||
pointer-events: none;
|
|
||||||
background-color: var(--dv-tab-divider-color);
|
|
||||||
width: 1px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
import {
|
import {
|
||||||
IDisposable,
|
IDisposable,
|
||||||
CompositeDisposable,
|
CompositeDisposable,
|
||||||
IValueDisposable,
|
Disposable,
|
||||||
|
MutableDisposable,
|
||||||
} from '../../../lifecycle';
|
} from '../../../lifecycle';
|
||||||
import { addDisposableListener, Emitter, Event } from '../../../events';
|
import { addDisposableListener, Emitter, Event } from '../../../events';
|
||||||
import { Tab } from '../tab/tab';
|
import { Tab } from '../tab/tab';
|
||||||
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
|
||||||
import { VoidContainer } from './voidContainer';
|
import { VoidContainer } from './voidContainer';
|
||||||
import { toggleClass } from '../../../dom';
|
import { findRelativeZIndexParent, toggleClass } from '../../../dom';
|
||||||
import { DockviewPanel, IDockviewPanel } from '../../dockviewPanel';
|
import { IDockviewPanel } from '../../dockviewPanel';
|
||||||
import { DockviewComponent } from '../../dockviewComponent';
|
import { DockviewComponent } from '../../dockviewComponent';
|
||||||
import { WillShowOverlayEvent } from '../../../dnd/droptarget';
|
import { WillShowOverlayLocationEvent } from '../../dockviewGroupPanelModel';
|
||||||
import {
|
|
||||||
DockviewGroupDropLocation,
|
|
||||||
WillShowOverlayLocationEvent,
|
|
||||||
} from '../../dockviewGroupPanelModel';
|
|
||||||
import { getPanelData } from '../../../dnd/dataTransfer';
|
import { getPanelData } from '../../../dnd/dataTransfer';
|
||||||
|
import { Tabs } from './tabs';
|
||||||
|
import {
|
||||||
|
createDropdownElementHandle,
|
||||||
|
DropdownElement,
|
||||||
|
} from './tabOverflowControl';
|
||||||
|
|
||||||
export interface TabDropIndexEvent {
|
export interface TabDropIndexEvent {
|
||||||
readonly event: DragEvent;
|
readonly event: DragEvent;
|
||||||
@ -60,25 +62,28 @@ export class TabsContainer
|
|||||||
implements ITabsContainer
|
implements ITabsContainer
|
||||||
{
|
{
|
||||||
private readonly _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
private readonly tabContainer: HTMLElement;
|
private readonly tabs: Tabs;
|
||||||
private readonly rightActionsContainer: HTMLElement;
|
private readonly rightActionsContainer: HTMLElement;
|
||||||
private readonly leftActionsContainer: HTMLElement;
|
private readonly leftActionsContainer: HTMLElement;
|
||||||
private readonly preActionsContainer: HTMLElement;
|
private readonly preActionsContainer: HTMLElement;
|
||||||
private readonly voidContainer: VoidContainer;
|
private readonly voidContainer: VoidContainer;
|
||||||
|
|
||||||
private tabs: IValueDisposable<Tab>[] = [];
|
|
||||||
private selectedIndex = -1;
|
|
||||||
private rightActions: HTMLElement | undefined;
|
private rightActions: HTMLElement | undefined;
|
||||||
private leftActions: HTMLElement | undefined;
|
private leftActions: HTMLElement | undefined;
|
||||||
private preActions: HTMLElement | undefined;
|
private preActions: HTMLElement | undefined;
|
||||||
|
|
||||||
private _hidden = false;
|
private _hidden = false;
|
||||||
|
|
||||||
|
private dropdownPart: DropdownElement | null = null;
|
||||||
|
private _overflowTabs: string[] = [];
|
||||||
|
private readonly _dropdownDisposable = new MutableDisposable();
|
||||||
|
|
||||||
private readonly _onDrop = new Emitter<TabDropIndexEvent>();
|
private readonly _onDrop = new Emitter<TabDropIndexEvent>();
|
||||||
readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event;
|
readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event;
|
||||||
|
|
||||||
private readonly _onTabDragStart = new Emitter<TabDragEvent>();
|
get onTabDragStart(): Event<TabDragEvent> {
|
||||||
readonly onTabDragStart: Event<TabDragEvent> = this._onTabDragStart.event;
|
return this.tabs.onTabDragStart;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly _onGroupDragStart = new Emitter<GroupDragEvent>();
|
private readonly _onGroupDragStart = new Emitter<GroupDragEvent>();
|
||||||
readonly onGroupDragStart: Event<GroupDragEvent> =
|
readonly onGroupDragStart: Event<GroupDragEvent> =
|
||||||
@ -90,11 +95,11 @@ export class TabsContainer
|
|||||||
this._onWillShowOverlay.event;
|
this._onWillShowOverlay.event;
|
||||||
|
|
||||||
get panels(): string[] {
|
get panels(): string[] {
|
||||||
return this.tabs.map((_) => _.value.panel.id);
|
return this.tabs.panels;
|
||||||
}
|
}
|
||||||
|
|
||||||
get size(): number {
|
get size(): number {
|
||||||
return this.tabs.length;
|
return this.tabs.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hidden(): boolean {
|
get hidden(): boolean {
|
||||||
@ -106,6 +111,118 @@ export class TabsContainer
|
|||||||
this.element.style.display = value ? 'none' : '';
|
this.element.style.display = value ? 'none' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get element(): HTMLElement {
|
||||||
|
return this._element;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly accessor: DockviewComponent,
|
||||||
|
private readonly group: DockviewGroupPanel
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._element = document.createElement('div');
|
||||||
|
this._element.className = 'dv-tabs-and-actions-container';
|
||||||
|
|
||||||
|
toggleClass(
|
||||||
|
this._element,
|
||||||
|
'dv-full-width-single-tab',
|
||||||
|
this.accessor.options.singleTabMode === 'fullwidth'
|
||||||
|
);
|
||||||
|
|
||||||
|
this.rightActionsContainer = document.createElement('div');
|
||||||
|
this.rightActionsContainer.className = 'dv-right-actions-container';
|
||||||
|
|
||||||
|
this.leftActionsContainer = document.createElement('div');
|
||||||
|
this.leftActionsContainer.className = 'dv-left-actions-container';
|
||||||
|
|
||||||
|
this.preActionsContainer = document.createElement('div');
|
||||||
|
this.preActionsContainer.className = 'dv-pre-actions-container';
|
||||||
|
|
||||||
|
this.tabs = new Tabs(group, accessor, {
|
||||||
|
showTabsOverflowControl: !accessor.options.disableTabsOverflowList,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.voidContainer = new VoidContainer(this.accessor, this.group);
|
||||||
|
|
||||||
|
this._element.appendChild(this.preActionsContainer);
|
||||||
|
this._element.appendChild(this.tabs.element);
|
||||||
|
this._element.appendChild(this.leftActionsContainer);
|
||||||
|
this._element.appendChild(this.voidContainer.element);
|
||||||
|
this._element.appendChild(this.rightActionsContainer);
|
||||||
|
|
||||||
|
this.addDisposables(
|
||||||
|
this.tabs.onDrop((e) => this._onDrop.fire(e)),
|
||||||
|
this.tabs.onWillShowOverlay((e) => this._onWillShowOverlay.fire(e)),
|
||||||
|
accessor.onDidOptionsChange(() => {
|
||||||
|
this.tabs.showTabsOverflowControl =
|
||||||
|
!accessor.options.disableTabsOverflowList;
|
||||||
|
}),
|
||||||
|
this.tabs.onOverflowTabsChange((event) => {
|
||||||
|
this.toggleDropdown(event);
|
||||||
|
}),
|
||||||
|
this.tabs,
|
||||||
|
this._onWillShowOverlay,
|
||||||
|
this._onDrop,
|
||||||
|
this._onGroupDragStart,
|
||||||
|
this.voidContainer,
|
||||||
|
this.voidContainer.onDragStart((event) => {
|
||||||
|
this._onGroupDragStart.fire({
|
||||||
|
nativeEvent: event,
|
||||||
|
group: this.group,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
this.voidContainer.onDrop((event) => {
|
||||||
|
this._onDrop.fire({
|
||||||
|
event: event.nativeEvent,
|
||||||
|
index: this.tabs.size,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
this.voidContainer.onWillShowOverlay((event) => {
|
||||||
|
this._onWillShowOverlay.fire(
|
||||||
|
new WillShowOverlayLocationEvent(event, {
|
||||||
|
kind: 'header_space',
|
||||||
|
panel: this.group.activePanel,
|
||||||
|
api: this.accessor.api,
|
||||||
|
group: this.group,
|
||||||
|
getData: getPanelData,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
addDisposableListener(
|
||||||
|
this.voidContainer.element,
|
||||||
|
'pointerdown',
|
||||||
|
(event) => {
|
||||||
|
if (event.defaultPrevented) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isFloatingGroupsEnabled =
|
||||||
|
!this.accessor.options.disableFloatingGroups;
|
||||||
|
|
||||||
|
if (
|
||||||
|
isFloatingGroupsEnabled &&
|
||||||
|
event.shiftKey &&
|
||||||
|
this.group.api.location.type !== 'floating'
|
||||||
|
) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const { top, left } =
|
||||||
|
this.element.getBoundingClientRect();
|
||||||
|
const { top: rootTop, left: rootLeft } =
|
||||||
|
this.accessor.element.getBoundingClientRect();
|
||||||
|
|
||||||
|
this.accessor.addFloatingGroup(this.group, {
|
||||||
|
x: left - rootLeft + 20,
|
||||||
|
y: top - rootTop + 20,
|
||||||
|
inDragMode: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
show(): void {
|
show(): void {
|
||||||
if (!this.hidden) {
|
if (!this.hidden) {
|
||||||
this.element.style.display = '';
|
this.element.style.display = '';
|
||||||
@ -158,289 +275,129 @@ export class TabsContainer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get element(): HTMLElement {
|
isActive(tab: Tab): boolean {
|
||||||
return this._element;
|
return this.tabs.isActive(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
public isActive(tab: Tab): boolean {
|
indexOf(id: string): number {
|
||||||
return (
|
return this.tabs.indexOf(id);
|
||||||
this.selectedIndex > -1 &&
|
|
||||||
this.tabs[this.selectedIndex].value === tab
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public indexOf(id: string): number {
|
setActive(_isGroupActive: boolean) {
|
||||||
return this.tabs.findIndex((tab) => tab.value.panel.id === id);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly accessor: DockviewComponent,
|
|
||||||
private readonly group: DockviewGroupPanel
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this._element = document.createElement('div');
|
|
||||||
this._element.className = 'tabs-and-actions-container';
|
|
||||||
|
|
||||||
toggleClass(
|
|
||||||
this._element,
|
|
||||||
'dv-full-width-single-tab',
|
|
||||||
this.accessor.options.singleTabMode === 'fullwidth'
|
|
||||||
);
|
|
||||||
|
|
||||||
this.rightActionsContainer = document.createElement('div');
|
|
||||||
this.rightActionsContainer.className = 'right-actions-container';
|
|
||||||
|
|
||||||
this.leftActionsContainer = document.createElement('div');
|
|
||||||
this.leftActionsContainer.className = 'left-actions-container';
|
|
||||||
|
|
||||||
this.preActionsContainer = document.createElement('div');
|
|
||||||
this.preActionsContainer.className = 'pre-actions-container';
|
|
||||||
|
|
||||||
this.tabContainer = document.createElement('div');
|
|
||||||
this.tabContainer.className = 'tabs-container';
|
|
||||||
|
|
||||||
this.voidContainer = new VoidContainer(this.accessor, this.group);
|
|
||||||
|
|
||||||
this._element.appendChild(this.preActionsContainer);
|
|
||||||
this._element.appendChild(this.tabContainer);
|
|
||||||
this._element.appendChild(this.leftActionsContainer);
|
|
||||||
this._element.appendChild(this.voidContainer.element);
|
|
||||||
this._element.appendChild(this.rightActionsContainer);
|
|
||||||
|
|
||||||
this.addDisposables(
|
|
||||||
this.accessor.onDidAddPanel((e) => {
|
|
||||||
if (e.api.group === this.group) {
|
|
||||||
toggleClass(
|
|
||||||
this._element,
|
|
||||||
'dv-single-tab',
|
|
||||||
this.size === 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
this.accessor.onDidRemovePanel((e) => {
|
|
||||||
if (e.api.group === this.group) {
|
|
||||||
toggleClass(
|
|
||||||
this._element,
|
|
||||||
'dv-single-tab',
|
|
||||||
this.size === 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
this._onWillShowOverlay,
|
|
||||||
this._onDrop,
|
|
||||||
this._onTabDragStart,
|
|
||||||
this._onGroupDragStart,
|
|
||||||
this.voidContainer,
|
|
||||||
this.voidContainer.onDragStart((event) => {
|
|
||||||
this._onGroupDragStart.fire({
|
|
||||||
nativeEvent: event,
|
|
||||||
group: this.group,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
this.voidContainer.onDrop((event) => {
|
|
||||||
this._onDrop.fire({
|
|
||||||
event: event.nativeEvent,
|
|
||||||
index: this.tabs.length,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
this.voidContainer.onWillShowOverlay((event) => {
|
|
||||||
this._onWillShowOverlay.fire(
|
|
||||||
new WillShowOverlayLocationEvent(event, {
|
|
||||||
kind: 'header_space',
|
|
||||||
panel: this.group.activePanel,
|
|
||||||
api: this.accessor.api,
|
|
||||||
group: this.group,
|
|
||||||
getData: getPanelData,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
addDisposableListener(
|
|
||||||
this.voidContainer.element,
|
|
||||||
'mousedown',
|
|
||||||
(event) => {
|
|
||||||
const isFloatingGroupsEnabled =
|
|
||||||
!this.accessor.options.disableFloatingGroups;
|
|
||||||
|
|
||||||
if (
|
|
||||||
isFloatingGroupsEnabled &&
|
|
||||||
event.shiftKey &&
|
|
||||||
this.group.api.location.type !== 'floating'
|
|
||||||
) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const { top, left } =
|
|
||||||
this.element.getBoundingClientRect();
|
|
||||||
const { top: rootTop, left: rootLeft } =
|
|
||||||
this.accessor.element.getBoundingClientRect();
|
|
||||||
|
|
||||||
this.accessor.addFloatingGroup(
|
|
||||||
this.group,
|
|
||||||
{
|
|
||||||
x: left - rootLeft + 20,
|
|
||||||
y: top - rootTop + 20,
|
|
||||||
},
|
|
||||||
{ inDragMode: true }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
addDisposableListener(this.tabContainer, 'mousedown', (event) => {
|
|
||||||
if (event.defaultPrevented) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isLeftClick = event.button === 0;
|
|
||||||
|
|
||||||
if (isLeftClick) {
|
|
||||||
this.accessor.doSetGroupActive(this.group);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public setActive(_isGroupActive: boolean) {
|
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
private addTab(
|
delete(id: string): void {
|
||||||
tab: IValueDisposable<Tab>,
|
this.tabs.delete(id);
|
||||||
index: number = this.tabs.length
|
this.updateClassnames();
|
||||||
): void {
|
|
||||||
if (index < 0 || index > this.tabs.length) {
|
|
||||||
throw new Error('invalid location');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tabContainer.insertBefore(
|
setActivePanel(panel: IDockviewPanel): void {
|
||||||
tab.value.element,
|
this.tabs.setActivePanel(panel);
|
||||||
this.tabContainer.children[index]
|
|
||||||
);
|
|
||||||
|
|
||||||
this.tabs = [
|
|
||||||
...this.tabs.slice(0, index),
|
|
||||||
tab,
|
|
||||||
...this.tabs.slice(index),
|
|
||||||
];
|
|
||||||
|
|
||||||
if (this.selectedIndex < 0) {
|
|
||||||
this.selectedIndex = index;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public delete(id: string): void {
|
openPanel(panel: IDockviewPanel, index: number = this.tabs.size): void {
|
||||||
const index = this.tabs.findIndex((tab) => tab.value.panel.id === id);
|
this.tabs.openPanel(panel, index);
|
||||||
|
this.updateClassnames();
|
||||||
const tabToRemove = this.tabs.splice(index, 1)[0];
|
|
||||||
|
|
||||||
const { value, disposable } = tabToRemove;
|
|
||||||
|
|
||||||
disposable.dispose();
|
|
||||||
value.dispose();
|
|
||||||
value.element.remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setActivePanel(panel: IDockviewPanel): void {
|
closePanel(panel: IDockviewPanel): void {
|
||||||
this.tabs.forEach((tab) => {
|
|
||||||
const isActivePanel = panel.id === tab.value.panel.id;
|
|
||||||
tab.value.setActive(isActivePanel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public openPanel(
|
|
||||||
panel: IDockviewPanel,
|
|
||||||
index: number = this.tabs.length
|
|
||||||
): void {
|
|
||||||
if (this.tabs.find((tab) => tab.value.panel.id === panel.id)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const tab = new Tab(panel, this.accessor, this.group);
|
|
||||||
if (!panel.view?.tab) {
|
|
||||||
throw new Error('invalid header component');
|
|
||||||
}
|
|
||||||
tab.setContent(panel.view.tab);
|
|
||||||
|
|
||||||
const disposable = new CompositeDisposable(
|
|
||||||
tab.onDragStart((event) => {
|
|
||||||
this._onTabDragStart.fire({ nativeEvent: event, panel });
|
|
||||||
}),
|
|
||||||
tab.onChanged((event) => {
|
|
||||||
const isFloatingGroupsEnabled =
|
|
||||||
!this.accessor.options.disableFloatingGroups;
|
|
||||||
|
|
||||||
const isFloatingWithOnePanel =
|
|
||||||
this.group.api.location.type === 'floating' &&
|
|
||||||
this.size === 1;
|
|
||||||
|
|
||||||
if (
|
|
||||||
isFloatingGroupsEnabled &&
|
|
||||||
!isFloatingWithOnePanel &&
|
|
||||||
event.shiftKey
|
|
||||||
) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const panel = this.accessor.getGroupPanel(tab.panel.id);
|
|
||||||
|
|
||||||
const { top, left } = tab.element.getBoundingClientRect();
|
|
||||||
const { top: rootTop, left: rootLeft } =
|
|
||||||
this.accessor.element.getBoundingClientRect();
|
|
||||||
|
|
||||||
this.accessor.addFloatingGroup(
|
|
||||||
panel as DockviewPanel,
|
|
||||||
{
|
|
||||||
x: left - rootLeft,
|
|
||||||
y: top - rootTop,
|
|
||||||
},
|
|
||||||
{ inDragMode: true }
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isLeftClick = event.button === 0;
|
|
||||||
|
|
||||||
if (!isLeftClick || event.defaultPrevented) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.group.activePanel !== panel) {
|
|
||||||
this.group.model.openPanel(panel);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
tab.onDrop((event) => {
|
|
||||||
this._onDrop.fire({
|
|
||||||
event: event.nativeEvent,
|
|
||||||
index: this.tabs.findIndex((x) => x.value === tab),
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
tab.onWillShowOverlay((event) => {
|
|
||||||
this._onWillShowOverlay.fire(
|
|
||||||
new WillShowOverlayLocationEvent(event, {
|
|
||||||
kind: 'tab',
|
|
||||||
panel: this.group.activePanel,
|
|
||||||
api: this.accessor.api,
|
|
||||||
group: this.group,
|
|
||||||
getData: getPanelData,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const value: IValueDisposable<Tab> = { value: tab, disposable };
|
|
||||||
|
|
||||||
this.addTab(value, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public closePanel(panel: IDockviewPanel): void {
|
|
||||||
this.delete(panel.id);
|
this.delete(panel.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose(): void {
|
private updateClassnames(): void {
|
||||||
super.dispose();
|
toggleClass(this._element, 'dv-single-tab', this.size === 1);
|
||||||
|
|
||||||
for (const { value, disposable } of this.tabs) {
|
|
||||||
disposable.dispose();
|
|
||||||
value.dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tabs = [];
|
private toggleDropdown(options: { tabs: string[]; reset: boolean }): void {
|
||||||
|
const tabs = options.reset ? [] : options.tabs;
|
||||||
|
this._overflowTabs = tabs;
|
||||||
|
|
||||||
|
if (this._overflowTabs.length > 0 && this.dropdownPart) {
|
||||||
|
this.dropdownPart.update({ tabs: tabs.length });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._overflowTabs.length === 0) {
|
||||||
|
this._dropdownDisposable.dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = document.createElement('div');
|
||||||
|
root.className = 'dv-tabs-overflow-dropdown-root';
|
||||||
|
|
||||||
|
const part = createDropdownElementHandle();
|
||||||
|
part.update({ tabs: tabs.length });
|
||||||
|
|
||||||
|
this.dropdownPart = part;
|
||||||
|
|
||||||
|
root.appendChild(part.element);
|
||||||
|
this.rightActionsContainer.prepend(root);
|
||||||
|
|
||||||
|
this._dropdownDisposable.value = new CompositeDisposable(
|
||||||
|
Disposable.from(() => {
|
||||||
|
root.remove();
|
||||||
|
this.dropdownPart?.dispose?.();
|
||||||
|
this.dropdownPart = null;
|
||||||
|
}),
|
||||||
|
addDisposableListener(
|
||||||
|
root,
|
||||||
|
'pointerdown',
|
||||||
|
(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
{ capture: true }
|
||||||
|
),
|
||||||
|
addDisposableListener(root, 'click', (event) => {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.style.overflow = 'auto';
|
||||||
|
el.className = 'dv-tabs-overflow-container';
|
||||||
|
|
||||||
|
for (const tab of this.tabs.tabs.filter((tab) =>
|
||||||
|
this._overflowTabs.includes(tab.panel.id)
|
||||||
|
)) {
|
||||||
|
const panelObject = this.group.panels.find(
|
||||||
|
(panel) => panel === tab.panel
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const tabComponent =
|
||||||
|
panelObject.view.createTabRenderer('headerOverflow');
|
||||||
|
|
||||||
|
const child = tabComponent.element;
|
||||||
|
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
toggleClass(wrapper, 'dv-tab', true);
|
||||||
|
toggleClass(
|
||||||
|
wrapper,
|
||||||
|
'dv-active-tab',
|
||||||
|
panelObject.api.isActive
|
||||||
|
);
|
||||||
|
toggleClass(
|
||||||
|
wrapper,
|
||||||
|
'dv-inactive-tab',
|
||||||
|
!panelObject.api.isActive
|
||||||
|
);
|
||||||
|
|
||||||
|
wrapper.addEventListener('pointerdown', () => {
|
||||||
|
this.accessor.popupService.close();
|
||||||
|
tab.element.scrollIntoView();
|
||||||
|
tab.panel.api.setActive();
|
||||||
|
});
|
||||||
|
wrapper.appendChild(child);
|
||||||
|
|
||||||
|
el.appendChild(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
const relativeParent = findRelativeZIndexParent(root);
|
||||||
|
|
||||||
|
this.accessor.popupService.openPopover(el, {
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY,
|
||||||
|
zIndex: relativeParent?.style.zIndex
|
||||||
|
? `calc(${relativeParent.style.zIndex} * 2)`
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { last } from '../../../array';
|
|
||||||
import { getPanelData } from '../../../dnd/dataTransfer';
|
import { getPanelData } from '../../../dnd/dataTransfer';
|
||||||
import {
|
import {
|
||||||
Droptarget,
|
Droptarget,
|
||||||
@ -10,6 +9,7 @@ import { DockviewComponent } from '../../dockviewComponent';
|
|||||||
import { addDisposableListener, Emitter, Event } from '../../../events';
|
import { addDisposableListener, Emitter, Event } from '../../../events';
|
||||||
import { CompositeDisposable } from '../../../lifecycle';
|
import { CompositeDisposable } from '../../../lifecycle';
|
||||||
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
|
||||||
|
import { DockviewGroupPanelModel } from '../../dockviewGroupPanelModel';
|
||||||
|
|
||||||
export class VoidContainer extends CompositeDisposable {
|
export class VoidContainer extends CompositeDisposable {
|
||||||
private readonly _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
@ -35,14 +35,13 @@ export class VoidContainer extends CompositeDisposable {
|
|||||||
|
|
||||||
this._element = document.createElement('div');
|
this._element = document.createElement('div');
|
||||||
|
|
||||||
this._element.className = 'void-container';
|
this._element.className = 'dv-void-container';
|
||||||
this._element.tabIndex = 0;
|
|
||||||
this._element.draggable = true;
|
this._element.draggable = true;
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this._onDrop,
|
this._onDrop,
|
||||||
this._onDragStart,
|
this._onDragStart,
|
||||||
addDisposableListener(this._element, 'click', () => {
|
addDisposableListener(this._element, 'pointerdown', () => {
|
||||||
this.accessor.doSetGroupActive(this.group);
|
this.accessor.doSetGroupActive(this.group);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -55,16 +54,7 @@ export class VoidContainer extends CompositeDisposable {
|
|||||||
const data = getPanelData();
|
const data = getPanelData();
|
||||||
|
|
||||||
if (data && this.accessor.id === data.viewId) {
|
if (data && this.accessor.id === data.viewId) {
|
||||||
if (
|
return true;
|
||||||
data.panelId === null &&
|
|
||||||
data.groupId === this.group.id
|
|
||||||
) {
|
|
||||||
// don't allow group move to drop on self
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't show the overlay if the tab being dragged is the last panel of this group
|
|
||||||
return last(this.group.panels)?.id !== data.panelId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return group.model.canDisplayOverlay(
|
return group.model.canDisplayOverlay(
|
||||||
@ -73,6 +63,7 @@ export class VoidContainer extends CompositeDisposable {
|
|||||||
'header_space'
|
'header_space'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
getOverrideTarget: () => group.model.dropTargetContainer?.model,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.onWillShowOverlay = this.dropTraget.onWillShowOverlay;
|
this.onWillShowOverlay = this.dropTraget.onWillShowOverlay;
|
||||||
|
@ -1,42 +1,4 @@
|
|||||||
.watermark {
|
.dv-watermark {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
height: 100%;
|
||||||
|
|
||||||
&.has-actions {
|
|
||||||
.watermark-title {
|
|
||||||
.actions-container {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.watermark-title {
|
|
||||||
height: 35px;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.watermark-content {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0px 8px;
|
|
||||||
|
|
||||||
.close-action {
|
|
||||||
padding: 4px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--dv-activegroup-hiddenpanel-tab-color);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-radius: 2px;
|
|
||||||
background-color: var(--dv-icon-hover-background-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,13 @@ import {
|
|||||||
IWatermarkRenderer,
|
IWatermarkRenderer,
|
||||||
WatermarkRendererInitParameters,
|
WatermarkRendererInitParameters,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
import { addDisposableListener } from '../../../events';
|
|
||||||
import { toggleClass } from '../../../dom';
|
|
||||||
import { CompositeDisposable } from '../../../lifecycle';
|
import { CompositeDisposable } from '../../../lifecycle';
|
||||||
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
|
|
||||||
import { PanelUpdateEvent } from '../../../panel/types';
|
|
||||||
import { createCloseButton } from '../../../svg';
|
|
||||||
import { DockviewApi } from '../../../api/component.api';
|
|
||||||
|
|
||||||
export class Watermark
|
export class Watermark
|
||||||
extends CompositeDisposable
|
extends CompositeDisposable
|
||||||
implements IWatermarkRenderer
|
implements IWatermarkRenderer
|
||||||
{
|
{
|
||||||
private _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
private _group: DockviewGroupPanel | undefined;
|
|
||||||
private _api: DockviewApi | undefined;
|
|
||||||
|
|
||||||
get element(): HTMLElement {
|
get element(): HTMLElement {
|
||||||
return this._element;
|
return this._element;
|
||||||
@ -25,70 +17,10 @@ export class Watermark
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._element = document.createElement('div');
|
this._element = document.createElement('div');
|
||||||
this._element.className = 'watermark';
|
this._element.className = 'dv-watermark';
|
||||||
|
|
||||||
const title = document.createElement('div');
|
|
||||||
title.className = 'watermark-title';
|
|
||||||
|
|
||||||
const emptySpace = document.createElement('span');
|
|
||||||
emptySpace.style.flexGrow = '1';
|
|
||||||
|
|
||||||
const content = document.createElement('div');
|
|
||||||
content.className = 'watermark-content';
|
|
||||||
|
|
||||||
this._element.appendChild(title);
|
|
||||||
this._element.appendChild(content);
|
|
||||||
|
|
||||||
const actionsContainer = document.createElement('div');
|
|
||||||
actionsContainer.className = 'actions-container';
|
|
||||||
|
|
||||||
const closeAnchor = document.createElement('div');
|
|
||||||
closeAnchor.className = 'close-action';
|
|
||||||
closeAnchor.appendChild(createCloseButton());
|
|
||||||
|
|
||||||
actionsContainer.appendChild(closeAnchor);
|
|
||||||
|
|
||||||
title.appendChild(emptySpace);
|
|
||||||
title.appendChild(actionsContainer);
|
|
||||||
|
|
||||||
this.addDisposables(
|
|
||||||
addDisposableListener(closeAnchor, 'click', (ev) => {
|
|
||||||
ev.preventDefault();
|
|
||||||
if (this._group) {
|
|
||||||
this._api?.removeGroup(this._group);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
update(_event: PanelUpdateEvent): void {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
focus(): void {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
layout(_width: number, _height: number): void {
|
|
||||||
// noop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_params: WatermarkRendererInitParameters): void {
|
init(_params: WatermarkRendererInitParameters): void {
|
||||||
this._api = _params.containerApi;
|
// noop
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateParentGroup(group: DockviewGroupPanel, _visible: boolean): void {
|
|
||||||
this._group = group;
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose(): void {
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private render(): void {
|
|
||||||
const isOneGroup = !!(this._api && this._api.size <= 1);
|
|
||||||
toggleClass(this.element, 'has-actions', isOneGroup);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,10 @@ export class DefaultDockviewDeserialzier implements IPanelDeserializer {
|
|||||||
view,
|
view,
|
||||||
{
|
{
|
||||||
renderer: panelData.renderer,
|
renderer: panelData.renderer,
|
||||||
|
minimumWidth: panelData.minimumWidth,
|
||||||
|
minimumHeight: panelData.minimumHeight,
|
||||||
|
maximumWidth: panelData.maximumWidth,
|
||||||
|
maximumHeight: panelData.maximumHeight,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -14,44 +14,19 @@
|
|||||||
.dv-overlay-render-container {
|
.dv-overlay-render-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.split-view-container {
|
|
||||||
&.horizontal {
|
|
||||||
> .view-container > .view {
|
|
||||||
&:not(:last-child) {
|
|
||||||
border-right: var(--dv-group-gap-size) solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(:first-child) {
|
|
||||||
border-left: var(--dv-group-gap-size) solid transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.vertical {
|
|
||||||
> .view-container > .view {
|
|
||||||
&:not(:last-child) {
|
|
||||||
border-bottom: var(--dv-group-gap-size) solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(:first-child) {
|
|
||||||
border-top: var(--dv-group-gap-size) solid transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.groupview {
|
.dv-groupview {
|
||||||
&.active-group {
|
&.dv-active-group {
|
||||||
> .tabs-and-actions-container > .tabs-container > .tab {
|
> .dv-tabs-and-actions-container {
|
||||||
&.active-tab {
|
.dv-tabs-container > .dv-tab {
|
||||||
|
&.dv-active-tab {
|
||||||
background-color: var(
|
background-color: var(
|
||||||
--dv-activegroup-visiblepanel-tab-background-color
|
--dv-activegroup-visiblepanel-tab-background-color
|
||||||
);
|
);
|
||||||
color: var(--dv-activegroup-visiblepanel-tab-color);
|
color: var(--dv-activegroup-visiblepanel-tab-color);
|
||||||
}
|
}
|
||||||
&.inactive-tab {
|
&.dv-inactive-tab {
|
||||||
background-color: var(
|
background-color: var(
|
||||||
--dv-activegroup-hiddenpanel-tab-background-color
|
--dv-activegroup-hiddenpanel-tab-background-color
|
||||||
);
|
);
|
||||||
@ -59,15 +34,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.inactive-group {
|
}
|
||||||
> .tabs-and-actions-container > .tabs-container > .tab {
|
&.dv-inactive-group {
|
||||||
&.active-tab {
|
> .dv-tabs-and-actions-container {
|
||||||
|
.dv-tabs-container > .dv-tab {
|
||||||
|
&.dv-active-tab {
|
||||||
background-color: var(
|
background-color: var(
|
||||||
--dv-inactivegroup-visiblepanel-tab-background-color
|
--dv-inactivegroup-visiblepanel-tab-background-color
|
||||||
);
|
);
|
||||||
color: var(--dv-inactivegroup-visiblepanel-tab-color);
|
color: var(--dv-inactivegroup-visiblepanel-tab-color);
|
||||||
}
|
}
|
||||||
&.inactive-tab {
|
&.dv-inactive-tab {
|
||||||
background-color: var(
|
background-color: var(
|
||||||
--dv-inactivegroup-hiddenpanel-tab-background-color
|
--dv-inactivegroup-hiddenpanel-tab-background-color
|
||||||
);
|
);
|
||||||
@ -75,13 +52,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* when a tab is dragged we lose the above stylings because they are conditional on parent elements
|
* when a tab is dragged we lose the above stylings because they are conditional on parent elements
|
||||||
* therefore we also set some stylings for the dragging event
|
* therefore we also set some stylings for the dragging event
|
||||||
**/
|
**/
|
||||||
.tab {
|
.dv-tab {
|
||||||
&.dv-tab-dragging {
|
&.dv-tab-dragging {
|
||||||
background-color: var(
|
background-color: var(
|
||||||
--dv-activegroup-visiblepanel-tab-background-color
|
--dv-activegroup-visiblepanel-tab-background-color
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,11 @@
|
|||||||
import { Overlay } from '../dnd/overlay';
|
import { Overlay } from '../overlay/overlay';
|
||||||
import { CompositeDisposable } from '../lifecycle';
|
import { CompositeDisposable } from '../lifecycle';
|
||||||
|
import { AnchoredBox } from '../types';
|
||||||
import { DockviewGroupPanel, IDockviewGroupPanel } from './dockviewGroupPanel';
|
import { DockviewGroupPanel, IDockviewGroupPanel } from './dockviewGroupPanel';
|
||||||
|
|
||||||
export interface IDockviewFloatingGroupPanel {
|
export interface IDockviewFloatingGroupPanel {
|
||||||
readonly group: IDockviewGroupPanel;
|
readonly group: IDockviewGroupPanel;
|
||||||
position(
|
position(bounds: Partial<AnchoredBox>): void;
|
||||||
bounds: Partial<{
|
|
||||||
top: number;
|
|
||||||
left: number;
|
|
||||||
height: number;
|
|
||||||
width: number;
|
|
||||||
}>
|
|
||||||
): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DockviewFloatingGroupPanel
|
export class DockviewFloatingGroupPanel
|
||||||
@ -20,18 +14,10 @@ export class DockviewFloatingGroupPanel
|
|||||||
{
|
{
|
||||||
constructor(readonly group: DockviewGroupPanel, readonly overlay: Overlay) {
|
constructor(readonly group: DockviewGroupPanel, readonly overlay: Overlay) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.addDisposables(overlay);
|
this.addDisposables(overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
position(
|
position(bounds: Partial<AnchoredBox>): void {
|
||||||
bounds: Partial<{
|
|
||||||
top: number;
|
|
||||||
left: number;
|
|
||||||
height: number;
|
|
||||||
width: number;
|
|
||||||
}>
|
|
||||||
): void {
|
|
||||||
this.overlay.setBounds(bounds);
|
this.overlay.setBounds(bounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
.groupview {
|
.dv-groupview {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -9,13 +9,7 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.empty {
|
> .dv-content-container {
|
||||||
> .tabs-and-actions-container {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .content-container {
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
@ -34,6 +34,38 @@ export class DockviewGroupPanel
|
|||||||
{
|
{
|
||||||
private readonly _model: DockviewGroupPanelModel;
|
private readonly _model: DockviewGroupPanelModel;
|
||||||
|
|
||||||
|
override get minimumWidth(): number {
|
||||||
|
const activePanelMinimumWidth = this.activePanel?.minimumWidth;
|
||||||
|
if (typeof activePanelMinimumWidth === 'number') {
|
||||||
|
return activePanelMinimumWidth;
|
||||||
|
}
|
||||||
|
return super.__minimumWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
override get minimumHeight(): number {
|
||||||
|
const activePanelMinimumHeight = this.activePanel?.minimumHeight;
|
||||||
|
if (typeof activePanelMinimumHeight === 'number') {
|
||||||
|
return activePanelMinimumHeight;
|
||||||
|
}
|
||||||
|
return super.__minimumHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
override get maximumWidth(): number {
|
||||||
|
const activePanelMaximumWidth = this.activePanel?.maximumWidth;
|
||||||
|
if (typeof activePanelMaximumWidth === 'number') {
|
||||||
|
return activePanelMaximumWidth;
|
||||||
|
}
|
||||||
|
return super.__maximumWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
override get maximumHeight(): number {
|
||||||
|
const activePanelMaximumHeight = this.activePanel?.maximumHeight;
|
||||||
|
if (typeof activePanelMaximumHeight === 'number') {
|
||||||
|
return activePanelMaximumHeight;
|
||||||
|
}
|
||||||
|
return super.__maximumHeight();
|
||||||
|
}
|
||||||
|
|
||||||
get panels(): IDockviewPanel[] {
|
get panels(): IDockviewPanel[] {
|
||||||
return this._model.panels;
|
return this._model.panels;
|
||||||
}
|
}
|
||||||
@ -71,8 +103,14 @@ export class DockviewGroupPanel
|
|||||||
id,
|
id,
|
||||||
'groupview_default',
|
'groupview_default',
|
||||||
{
|
{
|
||||||
minimumHeight: MINIMUM_DOCKVIEW_GROUP_PANEL_HEIGHT,
|
minimumHeight:
|
||||||
minimumWidth: MINIMUM_DOCKVIEW_GROUP_PANEL_WIDTH,
|
options.constraints?.minimumHeight ??
|
||||||
|
MINIMUM_DOCKVIEW_GROUP_PANEL_HEIGHT,
|
||||||
|
minimumWidth:
|
||||||
|
options.constraints?.maximumHeight ??
|
||||||
|
MINIMUM_DOCKVIEW_GROUP_PANEL_WIDTH,
|
||||||
|
maximumHeight: options.constraints?.maximumHeight,
|
||||||
|
maximumWidth: options.constraints?.maximumWidth,
|
||||||
},
|
},
|
||||||
new DockviewGroupPanelApiImpl(id, accessor)
|
new DockviewGroupPanelApiImpl(id, accessor)
|
||||||
);
|
);
|
||||||
@ -86,6 +124,12 @@ export class DockviewGroupPanel
|
|||||||
options,
|
options,
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.addDisposables(
|
||||||
|
this.model.onDidActivePanelChange((event) => {
|
||||||
|
this.api._onDidActivePanelChange.fire(event);
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
override focus(): void {
|
override focus(): void {
|
||||||
|
@ -36,8 +36,10 @@ import {
|
|||||||
DockviewUnhandledDragOverEvent,
|
DockviewUnhandledDragOverEvent,
|
||||||
IHeaderActionsRenderer,
|
IHeaderActionsRenderer,
|
||||||
} from './options';
|
} from './options';
|
||||||
import { OverlayRenderContainer } from '../overlayRenderContainer';
|
import { OverlayRenderContainer } from '../overlay/overlayRenderContainer';
|
||||||
import { TitleEvent } from '../api/dockviewPanelApi';
|
import { TitleEvent } from '../api/dockviewPanelApi';
|
||||||
|
import { Contraints } from '../gridview/gridviewPanel';
|
||||||
|
import { DropTargetAnchorContainer } from '../dnd/dropTargetAnchorContainer';
|
||||||
|
|
||||||
interface GroupMoveEvent {
|
interface GroupMoveEvent {
|
||||||
groupId: string;
|
groupId: string;
|
||||||
@ -50,6 +52,9 @@ interface CoreGroupOptions {
|
|||||||
locked?: DockviewGroupPanelLocked;
|
locked?: DockviewGroupPanelLocked;
|
||||||
hideHeader?: boolean;
|
hideHeader?: boolean;
|
||||||
skipSetActive?: boolean;
|
skipSetActive?: boolean;
|
||||||
|
constraints?: Partial<Contraints>;
|
||||||
|
initialWidth?: number;
|
||||||
|
initialHeight?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupOptions extends CoreGroupOptions {
|
export interface GroupOptions extends CoreGroupOptions {
|
||||||
@ -192,7 +197,7 @@ export interface IDockviewGroupPanelModel extends IPanel {
|
|||||||
export type DockviewGroupLocation =
|
export type DockviewGroupLocation =
|
||||||
| { type: 'grid' }
|
| { type: 'grid' }
|
||||||
| { type: 'floating' }
|
| { type: 'floating' }
|
||||||
| { type: 'popout'; getWindow: () => Window };
|
| { type: 'popout'; getWindow: () => Window; popoutUrl?: string };
|
||||||
|
|
||||||
export class WillShowOverlayLocationEvent implements IDockviewEvent {
|
export class WillShowOverlayLocationEvent implements IDockviewEvent {
|
||||||
get kind(): DockviewGroupDropLocation {
|
get kind(): DockviewGroupDropLocation {
|
||||||
@ -260,6 +265,9 @@ export class DockviewGroupPanelModel
|
|||||||
private _location: DockviewGroupLocation = { type: 'grid' };
|
private _location: DockviewGroupLocation = { type: 'grid' };
|
||||||
|
|
||||||
private mostRecentlyUsed: IDockviewPanel[] = [];
|
private mostRecentlyUsed: IDockviewPanel[] = [];
|
||||||
|
private _overwriteRenderContainer: OverlayRenderContainer | null = null;
|
||||||
|
private _overwriteDropTargetContainer: DropTargetAnchorContainer | null =
|
||||||
|
null;
|
||||||
|
|
||||||
private readonly _onDidChange = new Emitter<IViewSize | undefined>();
|
private readonly _onDidChange = new Emitter<IViewSize | undefined>();
|
||||||
readonly onDidChange: Event<IViewSize | undefined> =
|
readonly onDidChange: Event<IViewSize | undefined> =
|
||||||
@ -268,7 +276,7 @@ export class DockviewGroupPanelModel
|
|||||||
private _width = 0;
|
private _width = 0;
|
||||||
private _height = 0;
|
private _height = 0;
|
||||||
|
|
||||||
private _panels: IDockviewPanel[] = [];
|
private readonly _panels: IDockviewPanel[] = [];
|
||||||
private readonly _panelDisposables = new Map<string, IDisposable>();
|
private readonly _panelDisposables = new Map<string, IDisposable>();
|
||||||
|
|
||||||
private readonly _onMove = new Emitter<GroupMoveEvent>();
|
private readonly _onMove = new Emitter<GroupMoveEvent>();
|
||||||
@ -322,7 +330,7 @@ export class DockviewGroupPanelModel
|
|||||||
private readonly _api: DockviewApi;
|
private readonly _api: DockviewApi;
|
||||||
|
|
||||||
get element(): HTMLElement {
|
get element(): HTMLElement {
|
||||||
throw new Error('not supported');
|
throw new Error('dockview: not supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
get activePanel(): IDockviewPanel | undefined {
|
get activePanel(): IDockviewPanel | undefined {
|
||||||
@ -338,7 +346,7 @@ export class DockviewGroupPanelModel
|
|||||||
|
|
||||||
toggleClass(
|
toggleClass(
|
||||||
this.container,
|
this.container,
|
||||||
'locked-groupview',
|
'dv-locked-groupview',
|
||||||
value === 'no-drop-target' || value
|
value === 'no-drop-target' || value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -425,14 +433,14 @@ export class DockviewGroupPanelModel
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly container: HTMLElement,
|
private readonly container: HTMLElement,
|
||||||
private accessor: DockviewComponent,
|
private readonly accessor: DockviewComponent,
|
||||||
public id: string,
|
public id: string,
|
||||||
private readonly options: GroupOptions,
|
private readonly options: GroupOptions,
|
||||||
private readonly groupPanel: DockviewGroupPanel
|
private readonly groupPanel: DockviewGroupPanel
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
toggleClass(this.container, 'groupview', true);
|
toggleClass(this.container, 'dv-groupview', true);
|
||||||
|
|
||||||
this._api = new DockviewApi(this.accessor);
|
this._api = new DockviewApi(this.accessor);
|
||||||
|
|
||||||
@ -501,7 +509,9 @@ export class DockviewGroupPanelModel
|
|||||||
this._onDidAddPanel,
|
this._onDidAddPanel,
|
||||||
this._onDidRemovePanel,
|
this._onDidRemovePanel,
|
||||||
this._onDidActivePanelChange,
|
this._onDidActivePanelChange,
|
||||||
this._onUnhandledDragOverEvent
|
this._onUnhandledDragOverEvent,
|
||||||
|
this._onDidPanelTitleChange,
|
||||||
|
this._onDidPanelParametersChange
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,8 +519,6 @@ export class DockviewGroupPanelModel
|
|||||||
this.contentContainer.element.focus();
|
this.contentContainer.element.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _overwriteRenderContainer: OverlayRenderContainer | null = null;
|
|
||||||
|
|
||||||
set renderContainer(value: OverlayRenderContainer | null) {
|
set renderContainer(value: OverlayRenderContainer | null) {
|
||||||
this.panels.forEach((panel) => {
|
this.panels.forEach((panel) => {
|
||||||
this.renderContainer.detatch(panel);
|
this.renderContainer.detatch(panel);
|
||||||
@ -530,6 +538,17 @@ export class DockviewGroupPanelModel
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set dropTargetContainer(value: DropTargetAnchorContainer | null) {
|
||||||
|
this._overwriteDropTargetContainer = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get dropTargetContainer(): DropTargetAnchorContainer | null {
|
||||||
|
return (
|
||||||
|
this._overwriteDropTargetContainer ??
|
||||||
|
this.accessor.rootDropTargetContainer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
initialize(): void {
|
initialize(): void {
|
||||||
if (this.options.panels) {
|
if (this.options.panels) {
|
||||||
this.options.panels.forEach((panel) => {
|
this.options.panels.forEach((panel) => {
|
||||||
@ -784,7 +803,15 @@ export class DockviewGroupPanelModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private doClose(panel: IDockviewPanel): void {
|
private doClose(panel: IDockviewPanel): void {
|
||||||
this.accessor.removePanel(panel);
|
const isLast =
|
||||||
|
this.panels.length === 1 && this.accessor.groups.length === 1;
|
||||||
|
|
||||||
|
this.accessor.removePanel(
|
||||||
|
panel,
|
||||||
|
isLast && this.accessor.options.noPanelsOverlay === 'emptyGroup'
|
||||||
|
? { removeEmptyGroup: false }
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public isPanelActive(panel: IDockviewPanel): boolean {
|
public isPanelActive(panel: IDockviewPanel): boolean {
|
||||||
@ -802,8 +829,8 @@ export class DockviewGroupPanelModel
|
|||||||
|
|
||||||
this._isGroupActive = isGroupActive;
|
this._isGroupActive = isGroupActive;
|
||||||
|
|
||||||
toggleClass(this.container, 'active-group', isGroupActive);
|
toggleClass(this.container, 'dv-active-group', isGroupActive);
|
||||||
toggleClass(this.container, 'inactive-group', !isGroupActive);
|
toggleClass(this.container, 'dv-inactive-group', !isGroupActive);
|
||||||
|
|
||||||
this.tabsContainer.setActive(this.isActive);
|
this.tabsContainer.setActive(this.isActive);
|
||||||
|
|
||||||
@ -952,8 +979,6 @@ export class DockviewGroupPanelModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateContainer(): void {
|
private updateContainer(): void {
|
||||||
toggleClass(this.container, 'empty', this.isEmpty);
|
|
||||||
|
|
||||||
this.panels.forEach((panel) => panel.runEvents());
|
this.panels.forEach((panel) => panel.runEvents());
|
||||||
|
|
||||||
if (this.isEmpty && !this.watermark) {
|
if (this.isEmpty && !this.watermark) {
|
||||||
@ -964,22 +989,18 @@ export class DockviewGroupPanelModel
|
|||||||
});
|
});
|
||||||
this.watermark = watermark;
|
this.watermark = watermark;
|
||||||
|
|
||||||
addDisposableListener(this.watermark.element, 'click', () => {
|
addDisposableListener(this.watermark.element, 'pointerdown', () => {
|
||||||
if (!this.isActive) {
|
if (!this.isActive) {
|
||||||
this.accessor.doSetGroupActive(this.groupPanel);
|
this.accessor.doSetGroupActive(this.groupPanel);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tabsContainer.hide();
|
|
||||||
this.contentContainer.element.appendChild(this.watermark.element);
|
this.contentContainer.element.appendChild(this.watermark.element);
|
||||||
|
|
||||||
this.watermark.updateParentGroup(this.groupPanel, true);
|
|
||||||
}
|
}
|
||||||
if (!this.isEmpty && this.watermark) {
|
if (!this.isEmpty && this.watermark) {
|
||||||
this.watermark.element.remove();
|
this.watermark.element.remove();
|
||||||
this.watermark.dispose?.();
|
this.watermark.dispose?.();
|
||||||
this.watermark = undefined;
|
this.watermark = undefined;
|
||||||
this.tabsContainer.show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -993,7 +1014,7 @@ export class DockviewGroupPanelModel
|
|||||||
target,
|
target,
|
||||||
position,
|
position,
|
||||||
getPanelData,
|
getPanelData,
|
||||||
this.accessor.getPanel(this.id)!
|
this.accessor.getPanel(this.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
this._onUnhandledDragOverEvent.fire(firedEvent);
|
this._onUnhandledDragOverEvent.fire(firedEvent);
|
||||||
@ -1042,6 +1063,29 @@ export class DockviewGroupPanelModel
|
|||||||
const data = getPanelData();
|
const data = getPanelData();
|
||||||
|
|
||||||
if (data && data.viewId === this.accessor.id) {
|
if (data && data.viewId === this.accessor.id) {
|
||||||
|
if (type === 'content') {
|
||||||
|
if (data.groupId === this.id) {
|
||||||
|
// don't allow to drop on self for center position
|
||||||
|
|
||||||
|
if (position === 'center') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.panelId === null) {
|
||||||
|
// don't allow group move to drop anywhere on self
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'header') {
|
||||||
|
if (data.groupId === this.id) {
|
||||||
|
if (data.panelId === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (data.panelId === null) {
|
if (data.panelId === null) {
|
||||||
// this is a group move dnd event
|
// this is a group move dnd event
|
||||||
const { groupId } = data;
|
const { groupId } = data;
|
||||||
|
@ -9,8 +9,9 @@ import { CompositeDisposable, IDisposable } from '../lifecycle';
|
|||||||
import { IPanel, PanelUpdateEvent, Parameters } from '../panel/types';
|
import { IPanel, PanelUpdateEvent, Parameters } from '../panel/types';
|
||||||
import { IDockviewPanelModel } from './dockviewPanelModel';
|
import { IDockviewPanelModel } from './dockviewPanelModel';
|
||||||
import { DockviewComponent } from './dockviewComponent';
|
import { DockviewComponent } from './dockviewComponent';
|
||||||
import { DockviewPanelRenderer } from '../overlayRenderContainer';
|
import { DockviewPanelRenderer } from '../overlay/overlayRenderContainer';
|
||||||
import { WillFocusEvent } from '../api/panelApi';
|
import { WillFocusEvent } from '../api/panelApi';
|
||||||
|
import { Contraints } from '../gridview/gridviewPanel';
|
||||||
|
|
||||||
export interface IDockviewPanel extends IDisposable, IPanel {
|
export interface IDockviewPanel extends IDisposable, IPanel {
|
||||||
readonly view: IDockviewPanelModel;
|
readonly view: IDockviewPanelModel;
|
||||||
@ -18,6 +19,10 @@ export interface IDockviewPanel extends IDisposable, IPanel {
|
|||||||
readonly api: DockviewPanelApi;
|
readonly api: DockviewPanelApi;
|
||||||
readonly title: string | undefined;
|
readonly title: string | undefined;
|
||||||
readonly params: Parameters | undefined;
|
readonly params: Parameters | undefined;
|
||||||
|
readonly minimumWidth?: number;
|
||||||
|
readonly minimumHeight?: number;
|
||||||
|
readonly maximumWidth?: number;
|
||||||
|
readonly maximumHeight?: number;
|
||||||
updateParentGroup(
|
updateParentGroup(
|
||||||
group: DockviewGroupPanel,
|
group: DockviewGroupPanel,
|
||||||
options?: { skipSetActive?: boolean }
|
options?: { skipSetActive?: boolean }
|
||||||
@ -40,6 +45,11 @@ export class DockviewPanel
|
|||||||
private _title: string | undefined;
|
private _title: string | undefined;
|
||||||
private _renderer: DockviewPanelRenderer | undefined;
|
private _renderer: DockviewPanelRenderer | undefined;
|
||||||
|
|
||||||
|
private readonly _minimumWidth: number | undefined;
|
||||||
|
private readonly _minimumHeight: number | undefined;
|
||||||
|
private readonly _maximumWidth: number | undefined;
|
||||||
|
private readonly _maximumHeight: number | undefined;
|
||||||
|
|
||||||
get params(): Parameters | undefined {
|
get params(): Parameters | undefined {
|
||||||
return this._params;
|
return this._params;
|
||||||
}
|
}
|
||||||
@ -56,6 +66,22 @@ export class DockviewPanel
|
|||||||
return this._renderer ?? this.accessor.renderer;
|
return this._renderer ?? this.accessor.renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get minimumWidth(): number | undefined {
|
||||||
|
return this._minimumWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
get minimumHeight(): number | undefined {
|
||||||
|
return this._minimumHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
get maximumWidth(): number | undefined {
|
||||||
|
return this._maximumWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
get maximumHeight(): number | undefined {
|
||||||
|
return this._maximumHeight;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly id: string,
|
public readonly id: string,
|
||||||
component: string,
|
component: string,
|
||||||
@ -64,11 +90,15 @@ export class DockviewPanel
|
|||||||
private readonly containerApi: DockviewApi,
|
private readonly containerApi: DockviewApi,
|
||||||
group: DockviewGroupPanel,
|
group: DockviewGroupPanel,
|
||||||
readonly view: IDockviewPanelModel,
|
readonly view: IDockviewPanelModel,
|
||||||
options: { renderer?: DockviewPanelRenderer }
|
options: { renderer?: DockviewPanelRenderer } & Partial<Contraints>
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._renderer = options.renderer;
|
this._renderer = options.renderer;
|
||||||
this._group = group;
|
this._group = group;
|
||||||
|
this._minimumWidth = options.minimumWidth;
|
||||||
|
this._minimumHeight = options.minimumHeight;
|
||||||
|
this._maximumWidth = options.maximumWidth;
|
||||||
|
this._maximumHeight = options.maximumHeight;
|
||||||
|
|
||||||
this.api = new DockviewPanelApiImpl(
|
this.api = new DockviewPanelApiImpl(
|
||||||
this,
|
this,
|
||||||
@ -87,7 +117,7 @@ export class DockviewPanel
|
|||||||
// you are actually just resizing the panels parent which is the group
|
// you are actually just resizing the panels parent which is the group
|
||||||
this.group.api.setSize(event);
|
this.group.api.setSize(event);
|
||||||
}),
|
}),
|
||||||
this.api.onDidRendererChange((event) => {
|
this.api.onDidRendererChange(() => {
|
||||||
this.group.model.rerender(this);
|
this.group.model.rerender(this);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -129,6 +159,10 @@ export class DockviewPanel
|
|||||||
: undefined,
|
: undefined,
|
||||||
title: this.title,
|
title: this.title,
|
||||||
renderer: this._renderer,
|
renderer: this._renderer,
|
||||||
|
minimumHeight: this._minimumHeight,
|
||||||
|
maximumHeight: this._maximumHeight,
|
||||||
|
minimumWidth: this._minimumWidth,
|
||||||
|
maximumWidth: this._maximumWidth,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,13 +171,6 @@ export class DockviewPanel
|
|||||||
|
|
||||||
if (didTitleChange) {
|
if (didTitleChange) {
|
||||||
this._title = title;
|
this._title = title;
|
||||||
|
|
||||||
this.view.update({
|
|
||||||
params: {
|
|
||||||
params: this._params,
|
|
||||||
title: this.title,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.api._onDidTitleChange.fire({ title });
|
this.api._onDidTitleChange.fire({ title });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,10 +205,7 @@ export class DockviewPanel
|
|||||||
|
|
||||||
// update the view with the updated props
|
// update the view with the updated props
|
||||||
this.view.update({
|
this.view.update({
|
||||||
params: {
|
|
||||||
params: this._params,
|
params: this._params,
|
||||||
title: this.title,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,26 +4,29 @@ import {
|
|||||||
IContentRenderer,
|
IContentRenderer,
|
||||||
ITabRenderer,
|
ITabRenderer,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { DockviewGroupPanel } from './dockviewGroupPanel';
|
|
||||||
import { IDisposable } from '../lifecycle';
|
import { IDisposable } from '../lifecycle';
|
||||||
import { IDockviewComponent } from './dockviewComponent';
|
import { IDockviewComponent } from './dockviewComponent';
|
||||||
import { PanelUpdateEvent } from '../panel/types';
|
import { PanelUpdateEvent } from '../panel/types';
|
||||||
|
import { TabLocation } from './framework';
|
||||||
|
|
||||||
export interface IDockviewPanelModel extends IDisposable {
|
export interface IDockviewPanelModel extends IDisposable {
|
||||||
readonly contentComponent: string;
|
readonly contentComponent: string;
|
||||||
readonly tabComponent?: string;
|
readonly tabComponent?: string;
|
||||||
readonly content: IContentRenderer;
|
readonly content: IContentRenderer;
|
||||||
readonly tab?: ITabRenderer;
|
readonly tab: ITabRenderer;
|
||||||
update(event: PanelUpdateEvent): void;
|
update(event: PanelUpdateEvent): void;
|
||||||
layout(width: number, height: number): void;
|
layout(width: number, height: number): void;
|
||||||
init(params: GroupPanelPartInitParameters): void;
|
init(params: GroupPanelPartInitParameters): void;
|
||||||
updateParentGroup(group: DockviewGroupPanel, isPanelVisible: boolean): void;
|
createTabRenderer(tabLocation: TabLocation): ITabRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DockviewPanelModel implements IDockviewPanelModel {
|
export class DockviewPanelModel implements IDockviewPanelModel {
|
||||||
private readonly _content: IContentRenderer;
|
private readonly _content: IContentRenderer;
|
||||||
private readonly _tab: ITabRenderer;
|
private readonly _tab: ITabRenderer;
|
||||||
|
|
||||||
|
private _params: GroupPanelPartInitParameters | undefined;
|
||||||
|
private _updateEvent: PanelUpdateEvent | undefined;
|
||||||
|
|
||||||
get content(): IContentRenderer {
|
get content(): IContentRenderer {
|
||||||
return this._content;
|
return this._content;
|
||||||
}
|
}
|
||||||
@ -42,16 +45,23 @@ export class DockviewPanelModel implements IDockviewPanelModel {
|
|||||||
this._tab = this.createTabComponent(this.id, tabComponent);
|
this._tab = this.createTabComponent(this.id, tabComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
init(params: GroupPanelPartInitParameters): void {
|
createTabRenderer(tabLocation: TabLocation): ITabRenderer {
|
||||||
this.content.init(params);
|
const cmp = this.createTabComponent(this.id, this.tabComponent);
|
||||||
this.tab.init(params);
|
if (this._params) {
|
||||||
|
cmp.init({ ...this._params, tabLocation });
|
||||||
|
}
|
||||||
|
if (this._updateEvent) {
|
||||||
|
cmp.update?.(this._updateEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParentGroup(
|
return cmp;
|
||||||
_group: DockviewGroupPanel,
|
}
|
||||||
_isPanelVisible: boolean
|
|
||||||
): void {
|
init(params: GroupPanelPartInitParameters): void {
|
||||||
// noop
|
this._params = params;
|
||||||
|
|
||||||
|
this.content.init(params);
|
||||||
|
this.tab.init({ ...params, tabLocation: 'header' });
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(width: number, height: number): void {
|
layout(width: number, height: number): void {
|
||||||
@ -59,6 +69,8 @@ export class DockviewPanelModel implements IDockviewPanelModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update(event: PanelUpdateEvent): void {
|
update(event: PanelUpdateEvent): void {
|
||||||
|
this._updateEvent = event;
|
||||||
|
|
||||||
this.content.update?.(event);
|
this.content.update?.(event);
|
||||||
this.tab.update?.(event);
|
this.tab.update?.(event);
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,11 @@ export interface IGroupPanelBaseProps<T extends { [index: string]: any } = any>
|
|||||||
containerApi: DockviewApi;
|
containerApi: DockviewApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TabLocation = 'header' | 'headerOverflow';
|
||||||
|
|
||||||
export type IDockviewPanelHeaderProps<
|
export type IDockviewPanelHeaderProps<
|
||||||
T extends { [index: string]: any } = any
|
T extends { [index: string]: any } = any
|
||||||
> = IGroupPanelBaseProps<T>;
|
> = IGroupPanelBaseProps<T> & { tabLocation: TabLocation };
|
||||||
|
|
||||||
export type IDockviewPanelProps<T extends { [index: string]: any } = any> =
|
export type IDockviewPanelProps<T extends { [index: string]: any } = any> =
|
||||||
IGroupPanelBaseProps<T>;
|
IGroupPanelBaseProps<T>;
|
||||||
|
@ -12,8 +12,12 @@ import {
|
|||||||
GroupOptions,
|
GroupOptions,
|
||||||
} from './dockviewGroupPanelModel';
|
} from './dockviewGroupPanelModel';
|
||||||
import { IDockviewPanel } from './dockviewPanel';
|
import { IDockviewPanel } from './dockviewPanel';
|
||||||
import { DockviewPanelRenderer } from '../overlayRenderContainer';
|
import { DockviewPanelRenderer } from '../overlay/overlayRenderContainer';
|
||||||
import { IGroupHeaderProps } from './framework';
|
import { IGroupHeaderProps } from './framework';
|
||||||
|
import { FloatingGroupOptions } from './dockviewComponent';
|
||||||
|
import { Contraints } from '../gridview/gridviewPanel';
|
||||||
|
import { AcceptableEvent, IAcceptableEvent } from '../events';
|
||||||
|
import { DockviewTheme } from './theme';
|
||||||
|
|
||||||
export interface IHeaderActionsRenderer extends IDisposable {
|
export interface IHeaderActionsRenderer extends IDisposable {
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
@ -32,6 +36,10 @@ export interface ViewFactoryData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DockviewOptions {
|
export interface DockviewOptions {
|
||||||
|
/**
|
||||||
|
* Disable the auto-resizing which is controlled through a `ResizeObserver`.
|
||||||
|
* Call `.layout(width, height)` to manually resize the container.
|
||||||
|
*/
|
||||||
disableAutoResizing?: boolean;
|
disableAutoResizing?: boolean;
|
||||||
hideBorders?: boolean;
|
hideBorders?: boolean;
|
||||||
singleTabMode?: 'fullwidth' | 'default';
|
singleTabMode?: 'fullwidth' | 'default';
|
||||||
@ -45,43 +53,55 @@ export interface DockviewOptions {
|
|||||||
popoutUrl?: string;
|
popoutUrl?: string;
|
||||||
defaultRenderer?: DockviewPanelRenderer;
|
defaultRenderer?: DockviewPanelRenderer;
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
|
// #start dnd
|
||||||
|
dndEdges?: false | DroptargetOverlayModel;
|
||||||
|
/**
|
||||||
|
* @deprecated use `dndEdges` instead. To be removed in a future version.
|
||||||
|
* */
|
||||||
rootOverlayModel?: DroptargetOverlayModel;
|
rootOverlayModel?: DroptargetOverlayModel;
|
||||||
locked?: boolean;
|
|
||||||
disableDnd?: boolean;
|
disableDnd?: boolean;
|
||||||
|
// #end dnd
|
||||||
|
locked?: boolean;
|
||||||
|
className?: string;
|
||||||
|
/**
|
||||||
|
* Define the behaviour of the dock when there are no panels to display. Defaults to `watermark`.
|
||||||
|
*/
|
||||||
|
noPanelsOverlay?: 'emptyGroup' | 'watermark';
|
||||||
|
theme?: DockviewTheme;
|
||||||
|
disableTabsOverflowList?: boolean;
|
||||||
|
/**
|
||||||
|
* Select `native` to use built-in scrollbar behaviours and `custom` to use an internal implementation
|
||||||
|
* that allows for improved scrollbar overlay UX.
|
||||||
|
*
|
||||||
|
* This is only applied to the tab header section. Defaults to `custom`.
|
||||||
|
*/
|
||||||
|
scrollbars?: 'native' | 'custom';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DockviewDndOverlayEvent {
|
export interface DockviewDndOverlayEvent extends IAcceptableEvent {
|
||||||
nativeEvent: DragEvent;
|
nativeEvent: DragEvent;
|
||||||
target: DockviewGroupDropLocation;
|
target: DockviewGroupDropLocation;
|
||||||
position: Position;
|
position: Position;
|
||||||
group?: DockviewGroupPanel;
|
group?: DockviewGroupPanel;
|
||||||
getData: () => PanelTransfer | undefined;
|
getData: () => PanelTransfer | undefined;
|
||||||
//
|
|
||||||
isAccepted: boolean;
|
|
||||||
accept(): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DockviewUnhandledDragOverEvent implements DockviewDndOverlayEvent {
|
export class DockviewUnhandledDragOverEvent
|
||||||
private _isAccepted = false;
|
extends AcceptableEvent
|
||||||
|
implements DockviewDndOverlayEvent
|
||||||
get isAccepted(): boolean {
|
{
|
||||||
return this._isAccepted;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly nativeEvent: DragEvent,
|
readonly nativeEvent: DragEvent,
|
||||||
readonly target: DockviewGroupDropLocation,
|
readonly target: DockviewGroupDropLocation,
|
||||||
readonly position: Position,
|
readonly position: Position,
|
||||||
readonly getData: () => PanelTransfer | undefined,
|
readonly getData: () => PanelTransfer | undefined,
|
||||||
readonly group?: DockviewGroupPanel
|
readonly group?: DockviewGroupPanel
|
||||||
) {}
|
) {
|
||||||
|
super();
|
||||||
accept(): void {
|
|
||||||
this._isAccepted = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PROPERTY_KEYS: (keyof DockviewOptions)[] = (() => {
|
export const PROPERTY_KEYS_DOCKVIEW: (keyof DockviewOptions)[] = (() => {
|
||||||
/**
|
/**
|
||||||
* by readong the keys from an empty value object TypeScript will error
|
* by readong the keys from an empty value object TypeScript will error
|
||||||
* when we add or remove new properties to `DockviewOptions`
|
* when we add or remove new properties to `DockviewOptions`
|
||||||
@ -98,13 +118,29 @@ export const PROPERTY_KEYS: (keyof DockviewOptions)[] = (() => {
|
|||||||
rootOverlayModel: undefined,
|
rootOverlayModel: undefined,
|
||||||
locked: undefined,
|
locked: undefined,
|
||||||
disableDnd: undefined,
|
disableDnd: undefined,
|
||||||
|
className: undefined,
|
||||||
|
noPanelsOverlay: undefined,
|
||||||
|
dndEdges: undefined,
|
||||||
|
theme: undefined,
|
||||||
|
disableTabsOverflowList: undefined,
|
||||||
|
scrollbars: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Object.keys(properties) as (keyof DockviewOptions)[];
|
return Object.keys(properties) as (keyof DockviewOptions)[];
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
export interface CreateComponentOptions {
|
||||||
|
/**
|
||||||
|
* The unqiue identifer of the component
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* The component name, this should determine what is rendered.
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DockviewFrameworkOptions {
|
export interface DockviewFrameworkOptions {
|
||||||
parentElement: HTMLElement;
|
|
||||||
defaultTabComponent?: string;
|
defaultTabComponent?: string;
|
||||||
createRightHeaderActionComponent?: (
|
createRightHeaderActionComponent?: (
|
||||||
group: DockviewGroupPanel
|
group: DockviewGroupPanel
|
||||||
@ -115,14 +151,10 @@ export interface DockviewFrameworkOptions {
|
|||||||
createPrefixHeaderActionComponent?: (
|
createPrefixHeaderActionComponent?: (
|
||||||
group: DockviewGroupPanel
|
group: DockviewGroupPanel
|
||||||
) => IHeaderActionsRenderer;
|
) => IHeaderActionsRenderer;
|
||||||
createTabComponent?: (options: {
|
createTabComponent?: (
|
||||||
id: string;
|
options: CreateComponentOptions
|
||||||
name: string;
|
) => ITabRenderer | undefined;
|
||||||
}) => ITabRenderer | undefined;
|
createComponent: (options: CreateComponentOptions) => IContentRenderer;
|
||||||
createComponent: (options: {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}) => IContentRenderer;
|
|
||||||
createWatermarkComponent?: () => IWatermarkRenderer;
|
createWatermarkComponent?: () => IWatermarkRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,11 +172,19 @@ export interface PanelOptions<P extends object = Parameters> {
|
|||||||
type RelativePanel = {
|
type RelativePanel = {
|
||||||
direction?: Direction;
|
direction?: Direction;
|
||||||
referencePanel: string | IDockviewPanel;
|
referencePanel: string | IDockviewPanel;
|
||||||
|
/**
|
||||||
|
* The index to place the panel within a group, only applicable if the placement is within an existing group
|
||||||
|
*/
|
||||||
|
index?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type RelativeGroup = {
|
type RelativeGroup = {
|
||||||
direction?: Direction;
|
direction?: Direction;
|
||||||
referenceGroup: string | DockviewGroupPanel;
|
referenceGroup: string | DockviewGroupPanel;
|
||||||
|
/**
|
||||||
|
* The index to place the panel within a group, only applicable if the placement is within an existing group
|
||||||
|
*/
|
||||||
|
index?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AbsolutePosition = {
|
type AbsolutePosition = {
|
||||||
@ -175,19 +215,12 @@ export function isPanelOptionsWithGroup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AddPanelFloatingGroupUnion = {
|
type AddPanelFloatingGroupUnion = {
|
||||||
floating:
|
floating: Partial<FloatingGroupOptions> | true;
|
||||||
| {
|
|
||||||
height?: number;
|
|
||||||
width?: number;
|
|
||||||
x?: number;
|
|
||||||
y?: number;
|
|
||||||
}
|
|
||||||
| true;
|
|
||||||
position: never;
|
position: never;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AddPanelPositionUnion = {
|
type AddPanelPositionUnion = {
|
||||||
floating: false | never;
|
floating: false;
|
||||||
position: AddPanelPositionOptions;
|
position: AddPanelPositionOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -225,7 +258,10 @@ export type AddPanelOptions<P extends object = Parameters> = {
|
|||||||
* Defaults to `false` which forces newly added panels to become active.
|
* Defaults to `false` which forces newly added panels to become active.
|
||||||
*/
|
*/
|
||||||
inactive?: boolean;
|
inactive?: boolean;
|
||||||
} & Partial<AddPanelOptionsUnion>;
|
initialWidth?: number;
|
||||||
|
initialHeight?: number;
|
||||||
|
} & Partial<AddPanelOptionsUnion> &
|
||||||
|
Partial<Contraints>;
|
||||||
|
|
||||||
type AddGroupOptionsWithPanel = {
|
type AddGroupOptionsWithPanel = {
|
||||||
referencePanel: string | IDockviewPanel;
|
referencePanel: string | IDockviewPanel;
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
import { CompositeDisposable } from '../lifecycle';
|
||||||
|
import { DockviewComponent } from './dockviewComponent';
|
||||||
|
|
||||||
|
export class StrictEventsSequencing extends CompositeDisposable {
|
||||||
|
constructor(private readonly accessor: DockviewComponent) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private init(): void {
|
||||||
|
const panels = new Set<string>();
|
||||||
|
const groups = new Set<string>();
|
||||||
|
|
||||||
|
this.addDisposables(
|
||||||
|
this.accessor.onDidAddPanel((panel) => {
|
||||||
|
if (panels.has(panel.api.id)) {
|
||||||
|
throw new Error(
|
||||||
|
`dockview: Invalid event sequence. [onDidAddPanel] called for panel ${panel.api.id} but panel already exists`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
panels.add(panel.api.id);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
this.accessor.onDidRemovePanel((panel) => {
|
||||||
|
if (!panels.has(panel.api.id)) {
|
||||||
|
throw new Error(
|
||||||
|
`dockview: Invalid event sequence. [onDidRemovePanel] called for panel ${panel.api.id} but panel does not exists`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
panels.delete(panel.api.id);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
this.accessor.onDidAddGroup((group) => {
|
||||||
|
if (groups.has(group.api.id)) {
|
||||||
|
throw new Error(
|
||||||
|
`dockview: Invalid event sequence. [onDidAddGroup] called for group ${group.api.id} but group already exists`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
groups.add(group.api.id);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
this.accessor.onDidRemoveGroup((group) => {
|
||||||
|
if (!groups.has(group.api.id)) {
|
||||||
|
throw new Error(
|
||||||
|
`dockview: Invalid event sequence. [onDidRemoveGroup] called for group ${group.api.id} but group does not exists`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
groups.delete(group.api.id);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
70
packages/dockview-core/src/dockview/theme.ts
Normal file
70
packages/dockview-core/src/dockview/theme.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
export interface DockviewTheme {
|
||||||
|
/**
|
||||||
|
* The name of the theme
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* The class name to apply to the theme containing the CSS variables settings.
|
||||||
|
*/
|
||||||
|
className: string;
|
||||||
|
/**
|
||||||
|
* The gap between the groups
|
||||||
|
*/
|
||||||
|
gap?: number;
|
||||||
|
/**
|
||||||
|
* The mouting position of the overlay shown when dragging a panel. `absolute`
|
||||||
|
* will mount the overlay to root of the dockview component whereas `relative` will mount the overlay to the group container.
|
||||||
|
*/
|
||||||
|
dndOverlayMounting?: 'absolute' | 'relative';
|
||||||
|
/**
|
||||||
|
* When dragging a panel, the overlay can either encompass the panel contents or the entire group including the tab header space.
|
||||||
|
*/
|
||||||
|
dndPanelOverlay?: 'content' | 'group';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const themeDark: DockviewTheme = {
|
||||||
|
name: 'dark',
|
||||||
|
className: 'dockview-theme-dark',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const themeLight: DockviewTheme = {
|
||||||
|
name: 'light',
|
||||||
|
className: 'dockview-theme-light',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const themeVisualStudio: DockviewTheme = {
|
||||||
|
name: 'visualStudio',
|
||||||
|
className: 'dockview-theme-vs',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const themeAbyss: DockviewTheme = {
|
||||||
|
name: 'abyss',
|
||||||
|
className: 'dockview-theme-abyss',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const themeDracula: DockviewTheme = {
|
||||||
|
name: 'dracula',
|
||||||
|
className: 'dockview-theme-dracula',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const themeReplit: DockviewTheme = {
|
||||||
|
name: 'replit',
|
||||||
|
className: 'dockview-theme-replit',
|
||||||
|
gap: 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const themeAbyssSpaced: DockviewTheme = {
|
||||||
|
name: 'abyssSpaced',
|
||||||
|
className: 'dockview-theme-abyss-spaced',
|
||||||
|
gap: 10,
|
||||||
|
dndOverlayMounting: 'absolute',
|
||||||
|
dndPanelOverlay: 'group',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const themeLightSpaced: DockviewTheme = {
|
||||||
|
name: 'lightSpaced',
|
||||||
|
className: 'dockview-theme-light-spaced',
|
||||||
|
gap: 10,
|
||||||
|
dndOverlayMounting: 'absolute',
|
||||||
|
dndPanelOverlay: 'group',
|
||||||
|
};
|
@ -1,11 +1,10 @@
|
|||||||
import { IDockviewComponent } from './dockviewComponent';
|
|
||||||
import { DockviewPanelApi } from '../api/dockviewPanelApi';
|
import { DockviewPanelApi } from '../api/dockviewPanelApi';
|
||||||
import { PanelInitParameters, IPanel } from '../panel/types';
|
import { PanelInitParameters, IPanel } from '../panel/types';
|
||||||
import { DockviewApi } from '../api/component.api';
|
import { DockviewApi } from '../api/component.api';
|
||||||
import { Event } from '../events';
|
|
||||||
import { Optional } from '../types';
|
import { Optional } from '../types';
|
||||||
import { DockviewGroupPanel, IDockviewGroupPanel } from './dockviewGroupPanel';
|
import { IDockviewGroupPanel } from './dockviewGroupPanel';
|
||||||
import { DockviewPanelRenderer } from '../overlayRenderContainer';
|
import { DockviewPanelRenderer } from '../overlay/overlayRenderContainer';
|
||||||
|
import { TabLocation } from './framework';
|
||||||
|
|
||||||
export interface HeaderPartInitParameters {
|
export interface HeaderPartInitParameters {
|
||||||
title: string;
|
title: string;
|
||||||
@ -23,46 +22,39 @@ export interface WatermarkRendererInitParameters {
|
|||||||
group?: IDockviewGroupPanel;
|
group?: IDockviewGroupPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RendererMethodOptionalList =
|
||||||
|
| 'dispose'
|
||||||
|
| 'update'
|
||||||
|
| 'layout'
|
||||||
|
| 'toJSON'
|
||||||
|
| 'focus';
|
||||||
|
|
||||||
export interface IWatermarkRenderer
|
export interface IWatermarkRenderer
|
||||||
extends Optional<
|
extends Optional<Omit<IPanel, 'id' | 'init'>, RendererMethodOptionalList> {
|
||||||
Omit<IPanel, 'id' | 'init'>,
|
|
||||||
'dispose' | 'update' | 'layout' | 'toJSON' | 'focus'
|
|
||||||
> {
|
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
init: (params: WatermarkRendererInitParameters) => void;
|
init: (params: WatermarkRendererInitParameters) => void;
|
||||||
updateParentGroup(group: DockviewGroupPanel, visible: boolean): void;
|
}
|
||||||
|
|
||||||
|
export interface TabPartInitParameters extends GroupPanelPartInitParameters {
|
||||||
|
tabLocation: TabLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITabRenderer
|
export interface ITabRenderer
|
||||||
extends Optional<
|
extends Optional<Omit<IPanel, 'id'>, RendererMethodOptionalList> {
|
||||||
Omit<IPanel, 'id'>,
|
|
||||||
'dispose' | 'update' | 'layout' | 'toJSON' | 'focus'
|
|
||||||
> {
|
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
init(parameters: GroupPanelPartInitParameters): void;
|
init(parameters: TabPartInitParameters): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IContentRenderer
|
export interface IContentRenderer
|
||||||
extends Optional<
|
extends Optional<Omit<IPanel, 'id'>, RendererMethodOptionalList> {
|
||||||
Omit<IPanel, 'id'>,
|
|
||||||
'dispose' | 'update' | 'layout' | 'toJSON' | 'focus'
|
|
||||||
> {
|
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
init(parameters: GroupPanelPartInitParameters): void;
|
init(parameters: GroupPanelPartInitParameters): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// watermark component
|
// watermark component
|
||||||
|
|
||||||
export interface WatermarkPartInitParameters {
|
|
||||||
accessor: IDockviewComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// constructors
|
// constructors
|
||||||
|
|
||||||
export interface WatermarkConstructor {
|
|
||||||
new (): IWatermarkRenderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IGroupPanelInitParameters
|
export interface IGroupPanelInitParameters
|
||||||
extends PanelInitParameters,
|
extends PanelInitParameters,
|
||||||
HeaderPartInitParameters {
|
HeaderPartInitParameters {
|
||||||
@ -76,4 +68,8 @@ export interface GroupviewPanelState {
|
|||||||
title?: string;
|
title?: string;
|
||||||
renderer?: DockviewPanelRenderer;
|
renderer?: DockviewPanelRenderer;
|
||||||
params?: { [key: string]: any };
|
params?: { [key: string]: any };
|
||||||
|
minimumWidth?: number;
|
||||||
|
minimumHeight?: number;
|
||||||
|
maximumWidth?: number;
|
||||||
|
maximumHeight?: number;
|
||||||
}
|
}
|
||||||
|
90
packages/dockview-core/src/dockview/validate.ts
Normal file
90
packages/dockview-core/src/dockview/validate.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// import { SerializedGridObject } from '../gridview/gridview';
|
||||||
|
// import { Orientation } from '../splitview/splitview';
|
||||||
|
// import { SerializedDockview } from './dockviewComponent';
|
||||||
|
// import { GroupPanelViewState } from './dockviewGroupPanelModel';
|
||||||
|
|
||||||
|
// function typeValidate3(data: GroupPanelViewState, path: string): void {
|
||||||
|
// if (typeof data.id !== 'string') {
|
||||||
|
// throw new Error(`${path}.id must be a string`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (
|
||||||
|
// typeof data.activeView !== 'string' ||
|
||||||
|
// typeof data.activeView !== 'undefined'
|
||||||
|
// ) {
|
||||||
|
// throw new Error(`${path}.activeView must be a string of undefined`);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function typeValidate2(
|
||||||
|
// data: SerializedGridObject<GroupPanelViewState>,
|
||||||
|
// path: string
|
||||||
|
// ): void {
|
||||||
|
// if (typeof data.size !== 'number' && typeof data.size !== 'undefined') {
|
||||||
|
// throw new Error(`${path}.size must be a number or undefined`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (
|
||||||
|
// typeof data.visible !== 'boolean' &&
|
||||||
|
// typeof data.visible !== 'undefined'
|
||||||
|
// ) {
|
||||||
|
// throw new Error(`${path}.visible must be a boolean or undefined`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (data.type === 'leaf') {
|
||||||
|
// if (
|
||||||
|
// typeof data.data !== 'object' ||
|
||||||
|
// data.data === null ||
|
||||||
|
// Array.isArray(data.data)
|
||||||
|
// ) {
|
||||||
|
// throw new Error('object must be a non-null object');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// typeValidate3(data.data, `${path}.data`);
|
||||||
|
// } else if (data.type === 'branch') {
|
||||||
|
// if (!Array.isArray(data.data)) {
|
||||||
|
// throw new Error(`${path}.data must be an array`);
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// throw new Error(`${path}.type must be onew of {'branch', 'leaf'}`);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function typeValidate(data: SerializedDockview): void {
|
||||||
|
// if (typeof data !== 'object' || data === null) {
|
||||||
|
// throw new Error('object must be a non-null object');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const { grid, panels, activeGroup, floatingGroups } = data;
|
||||||
|
|
||||||
|
// if (typeof grid !== 'object' || grid === null) {
|
||||||
|
// throw new Error("'.grid' must be a non-null object");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (typeof grid.height !== 'number') {
|
||||||
|
// throw new Error("'.grid.height' must be a number");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (typeof grid.width !== 'number') {
|
||||||
|
// throw new Error("'.grid.width' must be a number");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (typeof grid.root !== 'object' || grid.root === null) {
|
||||||
|
// throw new Error("'.grid.root' must be a non-null object");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (grid.root.type !== 'branch') {
|
||||||
|
// throw new Error(".grid.root.type must be of type 'branch'");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (
|
||||||
|
// grid.orientation !== Orientation.HORIZONTAL &&
|
||||||
|
// grid.orientation !== Orientation.VERTICAL
|
||||||
|
// ) {
|
||||||
|
// throw new Error(
|
||||||
|
// `'.grid.width' must be one of {${Orientation.HORIZONTAL}, ${Orientation.VERTICAL}}`
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// typeValidate2(grid.root, '.grid.root');
|
||||||
|
// }
|
@ -2,10 +2,39 @@ import {
|
|||||||
Event as DockviewEvent,
|
Event as DockviewEvent,
|
||||||
Emitter,
|
Emitter,
|
||||||
addDisposableListener,
|
addDisposableListener,
|
||||||
addDisposableWindowListener,
|
|
||||||
} from './events';
|
} from './events';
|
||||||
import { IDisposable, CompositeDisposable } from './lifecycle';
|
import { IDisposable, CompositeDisposable } from './lifecycle';
|
||||||
|
|
||||||
|
export interface OverflowEvent {
|
||||||
|
hasScrollX: boolean;
|
||||||
|
hasScrollY: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OverflowObserver extends CompositeDisposable {
|
||||||
|
private readonly _onDidChange = new Emitter<OverflowEvent>();
|
||||||
|
readonly onDidChange = this._onDidChange.event;
|
||||||
|
|
||||||
|
private _value: OverflowEvent | null = null;
|
||||||
|
|
||||||
|
constructor(el: HTMLElement) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.addDisposables(
|
||||||
|
this._onDidChange,
|
||||||
|
watchElementResize(el, (entry) => {
|
||||||
|
const hasScrollX =
|
||||||
|
entry.target.scrollWidth > entry.target.clientWidth;
|
||||||
|
|
||||||
|
const hasScrollY =
|
||||||
|
entry.target.scrollHeight > entry.target.clientHeight;
|
||||||
|
|
||||||
|
this._value = { hasScrollX, hasScrollY };
|
||||||
|
this._onDidChange.fire(this._value);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function watchElementResize(
|
export function watchElementResize(
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
cb: (entry: ResizeObserverEntry) => void
|
cb: (entry: ResizeObserverEntry) => void
|
||||||
@ -82,8 +111,11 @@ export function isAncestor(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getElementsByTagName(tag: string): HTMLElement[] {
|
export function getElementsByTagName(
|
||||||
return Array.prototype.slice.call(document.getElementsByTagName(tag), 0);
|
tag: string,
|
||||||
|
document: ParentNode
|
||||||
|
): HTMLElement[] {
|
||||||
|
return Array.prototype.slice.call(document.querySelectorAll(tag), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFocusTracker extends IDisposable {
|
export interface IFocusTracker extends IDisposable {
|
||||||
@ -92,7 +124,7 @@ export interface IFocusTracker extends IDisposable {
|
|||||||
refreshState?(): void;
|
refreshState?(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackFocus(element: HTMLElement | Window): IFocusTracker {
|
export function trackFocus(element: HTMLElement): IFocusTracker {
|
||||||
return new FocusTracker(element);
|
return new FocusTracker(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,9 +138,9 @@ class FocusTracker extends CompositeDisposable implements IFocusTracker {
|
|||||||
private readonly _onDidBlur = new Emitter<void>();
|
private readonly _onDidBlur = new Emitter<void>();
|
||||||
public readonly onDidBlur: DockviewEvent<void> = this._onDidBlur.event;
|
public readonly onDidBlur: DockviewEvent<void> = this._onDidBlur.event;
|
||||||
|
|
||||||
private _refreshStateHandler: () => void;
|
private readonly _refreshStateHandler: () => void;
|
||||||
|
|
||||||
constructor(element: HTMLElement | Window) {
|
constructor(element: HTMLElement) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.addDisposables(this._onDidFocus, this._onDidBlur);
|
this.addDisposables(this._onDidFocus, this._onDidBlur);
|
||||||
@ -151,21 +183,12 @@ class FocusTracker extends CompositeDisposable implements IFocusTracker {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (element instanceof HTMLElement) {
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
addDisposableListener(element, 'focus', onFocus, true)
|
addDisposableListener(element, 'focus', onFocus, true)
|
||||||
);
|
);
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
addDisposableListener(element, 'blur', onBlur, true)
|
addDisposableListener(element, 'blur', onBlur, true)
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
this.addDisposables(
|
|
||||||
addDisposableWindowListener(element, 'focus', onFocus, true)
|
|
||||||
);
|
|
||||||
this.addDisposables(
|
|
||||||
addDisposableWindowListener(element, 'blur', onBlur, true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshState(): void {
|
refreshState(): void {
|
||||||
@ -253,3 +276,225 @@ export function isInDocument(element: Element): boolean {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addTestId(element: HTMLElement, id: string): void {
|
||||||
|
element.setAttribute('data-testid', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be more efficient than element.querySelectorAll("*") since there
|
||||||
|
* is no need to store every element in-memory using this approach
|
||||||
|
*/
|
||||||
|
function allTagsNamesInclusiveOfShadowDoms(tagNames: string[]) {
|
||||||
|
const iframes: HTMLElement[] = [];
|
||||||
|
|
||||||
|
function findIframesInNode(node: Element) {
|
||||||
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
if (tagNames.includes(node.tagName)) {
|
||||||
|
iframes.push(node as HTMLElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.shadowRoot) {
|
||||||
|
findIframesInNode(<any>node.shadowRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const child of node.children) {
|
||||||
|
findIframesInNode(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findIframesInNode(document.documentElement);
|
||||||
|
|
||||||
|
return iframes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function disableIframePointEvents(rootNode: ParentNode = document) {
|
||||||
|
const iframes = allTagsNamesInclusiveOfShadowDoms(['IFRAME', 'WEBVIEW']);
|
||||||
|
|
||||||
|
const original = new WeakMap<HTMLElement, string>(); // don't hold onto HTMLElement references longer than required
|
||||||
|
|
||||||
|
for (const iframe of iframes) {
|
||||||
|
original.set(iframe, iframe.style.pointerEvents);
|
||||||
|
iframe.style.pointerEvents = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
release: () => {
|
||||||
|
for (const iframe of iframes) {
|
||||||
|
iframe.style.pointerEvents = original.get(iframe) ?? 'auto';
|
||||||
|
}
|
||||||
|
iframes.splice(0, iframes.length); // don't hold onto HTMLElement references longer than required
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDockviewTheme(element: HTMLElement): string | undefined {
|
||||||
|
function toClassList(element: HTMLElement) {
|
||||||
|
const list: string[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < element.classList.length; i++) {
|
||||||
|
list.push(element.classList.item(i)!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
let theme: string | undefined = undefined;
|
||||||
|
let parent: HTMLElement | null = element;
|
||||||
|
|
||||||
|
while (parent !== null) {
|
||||||
|
theme = toClassList(parent).find((cls) =>
|
||||||
|
cls.startsWith('dockview-theme-')
|
||||||
|
);
|
||||||
|
if (typeof theme === 'string') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parent = parent.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Classnames {
|
||||||
|
private _classNames: string[] = [];
|
||||||
|
|
||||||
|
constructor(private readonly element: HTMLElement) {}
|
||||||
|
|
||||||
|
setClassNames(classNames: string) {
|
||||||
|
for (const className of this._classNames) {
|
||||||
|
toggleClass(this.element, className, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._classNames = classNames
|
||||||
|
.split(' ')
|
||||||
|
.filter((v) => v.trim().length > 0);
|
||||||
|
|
||||||
|
for (const className of this._classNames) {
|
||||||
|
toggleClass(this.element, className, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEBOUCE_DELAY = 100;
|
||||||
|
|
||||||
|
export function isChildEntirelyVisibleWithinParent(
|
||||||
|
child: HTMLElement,
|
||||||
|
parent: HTMLElement
|
||||||
|
): boolean {
|
||||||
|
//
|
||||||
|
const childPosition = getDomNodePagePosition(child);
|
||||||
|
const parentPosition = getDomNodePagePosition(parent);
|
||||||
|
|
||||||
|
if (childPosition.left < parentPosition.left) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
childPosition.left + childPosition.width >
|
||||||
|
parentPosition.left + parentPosition.width
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onDidWindowMoveEnd(window: Window): Emitter<void> {
|
||||||
|
const emitter = new Emitter<void>();
|
||||||
|
|
||||||
|
let previousScreenX = window.screenX;
|
||||||
|
let previousScreenY = window.screenY;
|
||||||
|
|
||||||
|
let timeout: any;
|
||||||
|
|
||||||
|
const checkMovement = () => {
|
||||||
|
if (window.closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentScreenX = window.screenX;
|
||||||
|
const currentScreenY = window.screenY;
|
||||||
|
|
||||||
|
if (
|
||||||
|
currentScreenX !== previousScreenX ||
|
||||||
|
currentScreenY !== previousScreenY
|
||||||
|
) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
emitter.fire();
|
||||||
|
}, DEBOUCE_DELAY);
|
||||||
|
|
||||||
|
previousScreenX = currentScreenX;
|
||||||
|
previousScreenY = currentScreenY;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(checkMovement);
|
||||||
|
};
|
||||||
|
|
||||||
|
checkMovement();
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onDidWindowResizeEnd(element: Window, cb: () => void) {
|
||||||
|
let resizeTimeout: any;
|
||||||
|
|
||||||
|
const disposable = new CompositeDisposable(
|
||||||
|
addDisposableListener(element, 'resize', () => {
|
||||||
|
clearTimeout(resizeTimeout);
|
||||||
|
resizeTimeout = setTimeout(() => {
|
||||||
|
cb();
|
||||||
|
}, DEBOUCE_DELAY);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return disposable;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shiftAbsoluteElementIntoView(
|
||||||
|
element: HTMLElement,
|
||||||
|
root: HTMLElement,
|
||||||
|
options: { buffer: number } = { buffer: 10 }
|
||||||
|
) {
|
||||||
|
const buffer = options.buffer;
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
const rootRect = root.getBoundingClientRect();
|
||||||
|
|
||||||
|
let translateX = 0;
|
||||||
|
let translateY = 0;
|
||||||
|
|
||||||
|
const left = rect.left - rootRect.left;
|
||||||
|
const top = rect.top - rootRect.top;
|
||||||
|
const bottom = rect.bottom - rootRect.bottom;
|
||||||
|
const right = rect.right - rootRect.right;
|
||||||
|
|
||||||
|
// Check horizontal overflow
|
||||||
|
if (left < buffer) {
|
||||||
|
translateX = buffer - left;
|
||||||
|
} else if (right > buffer) {
|
||||||
|
translateX = -buffer - right;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check vertical overflow
|
||||||
|
if (top < buffer) {
|
||||||
|
translateY = buffer - top;
|
||||||
|
} else if (bottom > buffer) {
|
||||||
|
translateY = -bottom - buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the translation if needed
|
||||||
|
if (translateX !== 0 || translateY !== 0) {
|
||||||
|
element.style.transform = `translate(${translateX}px, ${translateY}px)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findRelativeZIndexParent(el: HTMLElement): HTMLElement | null {
|
||||||
|
let tmp: HTMLElement | null = el;
|
||||||
|
|
||||||
|
while (tmp && (tmp.style.zIndex === 'auto' || tmp.style.zIndex === '')) {
|
||||||
|
tmp = tmp.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
@ -41,6 +41,23 @@ export class DockviewEvent implements IDockviewEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IAcceptableEvent {
|
||||||
|
readonly isAccepted: boolean;
|
||||||
|
accept(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AcceptableEvent implements IAcceptableEvent {
|
||||||
|
private _isAccepted = false;
|
||||||
|
|
||||||
|
get isAccepted(): boolean {
|
||||||
|
return this._isAccepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
accept(): void {
|
||||||
|
this._isAccepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class LeakageMonitor {
|
class LeakageMonitor {
|
||||||
readonly events = new Map<Event<any>, Stacktrace>();
|
readonly events = new Map<Event<any>, Stacktrace>();
|
||||||
|
|
||||||
@ -69,7 +86,7 @@ class Stacktrace {
|
|||||||
private constructor(readonly value: string) {}
|
private constructor(readonly value: string) {}
|
||||||
|
|
||||||
print(): void {
|
print(): void {
|
||||||
console.warn(this.value);
|
console.warn('dockview: stacktrace', this.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +141,7 @@ export class Emitter<T> implements IDisposable {
|
|||||||
this._listeners.splice(index, 1);
|
this._listeners.splice(index, 1);
|
||||||
} else if (Emitter.ENABLE_TRACKING) {
|
} else if (Emitter.ENABLE_TRACKING) {
|
||||||
// console.warn(
|
// console.warn(
|
||||||
// `Listener already disposed`,
|
// `dockview: listener already disposed`,
|
||||||
// Stacktrace.create().print()
|
// Stacktrace.create().print()
|
||||||
// );
|
// );
|
||||||
}
|
}
|
||||||
@ -158,7 +175,10 @@ export class Emitter<T> implements IDisposable {
|
|||||||
queueMicrotask(() => {
|
queueMicrotask(() => {
|
||||||
// don't check until stack of execution is completed to allow for out-of-order disposals within the same execution block
|
// don't check until stack of execution is completed to allow for out-of-order disposals within the same execution block
|
||||||
for (const listener of this._listeners) {
|
for (const listener of this._listeners) {
|
||||||
console.warn(listener.stacktrace?.print());
|
console.warn(
|
||||||
|
'dockview: stacktrace',
|
||||||
|
listener.stacktrace?.print()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -173,32 +193,38 @@ export class Emitter<T> implements IDisposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addDisposableWindowListener<K extends keyof WindowEventMap>(
|
export function addDisposableListener<K extends keyof WindowEventMap>(
|
||||||
element: Window,
|
element: Window,
|
||||||
type: K,
|
type: K,
|
||||||
listener: (this: Window, ev: WindowEventMap[K]) => any,
|
listener: (this: Window, ev: WindowEventMap[K]) => any,
|
||||||
options?: boolean | AddEventListenerOptions
|
options?: boolean | AddEventListenerOptions
|
||||||
): IDisposable {
|
): IDisposable;
|
||||||
element.addEventListener(type, listener, options);
|
|
||||||
|
|
||||||
return {
|
|
||||||
dispose: () => {
|
|
||||||
element.removeEventListener(type, listener, options);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addDisposableListener<K extends keyof HTMLElementEventMap>(
|
export function addDisposableListener<K extends keyof HTMLElementEventMap>(
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
type: K,
|
type: K,
|
||||||
listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,
|
listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,
|
||||||
options?: boolean | AddEventListenerOptions
|
options?: boolean | AddEventListenerOptions
|
||||||
|
): IDisposable;
|
||||||
|
export function addDisposableListener<
|
||||||
|
K extends keyof HTMLElementEventMap | keyof WindowEventMap
|
||||||
|
>(
|
||||||
|
element: HTMLElement | Window,
|
||||||
|
type: K,
|
||||||
|
listener: (
|
||||||
|
this: K extends keyof HTMLElementEventMap ? HTMLElement : Window,
|
||||||
|
ev: K extends keyof HTMLElementEventMap
|
||||||
|
? HTMLElementEventMap[K]
|
||||||
|
: K extends keyof WindowEventMap
|
||||||
|
? WindowEventMap[K]
|
||||||
|
: never
|
||||||
|
) => any,
|
||||||
|
options?: boolean | AddEventListenerOptions
|
||||||
): IDisposable {
|
): IDisposable {
|
||||||
element.addEventListener(type, listener, options);
|
element.addEventListener(type, <any>listener, options);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dispose: () => {
|
dispose: () => {
|
||||||
element.removeEventListener(type, listener, options);
|
element.removeEventListener(type, <any>listener, options);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
import { Emitter, Event, AsapEvent } from '../events';
|
import { Emitter, Event, AsapEvent } from '../events';
|
||||||
import { getGridLocation, Gridview, IGridView } from './gridview';
|
import { getGridLocation, Gridview, IGridView } from './gridview';
|
||||||
import { Position } from '../dnd/droptarget';
|
import { Position } from '../dnd/droptarget';
|
||||||
import { Disposable, IValueDisposable } from '../lifecycle';
|
import { Disposable, IDisposable, IValueDisposable } from '../lifecycle';
|
||||||
import { sequentialNumberGenerator } from '../math';
|
import { sequentialNumberGenerator } from '../math';
|
||||||
import { ISplitviewStyles, Orientation, Sizing } from '../splitview/splitview';
|
import { ISplitviewStyles, Orientation, Sizing } from '../splitview/splitview';
|
||||||
import { IPanel } from '../panel/types';
|
import { IPanel } from '../panel/types';
|
||||||
import { MovementOptions2 } from '../dockview/options';
|
import { MovementOptions2 } from '../dockview/options';
|
||||||
import { Resizable } from '../resizable';
|
import { Resizable } from '../resizable';
|
||||||
|
import { Classnames } from '../dom';
|
||||||
|
|
||||||
const nextLayoutId = sequentialNumberGenerator();
|
const nextLayoutId = sequentialNumberGenerator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A direction in which a panel can be moved or placed relative to another panel.
|
||||||
|
*/
|
||||||
export type Direction = 'left' | 'right' | 'above' | 'below' | 'within';
|
export type Direction = 'left' | 'right' | 'above' | 'below' | 'within';
|
||||||
|
|
||||||
export function toTarget(direction: Direction): Position {
|
export function toTarget(direction: Direction): Position {
|
||||||
@ -28,13 +32,19 @@ export function toTarget(direction: Direction): Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MaximizedChanged<T extends IGridPanelView> {
|
||||||
|
panel: T;
|
||||||
|
isMaximized: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface BaseGridOptions {
|
export interface BaseGridOptions {
|
||||||
readonly proportionalLayout: boolean;
|
readonly proportionalLayout: boolean;
|
||||||
readonly orientation: Orientation;
|
readonly orientation: Orientation;
|
||||||
readonly styles?: ISplitviewStyles;
|
readonly styles?: ISplitviewStyles;
|
||||||
readonly parentElement: HTMLElement;
|
|
||||||
readonly disableAutoResizing?: boolean;
|
readonly disableAutoResizing?: boolean;
|
||||||
readonly locked?: boolean;
|
readonly locked?: boolean;
|
||||||
|
readonly margin?: number;
|
||||||
|
readonly className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGridPanelView extends IGridView, IPanel {
|
export interface IGridPanelView extends IGridView, IPanel {
|
||||||
@ -42,7 +52,7 @@ export interface IGridPanelView extends IGridView, IPanel {
|
|||||||
readonly isActive: boolean;
|
readonly isActive: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBaseGrid<T extends IGridPanelView> {
|
export interface IBaseGrid<T extends IGridPanelView> extends IDisposable {
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly width: number;
|
readonly width: number;
|
||||||
@ -54,6 +64,8 @@ export interface IBaseGrid<T extends IGridPanelView> {
|
|||||||
readonly activeGroup: T | undefined;
|
readonly activeGroup: T | undefined;
|
||||||
readonly size: number;
|
readonly size: number;
|
||||||
readonly groups: T[];
|
readonly groups: T[];
|
||||||
|
readonly onDidMaximizedChange: Event<MaximizedChanged<T>>;
|
||||||
|
readonly onDidLayoutChange: Event<void>;
|
||||||
getPanel(id: string): T | undefined;
|
getPanel(id: string): T | undefined;
|
||||||
toJSON(): object;
|
toJSON(): object;
|
||||||
fromJSON(data: any): void;
|
fromJSON(data: any): void;
|
||||||
@ -65,8 +77,6 @@ export interface IBaseGrid<T extends IGridPanelView> {
|
|||||||
isMaximizedGroup(panel: T): boolean;
|
isMaximizedGroup(panel: T): boolean;
|
||||||
exitMaximizedGroup(): void;
|
exitMaximizedGroup(): void;
|
||||||
hasMaximizedGroup(): boolean;
|
hasMaximizedGroup(): boolean;
|
||||||
readonly onDidMaximizedGroupChange: Event<void>;
|
|
||||||
readonly onDidLayoutChange: Event<void>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class BaseGrid<T extends IGridPanelView>
|
export abstract class BaseGrid<T extends IGridPanelView>
|
||||||
@ -85,6 +95,10 @@ export abstract class BaseGrid<T extends IGridPanelView>
|
|||||||
private readonly _onDidAdd = new Emitter<T>();
|
private readonly _onDidAdd = new Emitter<T>();
|
||||||
readonly onDidAdd: Event<T> = this._onDidAdd.event;
|
readonly onDidAdd: Event<T> = this._onDidAdd.event;
|
||||||
|
|
||||||
|
private readonly _onDidMaximizedChange = new Emitter<MaximizedChanged<T>>();
|
||||||
|
readonly onDidMaximizedChange: Event<MaximizedChanged<T>> =
|
||||||
|
this._onDidMaximizedChange.event;
|
||||||
|
|
||||||
private readonly _onDidActiveChange = new Emitter<T | undefined>();
|
private readonly _onDidActiveChange = new Emitter<T | undefined>();
|
||||||
readonly onDidActiveChange: Event<T | undefined> =
|
readonly onDidActiveChange: Event<T | undefined> =
|
||||||
this._onDidActiveChange.event;
|
this._onDidActiveChange.event;
|
||||||
@ -93,6 +107,12 @@ export abstract class BaseGrid<T extends IGridPanelView>
|
|||||||
readonly onDidLayoutChange: Event<void> =
|
readonly onDidLayoutChange: Event<void> =
|
||||||
this._bufferOnDidLayoutChange.onEvent;
|
this._bufferOnDidLayoutChange.onEvent;
|
||||||
|
|
||||||
|
private readonly _onDidViewVisibilityChangeMicroTaskQueue = new AsapEvent();
|
||||||
|
readonly onDidViewVisibilityChangeMicroTaskQueue =
|
||||||
|
this._onDidViewVisibilityChangeMicroTaskQueue.onEvent;
|
||||||
|
|
||||||
|
private readonly _classNames: Classnames;
|
||||||
|
|
||||||
get id(): string {
|
get id(): string {
|
||||||
return this._id;
|
return this._id;
|
||||||
}
|
}
|
||||||
@ -138,17 +158,23 @@ export abstract class BaseGrid<T extends IGridPanelView>
|
|||||||
this.gridview.locked = value;
|
this.gridview.locked = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(options: BaseGridOptions) {
|
constructor(container: HTMLElement, options: BaseGridOptions) {
|
||||||
super(document.createElement('div'), options.disableAutoResizing);
|
super(document.createElement('div'), options.disableAutoResizing);
|
||||||
this.element.style.height = '100%';
|
this.element.style.height = '100%';
|
||||||
this.element.style.width = '100%';
|
this.element.style.width = '100%';
|
||||||
|
|
||||||
options.parentElement.appendChild(this.element);
|
this._classNames = new Classnames(this.element);
|
||||||
|
this._classNames.setClassNames(options.className ?? '');
|
||||||
|
|
||||||
|
// the container is owned by the third-party, do not modify/delete it
|
||||||
|
container.appendChild(this.element);
|
||||||
|
|
||||||
this.gridview = new Gridview(
|
this.gridview = new Gridview(
|
||||||
!!options.proportionalLayout,
|
!!options.proportionalLayout,
|
||||||
options.styles,
|
options.styles,
|
||||||
options.orientation
|
options.orientation,
|
||||||
|
options.locked,
|
||||||
|
options.margin
|
||||||
);
|
);
|
||||||
|
|
||||||
this.gridview.locked = !!options.locked;
|
this.gridview.locked = !!options.locked;
|
||||||
@ -158,6 +184,18 @@ export abstract class BaseGrid<T extends IGridPanelView>
|
|||||||
this.layout(0, 0, true); // set some elements height/widths
|
this.layout(0, 0, true); // set some elements height/widths
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
|
this.gridview.onDidMaximizedNodeChange((event) => {
|
||||||
|
this._onDidMaximizedChange.fire({
|
||||||
|
panel: event.view as T,
|
||||||
|
isMaximized: event.isMaximized,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
this.gridview.onDidViewVisibilityChange(() =>
|
||||||
|
this._onDidViewVisibilityChangeMicroTaskQueue.fire()
|
||||||
|
),
|
||||||
|
this.onDidViewVisibilityChangeMicroTaskQueue(() => {
|
||||||
|
this.layout(this.width, this.height, true);
|
||||||
|
}),
|
||||||
Disposable.from(() => {
|
Disposable.from(() => {
|
||||||
this.element.parentElement?.removeChild(this.element);
|
this.element.parentElement?.removeChild(this.element);
|
||||||
}),
|
}),
|
||||||
@ -171,6 +209,8 @@ export abstract class BaseGrid<T extends IGridPanelView>
|
|||||||
)(() => {
|
)(() => {
|
||||||
this._bufferOnDidLayoutChange.fire();
|
this._bufferOnDidLayoutChange.fire();
|
||||||
}),
|
}),
|
||||||
|
this._onDidMaximizedChange,
|
||||||
|
this._onDidViewVisibilityChangeMicroTaskQueue,
|
||||||
this._bufferOnDidLayoutChange
|
this._bufferOnDidLayoutChange
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -190,6 +230,30 @@ export abstract class BaseGrid<T extends IGridPanelView>
|
|||||||
return this.gridview.isViewVisible(getGridLocation(panel.element));
|
return this.gridview.isViewVisible(getGridLocation(panel.element));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateOptions(options: Partial<BaseGridOptions>) {
|
||||||
|
if (typeof options.proportionalLayout === 'boolean') {
|
||||||
|
// this.gridview.proportionalLayout = options.proportionalLayout; // not supported
|
||||||
|
}
|
||||||
|
if (options.orientation) {
|
||||||
|
this.gridview.orientation = options.orientation;
|
||||||
|
}
|
||||||
|
if ('styles' in options) {
|
||||||
|
// this.gridview.styles = options.styles; // not supported
|
||||||
|
}
|
||||||
|
if ('disableResizing' in options) {
|
||||||
|
this.disableResizing = options.disableAutoResizing ?? false;
|
||||||
|
}
|
||||||
|
if ('locked' in options) {
|
||||||
|
this.locked = options.locked ?? false;
|
||||||
|
}
|
||||||
|
if ('margin' in options) {
|
||||||
|
this.gridview.margin = options.margin ?? 0;
|
||||||
|
}
|
||||||
|
if ('className' in options) {
|
||||||
|
this._classNames.setClassNames(options.className ?? '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
maximizeGroup(panel: T): void {
|
maximizeGroup(panel: T): void {
|
||||||
this.gridview.maximizeView(panel);
|
this.gridview.maximizeView(panel);
|
||||||
this.doSetGroupActive(panel);
|
this.doSetGroupActive(panel);
|
||||||
@ -207,10 +271,6 @@ export abstract class BaseGrid<T extends IGridPanelView>
|
|||||||
return this.gridview.hasMaximizedView();
|
return this.gridview.hasMaximizedView();
|
||||||
}
|
}
|
||||||
|
|
||||||
get onDidMaximizedGroupChange(): Event<void> {
|
|
||||||
return this.gridview.onDidMaximizedNodeChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected doAddGroup(
|
protected doAddGroup(
|
||||||
group: T,
|
group: T,
|
||||||
location: number[] = [0],
|
location: number[] = [0],
|
||||||
@ -310,7 +370,7 @@ export abstract class BaseGrid<T extends IGridPanelView>
|
|||||||
|
|
||||||
public layout(width: number, height: number, forceResize?: boolean): void {
|
public layout(width: number, height: number, forceResize?: boolean): void {
|
||||||
const different =
|
const different =
|
||||||
forceResize ?? (width !== this.width || height !== this.height);
|
forceResize || width !== this.width || height !== this.height;
|
||||||
|
|
||||||
if (!different) {
|
if (!different) {
|
||||||
return;
|
return;
|
||||||
|
@ -32,7 +32,7 @@ export abstract class BasePanelView<T extends PanelApiImpl>
|
|||||||
{
|
{
|
||||||
private _height = 0;
|
private _height = 0;
|
||||||
private _width = 0;
|
private _width = 0;
|
||||||
private _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
protected part?: IFrameworkPart;
|
protected part?: IFrameworkPart;
|
||||||
protected _params?: PanelInitParameters;
|
protected _params?: PanelInitParameters;
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ import { CompositeDisposable, IDisposable, Disposable } from '../lifecycle';
|
|||||||
|
|
||||||
export class BranchNode extends CompositeDisposable implements IView {
|
export class BranchNode extends CompositeDisposable implements IView {
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
private splitview: Splitview;
|
private readonly splitview: Splitview;
|
||||||
private _orthogonalSize: number;
|
private _orthogonalSize: number;
|
||||||
private _size: number;
|
private _size: number;
|
||||||
private _childrenDisposable: IDisposable = Disposable.NONE;
|
private _childrenDisposable: IDisposable = Disposable.NONE;
|
||||||
@ -33,9 +33,12 @@ export class BranchNode extends CompositeDisposable implements IView {
|
|||||||
readonly onDidChange: Event<{ size?: number; orthogonalSize?: number }> =
|
readonly onDidChange: Event<{ size?: number; orthogonalSize?: number }> =
|
||||||
this._onDidChange.event;
|
this._onDidChange.event;
|
||||||
|
|
||||||
private readonly _onDidVisibilityChange = new Emitter<boolean>();
|
private readonly _onDidVisibilityChange = new Emitter<{
|
||||||
readonly onDidVisibilityChange: Event<boolean> =
|
visible: boolean;
|
||||||
this._onDidVisibilityChange.event;
|
}>();
|
||||||
|
readonly onDidVisibilityChange: Event<{
|
||||||
|
visible: boolean;
|
||||||
|
}> = this._onDidVisibilityChange.event;
|
||||||
|
|
||||||
get width(): number {
|
get width(): number {
|
||||||
return this.orientation === Orientation.HORIZONTAL
|
return this.orientation === Orientation.HORIZONTAL
|
||||||
@ -139,6 +142,20 @@ export class BranchNode extends CompositeDisposable implements IView {
|
|||||||
this.splitview.disabled = value;
|
this.splitview.disabled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get margin(): number {
|
||||||
|
return this.splitview.margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
set margin(value: number) {
|
||||||
|
this.splitview.margin = value;
|
||||||
|
|
||||||
|
this.children.forEach((child) => {
|
||||||
|
if (child instanceof BranchNode) {
|
||||||
|
child.margin = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly orientation: Orientation,
|
readonly orientation: Orientation,
|
||||||
readonly proportionalLayout: boolean,
|
readonly proportionalLayout: boolean,
|
||||||
@ -146,6 +163,7 @@ export class BranchNode extends CompositeDisposable implements IView {
|
|||||||
size: number,
|
size: number,
|
||||||
orthogonalSize: number,
|
orthogonalSize: number,
|
||||||
disabled: boolean,
|
disabled: boolean,
|
||||||
|
margin: number | undefined,
|
||||||
childDescriptors?: INodeDescriptor[]
|
childDescriptors?: INodeDescriptor[]
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
@ -153,13 +171,14 @@ export class BranchNode extends CompositeDisposable implements IView {
|
|||||||
this._size = size;
|
this._size = size;
|
||||||
|
|
||||||
this.element = document.createElement('div');
|
this.element = document.createElement('div');
|
||||||
this.element.className = 'branch-node';
|
this.element.className = 'dv-branch-node';
|
||||||
|
|
||||||
if (!childDescriptors) {
|
if (!childDescriptors) {
|
||||||
this.splitview = new Splitview(this.element, {
|
this.splitview = new Splitview(this.element, {
|
||||||
orientation: this.orientation,
|
orientation: this.orientation,
|
||||||
proportionalLayout,
|
proportionalLayout,
|
||||||
styles,
|
styles,
|
||||||
|
margin,
|
||||||
});
|
});
|
||||||
this.splitview.layout(this.size, this.orthogonalSize);
|
this.splitview.layout(this.size, this.orthogonalSize);
|
||||||
} else {
|
} else {
|
||||||
@ -184,6 +203,7 @@ export class BranchNode extends CompositeDisposable implements IView {
|
|||||||
descriptor,
|
descriptor,
|
||||||
proportionalLayout,
|
proportionalLayout,
|
||||||
styles,
|
styles,
|
||||||
|
margin,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,10 +220,8 @@ export class BranchNode extends CompositeDisposable implements IView {
|
|||||||
this.setupChildrenEvents();
|
this.setupChildrenEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisible(visible: boolean): void {
|
setVisible(_visible: boolean): void {
|
||||||
for (const child of this.children) {
|
// noop
|
||||||
child.setVisible(visible);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isChildVisible(index: number): boolean {
|
isChildVisible(index: number): boolean {
|
||||||
@ -224,7 +242,9 @@ export class BranchNode extends CompositeDisposable implements IView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wereAllChildrenHidden = this.splitview.contentSize === 0;
|
const wereAllChildrenHidden = this.splitview.contentSize === 0;
|
||||||
|
|
||||||
this.splitview.setViewVisible(index, visible);
|
this.splitview.setViewVisible(index, visible);
|
||||||
|
// }
|
||||||
const areAllChildrenHidden = this.splitview.contentSize === 0;
|
const areAllChildrenHidden = this.splitview.contentSize === 0;
|
||||||
|
|
||||||
// If all children are hidden then the parent should hide the entire splitview
|
// If all children are hidden then the parent should hide the entire splitview
|
||||||
@ -233,7 +253,7 @@ export class BranchNode extends CompositeDisposable implements IView {
|
|||||||
(visible && wereAllChildrenHidden) ||
|
(visible && wereAllChildrenHidden) ||
|
||||||
(!visible && areAllChildrenHidden)
|
(!visible && areAllChildrenHidden)
|
||||||
) {
|
) {
|
||||||
this._onDidVisibilityChange.fire(visible);
|
this._onDidVisibilityChange.fire({ visible });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +355,7 @@ export class BranchNode extends CompositeDisposable implements IView {
|
|||||||
}),
|
}),
|
||||||
...this.children.map((c, i) => {
|
...this.children.map((c, i) => {
|
||||||
if (c instanceof BranchNode) {
|
if (c instanceof BranchNode) {
|
||||||
return c.onDidVisibilityChange((visible) => {
|
return c.onDidVisibilityChange(({ visible }) => {
|
||||||
this.setChildVisible(i, visible);
|
this.setChildVisible(i, visible);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.grid-view,
|
.dv-grid-view,
|
||||||
.branch-node {
|
.dv-branch-node {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,8 @@ function flipNode<T extends Node>(
|
|||||||
node.styles,
|
node.styles,
|
||||||
size,
|
size,
|
||||||
orthogonalSize,
|
orthogonalSize,
|
||||||
node.disabled
|
node.disabled,
|
||||||
|
node.margin
|
||||||
);
|
);
|
||||||
|
|
||||||
let totalSize = 0;
|
let totalSize = 0;
|
||||||
@ -112,7 +113,7 @@ export function getGridLocation(element: HTMLElement): number[] {
|
|||||||
throw new Error('Invalid grid element');
|
throw new Error('Invalid grid element');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/\bgrid-view\b/.test(parentElement.className)) {
|
if (/\bdv-grid-view\b/.test(parentElement.className)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,6 +172,7 @@ export interface IGridView {
|
|||||||
readonly maximumWidth: number;
|
readonly maximumWidth: number;
|
||||||
readonly minimumHeight: number;
|
readonly minimumHeight: number;
|
||||||
readonly maximumHeight: number;
|
readonly maximumHeight: number;
|
||||||
|
readonly isVisible: boolean;
|
||||||
priority?: LayoutPriority;
|
priority?: LayoutPriority;
|
||||||
layout(width: number, height: number): void;
|
layout(width: number, height: number): void;
|
||||||
toJSON(): object;
|
toJSON(): object;
|
||||||
@ -263,11 +265,21 @@ export interface IViewDeserializer {
|
|||||||
fromJSON: (data: ISerializedLeafNode) => IGridView;
|
fromJSON: (data: ISerializedLeafNode) => IGridView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SerializedNodeDescriptor {
|
||||||
|
location: number[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface SerializedGridview<T> {
|
export interface SerializedGridview<T> {
|
||||||
root: SerializedGridObject<T>;
|
root: SerializedGridObject<T>;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
orientation: Orientation;
|
orientation: Orientation;
|
||||||
|
maximizedNode?: SerializedNodeDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MaximizedViewChanged {
|
||||||
|
view: IGridView;
|
||||||
|
isMaximized: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Gridview implements IDisposable {
|
export class Gridview implements IDisposable {
|
||||||
@ -275,6 +287,7 @@ export class Gridview implements IDisposable {
|
|||||||
|
|
||||||
private _root: BranchNode | undefined;
|
private _root: BranchNode | undefined;
|
||||||
private _locked = false;
|
private _locked = false;
|
||||||
|
private _margin = 0;
|
||||||
private _maximizedNode:
|
private _maximizedNode:
|
||||||
| { leaf: LeafNode; hiddenOnMaximize: LeafNode[] }
|
| { leaf: LeafNode; hiddenOnMaximize: LeafNode[] }
|
||||||
| undefined = undefined;
|
| undefined = undefined;
|
||||||
@ -287,7 +300,11 @@ export class Gridview implements IDisposable {
|
|||||||
readonly onDidChange: Event<{ size?: number; orthogonalSize?: number }> =
|
readonly onDidChange: Event<{ size?: number; orthogonalSize?: number }> =
|
||||||
this._onDidChange.event;
|
this._onDidChange.event;
|
||||||
|
|
||||||
private readonly _onDidMaximizedNodeChange = new Emitter<void>();
|
private readonly _onDidViewVisibilityChange = new Emitter<void>();
|
||||||
|
readonly onDidViewVisibilityChange = this._onDidViewVisibilityChange.event;
|
||||||
|
|
||||||
|
private readonly _onDidMaximizedNodeChange =
|
||||||
|
new Emitter<MaximizedViewChanged>();
|
||||||
readonly onDidMaximizedNodeChange = this._onDidMaximizedNodeChange.event;
|
readonly onDidMaximizedNodeChange = this._onDidMaximizedNodeChange.event;
|
||||||
|
|
||||||
public get length(): number {
|
public get length(): number {
|
||||||
@ -356,6 +373,15 @@ export class Gridview implements IDisposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get margin(): number {
|
||||||
|
return this._margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
set margin(value: number) {
|
||||||
|
this._margin = value;
|
||||||
|
this.root.margin = value;
|
||||||
|
}
|
||||||
|
|
||||||
maximizedView(): IGridView | undefined {
|
maximizedView(): IGridView | undefined {
|
||||||
return this._maximizedNode?.leaf.view;
|
return this._maximizedNode?.leaf.view;
|
||||||
}
|
}
|
||||||
@ -380,6 +406,8 @@ export class Gridview implements IDisposable {
|
|||||||
this.exitMaximizedView();
|
this.exitMaximizedView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serializeBranchNode(this.getView(), this.orientation);
|
||||||
|
|
||||||
const hiddenOnMaximize: LeafNode[] = [];
|
const hiddenOnMaximize: LeafNode[] = [];
|
||||||
|
|
||||||
function hideAllViewsBut(parent: BranchNode, exclude: LeafNode): void {
|
function hideAllViewsBut(parent: BranchNode, exclude: LeafNode): void {
|
||||||
@ -401,7 +429,10 @@ export class Gridview implements IDisposable {
|
|||||||
|
|
||||||
hideAllViewsBut(this.root, node);
|
hideAllViewsBut(this.root, node);
|
||||||
this._maximizedNode = { leaf: node, hiddenOnMaximize };
|
this._maximizedNode = { leaf: node, hiddenOnMaximize };
|
||||||
this._onDidMaximizedNodeChange.fire();
|
this._onDidMaximizedNodeChange.fire({
|
||||||
|
view: node.view,
|
||||||
|
isMaximized: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
exitMaximizedView(): void {
|
exitMaximizedView(): void {
|
||||||
@ -426,33 +457,68 @@ export class Gridview implements IDisposable {
|
|||||||
|
|
||||||
showViewsInReverseOrder(this.root);
|
showViewsInReverseOrder(this.root);
|
||||||
|
|
||||||
|
const tmp = this._maximizedNode.leaf;
|
||||||
this._maximizedNode = undefined;
|
this._maximizedNode = undefined;
|
||||||
this._onDidMaximizedNodeChange.fire();
|
this._onDidMaximizedNodeChange.fire({
|
||||||
|
view: tmp.view,
|
||||||
|
isMaximized: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public serialize(): SerializedGridview<any> {
|
public serialize(): SerializedGridview<any> {
|
||||||
|
const maximizedView = this.maximizedView();
|
||||||
|
|
||||||
|
let maxmizedViewLocation: number[] | undefined;
|
||||||
|
|
||||||
|
if (maximizedView) {
|
||||||
|
/**
|
||||||
|
* The minimum information we can get away with in order to serialize a maxmized view is it's location within the grid
|
||||||
|
* which is represented as a branch of indices
|
||||||
|
*/
|
||||||
|
maxmizedViewLocation = getGridLocation(maximizedView.element);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.hasMaximizedView()) {
|
if (this.hasMaximizedView()) {
|
||||||
/**
|
/**
|
||||||
* do not persist maximized view state
|
* the saved layout cannot be in its maxmized state otherwise all of the underlying
|
||||||
* firstly exit any maximized views to ensure the correct dimensions are persisted
|
* view dimensions will be wrong
|
||||||
|
*
|
||||||
|
* To counteract this we temporaily remove the maximized view to compute the serialized output
|
||||||
|
* of the grid before adding back the maxmized view as to not alter the layout from the users
|
||||||
|
* perspective when `.toJSON()` is called
|
||||||
*/
|
*/
|
||||||
this.exitMaximizedView();
|
this.exitMaximizedView();
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = serializeBranchNode(this.getView(), this.orientation);
|
const root = serializeBranchNode(this.getView(), this.orientation);
|
||||||
|
|
||||||
return {
|
const resullt: SerializedGridview<any> = {
|
||||||
root,
|
root,
|
||||||
width: this.width,
|
width: this.width,
|
||||||
height: this.height,
|
height: this.height,
|
||||||
orientation: this.orientation,
|
orientation: this.orientation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (maxmizedViewLocation) {
|
||||||
|
resullt.maximizedNode = {
|
||||||
|
location: maxmizedViewLocation,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maximizedView) {
|
||||||
|
// replace any maximzied view that was removed for serialization purposes
|
||||||
|
this.maximizeView(maximizedView);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resullt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
this.disposable.dispose();
|
this.disposable.dispose();
|
||||||
this._onDidChange.dispose();
|
this._onDidChange.dispose();
|
||||||
this._onDidMaximizedNodeChange.dispose();
|
this._onDidMaximizedNodeChange.dispose();
|
||||||
|
this._onDidViewVisibilityChange.dispose();
|
||||||
|
|
||||||
this.root.dispose();
|
this.root.dispose();
|
||||||
this._maximizedNode = undefined;
|
this._maximizedNode = undefined;
|
||||||
this.element.remove();
|
this.element.remove();
|
||||||
@ -466,7 +532,8 @@ export class Gridview implements IDisposable {
|
|||||||
this.styles,
|
this.styles,
|
||||||
this.root.size,
|
this.root.size,
|
||||||
this.root.orthogonalSize,
|
this.root.orthogonalSize,
|
||||||
this._locked
|
this.locked,
|
||||||
|
this.margin
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,6 +551,24 @@ export class Gridview implements IDisposable {
|
|||||||
deserializer,
|
deserializer,
|
||||||
height
|
height
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The deserialied layout must be positioned through this.layout(...)
|
||||||
|
* before any maximizedNode can be positioned
|
||||||
|
*/
|
||||||
|
this.layout(json.width, json.height);
|
||||||
|
|
||||||
|
if (json.maximizedNode) {
|
||||||
|
const location = json.maximizedNode.location;
|
||||||
|
|
||||||
|
const [_, node] = this.getNode(location);
|
||||||
|
|
||||||
|
if (!(node instanceof LeafNode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.maximizeView(node.view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _deserialize(
|
private _deserialize(
|
||||||
@ -527,16 +612,17 @@ export class Gridview implements IDisposable {
|
|||||||
this.styles,
|
this.styles,
|
||||||
node.size, // <- orthogonal size - flips at each depth
|
node.size, // <- orthogonal size - flips at each depth
|
||||||
orthogonalSize, // <- size - flips at each depth,
|
orthogonalSize, // <- size - flips at each depth,
|
||||||
this._locked,
|
this.locked,
|
||||||
|
this.margin,
|
||||||
children
|
children
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
result = new LeafNode(
|
const view = deserializer.fromJSON(node);
|
||||||
deserializer.fromJSON(node),
|
if (typeof node.visible === 'boolean') {
|
||||||
orientation,
|
view.setVisible?.(node.visible);
|
||||||
orthogonalSize,
|
}
|
||||||
node.size
|
|
||||||
);
|
result = new LeafNode(view, orientation, orthogonalSize, node.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -580,7 +666,8 @@ export class Gridview implements IDisposable {
|
|||||||
this.styles,
|
this.styles,
|
||||||
this.root.orthogonalSize,
|
this.root.orthogonalSize,
|
||||||
this.root.size,
|
this.root.size,
|
||||||
this._locked
|
this.locked,
|
||||||
|
this.margin
|
||||||
);
|
);
|
||||||
|
|
||||||
if (oldRoot.children.length === 0) {
|
if (oldRoot.children.length === 0) {
|
||||||
@ -686,17 +773,24 @@ export class Gridview implements IDisposable {
|
|||||||
constructor(
|
constructor(
|
||||||
readonly proportionalLayout: boolean,
|
readonly proportionalLayout: boolean,
|
||||||
readonly styles: ISplitviewStyles | undefined,
|
readonly styles: ISplitviewStyles | undefined,
|
||||||
orientation: Orientation
|
orientation: Orientation,
|
||||||
|
locked?: boolean,
|
||||||
|
margin?: number
|
||||||
) {
|
) {
|
||||||
this.element = document.createElement('div');
|
this.element = document.createElement('div');
|
||||||
this.element.className = 'grid-view';
|
this.element.className = 'dv-grid-view';
|
||||||
|
|
||||||
|
this._locked = locked ?? false;
|
||||||
|
this._margin = margin ?? 0;
|
||||||
|
|
||||||
this.root = new BranchNode(
|
this.root = new BranchNode(
|
||||||
orientation,
|
orientation,
|
||||||
proportionalLayout,
|
proportionalLayout,
|
||||||
styles,
|
styles,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
this._locked
|
this.locked,
|
||||||
|
this.margin
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -723,6 +817,8 @@ export class Gridview implements IDisposable {
|
|||||||
throw new Error('Invalid from location');
|
throw new Error('Invalid from location');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._onDidViewVisibilityChange.fire();
|
||||||
|
|
||||||
parent.setChildVisible(index, visible);
|
parent.setChildVisible(index, visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -781,7 +877,8 @@ export class Gridview implements IDisposable {
|
|||||||
this.styles,
|
this.styles,
|
||||||
parent.size,
|
parent.size,
|
||||||
parent.orthogonalSize,
|
parent.orthogonalSize,
|
||||||
this._locked
|
this.locked,
|
||||||
|
this.margin
|
||||||
);
|
);
|
||||||
grandParent.addChild(newParent, parent.size, parentIndex);
|
grandParent.addChild(newParent, parent.size, parentIndex);
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ import {
|
|||||||
} from './gridviewPanel';
|
} from './gridviewPanel';
|
||||||
import { BaseComponentOptions, Parameters } from '../panel/types';
|
import { BaseComponentOptions, Parameters } from '../panel/types';
|
||||||
import { Orientation, Sizing } from '../splitview/splitview';
|
import { Orientation, Sizing } from '../splitview/splitview';
|
||||||
import { createComponent } from '../panel/componentFactory';
|
|
||||||
import { Emitter, Event } from '../events';
|
import { Emitter, Event } from '../events';
|
||||||
import { Position } from '../dnd/droptarget';
|
import { Position } from '../dnd/droptarget';
|
||||||
|
|
||||||
@ -49,15 +48,10 @@ export interface IGridPanelComponentView extends IGridPanelView {
|
|||||||
init: (params: GridviewInitParameters) => void;
|
init: (params: GridviewInitParameters) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GridviewComponentUpdateOptions = Pick<
|
|
||||||
GridviewComponentOptions,
|
|
||||||
'orientation' | 'components' | 'frameworkComponents'
|
|
||||||
>;
|
|
||||||
|
|
||||||
export interface IGridviewComponent extends IBaseGrid<GridviewPanel> {
|
export interface IGridviewComponent extends IBaseGrid<GridviewPanel> {
|
||||||
readonly orientation: Orientation;
|
readonly orientation: Orientation;
|
||||||
readonly onDidLayoutFromJSON: Event<void>;
|
readonly onDidLayoutFromJSON: Event<void>;
|
||||||
updateOptions(options: Partial<GridviewComponentUpdateOptions>): void;
|
updateOptions(options: Partial<GridviewComponentOptions>): void;
|
||||||
addPanel<T extends object = Parameters>(
|
addPanel<T extends object = Parameters>(
|
||||||
options: AddComponentOptions<T>
|
options: AddComponentOptions<T>
|
||||||
): IGridviewPanel;
|
): IGridviewPanel;
|
||||||
@ -119,13 +113,15 @@ export class GridviewComponent
|
|||||||
this._deserializer = value;
|
this._deserializer = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(options: GridviewComponentOptions) {
|
constructor(container: HTMLElement, options: GridviewComponentOptions) {
|
||||||
super({
|
super(container, {
|
||||||
parentElement: options.parentElement,
|
proportionalLayout: options.proportionalLayout ?? true,
|
||||||
proportionalLayout: options.proportionalLayout,
|
|
||||||
orientation: options.orientation,
|
orientation: options.orientation,
|
||||||
styles: options.styles,
|
styles: options.hideBorders
|
||||||
|
? { separatorBorder: 'transparent' }
|
||||||
|
: undefined,
|
||||||
disableAutoResizing: options.disableAutoResizing,
|
disableAutoResizing: options.disableAutoResizing,
|
||||||
|
className: options.className,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._options = options;
|
this._options = options;
|
||||||
@ -144,16 +140,11 @@ export class GridviewComponent
|
|||||||
this._onDidActiveGroupChange.fire(event);
|
this._onDidActiveGroupChange.fire(event);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!this.options.components) {
|
|
||||||
this.options.components = {};
|
|
||||||
}
|
|
||||||
if (!this.options.frameworkComponents) {
|
|
||||||
this.options.frameworkComponents = {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOptions(options: Partial<GridviewComponentUpdateOptions>): void {
|
override updateOptions(options: Partial<GridviewComponentOptions>): void {
|
||||||
|
super.updateOptions(options);
|
||||||
|
|
||||||
const hasOrientationChanged =
|
const hasOrientationChanged =
|
||||||
typeof options.orientation === 'string' &&
|
typeof options.orientation === 'string' &&
|
||||||
this.gridview.orientation !== options.orientation;
|
this.gridview.orientation !== options.orientation;
|
||||||
@ -219,19 +210,11 @@ export class GridviewComponent
|
|||||||
this.gridview.deserialize(grid, {
|
this.gridview.deserialize(grid, {
|
||||||
fromJSON: (node) => {
|
fromJSON: (node) => {
|
||||||
const { data } = node;
|
const { data } = node;
|
||||||
const view = createComponent(
|
|
||||||
data.id,
|
const view = this.options.createComponent({
|
||||||
data.component,
|
id: data.id,
|
||||||
this.options.components ?? {},
|
name: data.component,
|
||||||
this.options.frameworkComponents ?? {},
|
});
|
||||||
this.options.frameworkComponentFactory
|
|
||||||
? {
|
|
||||||
createComponent:
|
|
||||||
this.options.frameworkComponentFactory
|
|
||||||
.createComponent,
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
queue.push(() =>
|
queue.push(() =>
|
||||||
view.init({
|
view.init({
|
||||||
@ -366,19 +349,10 @@ export class GridviewComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const view = createComponent(
|
const view = this.options.createComponent({
|
||||||
options.id,
|
id: options.id,
|
||||||
options.component,
|
name: options.component,
|
||||||
this.options.components ?? {},
|
});
|
||||||
this.options.frameworkComponents ?? {},
|
|
||||||
this.options.frameworkComponentFactory
|
|
||||||
? {
|
|
||||||
createComponent:
|
|
||||||
this.options.frameworkComponentFactory
|
|
||||||
.createComponent,
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
view.init({
|
view.init({
|
||||||
params: options.params ?? {},
|
params: options.params ?? {},
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { PanelInitParameters } from '../panel/types';
|
import { PanelInitParameters } from '../panel/types';
|
||||||
import {
|
import { IGridPanelComponentView } from './gridviewComponent';
|
||||||
GridviewComponent,
|
|
||||||
IGridPanelComponentView,
|
|
||||||
} from './gridviewComponent';
|
|
||||||
import { FunctionOrValue } from '../types';
|
import { FunctionOrValue } from '../types';
|
||||||
import {
|
import {
|
||||||
BasePanelView,
|
BasePanelView,
|
||||||
@ -18,6 +15,13 @@ import { Emitter, Event } from '../events';
|
|||||||
import { IViewSize } from './gridview';
|
import { IViewSize } from './gridview';
|
||||||
import { BaseGrid, IGridPanelView } from './baseComponentGridview';
|
import { BaseGrid, IGridPanelView } from './baseComponentGridview';
|
||||||
|
|
||||||
|
export interface Contraints {
|
||||||
|
minimumWidth?: number;
|
||||||
|
maximumWidth?: number;
|
||||||
|
minimumHeight?: number;
|
||||||
|
maximumHeight?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GridviewInitParameters extends PanelInitParameters {
|
export interface GridviewInitParameters extends PanelInitParameters {
|
||||||
minimumWidth?: number;
|
minimumWidth?: number;
|
||||||
maximumWidth?: number;
|
maximumWidth?: number;
|
||||||
@ -70,6 +74,38 @@ export abstract class GridviewPanel<
|
|||||||
}
|
}
|
||||||
|
|
||||||
get minimumWidth(): number {
|
get minimumWidth(): number {
|
||||||
|
/**
|
||||||
|
* defer to protected function to allow subclasses to override easily.
|
||||||
|
* see https://github.com/microsoft/TypeScript/issues/338
|
||||||
|
*/
|
||||||
|
return this.__minimumWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
get minimumHeight(): number {
|
||||||
|
/**
|
||||||
|
* defer to protected function to allow subclasses to override easily.
|
||||||
|
* see https://github.com/microsoft/TypeScript/issues/338
|
||||||
|
*/
|
||||||
|
return this.__minimumHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
get maximumHeight(): number {
|
||||||
|
/**
|
||||||
|
* defer to protected function to allow subclasses to override easily.
|
||||||
|
* see https://github.com/microsoft/TypeScript/issues/338
|
||||||
|
*/
|
||||||
|
return this.__maximumHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
get maximumWidth(): number {
|
||||||
|
/**
|
||||||
|
* defer to protected function to allow subclasses to override easily.
|
||||||
|
* see https://github.com/microsoft/TypeScript/issues/338
|
||||||
|
*/
|
||||||
|
return this.__maximumWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected __minimumWidth(): number {
|
||||||
const width =
|
const width =
|
||||||
typeof this._minimumWidth === 'function'
|
typeof this._minimumWidth === 'function'
|
||||||
? this._minimumWidth()
|
? this._minimumWidth()
|
||||||
@ -83,35 +119,7 @@ export abstract class GridviewPanel<
|
|||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
get minimumHeight(): number {
|
protected __maximumWidth(): number {
|
||||||
const height =
|
|
||||||
typeof this._minimumHeight === 'function'
|
|
||||||
? this._minimumHeight()
|
|
||||||
: this._minimumHeight;
|
|
||||||
|
|
||||||
if (height !== this._evaluatedMinimumHeight) {
|
|
||||||
this._evaluatedMinimumHeight = height;
|
|
||||||
this.updateConstraints();
|
|
||||||
}
|
|
||||||
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
get maximumHeight(): number {
|
|
||||||
const height =
|
|
||||||
typeof this._maximumHeight === 'function'
|
|
||||||
? this._maximumHeight()
|
|
||||||
: this._maximumHeight;
|
|
||||||
|
|
||||||
if (height !== this._evaluatedMaximumHeight) {
|
|
||||||
this._evaluatedMaximumHeight = height;
|
|
||||||
this.updateConstraints();
|
|
||||||
}
|
|
||||||
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
get maximumWidth(): number {
|
|
||||||
const width =
|
const width =
|
||||||
typeof this._maximumWidth === 'function'
|
typeof this._maximumWidth === 'function'
|
||||||
? this._maximumWidth()
|
? this._maximumWidth()
|
||||||
@ -125,10 +133,42 @@ export abstract class GridviewPanel<
|
|||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected __minimumHeight(): number {
|
||||||
|
const height =
|
||||||
|
typeof this._minimumHeight === 'function'
|
||||||
|
? this._minimumHeight()
|
||||||
|
: this._minimumHeight;
|
||||||
|
|
||||||
|
if (height !== this._evaluatedMinimumHeight) {
|
||||||
|
this._evaluatedMinimumHeight = height;
|
||||||
|
this.updateConstraints();
|
||||||
|
}
|
||||||
|
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected __maximumHeight(): number {
|
||||||
|
const height =
|
||||||
|
typeof this._maximumHeight === 'function'
|
||||||
|
? this._maximumHeight()
|
||||||
|
: this._maximumHeight;
|
||||||
|
|
||||||
|
if (height !== this._evaluatedMaximumHeight) {
|
||||||
|
this._evaluatedMaximumHeight = height;
|
||||||
|
this.updateConstraints();
|
||||||
|
}
|
||||||
|
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
get isActive(): boolean {
|
get isActive(): boolean {
|
||||||
return this.api.isActive;
|
return this.api.isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isVisible(): boolean {
|
||||||
|
return this.api.isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
id: string,
|
id: string,
|
||||||
component: string,
|
component: string,
|
||||||
|
@ -17,7 +17,7 @@ export class LeafNode implements IView {
|
|||||||
this._onDidChange.event;
|
this._onDidChange.event;
|
||||||
private _size: number;
|
private _size: number;
|
||||||
private _orthogonalSize: number;
|
private _orthogonalSize: number;
|
||||||
private _disposable: IDisposable;
|
private readonly _disposable: IDisposable;
|
||||||
|
|
||||||
private get minimumWidth(): number {
|
private get minimumWidth(): number {
|
||||||
return this.view.minimumWidth;
|
return this.view.minimumWidth;
|
||||||
|
@ -1,21 +1,34 @@
|
|||||||
import { GridviewPanel } from './gridviewPanel';
|
import { GridviewPanel } from './gridviewPanel';
|
||||||
import { ISplitviewStyles, Orientation } from '../splitview/splitview';
|
import { Orientation } from '../splitview/splitview';
|
||||||
import {
|
import { CreateComponentOptions } from '../dockview/options';
|
||||||
ComponentConstructor,
|
|
||||||
FrameworkFactory,
|
|
||||||
} from '../panel/componentFactory';
|
|
||||||
|
|
||||||
export interface GridviewComponentOptions {
|
export interface GridviewOptions {
|
||||||
disableAutoResizing?: boolean;
|
disableAutoResizing?: boolean;
|
||||||
proportionalLayout: boolean;
|
proportionalLayout?: boolean;
|
||||||
orientation: Orientation;
|
orientation: Orientation;
|
||||||
components?: {
|
className?: string;
|
||||||
[componentName: string]: ComponentConstructor<GridviewPanel>;
|
hideBorders?: boolean;
|
||||||
};
|
|
||||||
frameworkComponents?: {
|
|
||||||
[componentName: string]: any;
|
|
||||||
};
|
|
||||||
frameworkComponentFactory?: FrameworkFactory<GridviewPanel>;
|
|
||||||
styles?: ISplitviewStyles;
|
|
||||||
parentElement: HTMLElement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GridviewFrameworkOptions {
|
||||||
|
createComponent: (options: CreateComponentOptions) => GridviewPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GridviewComponentOptions = GridviewOptions &
|
||||||
|
GridviewFrameworkOptions;
|
||||||
|
|
||||||
|
export const PROPERTY_KEYS_GRIDVIEW: (keyof GridviewOptions)[] = (() => {
|
||||||
|
/**
|
||||||
|
* by readong the keys from an empty value object TypeScript will error
|
||||||
|
* when we add or remove new properties to `DockviewOptions`
|
||||||
|
*/
|
||||||
|
const properties: Record<keyof GridviewOptions, undefined> = {
|
||||||
|
disableAutoResizing: undefined,
|
||||||
|
proportionalLayout: undefined,
|
||||||
|
orientation: undefined,
|
||||||
|
hideBorders: undefined,
|
||||||
|
className: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Object.keys(properties) as (keyof GridviewOptions)[];
|
||||||
|
})();
|
||||||
|
@ -1,31 +1,48 @@
|
|||||||
export * from './dnd/dataTransfer';
|
export {
|
||||||
|
getPaneData,
|
||||||
|
getPanelData,
|
||||||
|
PaneTransfer,
|
||||||
|
PanelTransfer,
|
||||||
|
} from './dnd/dataTransfer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Events, Emitters and Disposables are very common concepts that most codebases will contain.
|
* Events, Emitters and Disposables are very common concepts that many codebases will contain, however we need
|
||||||
* We export them with a 'Dockview' prefix here to prevent accidental use by others.
|
* to export them for dockview framework packages to use.
|
||||||
|
* To be a good citizen these are exported with a `Dockview` prefix to prevent accidental use by others.
|
||||||
*/
|
*/
|
||||||
export { Emitter as DockviewEmitter, Event as DockviewEvent } from './events';
|
export { Emitter as DockviewEmitter, Event as DockviewEvent } from './events';
|
||||||
export {
|
export {
|
||||||
IDisposable as IDockviewDisposable,
|
IDisposable as DockviewIDisposable,
|
||||||
MutableDisposable as DockviewMutableDisposable,
|
MutableDisposable as DockviewMutableDisposable,
|
||||||
CompositeDisposable as DockviewCompositeDisposable,
|
CompositeDisposable as DockviewCompositeDisposable,
|
||||||
|
Disposable as DockviewDisposable,
|
||||||
} from './lifecycle';
|
} from './lifecycle';
|
||||||
|
|
||||||
export * from './panel/types';
|
export * from './panel/types';
|
||||||
export * from './panel/componentFactory';
|
|
||||||
|
|
||||||
export * from './splitview/splitview';
|
export * from './splitview/splitview';
|
||||||
export {
|
export {
|
||||||
SplitviewComponentOptions,
|
SplitviewComponentOptions,
|
||||||
PanelViewInitParameters,
|
PanelViewInitParameters,
|
||||||
|
SplitviewOptions,
|
||||||
|
SplitviewFrameworkOptions,
|
||||||
|
PROPERTY_KEYS_SPLITVIEW,
|
||||||
} from './splitview/options';
|
} from './splitview/options';
|
||||||
|
|
||||||
export * from './paneview/paneview';
|
export * from './paneview/paneview';
|
||||||
export * from './gridview/gridview';
|
export * from './gridview/gridview';
|
||||||
export { GridviewComponentOptions } from './gridview/options';
|
export {
|
||||||
|
GridviewComponentOptions,
|
||||||
|
GridviewOptions,
|
||||||
|
GridviewFrameworkOptions,
|
||||||
|
PROPERTY_KEYS_GRIDVIEW,
|
||||||
|
} from './gridview/options';
|
||||||
export * from './gridview/baseComponentGridview';
|
export * from './gridview/baseComponentGridview';
|
||||||
|
|
||||||
export * from './paneview/draggablePaneviewPanel';
|
export {
|
||||||
|
DraggablePaneviewPanel,
|
||||||
|
PaneviewDidDropEvent as PaneviewDropEvent,
|
||||||
|
} from './paneview/draggablePaneviewPanel';
|
||||||
|
|
||||||
export * from './dockview/components/panel/content';
|
export * from './dockview/components/panel/content';
|
||||||
export * from './dockview/components/tab/tab';
|
export * from './dockview/components/tab/tab';
|
||||||
@ -47,22 +64,33 @@ export {
|
|||||||
} from './dockview/framework';
|
} from './dockview/framework';
|
||||||
|
|
||||||
export * from './dockview/options';
|
export * from './dockview/options';
|
||||||
|
export * from './dockview/theme';
|
||||||
export * from './dockview/dockviewPanel';
|
export * from './dockview/dockviewPanel';
|
||||||
export * from './dockview/components/tab/defaultTab';
|
export { DefaultTab } from './dockview/components/tab/defaultTab';
|
||||||
export * from './dockview/deserializer';
|
export {
|
||||||
|
DefaultDockviewDeserialzier,
|
||||||
|
IPanelDeserializer,
|
||||||
|
} from './dockview/deserializer';
|
||||||
|
|
||||||
export * from './dockview/dockviewComponent';
|
export * from './dockview/dockviewComponent';
|
||||||
export * from './gridview/gridviewComponent';
|
export * from './gridview/gridviewComponent';
|
||||||
export * from './splitview/splitviewComponent';
|
export * from './splitview/splitviewComponent';
|
||||||
export * from './paneview/paneviewComponent';
|
export * from './paneview/paneviewComponent';
|
||||||
export { PaneviewComponentOptions } from './paneview/options';
|
export {
|
||||||
|
PaneviewComponentOptions,
|
||||||
|
PaneviewOptions,
|
||||||
|
PaneviewFrameworkOptions,
|
||||||
|
PROPERTY_KEYS_PANEVIEW,
|
||||||
|
PaneviewUnhandledDragOverEvent,
|
||||||
|
PaneviewDndOverlayEvent,
|
||||||
|
} from './paneview/options';
|
||||||
|
|
||||||
export * from './gridview/gridviewPanel';
|
export * from './gridview/gridviewPanel';
|
||||||
export * from './splitview/splitviewPanel';
|
export { SplitviewPanel, ISplitviewPanel } from './splitview/splitviewPanel';
|
||||||
export * from './paneview/paneviewPanel';
|
export * from './paneview/paneviewPanel';
|
||||||
export * from './dockview/types';
|
export * from './dockview/types';
|
||||||
|
|
||||||
export { DockviewPanelRenderer } from './overlayRenderContainer';
|
export { DockviewPanelRenderer } from './overlay/overlayRenderContainer';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Position,
|
Position,
|
||||||
@ -71,6 +99,7 @@ export {
|
|||||||
MeasuredValue,
|
MeasuredValue,
|
||||||
DroptargetOverlayModel,
|
DroptargetOverlayModel,
|
||||||
} from './dnd/droptarget';
|
} from './dnd/droptarget';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
FocusEvent,
|
FocusEvent,
|
||||||
PanelDimensionChangeEvent,
|
PanelDimensionChangeEvent,
|
||||||
@ -87,6 +116,7 @@ export {
|
|||||||
TitleEvent,
|
TitleEvent,
|
||||||
RendererChangedEvent,
|
RendererChangedEvent,
|
||||||
DockviewPanelApi,
|
DockviewPanelApi,
|
||||||
|
DockviewPanelMoveParams,
|
||||||
} from './api/dockviewPanelApi';
|
} from './api/dockviewPanelApi';
|
||||||
export {
|
export {
|
||||||
PanelSizeEvent,
|
PanelSizeEvent,
|
||||||
@ -97,6 +127,7 @@ export { ExpansionEvent, PaneviewPanelApi } from './api/paneviewPanelApi';
|
|||||||
export {
|
export {
|
||||||
DockviewGroupPanelApi,
|
DockviewGroupPanelApi,
|
||||||
DockviewGroupPanelFloatingChangeEvent,
|
DockviewGroupPanelFloatingChangeEvent,
|
||||||
|
DockviewGroupMoveParams,
|
||||||
} from './api/dockviewGroupPanelApi';
|
} from './api/dockviewGroupPanelApi';
|
||||||
export {
|
export {
|
||||||
CommonApi,
|
CommonApi,
|
||||||
@ -105,3 +136,9 @@ export {
|
|||||||
GridviewApi,
|
GridviewApi,
|
||||||
DockviewApi,
|
DockviewApi,
|
||||||
} from './api/component.api';
|
} from './api/component.api';
|
||||||
|
export {
|
||||||
|
createDockview,
|
||||||
|
createGridview,
|
||||||
|
createPaneview,
|
||||||
|
createSplitview,
|
||||||
|
} from './api/entryPoints';
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
export const clamp = (value: number, min: number, max: number): number => {
|
export const clamp = (value: number, min: number, max: number): number => {
|
||||||
if (min > max) {
|
if (min > max) {
|
||||||
throw new Error(`${min} > ${max} is an invalid condition`);
|
/**
|
||||||
|
* caveat: an error should be thrown here if this was a proper `clamp` function but we need to handle
|
||||||
|
* cases where `min` > `max` and in those cases return `min`.
|
||||||
|
*/
|
||||||
|
return min;
|
||||||
}
|
}
|
||||||
return Math.min(max, Math.max(value, min));
|
return Math.min(max, Math.max(value, min));
|
||||||
};
|
};
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user