mirror of
https://github.com/mathuo/dockview
synced 2025-03-15 10:22:03 +00:00
code
This commit is contained in:
parent
47f27633da
commit
62b2e30ed6
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,4 +2,4 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
typedocs/
|
typedocs/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
lerna-debug.log
|
*-debug.log
|
1
.vscode/settings.json
vendored
Normal file
1
.vscode/settings.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
165
package-lock.json
generated
165
package-lock.json
generated
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "splitview",
|
"name": "splitview-root",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
@ -611,6 +611,57 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@gulp-sourcemaps/identity-map": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-ciiioYMLdo16ShmfHBXJBOFm3xPC4AuwO4xeRpFeHz7WK9PYsWCmigagG2XyzZpubK4a3qNKoUBDhbzHfa50LQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"acorn": "^5.0.3",
|
||||||
|
"css": "^2.2.1",
|
||||||
|
"normalize-path": "^2.1.1",
|
||||||
|
"source-map": "^0.6.0",
|
||||||
|
"through2": "^2.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": {
|
||||||
|
"version": "5.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
|
||||||
|
"integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"normalize-path": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
|
||||||
|
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"remove-trailing-separator": "^1.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@gulp-sourcemaps/map-sources": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"normalize-path": "^2.0.1",
|
||||||
|
"through2": "^2.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"normalize-path": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
|
||||||
|
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"remove-trailing-separator": "^1.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@istanbuljs/load-nyc-config": {
|
"@istanbuljs/load-nyc-config": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
||||||
@ -5788,6 +5839,28 @@
|
|||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"debug-fabulous": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"debug": "3.X",
|
||||||
|
"memoizee": "0.4.X",
|
||||||
|
"object-assign": "4.X"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "3.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||||
|
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"debuglog": {
|
"debuglog": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
|
||||||
@ -6467,6 +6540,16 @@
|
|||||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
|
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"event-emitter": {
|
||||||
|
"version": "0.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
|
||||||
|
"integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"d": "1",
|
||||||
|
"es5-ext": "~0.10.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"eventemitter3": {
|
"eventemitter3": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
|
||||||
@ -8809,6 +8892,39 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"gulp-sourcemaps": {
|
||||||
|
"version": "2.6.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz",
|
||||||
|
"integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@gulp-sourcemaps/identity-map": "1.X",
|
||||||
|
"@gulp-sourcemaps/map-sources": "1.X",
|
||||||
|
"acorn": "5.X",
|
||||||
|
"convert-source-map": "1.X",
|
||||||
|
"css": "2.X",
|
||||||
|
"debug-fabulous": "1.X",
|
||||||
|
"detect-newline": "2.X",
|
||||||
|
"graceful-fs": "4.X",
|
||||||
|
"source-map": "~0.6.0",
|
||||||
|
"strip-bom-string": "1.X",
|
||||||
|
"through2": "2.X"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": {
|
||||||
|
"version": "5.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
|
||||||
|
"integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"detect-newline": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
|
||||||
|
"integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"gulp-typescript": {
|
"gulp-typescript": {
|
||||||
"version": "6.0.0-alpha.1",
|
"version": "6.0.0-alpha.1",
|
||||||
"resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-6.0.0-alpha.1.tgz",
|
"resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-6.0.0-alpha.1.tgz",
|
||||||
@ -9916,6 +10032,12 @@
|
|||||||
"integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=",
|
"integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"is-promise": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"is-regex": {
|
"is-regex": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz",
|
||||||
@ -11710,6 +11832,15 @@
|
|||||||
"yallist": "^2.1.2"
|
"yallist": "^2.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lru-queue": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
|
||||||
|
"integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"es5-ext": "~0.10.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"macos-release": {
|
"macos-release": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz",
|
||||||
@ -11967,6 +12098,22 @@
|
|||||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
|
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"memoizee": {
|
||||||
|
"version": "0.4.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz",
|
||||||
|
"integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"d": "1",
|
||||||
|
"es5-ext": "^0.10.45",
|
||||||
|
"es6-weak-map": "^2.0.2",
|
||||||
|
"event-emitter": "^0.3.5",
|
||||||
|
"is-promise": "^2.1",
|
||||||
|
"lru-queue": "0.1",
|
||||||
|
"next-tick": "1",
|
||||||
|
"timers-ext": "^0.1.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"memory-fs": {
|
"memory-fs": {
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
|
||||||
@ -15658,6 +15805,12 @@
|
|||||||
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
|
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"strip-bom-string": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"strip-eof": {
|
"strip-eof": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
|
||||||
@ -15962,6 +16115,16 @@
|
|||||||
"setimmediate": "^1.0.4"
|
"setimmediate": "^1.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"timers-ext": {
|
||||||
|
"version": "0.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz",
|
||||||
|
"integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"es5-ext": "~0.10.46",
|
||||||
|
"next-tick": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tmp": {
|
"tmp": {
|
||||||
"version": "0.0.33",
|
"version": "0.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
"gulp-concat": "^2.6.1",
|
"gulp-concat": "^2.6.1",
|
||||||
"gulp-header": "^2.0.9",
|
"gulp-header": "^2.0.9",
|
||||||
"gulp-sass": "^4.1.0",
|
"gulp-sass": "^4.1.0",
|
||||||
|
"gulp-sourcemaps": "^2.6.5",
|
||||||
"gulp-typescript": "^6.0.0-alpha.1",
|
"gulp-typescript": "^6.0.0-alpha.1",
|
||||||
"jest": "^26.1.0",
|
"jest": "^26.1.0",
|
||||||
"jsdom": "^16.2.2",
|
"jsdom": "^16.2.2",
|
||||||
|
33
packages/splitview-demo/src/layout-grid/editorPanel.tsx
Normal file
33
packages/splitview-demo/src/layout-grid/editorPanel.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { Api, IPanelProps } from "splitview";
|
||||||
|
|
||||||
|
export const Editor = (props: IPanelProps & { layoutApi: Api }) => {
|
||||||
|
const [tabHeight, setTabHeight] = React.useState<number>(0);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (props.layoutApi) {
|
||||||
|
setTabHeight(props.layoutApi.getTabHeight());
|
||||||
|
}
|
||||||
|
}, [props.layoutApi]);
|
||||||
|
|
||||||
|
const onTabHeightChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const value = Number(event.target.value);
|
||||||
|
if (!Number.isNaN(value)) {
|
||||||
|
setTabHeight(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
props.layoutApi.setTabHeight(tabHeight);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: "100%", backgroundColor: "white", color: "black" }}>
|
||||||
|
<label>
|
||||||
|
Tab height
|
||||||
|
<input onChange={onTabHeightChange} value={tabHeight} type="number" />
|
||||||
|
<button onClick={onClick}>Apply</button>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -10,39 +10,9 @@ import {
|
|||||||
GroupChangeKind,
|
GroupChangeKind,
|
||||||
} from "splitview";
|
} from "splitview";
|
||||||
import { CustomTab } from "./customTab";
|
import { CustomTab } from "./customTab";
|
||||||
|
import { Editor } from "./editorPanel";
|
||||||
import { SplitPanel } from "./splitPanel";
|
import { SplitPanel } from "./splitPanel";
|
||||||
|
|
||||||
const Editor = (props: IPanelProps & { layoutApi: Api }) => {
|
|
||||||
const [tabHeight, setTabHeight] = React.useState<number>(0);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (props.layoutApi) {
|
|
||||||
setTabHeight(props.layoutApi.getTabHeight());
|
|
||||||
}
|
|
||||||
}, [props.layoutApi]);
|
|
||||||
|
|
||||||
const onTabHeightChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const value = Number(event.target.value);
|
|
||||||
if (!Number.isNaN(value)) {
|
|
||||||
setTabHeight(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onClick = () => {
|
|
||||||
props.layoutApi.setTabHeight(tabHeight);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ height: "100%", backgroundColor: "white", color: "black" }}>
|
|
||||||
<label>
|
|
||||||
Tab height
|
|
||||||
<input onChange={onTabHeightChange} value={tabHeight} type="number" />
|
|
||||||
<button onClick={onClick}>Apply</button>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
inner_component: (props: IPanelProps) => {
|
inner_component: (props: IPanelProps) => {
|
||||||
const _api = React.useRef<Api>();
|
const _api = React.useRef<Api>();
|
||||||
@ -51,7 +21,7 @@ const components = {
|
|||||||
const onReady = (event: OnReadyEvent) => {
|
const onReady = (event: OnReadyEvent) => {
|
||||||
_api.current = event.api;
|
_api.current = event.api;
|
||||||
|
|
||||||
const layout = props.api.getState()["layout"];
|
const layout = props.api.getStateKey<object>("layout");
|
||||||
if (layout) {
|
if (layout) {
|
||||||
event.api.deserialize(layout);
|
event.api.deserialize(layout);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import {
|
||||||
|
CompositeDisposable,
|
||||||
IPanelProps,
|
IPanelProps,
|
||||||
|
ISplitviewPanelProps,
|
||||||
Orientation,
|
Orientation,
|
||||||
SplitviewFacade,
|
SplitviewFacade,
|
||||||
SplitviewReadyEvent,
|
SplitviewReadyEvent,
|
||||||
@ -8,7 +10,20 @@ import {
|
|||||||
import { SplitViewComponent } from "splitview";
|
import { SplitViewComponent } from "splitview";
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
default1: (props) => {
|
default1: (props: ISplitviewPanelProps) => {
|
||||||
|
React.useEffect(() => {
|
||||||
|
const disposable = new CompositeDisposable();
|
||||||
|
disposable.addDisposables(
|
||||||
|
props.api.onDidPanelDimensionChange((event) => {
|
||||||
|
//
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return <div style={{ height: "100%", width: "100%" }}>hiya</div>;
|
return <div style={{ height: "100%", width: "100%" }}>hiya</div>;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -18,26 +33,47 @@ export const SplitPanel = (props: IPanelProps) => {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
props.api.onDidPanelDimensionChange((event) => {
|
props.api.onDidPanelDimensionChange((event) => {
|
||||||
// const [height,width] = [event.height, event.width]
|
api.current?.layout(event.width, event.height - 20);
|
||||||
// const [size, orthogonalSize] =
|
});
|
||||||
// props.orientation === Orientation.HORIZONTAL
|
|
||||||
// ? [width, height]
|
api.current.onChange((event) => {
|
||||||
// : [height, width];
|
props.api.setState("sview_layout", api.current.toJSON());
|
||||||
api.current?.layout(event.width, event.height);
|
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onReady = (event: SplitviewReadyEvent) => {
|
const onReady = (event: SplitviewReadyEvent) => {
|
||||||
event.api.addFromComponent({ id: "1", component: "default1" });
|
const existingLayout = props.api.getStateKey("sview_layout");
|
||||||
event.api.addFromComponent({ id: "2", component: "default1" });
|
|
||||||
|
if (existingLayout) {
|
||||||
|
event.api.deserialize(existingLayout);
|
||||||
|
} else {
|
||||||
|
event.api.addFromComponent({ id: "1", component: "default1" });
|
||||||
|
event.api.addFromComponent({ id: "2", component: "default1" });
|
||||||
|
}
|
||||||
api.current = event.api;
|
api.current = event.api;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onSave = () => {
|
||||||
|
props.api.setState("sview_layout", api.current.toJSON());
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SplitViewComponent
|
<div
|
||||||
components={components}
|
style={{
|
||||||
onReady={onReady}
|
display: "flex",
|
||||||
orientation={Orientation.VERTICAL}
|
flexDirection: "column",
|
||||||
/>
|
height: "100%",
|
||||||
|
color: "white",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ height: "20px", flexShrink: 0 }}>
|
||||||
|
<button onClick={onSave}>save</button>
|
||||||
|
</div>
|
||||||
|
<SplitViewComponent
|
||||||
|
components={components}
|
||||||
|
onReady={onReady}
|
||||||
|
orientation={Orientation.VERTICAL}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "dist/esm/index.js",
|
"main": "dist/esm/index.js",
|
||||||
"types": "dist/esm/index.d.ts",
|
"types": "dist/esm/index.d.ts",
|
||||||
|
"module": "dist/esm/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "gulp run",
|
"build": "gulp run",
|
||||||
"docs": "typedoc"
|
"docs": "typedoc"
|
||||||
|
@ -7,7 +7,7 @@ import { Event, Emitter, addDisposableListener } from "../events";
|
|||||||
import { IGroupAccessor, Layout } from "../layout";
|
import { IGroupAccessor, Layout } from "../layout";
|
||||||
import { toggleClass } from "../dom";
|
import { toggleClass } from "../dom";
|
||||||
import { ClosePanelResult, WatermarkPart } from "./panel/parts";
|
import { ClosePanelResult, WatermarkPart } from "./panel/parts";
|
||||||
import { IPanel } from "./panel/types";
|
import { IGroupPanel } from "./panel/types";
|
||||||
import { timeoutPromise } from "../async";
|
import { timeoutPromise } from "../async";
|
||||||
import {
|
import {
|
||||||
extractData,
|
extractData,
|
||||||
@ -52,40 +52,43 @@ interface GroupMoveEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupOptions {
|
export interface GroupOptions {
|
||||||
panels: IPanel[];
|
panels: IGroupPanel[];
|
||||||
activePanel?: IPanel;
|
activePanel?: IGroupPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupChangeEvent {
|
export interface GroupChangeEvent {
|
||||||
kind: GroupChangeKind;
|
kind: GroupChangeKind;
|
||||||
panel?: IPanel;
|
panel?: IGroupPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGroupview extends IDisposable, IGridView {
|
export interface IGroupview extends IDisposable, IGridView {
|
||||||
id: string;
|
id: string;
|
||||||
size: number;
|
size: number;
|
||||||
panels: IPanel[];
|
panels: IGroupPanel[];
|
||||||
tabHeight: number;
|
tabHeight: number;
|
||||||
setActive: (isActive: boolean) => void;
|
setActive: (isActive: boolean) => void;
|
||||||
// state
|
// state
|
||||||
isPanelActive: (panel: IPanel) => boolean;
|
isPanelActive: (panel: IGroupPanel) => boolean;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
activePanel: IPanel;
|
activePanel: IGroupPanel;
|
||||||
indexOf(panel: IPanel): number;
|
indexOf(panel: IGroupPanel): number;
|
||||||
// panel lifecycle
|
// panel lifecycle
|
||||||
openPanel(panel: IPanel, index?: number): void;
|
openPanel(panel: IGroupPanel, index?: number): void;
|
||||||
closePanel(panel: IPanel): Promise<boolean>;
|
closePanel(panel: IGroupPanel): Promise<boolean>;
|
||||||
closeAllPanels(): Promise<boolean>;
|
closeAllPanels(): Promise<boolean>;
|
||||||
containsPanel(panel: IPanel): boolean;
|
containsPanel(panel: IGroupPanel): boolean;
|
||||||
removePanel: (panelOrId: IPanel | string) => IPanel;
|
removePanel: (panelOrId: IGroupPanel | string) => IGroupPanel;
|
||||||
// events
|
// events
|
||||||
onDidGroupChange: Event<{ kind: GroupChangeKind }>;
|
onDidGroupChange: Event<{ kind: GroupChangeKind }>;
|
||||||
onMove: Event<GroupMoveEvent>;
|
onMove: Event<GroupMoveEvent>;
|
||||||
//
|
//
|
||||||
startActiveDrag(panel: IPanel): IDisposable;
|
startActiveDrag(panel: IGroupPanel): IDisposable;
|
||||||
//
|
//
|
||||||
moveToNext(options?: { panel?: IPanel; suppressRoll?: boolean }): void;
|
moveToNext(options?: { panel?: IGroupPanel; suppressRoll?: boolean }): void;
|
||||||
moveToPrevious(options?: { panel?: IPanel; suppressRoll?: boolean }): void;
|
moveToPrevious(options?: {
|
||||||
|
panel?: IGroupPanel;
|
||||||
|
suppressRoll?: boolean;
|
||||||
|
}): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupDropEvent {
|
export interface GroupDropEvent {
|
||||||
@ -100,14 +103,14 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
private tabContainer: ITabContainer;
|
private tabContainer: ITabContainer;
|
||||||
private contentContainer: IContentContainer;
|
private contentContainer: IContentContainer;
|
||||||
private _active: boolean;
|
private _active: boolean;
|
||||||
private _activePanel: IPanel;
|
private _activePanel: IGroupPanel;
|
||||||
private dropTarget: Droptarget;
|
private dropTarget: Droptarget;
|
||||||
private watermark: WatermarkPart;
|
private watermark: WatermarkPart;
|
||||||
|
|
||||||
private _width: number;
|
private _width: number;
|
||||||
private _height: number;
|
private _height: number;
|
||||||
|
|
||||||
private _panels: IPanel[] = [];
|
private _panels: IGroupPanel[] = [];
|
||||||
|
|
||||||
private readonly _onMove = new Emitter<GroupMoveEvent>();
|
private readonly _onMove = new Emitter<GroupMoveEvent>();
|
||||||
readonly onMove: Event<GroupMoveEvent> = this._onMove.event;
|
readonly onMove: Event<GroupMoveEvent> = this._onMove.event;
|
||||||
@ -168,7 +171,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
return Number.MAX_SAFE_INTEGER;
|
return Number.MAX_SAFE_INTEGER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public indexOf(panel: IPanel) {
|
public indexOf(panel: IGroupPanel) {
|
||||||
return this.tabContainer.indexOf(panel.id);
|
return this.tabContainer.indexOf(panel.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +182,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public startActiveDrag(panel: IPanel): IDisposable {
|
public startActiveDrag(panel: IGroupPanel): IDisposable {
|
||||||
const index = this.tabContainer.indexOf(panel.id);
|
const index = this.tabContainer.indexOf(panel.id);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
const tab = this.tabContainer.at(index);
|
const tab = this.tabContainer.at(index);
|
||||||
@ -193,7 +196,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
return Disposable.NONE;
|
return Disposable.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public moveToNext(options?: { panel?: IPanel; suppressRoll?: boolean }) {
|
public moveToNext(options?: { panel?: IGroupPanel; suppressRoll?: boolean }) {
|
||||||
if (!options) {
|
if (!options) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
@ -218,7 +221,10 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
this.openPanel(this.panels[normalizedIndex]);
|
this.openPanel(this.panels[normalizedIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public moveToPrevious(options?: { panel?: IPanel; suppressRoll?: boolean }) {
|
public moveToPrevious(options?: {
|
||||||
|
panel?: IGroupPanel;
|
||||||
|
suppressRoll?: boolean;
|
||||||
|
}) {
|
||||||
if (!options) {
|
if (!options) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
@ -243,7 +249,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
this.openPanel(this.panels[normalizedIndex]);
|
this.openPanel(this.panels[normalizedIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public containsPanel(panel: IPanel) {
|
public containsPanel(panel: IGroupPanel) {
|
||||||
return this.panels.includes(panel);
|
return this.panels.includes(panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +319,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
this.updateContainer();
|
this.updateContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public openPanel(panel: IPanel, index: number = this.panels.length) {
|
public openPanel(panel: IGroupPanel, index: number = this.panels.length) {
|
||||||
if (this._activePanel === panel) {
|
if (this._activePanel === panel) {
|
||||||
this.accessor.doSetGroupActive(this);
|
this.accessor.doSetGroupActive(this);
|
||||||
return;
|
return;
|
||||||
@ -330,7 +336,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
this.updateContainer();
|
this.updateContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public removePanel(groupItemOrId: IPanel | string): IPanel {
|
public removePanel(groupItemOrId: IGroupPanel | string): IGroupPanel {
|
||||||
const id =
|
const id =
|
||||||
typeof groupItemOrId === "string" ? groupItemOrId : groupItemOrId.id;
|
typeof groupItemOrId === "string" ? groupItemOrId : groupItemOrId.id;
|
||||||
|
|
||||||
@ -386,7 +392,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public closePanel = async (panel: IPanel) => {
|
public closePanel = async (panel: IGroupPanel) => {
|
||||||
if (panel.close && (await panel.close()) === ClosePanelResult.DONT_CLOSE) {
|
if (panel.close && (await panel.close()) === ClosePanelResult.DONT_CLOSE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -395,7 +401,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
private doClose(panel: IPanel) {
|
private doClose(panel: IGroupPanel) {
|
||||||
this._removePanel(panel);
|
this._removePanel(panel);
|
||||||
|
|
||||||
(this.accessor as Layout).unregisterPanel(panel);
|
(this.accessor as Layout).unregisterPanel(panel);
|
||||||
@ -407,7 +413,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public isPanelActive(panel: IPanel) {
|
public isPanelActive(panel: IGroupPanel) {
|
||||||
return this._activePanel === panel;
|
return this._activePanel === panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,7 +453,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _removePanel(panel: IPanel) {
|
private _removePanel(panel: IGroupPanel) {
|
||||||
const index = this._panels.indexOf(panel);
|
const index = this._panels.indexOf(panel);
|
||||||
|
|
||||||
const isActivePanel = this._activePanel === panel;
|
const isActivePanel = this._activePanel === panel;
|
||||||
@ -467,7 +473,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private doRemovePanel(panel: IPanel) {
|
private doRemovePanel(panel: IGroupPanel) {
|
||||||
const index = this.panels.indexOf(panel);
|
const index = this.panels.indexOf(panel);
|
||||||
|
|
||||||
if (this._activePanel === panel) {
|
if (this._activePanel === panel) {
|
||||||
@ -480,7 +486,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
this._onDidGroupChange.fire({ kind: GroupChangeKind.REMOVE_PANEL, panel });
|
this._onDidGroupChange.fire({ kind: GroupChangeKind.REMOVE_PANEL, panel });
|
||||||
}
|
}
|
||||||
|
|
||||||
private doAddPanel(panel: IPanel, index: number) {
|
private doAddPanel(panel: IGroupPanel, index: number) {
|
||||||
const existingPanel = this._panels.indexOf(panel);
|
const existingPanel = this._panels.indexOf(panel);
|
||||||
const hasExistingPabel = existingPanel > -1;
|
const hasExistingPabel = existingPanel > -1;
|
||||||
|
|
||||||
@ -494,7 +500,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
this._onDidGroupChange.fire({ kind: GroupChangeKind.ADD_PANEL });
|
this._onDidGroupChange.fire({ kind: GroupChangeKind.ADD_PANEL });
|
||||||
}
|
}
|
||||||
|
|
||||||
private doSetActivePanel(panel: IPanel) {
|
private doSetActivePanel(panel: IGroupPanel) {
|
||||||
this._activePanel = panel;
|
this._activePanel = panel;
|
||||||
this.tabContainer.setActivePanel(panel);
|
this.tabContainer.setActivePanel(panel);
|
||||||
panel.layout(this._width, this._height);
|
panel.layout(this._width, this._height);
|
||||||
|
@ -1,53 +1,39 @@
|
|||||||
import { IGroupview } from "../groupview";
|
import { IGroupview } from "../groupview";
|
||||||
import { Event, Emitter } from "../../events";
|
import { Event } from "../../events";
|
||||||
import { ClosePanelResult } from "./parts";
|
import { ClosePanelResult } from "./parts";
|
||||||
import { IPanel } from "./types";
|
import { IGroupPanel } from "./types";
|
||||||
import { CompositeDisposable, IDisposable } from "../../lifecycle";
|
import {
|
||||||
|
BasePanelApi,
|
||||||
|
IBasePanelApi,
|
||||||
|
PanelDimensionChangeEvent,
|
||||||
|
} from "../../panel/api";
|
||||||
|
|
||||||
export interface PanelStateChangeEvent {
|
export interface PanelStateChangeEvent {
|
||||||
isPanelVisible: boolean;
|
isPanelVisible: boolean;
|
||||||
isGroupActive: boolean;
|
isGroupActive: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelDimensionChangeEvent {
|
export interface PanelApi extends IBasePanelApi {
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PanelApi extends IDisposable {
|
|
||||||
onDidPanelStateChange: Event<PanelStateChangeEvent>;
|
onDidPanelStateChange: Event<PanelStateChangeEvent>;
|
||||||
onDidPanelDimensionChange: Event<PanelDimensionChangeEvent>;
|
|
||||||
isPanelVisible: boolean;
|
isPanelVisible: boolean;
|
||||||
isGroupActive: boolean;
|
isGroupActive: boolean;
|
||||||
group: IGroupview;
|
group: IGroupview;
|
||||||
close: () => Promise<boolean>;
|
close: () => Promise<boolean>;
|
||||||
setClosePanelHook(callback: () => Promise<ClosePanelResult>): void;
|
setClosePanelHook(callback: () => Promise<ClosePanelResult>): void;
|
||||||
canClose: () => Promise<ClosePanelResult>;
|
canClose: () => Promise<ClosePanelResult>;
|
||||||
setState(key: string, value: any);
|
|
||||||
setState(state: { [index: string]: any });
|
|
||||||
getState: () => { [index: string]: any };
|
|
||||||
onDidStateChange: Event<any>;
|
|
||||||
onDidDirtyChange: Event<boolean>;
|
onDidDirtyChange: Event<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PanelApiImpl extends CompositeDisposable implements PanelApi {
|
export class PanelApiImpl extends BasePanelApi implements PanelApi {
|
||||||
private _isPanelVisible: boolean;
|
private _isPanelVisible: boolean;
|
||||||
private _isGroupActive: boolean;
|
private _isGroupActive: boolean;
|
||||||
private _group: IGroupview;
|
private _group: IGroupview;
|
||||||
private _closePanelCallback: () => Promise<ClosePanelResult>;
|
private _closePanelCallback: () => Promise<ClosePanelResult>;
|
||||||
private _state: { [index: string]: any } = {};
|
|
||||||
|
|
||||||
private readonly _onDidStateChange = new Emitter<any>();
|
|
||||||
readonly onDidStateChange: Event<any> = this._onDidStateChange.event;
|
|
||||||
|
|
||||||
get onDidPanelStateChange() {
|
get onDidPanelStateChange() {
|
||||||
return this._event;
|
return this._event;
|
||||||
}
|
}
|
||||||
|
|
||||||
get onDidPanelDimensionChange() {
|
|
||||||
return this._dimensionEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
get onDidDirtyChange() {
|
get onDidDirtyChange() {
|
||||||
return this._dirtyEvent;
|
return this._dirtyEvent;
|
||||||
}
|
}
|
||||||
@ -74,12 +60,12 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _event: Event<PanelStateChangeEvent>,
|
private _event: Event<PanelStateChangeEvent>,
|
||||||
private _dimensionEvent: Event<PanelDimensionChangeEvent>,
|
_dimensionEvent: Event<PanelDimensionChangeEvent>,
|
||||||
private _dirtyEvent: Event<boolean>,
|
private _dirtyEvent: Event<boolean>,
|
||||||
private panel: IPanel,
|
private panel: IGroupPanel,
|
||||||
group: IGroupview
|
group: IGroupview
|
||||||
) {
|
) {
|
||||||
super();
|
super(_dimensionEvent);
|
||||||
this._group = group;
|
this._group = group;
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
@ -90,19 +76,6 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setState(key: string | { [index: string]: any }, value?: any) {
|
|
||||||
if (typeof key === "object") {
|
|
||||||
this._state = key;
|
|
||||||
} else {
|
|
||||||
this._state[key] = value;
|
|
||||||
}
|
|
||||||
this._onDidStateChange.fire(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getState(): { [index: string]: any } {
|
|
||||||
return this._state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public close() {
|
public close() {
|
||||||
return this.group.closePanel(this.panel);
|
return this.group.closePanel(this.panel);
|
||||||
}
|
}
|
||||||
@ -113,7 +86,5 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi {
|
|||||||
|
|
||||||
public dispose() {
|
public dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
||||||
this._onDidStateChange.dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
import { IPanel, PanelInitParameters, PanelUpdateEvent } from "./types";
|
import { IGroupPanel, PanelInitParameters } from "./types";
|
||||||
import {
|
import { PanelApiImpl, PanelStateChangeEvent, PanelApi } from "./api";
|
||||||
PanelApiImpl,
|
|
||||||
PanelStateChangeEvent,
|
|
||||||
PanelDimensionChangeEvent,
|
|
||||||
PanelApi,
|
|
||||||
} from "./api";
|
|
||||||
import { Emitter, Event } from "../../events";
|
import { Emitter, Event } from "../../events";
|
||||||
import { IGroupview, GroupChangeKind } from "../groupview";
|
import { IGroupview, GroupChangeKind } from "../groupview";
|
||||||
import { MutableDisposable, CompositeDisposable } from "../../lifecycle";
|
import { MutableDisposable, CompositeDisposable } from "../../lifecycle";
|
||||||
import { PanelContentPart, PanelHeaderPart, ClosePanelResult } from "./parts";
|
import { PanelContentPart, PanelHeaderPart, ClosePanelResult } from "./parts";
|
||||||
|
import { PanelDimensionChangeEvent } from "../../panel/api";
|
||||||
|
import { PanelUpdateEvent } from "../../panel/types";
|
||||||
|
|
||||||
export class DefaultPanel extends CompositeDisposable implements IPanel {
|
export class DefaultPanel extends CompositeDisposable implements IGroupPanel {
|
||||||
private readonly mutableDisposable = new MutableDisposable();
|
private readonly mutableDisposable = new MutableDisposable();
|
||||||
private readonly _onDidPanelStateChange = new Emitter<PanelStateChangeEvent>({
|
private readonly _onDidPanelStateChange = new Emitter<PanelStateChangeEvent>({
|
||||||
emitLastValue: true,
|
emitLastValue: true,
|
||||||
|
@ -3,13 +3,14 @@ import { IGroupview } from "../groupview";
|
|||||||
import { IGroupAccessor } from "../../layout";
|
import { IGroupAccessor } from "../../layout";
|
||||||
import { PanelApi } from "./api";
|
import { PanelApi } from "./api";
|
||||||
import { PanelInitParameters } from "./types";
|
import { PanelInitParameters } from "./types";
|
||||||
|
import { Constructor } from "../../types";
|
||||||
|
|
||||||
export enum ClosePanelResult {
|
export enum ClosePanelResult {
|
||||||
CLOSE = "CLOSE",
|
CLOSE = "CLOSE",
|
||||||
DONT_CLOSE = "DONT_CLOSE",
|
DONT_CLOSE = "DONT_CLOSE",
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Methods extends IDisposable {
|
interface BasePart extends IDisposable {
|
||||||
init?(params: PartInitParameters): void;
|
init?(params: PartInitParameters): void;
|
||||||
setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void;
|
setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void;
|
||||||
}
|
}
|
||||||
@ -22,14 +23,14 @@ export interface PartInitParameters extends PanelInitParameters {
|
|||||||
api: PanelApi;
|
api: PanelApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelHeaderPart extends Methods {
|
export interface PanelHeaderPart extends BasePart {
|
||||||
id: string;
|
id: string;
|
||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
layout?(height: string): void;
|
layout?(height: string): void;
|
||||||
toJSON(): {};
|
toJSON(): {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelContentPart extends Methods {
|
export interface PanelContentPart extends BasePart {
|
||||||
id: string;
|
id: string;
|
||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
layout?(width: number, height: number): void;
|
layout?(width: number, height: number): void;
|
||||||
@ -46,13 +47,11 @@ export interface WatermarkPart extends IDisposable {
|
|||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelHeaderPartConstructor {
|
// constructors
|
||||||
new (): PanelHeaderPart;
|
|
||||||
}
|
|
||||||
export interface PanelContentPartConstructor {
|
|
||||||
new (): PanelContentPart;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WatermarkConstructor {
|
export interface PanelHeaderPartConstructor
|
||||||
new (): WatermarkPart;
|
extends Constructor<PanelHeaderPart> {}
|
||||||
}
|
export interface PanelContentPartConstructor
|
||||||
|
extends Constructor<PanelContentPart> {}
|
||||||
|
|
||||||
|
export interface WatermarkConstructor extends Constructor<WatermarkPart> {}
|
||||||
|
@ -2,31 +2,20 @@ import { IGroupview } from "../groupview";
|
|||||||
import { IDisposable, ISerializable } from "../../lifecycle";
|
import { IDisposable, ISerializable } from "../../lifecycle";
|
||||||
import { Event } from "../../events";
|
import { Event } from "../../events";
|
||||||
import { PanelHeaderPart, PanelContentPart, ClosePanelResult } from "./parts";
|
import { PanelHeaderPart, PanelContentPart, ClosePanelResult } from "./parts";
|
||||||
|
import { InitParameters, IPanel } from "../../panel/types";
|
||||||
// objects
|
|
||||||
|
|
||||||
export interface PanelUpdateEvent {
|
|
||||||
params: { [key: string]: any };
|
|
||||||
}
|
|
||||||
|
|
||||||
// init parameters
|
// init parameters
|
||||||
|
|
||||||
export interface PanelInitParameters {
|
export interface PanelInitParameters extends InitParameters {
|
||||||
title: string;
|
title: string;
|
||||||
suppressClosable?: boolean;
|
suppressClosable?: boolean;
|
||||||
params: { [index: string]: any };
|
|
||||||
state?: { [index: string]: any };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// constructors
|
// constructors
|
||||||
|
|
||||||
export interface PanelConstructor {
|
|
||||||
new (): IPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
// panel
|
// panel
|
||||||
|
|
||||||
export interface IPanel extends IDisposable, ISerializable {
|
export interface IGroupPanel extends IDisposable, ISerializable, IPanel {
|
||||||
id: string;
|
id: string;
|
||||||
header: PanelHeaderPart;
|
header: PanelHeaderPart;
|
||||||
content: PanelContentPart;
|
content: PanelContentPart;
|
||||||
@ -36,8 +25,6 @@ export interface IPanel extends IDisposable, ISerializable {
|
|||||||
setVisible(isGroupActive: boolean, group: IGroupview): void;
|
setVisible(isGroupActive: boolean, group: IGroupview): void;
|
||||||
setDirty(isDirty: boolean): void;
|
setDirty(isDirty: boolean): void;
|
||||||
close?(): Promise<ClosePanelResult>;
|
close?(): Promise<ClosePanelResult>;
|
||||||
layout?(width: number, height: number): void;
|
|
||||||
init?(params: PanelInitParameters & { [index: string]: string }): void;
|
init?(params: PanelInitParameters & { [index: string]: string }): void;
|
||||||
update?(event: PanelUpdateEvent): void;
|
|
||||||
onDidStateChange: Event<any>;
|
onDidStateChange: Event<any>;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import { IDisposable, CompositeDisposable } from "../../lifecycle";
|
import {
|
||||||
|
IDisposable,
|
||||||
|
CompositeDisposable,
|
||||||
|
IValueDisposable,
|
||||||
|
} from "../../lifecycle";
|
||||||
import { addDisposableListener, Emitter, Event } from "../../events";
|
import { addDisposableListener, Emitter, Event } from "../../events";
|
||||||
import { ITab, Tab, TabInteractionKind } from "../panel/tab/tab";
|
import { ITab, Tab, TabInteractionKind } from "../panel/tab/tab";
|
||||||
import { removeClasses, addClasses, toggleClass } from "../../dom";
|
import { removeClasses, addClasses, toggleClass } from "../../dom";
|
||||||
@ -9,7 +13,7 @@ import { IGroupview } from "../groupview";
|
|||||||
import { IGroupAccessor } from "../../layout";
|
import { IGroupAccessor } from "../../layout";
|
||||||
import { last } from "../../array";
|
import { last } from "../../array";
|
||||||
import { DataTransferSingleton } from "../droptarget/dataTransfer";
|
import { DataTransferSingleton } from "../droptarget/dataTransfer";
|
||||||
import { IPanel } from "../panel/types";
|
import { IGroupPanel } from "../panel/types";
|
||||||
|
|
||||||
export interface ITabContainer extends IDisposable {
|
export interface ITabContainer extends IDisposable {
|
||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
@ -21,10 +25,10 @@ export interface ITabContainer extends IDisposable {
|
|||||||
at: (index: number) => ITab;
|
at: (index: number) => ITab;
|
||||||
onDropEvent: Event<TabDropEvent>;
|
onDropEvent: Event<TabDropEvent>;
|
||||||
setActive: (isGroupActive: boolean) => void;
|
setActive: (isGroupActive: boolean) => void;
|
||||||
setActivePanel: (panel: IPanel) => void;
|
setActivePanel: (panel: IGroupPanel) => void;
|
||||||
isActive: (tab: ITab) => boolean;
|
isActive: (tab: ITab) => boolean;
|
||||||
closePanel: (panel: IPanel) => void;
|
closePanel: (panel: IGroupPanel) => void;
|
||||||
openPanel: (panel: IPanel, index?: number) => void;
|
openPanel: (panel: IGroupPanel, index?: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TabContainer extends CompositeDisposable implements ITabContainer {
|
export class TabContainer extends CompositeDisposable implements ITabContainer {
|
||||||
@ -32,10 +36,10 @@ export class TabContainer extends CompositeDisposable implements ITabContainer {
|
|||||||
private _element: HTMLElement;
|
private _element: HTMLElement;
|
||||||
private actionContainer: HTMLElement;
|
private actionContainer: HTMLElement;
|
||||||
|
|
||||||
private tabs: ITab[] = [];
|
private tabs: IValueDisposable<ITab>[] = [];
|
||||||
private selectedIndex: number = -1;
|
private selectedIndex: number = -1;
|
||||||
private active: boolean;
|
private active: boolean;
|
||||||
private activePanel: IPanel;
|
private activePanel: IGroupPanel;
|
||||||
|
|
||||||
private _visible: boolean = true;
|
private _visible: boolean = true;
|
||||||
private _height: number;
|
private _height: number;
|
||||||
@ -67,20 +71,22 @@ export class TabContainer extends CompositeDisposable implements ITabContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isActive(tab: ITab) {
|
public isActive(tab: ITab) {
|
||||||
return this.selectedIndex > -1 && this.tabs[this.selectedIndex] === tab;
|
return (
|
||||||
|
this.selectedIndex > -1 && this.tabs[this.selectedIndex].value === tab
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get hasActiveDragEvent() {
|
public get hasActiveDragEvent() {
|
||||||
return !!this.tabs.find((tab) => tab.hasActiveDragEvent);
|
return !!this.tabs.find((tab) => tab.value.hasActiveDragEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public at(index: number) {
|
public at(index: number) {
|
||||||
return this.tabs[index];
|
return this.tabs[index]?.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public indexOf(tabOrId: ITab) {
|
public indexOf(tabOrId: ITab) {
|
||||||
const id = typeof tabOrId === "string" ? tabOrId : tabOrId.id;
|
const id = typeof tabOrId === "string" ? tabOrId : tabOrId.id;
|
||||||
return this.tabs.findIndex((tab) => tab.id === id);
|
return this.tabs.findIndex((tab) => tab.value.id === id);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private accessor: IGroupAccessor, private group: IGroupview) {
|
constructor(private accessor: IGroupAccessor, private group: IGroupview) {
|
||||||
@ -111,7 +117,7 @@ export class TabContainer extends CompositeDisposable implements ITabContainer {
|
|||||||
console.debug("[tabs] invalid drop event");
|
console.debug("[tabs] invalid drop event");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!last(this.tabs).hasActiveDragEvent) {
|
if (!last(this.tabs).value.hasActiveDragEvent) {
|
||||||
addClasses(this.tabContainer, "drag-over-target");
|
addClasses(this.tabContainer, "drag-over-target");
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@ -132,10 +138,11 @@ export class TabContainer extends CompositeDisposable implements ITabContainer {
|
|||||||
}
|
}
|
||||||
removeClasses(this.tabContainer, "drag-over-target");
|
removeClasses(this.tabContainer, "drag-over-target");
|
||||||
|
|
||||||
const activetab = this.tabs.find((tab) => tab.hasActiveDragEvent);
|
const activetab = this.tabs.find((tab) => tab.value.hasActiveDragEvent);
|
||||||
|
|
||||||
const ignore = !!(
|
const ignore = !!(
|
||||||
activetab && event.composedPath().find((x) => activetab.element === x)
|
activetab &&
|
||||||
|
event.composedPath().find((x) => activetab.value.element === x)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ignore) {
|
if (ignore) {
|
||||||
@ -155,13 +162,16 @@ export class TabContainer extends CompositeDisposable implements ITabContainer {
|
|||||||
this.active = isGroupActive;
|
this.active = isGroupActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
private addTab(tab: ITab, index: number = this.tabs.length) {
|
private addTab(
|
||||||
|
tab: IValueDisposable<ITab>,
|
||||||
|
index: number = this.tabs.length
|
||||||
|
) {
|
||||||
if (index < 0 || index > this.tabs.length) {
|
if (index < 0 || index > this.tabs.length) {
|
||||||
throw new Error("invalid location");
|
throw new Error("invalid location");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tabContainer.insertBefore(
|
this.tabContainer.insertBefore(
|
||||||
tab.element,
|
tab.value.element,
|
||||||
this.tabContainer.children[index]
|
this.tabContainer.children[index]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -173,28 +183,31 @@ export class TabContainer extends CompositeDisposable implements ITabContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public delete(id: string) {
|
public delete(id: string) {
|
||||||
const index = this.tabs.findIndex((tab) => tab.id === id);
|
const index = this.tabs.findIndex((tab) => tab.value.id === id);
|
||||||
|
|
||||||
const tab = this.tabs.splice(index, 1)[0];
|
const tab = this.tabs.splice(index, 1)[0];
|
||||||
tab.element.remove();
|
|
||||||
|
const { value, disposable } = tab;
|
||||||
|
|
||||||
|
disposable.dispose();
|
||||||
|
value.element.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setActivePanel(panel: IPanel) {
|
public setActivePanel(panel: IGroupPanel) {
|
||||||
this.tabs.forEach((tab) => {
|
this.tabs.forEach((tab) => {
|
||||||
const isActivePanel = panel.id === tab.id;
|
const isActivePanel = panel.id === tab.value.id;
|
||||||
tab.setActive(isActivePanel);
|
tab.value.setActive(isActivePanel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public openPanel(panel: IPanel, index: number = this.tabs.length) {
|
public openPanel(panel: IGroupPanel, index: number = this.tabs.length) {
|
||||||
if (this.tabs.find((tab) => tab.id === panel.id)) {
|
if (this.tabs.find((tab) => tab.value.id === panel.id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tab = new Tab(panel.id, this.accessor, this.group);
|
const tab = new Tab(panel.id, this.accessor, this.group);
|
||||||
tab.setContent(panel.header.element);
|
tab.setContent(panel.header.element);
|
||||||
|
|
||||||
// TODO - dispose of resources
|
const disposable = CompositeDisposable.from(
|
||||||
const disposables = CompositeDisposable.from(
|
|
||||||
tab.onChanged((event) => {
|
tab.onChanged((event) => {
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case TabInteractionKind.CLICK:
|
case TabInteractionKind.CLICK:
|
||||||
@ -209,15 +222,22 @@ export class TabContainer extends CompositeDisposable implements ITabContainer {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addTab(tab, index);
|
const value: IValueDisposable<ITab> = { value: tab, disposable };
|
||||||
|
|
||||||
|
this.addTab(value, index);
|
||||||
this.activePanel = panel;
|
this.activePanel = panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public closePanel(panel: IPanel) {
|
public closePanel(panel: IGroupPanel) {
|
||||||
this.delete(panel.id);
|
this.delete(panel.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose() {
|
public dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
||||||
|
this.tabs.forEach((tab) => {
|
||||||
|
tab.disposable.dispose();
|
||||||
|
});
|
||||||
|
this.tabs = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export * from "./splitview/splitview";
|
export * from "./splitview/splitview";
|
||||||
export * from "./splitview/paneview";
|
export * from "./paneview/paneview";
|
||||||
export * from "./gridview/gridview";
|
export * from "./gridview/gridview";
|
||||||
export * from "./groupview/groupview";
|
export * from "./groupview/groupview";
|
||||||
export * from "./groupview/panel/content/content";
|
export * from "./groupview/panel/content/content";
|
||||||
|
@ -4,6 +4,7 @@ import {
|
|||||||
PanelHeaderPart,
|
PanelHeaderPart,
|
||||||
PanelHeaderPartConstructor,
|
PanelHeaderPartConstructor,
|
||||||
} from "../groupview/panel/parts";
|
} from "../groupview/panel/parts";
|
||||||
|
import { FrameworkFactory } from "../types";
|
||||||
import { DefaultTab } from "./components/tab/defaultTab";
|
import { DefaultTab } from "./components/tab/defaultTab";
|
||||||
|
|
||||||
export function createContentComponent(
|
export function createContentComponent(
|
||||||
@ -14,7 +15,7 @@ export function createContentComponent(
|
|||||||
frameworkComponents: {
|
frameworkComponents: {
|
||||||
[componentName: string]: any;
|
[componentName: string]: any;
|
||||||
},
|
},
|
||||||
createFrameworkComponent: (id: string, component: any) => PanelContentPart
|
createFrameworkComponent: FrameworkFactory<PanelContentPart>
|
||||||
): PanelContentPart {
|
): PanelContentPart {
|
||||||
const Component =
|
const Component =
|
||||||
typeof componentName === "string"
|
typeof componentName === "string"
|
||||||
@ -35,7 +36,7 @@ export function createContentComponent(
|
|||||||
"you must register a frameworkPanelWrapper to use framework components"
|
"you must register a frameworkPanelWrapper to use framework components"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const wrappedComponent = createFrameworkComponent(
|
const wrappedComponent = createFrameworkComponent.createComponent(
|
||||||
componentName,
|
componentName,
|
||||||
FrameworkComponent
|
FrameworkComponent
|
||||||
);
|
);
|
||||||
@ -52,7 +53,7 @@ export function createTabComponent(
|
|||||||
frameworkComponents: {
|
frameworkComponents: {
|
||||||
[componentName: string]: any;
|
[componentName: string]: any;
|
||||||
},
|
},
|
||||||
createFrameworkComponent: (id: string, component: any) => PanelHeaderPart
|
createFrameworkComponent: FrameworkFactory<PanelHeaderPart>
|
||||||
): PanelHeaderPart {
|
): PanelHeaderPart {
|
||||||
const Component =
|
const Component =
|
||||||
typeof componentName === "string"
|
typeof componentName === "string"
|
||||||
@ -73,7 +74,7 @@ export function createTabComponent(
|
|||||||
"you must register a frameworkPanelWrapper to use framework components"
|
"you must register a frameworkPanelWrapper to use framework components"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const wrappedComponent = createFrameworkComponent(
|
const wrappedComponent = createFrameworkComponent.createComponent(
|
||||||
componentName,
|
componentName,
|
||||||
FrameworkComponent
|
FrameworkComponent
|
||||||
);
|
);
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import { IGridView, IViewDeserializer } from "../gridview/gridview";
|
import { IGridView, IViewDeserializer } from "../gridview/gridview";
|
||||||
import { IPanel } from "../groupview/panel/types";
|
import { IGroupPanel } from "../groupview/panel/types";
|
||||||
import { Layout } from "./layout";
|
import { Layout } from "./layout";
|
||||||
|
|
||||||
export interface IPanelDeserializer {
|
export interface IPanelDeserializer {
|
||||||
fromJSON(panelData: { [index: string]: any }): IPanel;
|
fromJSON(panelData: { [index: string]: any }): IGroupPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DefaultDeserializer implements IViewDeserializer {
|
export class DefaultDeserializer implements IViewDeserializer {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly layout: Layout,
|
private readonly layout: Layout,
|
||||||
private panelDeserializer: { createPanel: (id: string) => IPanel }
|
private panelDeserializer: { createPanel: (id: string) => IGroupPanel }
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public fromJSON(data: { [key: string]: any }): IGridView {
|
public fromJSON(data: { [key: string]: any }): IGridView {
|
||||||
const children = data.views;
|
const children = data.views;
|
||||||
const active = data.activeView;
|
const active = data.activeView;
|
||||||
|
|
||||||
const panels: IPanel[] = [];
|
const panels: IGroupPanel[] = [];
|
||||||
|
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
const panel = this.panelDeserializer.createPanel(child);
|
const panel = this.panelDeserializer.createPanel(child);
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
GroupChangeEvent,
|
GroupChangeEvent,
|
||||||
GroupDropEvent,
|
GroupDropEvent,
|
||||||
} from "../groupview/groupview";
|
} from "../groupview/groupview";
|
||||||
import { IPanel } from "../groupview/panel/types";
|
import { IGroupPanel } from "../groupview/panel/types";
|
||||||
import { DefaultPanel } from "../groupview/panel/panel";
|
import { DefaultPanel } from "../groupview/panel/panel";
|
||||||
import {
|
import {
|
||||||
CompositeDisposable,
|
CompositeDisposable,
|
||||||
@ -103,9 +103,9 @@ export interface IGroupAccessor {
|
|||||||
activeGroup: IGroupview;
|
activeGroup: IGroupview;
|
||||||
//
|
//
|
||||||
addPanelFromComponent(options: AddPanelOptions): PanelReference;
|
addPanelFromComponent(options: AddPanelOptions): PanelReference;
|
||||||
addPanel(options: AddPanelOptions): IPanel;
|
addPanel(options: AddPanelOptions): IGroupPanel;
|
||||||
//
|
//
|
||||||
getPanel: (id: string) => IPanel;
|
getPanel: (id: string) => IGroupPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILayout extends IGroupAccessor, Api {}
|
export interface ILayout extends IGroupAccessor, Api {}
|
||||||
@ -118,10 +118,10 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
private readonly _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
private readonly _id = nextLayoutId.next();
|
private readonly _id = nextLayoutId.next();
|
||||||
private readonly groups = new Map<string, IValueDisposable<IGroupview>>();
|
private readonly groups = new Map<string, IValueDisposable<IGroupview>>();
|
||||||
private readonly panels = new Map<string, IValueDisposable<IPanel>>();
|
private readonly panels = new Map<string, IValueDisposable<IGroupPanel>>();
|
||||||
private readonly gridview: Gridview = new Gridview();
|
private readonly gridview: Gridview = new Gridview();
|
||||||
private readonly dirtyPanels = new Set<IPanel>();
|
private readonly dirtyPanels = new Set<IGroupPanel>();
|
||||||
private readonly debouncedDeque = debounce(this.persist.bind(this), 5000);
|
private readonly debouncedDeque = debounce(this.syncConfigs.bind(this), 5000);
|
||||||
// events
|
// events
|
||||||
private readonly _onDidLayoutChange = new Emitter<GroupChangeEvent>();
|
private readonly _onDidLayoutChange = new Emitter<GroupChangeEvent>();
|
||||||
readonly onDidLayoutChange: Event<GroupChangeEvent> = this._onDidLayoutChange
|
readonly onDidLayoutChange: Event<GroupChangeEvent> = this._onDidLayoutChange
|
||||||
@ -205,7 +205,7 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
return this._element;
|
return this._element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPanel(id: string): IPanel {
|
public getPanel(id: string): IGroupPanel {
|
||||||
return this.panels.get(id)?.value;
|
return this.panels.get(id)?.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +305,7 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
this.doSetGroupActive(next);
|
this.doSetGroupActive(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerPanel(panel: IPanel) {
|
public registerPanel(panel: IGroupPanel) {
|
||||||
if (this.panels.has(panel.id)) {
|
if (this.panels.has(panel.id)) {
|
||||||
throw new Error(`panel ${panel.id} already exists`);
|
throw new Error(`panel ${panel.id} already exists`);
|
||||||
}
|
}
|
||||||
@ -319,7 +319,7 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_CREATED });
|
this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_CREATED });
|
||||||
}
|
}
|
||||||
|
|
||||||
public unregisterPanel(panel: IPanel) {
|
public unregisterPanel(panel: IGroupPanel) {
|
||||||
if (!this.panels.has(panel.id)) {
|
if (!this.panels.has(panel.id)) {
|
||||||
throw new Error(`panel ${panel.id} doesn't exist`);
|
throw new Error(`panel ${panel.id} doesn't exist`);
|
||||||
}
|
}
|
||||||
@ -339,6 +339,8 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
* @returns A JSON respresentation of the layout
|
* @returns A JSON respresentation of the layout
|
||||||
*/
|
*/
|
||||||
public toJSON() {
|
public toJSON() {
|
||||||
|
this.syncConfigs();
|
||||||
|
|
||||||
const data = this.gridview.serialize();
|
const data = this.gridview.serialize();
|
||||||
|
|
||||||
const state = { ...this.panelState };
|
const state = { ...this.panelState };
|
||||||
@ -356,6 +358,43 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
return { grid: data, panels };
|
return { grid: data, panels };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the local copy of the layout state is up-to-date
|
||||||
|
*/
|
||||||
|
private syncConfigs() {
|
||||||
|
const dirtyPanels = Array.from(this.dirtyPanels);
|
||||||
|
|
||||||
|
if (dirtyPanels.length === 0) {
|
||||||
|
console.debug("[layout#syncConfigs] no dirty panels");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dirtyPanels.clear();
|
||||||
|
|
||||||
|
const partialPanelState = dirtyPanels
|
||||||
|
.map((panel) => this.panels.get(panel.id))
|
||||||
|
.filter((_) => !!_)
|
||||||
|
.reduce((collection, panel) => {
|
||||||
|
collection[panel.value.id] = panel.value.toJSON();
|
||||||
|
return collection;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
this.panelState = {
|
||||||
|
...this.panelState,
|
||||||
|
...partialPanelState,
|
||||||
|
};
|
||||||
|
|
||||||
|
dirtyPanels
|
||||||
|
.filter((p) => this.panels.has(p.id))
|
||||||
|
.forEach((panel) => {
|
||||||
|
panel.setDirty(false);
|
||||||
|
this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_CLEAN });
|
||||||
|
});
|
||||||
|
|
||||||
|
this._onDidLayoutChange.fire({
|
||||||
|
kind: GroupChangeKind.LAYOUT_CONFIG_UPDATED,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public deserialize(data: any) {
|
public deserialize(data: any) {
|
||||||
this.gridview.clear();
|
this.gridview.clear();
|
||||||
this.panels.forEach((panel) => {
|
this.panels.forEach((panel) => {
|
||||||
@ -469,7 +508,7 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public addPanel(options: AddPanelOptions): IPanel {
|
public addPanel(options: AddPanelOptions): IGroupPanel {
|
||||||
const component = this.createContentComponent(options.componentName);
|
const component = this.createContentComponent(options.componentName);
|
||||||
const tabComponent = this.createTabComponent(options.tabComponentName);
|
const tabComponent = this.createTabComponent(options.tabComponentName);
|
||||||
|
|
||||||
@ -491,7 +530,7 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
componentName,
|
componentName,
|
||||||
this.options.components,
|
this.options.components,
|
||||||
this.options.frameworkComponents,
|
this.options.frameworkComponents,
|
||||||
this.options.frameworkPanelWrapper.createContentWrapper
|
this.options.frameworkComponentFactory.content
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,7 +541,7 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
componentName,
|
componentName,
|
||||||
this.options.tabComponents,
|
this.options.tabComponents,
|
||||||
this.options.frameworkTabComponents,
|
this.options.frameworkTabComponents,
|
||||||
this.options.frameworkPanelWrapper.createTabWrapper
|
this.options.frameworkComponentFactory.tab
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,7 +583,7 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
this.doRemoveGroup(group);
|
this.doRemoveGroup(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
private addPanelToNewGroup(panel: IPanel, location: number[] = [0]) {
|
private addPanelToNewGroup(panel: IGroupPanel, location: number[] = [0]) {
|
||||||
let group: IGroupview;
|
let group: IGroupview;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -735,48 +774,19 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
this.gridview.layout(size, orthogonalSize);
|
this.gridview.layout(size, orthogonalSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private findGroup(panel: IPanel): IGroupview | undefined {
|
private findGroup(panel: IGroupPanel): IGroupview | undefined {
|
||||||
return Array.from(this.groups.values()).find((group) =>
|
return Array.from(this.groups.values()).find((group) =>
|
||||||
group.value.containsPanel(panel)
|
group.value.containsPanel(panel)
|
||||||
).value;
|
).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private addDirtyPanel(panel: IPanel) {
|
private addDirtyPanel(panel: IGroupPanel) {
|
||||||
this.dirtyPanels.add(panel);
|
this.dirtyPanels.add(panel);
|
||||||
panel.setDirty(true);
|
panel.setDirty(true);
|
||||||
this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_DIRTY });
|
this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_DIRTY });
|
||||||
this.debouncedDeque();
|
this.debouncedDeque();
|
||||||
}
|
}
|
||||||
|
|
||||||
private persist() {
|
|
||||||
const dirtyPanels = Array.from(this.dirtyPanels);
|
|
||||||
this.dirtyPanels.clear();
|
|
||||||
|
|
||||||
const partialPanelState = dirtyPanels
|
|
||||||
.map((p) => this.panels.get(p.id))
|
|
||||||
.filter((_) => !!_)
|
|
||||||
.reduce((collection, panel) => {
|
|
||||||
collection[panel.value.id] = panel.value.toJSON();
|
|
||||||
return collection;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
this.panelState = {
|
|
||||||
...this.panelState,
|
|
||||||
...partialPanelState,
|
|
||||||
};
|
|
||||||
|
|
||||||
dirtyPanels
|
|
||||||
.filter((p) => this.panels.has(p.id))
|
|
||||||
.forEach((panel) => {
|
|
||||||
panel.setDirty(false);
|
|
||||||
this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_CLEAN });
|
|
||||||
});
|
|
||||||
|
|
||||||
this._onDidLayoutChange.fire({
|
|
||||||
kind: GroupChangeKind.LAYOUT_CONFIG_UPDATED,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private toTarget(direction: "left" | "right" | "above" | "below" | "within") {
|
private toTarget(direction: "left" | "right" | "above" | "below" | "within") {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case "left":
|
case "left":
|
||||||
|
@ -7,19 +7,20 @@ import {
|
|||||||
PanelHeaderPartConstructor,
|
PanelHeaderPartConstructor,
|
||||||
WatermarkConstructor,
|
WatermarkConstructor,
|
||||||
} from "../groupview/panel/parts";
|
} from "../groupview/panel/parts";
|
||||||
import { IPanel } from "../groupview/panel/types";
|
import { IGroupPanel } from "../groupview/panel/types";
|
||||||
|
import { FrameworkFactory } from "../types";
|
||||||
import { Api } from "./layout";
|
import { Api } from "./layout";
|
||||||
|
|
||||||
export interface FrameworkPanelWrapper {
|
export interface FrameworkComponentFactory {
|
||||||
createContentWrapper: (id: string, component: any) => PanelContentPart;
|
content: FrameworkFactory<PanelContentPart>;
|
||||||
createTabWrapper: (id: string, component: any) => PanelHeaderPart;
|
tab: FrameworkFactory<PanelHeaderPart>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TabContextMenuEvent {
|
export interface TabContextMenuEvent {
|
||||||
event: MouseEvent;
|
event: MouseEvent;
|
||||||
api: Api;
|
api: Api;
|
||||||
panelApi: PanelApi;
|
panelApi: PanelApi;
|
||||||
panel: IPanel;
|
panel: IGroupPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LayoutOptions {
|
export interface LayoutOptions {
|
||||||
@ -37,7 +38,7 @@ export interface LayoutOptions {
|
|||||||
};
|
};
|
||||||
watermarkComponent?: WatermarkConstructor;
|
watermarkComponent?: WatermarkConstructor;
|
||||||
watermarkFrameworkComponent?: any;
|
watermarkFrameworkComponent?: any;
|
||||||
frameworkPanelWrapper: FrameworkPanelWrapper;
|
frameworkComponentFactory: FrameworkComponentFactory;
|
||||||
tabHeight?: number;
|
tabHeight?: number;
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
enableExternalDragEvents?: boolean;
|
enableExternalDragEvents?: boolean;
|
||||||
|
71
packages/splitview/src/panel/api.ts
Normal file
71
packages/splitview/src/panel/api.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { Emitter, Event } from "../events";
|
||||||
|
import { CompositeDisposable, IDisposable } from "../lifecycle";
|
||||||
|
|
||||||
|
export interface PanelDimensionChangeEvent {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try and do a bit better than the 'any' type.
|
||||||
|
// anything that is serializable JSON should be valid
|
||||||
|
type StateObject =
|
||||||
|
| number
|
||||||
|
| string
|
||||||
|
| boolean
|
||||||
|
| undefined
|
||||||
|
| null
|
||||||
|
| object
|
||||||
|
| StateObject[]
|
||||||
|
| { [key: string]: StateObject };
|
||||||
|
|
||||||
|
export interface IBasePanelApi extends IDisposable {
|
||||||
|
// events
|
||||||
|
onDidPanelDimensionChange: Event<PanelDimensionChangeEvent>;
|
||||||
|
// state
|
||||||
|
setState(key: string, value: StateObject): void;
|
||||||
|
setState(state: { [key: string]: StateObject }): void;
|
||||||
|
getState: () => { [key: string]: StateObject };
|
||||||
|
getStateKey: <T extends StateObject>(key: string) => T;
|
||||||
|
onDidStateChange: Event<void>;
|
||||||
|
}
|
||||||
|
export class BasePanelApi extends CompositeDisposable implements IBasePanelApi {
|
||||||
|
private _state: { [key: string]: StateObject } = {};
|
||||||
|
|
||||||
|
private readonly _onDidStateChange = new Emitter<void>();
|
||||||
|
readonly onDidStateChange: Event<void> = this._onDidStateChange.event;
|
||||||
|
|
||||||
|
get onDidPanelDimensionChange() {
|
||||||
|
return this._dimensionEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private _dimensionEvent: Event<PanelDimensionChangeEvent>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setState(
|
||||||
|
key: string | { [key: string]: StateObject },
|
||||||
|
value?: StateObject
|
||||||
|
) {
|
||||||
|
if (typeof key === "object") {
|
||||||
|
this._state = key;
|
||||||
|
} else {
|
||||||
|
this._state[key] = value;
|
||||||
|
}
|
||||||
|
this._onDidStateChange.fire(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getState(): { [key: string]: StateObject } {
|
||||||
|
return this._state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getStateKey(key: string) {
|
||||||
|
// TODO - find an alternative to 'as any'
|
||||||
|
return this._state[key] as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
super.dispose();
|
||||||
|
|
||||||
|
this._onDidStateChange.dispose();
|
||||||
|
}
|
||||||
|
}
|
14
packages/splitview/src/panel/types.ts
Normal file
14
packages/splitview/src/panel/types.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export interface InitParameters {
|
||||||
|
params: { [index: string]: any };
|
||||||
|
state?: { [index: string]: any };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PanelUpdateEvent {
|
||||||
|
params: { [index: string]: any };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPanel {
|
||||||
|
init?(params: InitParameters): void;
|
||||||
|
layout?(width: number, height: number): void;
|
||||||
|
update?(event: PanelUpdateEvent): void;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { SplitView, IView, Orientation } from "./splitview";
|
import { SplitView, IView, Orientation } from "../splitview/splitview";
|
||||||
import { IDisposable } from "../lifecycle";
|
import { IDisposable } from "../lifecycle";
|
||||||
import { Emitter } from "../events";
|
import { Emitter } from "../events";
|
||||||
import { addClasses, removeClasses } from "../dom";
|
import { addClasses, removeClasses } from "../dom";
|
@ -1,4 +1,4 @@
|
|||||||
import { IPanel } from "../groupview/panel/types";
|
import { IGroupPanel } from "../groupview/panel/types";
|
||||||
import { Layout } from "../layout/layout";
|
import { Layout } from "../layout/layout";
|
||||||
import { DefaultPanel } from "../groupview/panel/panel";
|
import { DefaultPanel } from "../groupview/panel/panel";
|
||||||
import { PanelContentPart, PanelHeaderPart } from "../groupview/panel/parts";
|
import { PanelContentPart, PanelHeaderPart } from "../groupview/panel/parts";
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
export class ReactPanelDeserialzier implements IPanelDeserializer {
|
export class ReactPanelDeserialzier implements IPanelDeserializer {
|
||||||
constructor(private readonly layout: Layout) {}
|
constructor(private readonly layout: Layout) {}
|
||||||
|
|
||||||
public fromJSON(panelData: { [index: string]: any }): IPanel {
|
public fromJSON(panelData: { [index: string]: any }): IGroupPanel {
|
||||||
const panelId = panelData.id;
|
const panelId = panelData.id;
|
||||||
const content = panelData.content;
|
const content = panelData.content;
|
||||||
const tab = panelData.tab;
|
const tab = panelData.tab;
|
||||||
@ -24,14 +24,14 @@ export class ReactPanelDeserialzier implements IPanelDeserializer {
|
|||||||
content.id,
|
content.id,
|
||||||
this.layout.options.components,
|
this.layout.options.components,
|
||||||
this.layout.options.frameworkComponents,
|
this.layout.options.frameworkComponents,
|
||||||
this.layout.options.frameworkPanelWrapper.createContentWrapper
|
this.layout.options.frameworkComponentFactory.content
|
||||||
) as PanelContentPart;
|
) as PanelContentPart;
|
||||||
|
|
||||||
const headerPart = createTabComponent(
|
const headerPart = createTabComponent(
|
||||||
tab.id,
|
tab.id,
|
||||||
this.layout.options.tabComponents,
|
this.layout.options.tabComponents,
|
||||||
this.layout.options.frameworkPanelWrapper,
|
this.layout.options.frameworkComponentFactory,
|
||||||
this.layout.options.frameworkPanelWrapper.createTabWrapper
|
this.layout.options.frameworkComponentFactory.tab
|
||||||
) as PanelHeaderPart;
|
) as PanelHeaderPart;
|
||||||
|
|
||||||
const panel = new DefaultPanel(panelId, headerPart, contentPart);
|
const panel = new DefaultPanel(panelId, headerPart, contentPart);
|
||||||
|
@ -5,6 +5,7 @@ import { ReactPanelContentPart } from "./reactContentPart";
|
|||||||
import { ReactPanelHeaderPart } from "./reactHeaderPart";
|
import { ReactPanelHeaderPart } from "./reactHeaderPart";
|
||||||
import { IPanelProps } from "./react";
|
import { IPanelProps } from "./react";
|
||||||
import { ReactPanelDeserialzier } from "./deserializer";
|
import { ReactPanelDeserialzier } from "./deserializer";
|
||||||
|
import { FrameworkComponentFactory } from "../layout/options";
|
||||||
|
|
||||||
export interface OnReadyEvent {
|
export interface OnReadyEvent {
|
||||||
api: Api;
|
api: Api;
|
||||||
@ -55,23 +56,27 @@ export const ReactGrid = (props: IReactGridProps) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const frameworkPanelWrapper = {
|
const frameworkPanelWrapper: FrameworkComponentFactory = {
|
||||||
createContentWrapper: (
|
content: {
|
||||||
id: string,
|
createComponent: (
|
||||||
component: React.FunctionComponent<IPanelProps>
|
id: string,
|
||||||
) => {
|
component: React.FunctionComponent<IPanelProps>
|
||||||
return new ReactPanelContentPart(id, component, { addPortal });
|
) => {
|
||||||
|
return new ReactPanelContentPart(id, component, { addPortal });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
createTabWrapper: (
|
tab: {
|
||||||
id: string,
|
createComponent: (
|
||||||
component: React.FunctionComponent<IPanelProps>
|
id: string,
|
||||||
) => {
|
component: React.FunctionComponent<IPanelProps>
|
||||||
return new ReactPanelHeaderPart(id, component, { addPortal });
|
) => {
|
||||||
|
return new ReactPanelHeaderPart(id, component, { addPortal });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const layout = new Layout({
|
const layout = new Layout({
|
||||||
frameworkPanelWrapper,
|
frameworkComponentFactory: frameworkPanelWrapper,
|
||||||
frameworkComponents: props.components,
|
frameworkComponents: props.components,
|
||||||
frameworkTabComponents: props.tabComponents,
|
frameworkTabComponents: props.tabComponents,
|
||||||
tabHeight: props.tabHeight,
|
tabHeight: props.tabHeight,
|
||||||
|
@ -3,6 +3,7 @@ import * as ReactDOM from "react-dom";
|
|||||||
import { IDisposable } from "../lifecycle";
|
import { IDisposable } from "../lifecycle";
|
||||||
import { PanelApi } from "../groupview/panel/api";
|
import { PanelApi } from "../groupview/panel/api";
|
||||||
import { sequentialNumberGenerator } from "../math";
|
import { sequentialNumberGenerator } from "../math";
|
||||||
|
import { IBasePanelApi } from "../panel/api";
|
||||||
|
|
||||||
export interface IPanelProps {
|
export interface IPanelProps {
|
||||||
api: PanelApi;
|
api: PanelApi;
|
||||||
@ -53,9 +54,9 @@ export class ReactPart implements IDisposable {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly parent: HTMLElement,
|
private readonly parent: HTMLElement,
|
||||||
private readonly api: PanelApi,
|
private readonly api: IBasePanelApi,
|
||||||
private readonly addPortal: (portal: React.ReactPortal) => IDisposable,
|
private readonly addPortal: (portal: React.ReactPortal) => IDisposable,
|
||||||
private readonly component: React.FunctionComponent<IPanelProps>,
|
private readonly component: React.FunctionComponent<{}>,
|
||||||
private readonly parameters: { [key: string]: any }
|
private readonly parameters: { [key: string]: any }
|
||||||
) {
|
) {
|
||||||
this.createPortal();
|
this.createPortal();
|
||||||
@ -77,7 +78,7 @@ export class ReactPart implements IDisposable {
|
|||||||
let props = {
|
let props = {
|
||||||
api: this.api,
|
api: this.api,
|
||||||
...this.parameters,
|
...this.parameters,
|
||||||
} as IPanelProps;
|
} as any;
|
||||||
|
|
||||||
const wrapper = React.createElement(PanelWrapper, {
|
const wrapper = React.createElement(PanelWrapper, {
|
||||||
component: this.component,
|
component: this.component,
|
||||||
|
102
packages/splitview/src/react/reactComponentView.ts
Normal file
102
packages/splitview/src/react/reactComponentView.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { trackFocus } from "../dom";
|
||||||
|
import { Emitter } from "../events";
|
||||||
|
import { BasePanelApi, PanelDimensionChangeEvent } from "../panel/api";
|
||||||
|
import { CompositeDisposable } from "../lifecycle";
|
||||||
|
import { IView } from "../splitview/splitview";
|
||||||
|
import { ReactLayout } from "./layout";
|
||||||
|
import { ReactPart } from "./react";
|
||||||
|
import { ISplitviewPanelProps } from "./splitview";
|
||||||
|
import { PanelUpdateEvent, InitParameters, IPanel } from "../panel/types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A no-thrills implementation of IView that renders a React component
|
||||||
|
*/
|
||||||
|
export class ReactComponentView
|
||||||
|
extends CompositeDisposable
|
||||||
|
implements IView, IPanel {
|
||||||
|
private _element: HTMLElement;
|
||||||
|
private part: ReactPart;
|
||||||
|
private params: { params: any };
|
||||||
|
private api: BasePanelApi;
|
||||||
|
private readonly _onDidPanelDimensionsChange = new Emitter<
|
||||||
|
PanelDimensionChangeEvent
|
||||||
|
>();
|
||||||
|
|
||||||
|
private _onDidChange: Emitter<number | undefined> = new Emitter<
|
||||||
|
number | undefined
|
||||||
|
>();
|
||||||
|
public onDidChange = this._onDidChange.event;
|
||||||
|
|
||||||
|
get element() {
|
||||||
|
return this._element;
|
||||||
|
}
|
||||||
|
|
||||||
|
get minimumSize() {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
// get snapSize() {
|
||||||
|
// return 100;
|
||||||
|
// }
|
||||||
|
|
||||||
|
get maximumSize() {
|
||||||
|
return Number.MAX_SAFE_INTEGER;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly id: string,
|
||||||
|
private readonly componentName: string,
|
||||||
|
private readonly component: React.FunctionComponent<ISplitviewPanelProps>,
|
||||||
|
private readonly parent: ReactLayout
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this.api = new BasePanelApi(this._onDidPanelDimensionsChange.event);
|
||||||
|
if (!this.component) {
|
||||||
|
throw new Error("React.FunctionalComponent cannot be undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
this._element = document.createElement("div");
|
||||||
|
|
||||||
|
const { onDidFocus } = trackFocus(this.element);
|
||||||
|
|
||||||
|
this.addDisposables(
|
||||||
|
this._onDidPanelDimensionsChange,
|
||||||
|
onDidFocus(() => {
|
||||||
|
//
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(width: number, height: number) {
|
||||||
|
this._onDidPanelDimensionsChange.fire({ width, height });
|
||||||
|
}
|
||||||
|
|
||||||
|
init(parameters: InitParameters): void {
|
||||||
|
this.params = parameters;
|
||||||
|
this.part = new ReactPart(
|
||||||
|
this.element,
|
||||||
|
this.api,
|
||||||
|
this.parent.addPortal,
|
||||||
|
this.component,
|
||||||
|
parameters.params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(params: PanelUpdateEvent) {
|
||||||
|
this.params = { ...this.params.params, ...params };
|
||||||
|
this.part.update(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): object {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
component: this.componentName,
|
||||||
|
props: this.params.params,
|
||||||
|
state: this.api.getState(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this._onDidPanelDimensionsChange.dispose();
|
||||||
|
this.api.dispose();
|
||||||
|
}
|
||||||
|
}
|
@ -1,57 +0,0 @@
|
|||||||
import { Emitter } from "../events";
|
|
||||||
import { IView } from "../splitview/splitview";
|
|
||||||
import { ReactLayout } from "./layout";
|
|
||||||
import { ReactPart } from "./react";
|
|
||||||
|
|
||||||
export class ReactView implements IView {
|
|
||||||
private _element: HTMLElement;
|
|
||||||
private part: ReactPart;
|
|
||||||
|
|
||||||
private _onDidChange: Emitter<number | undefined> = new Emitter<
|
|
||||||
number | undefined
|
|
||||||
>();
|
|
||||||
public onDidChange = this._onDidChange.event;
|
|
||||||
|
|
||||||
get element() {
|
|
||||||
return this._element;
|
|
||||||
}
|
|
||||||
|
|
||||||
get minimumSize() {
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
// get snapSize() {
|
|
||||||
// return 100;
|
|
||||||
// }
|
|
||||||
|
|
||||||
get maximumSize() {
|
|
||||||
return Number.MAX_SAFE_INTEGER;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public readonly id: string,
|
|
||||||
private readonly component: React.FunctionComponent<{}>,
|
|
||||||
private readonly parent: ReactLayout
|
|
||||||
) {
|
|
||||||
if (!this.component) {
|
|
||||||
throw new Error("React.FunctionalComponent cannot be undefined");
|
|
||||||
}
|
|
||||||
|
|
||||||
this._element = document.createElement("div");
|
|
||||||
}
|
|
||||||
|
|
||||||
layout(size: number, orthogonalSize: number) {}
|
|
||||||
|
|
||||||
init(parameters: { params: any }): void {
|
|
||||||
this.part = new ReactPart(
|
|
||||||
this.element,
|
|
||||||
{} as any,
|
|
||||||
this.parent.addPortal,
|
|
||||||
this.component,
|
|
||||||
parameters.params
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
update(params: {}) {
|
|
||||||
this.part.update(params);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +1,44 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Orientation, SplitView } from "../splitview/splitview";
|
import { IBasePanelApi } from "../panel/api";
|
||||||
import { ReactView } from "./reactView";
|
import { IDisposable } from "../lifecycle";
|
||||||
|
import {
|
||||||
|
IComponentSplitview,
|
||||||
|
ComponentSplitview,
|
||||||
|
} from "../splitview/componentSplitview";
|
||||||
|
import { Orientation } from "../splitview/splitview";
|
||||||
|
import { ReactComponentView } from "./reactComponentView";
|
||||||
|
|
||||||
export interface SplitviewFacade {
|
export interface SplitviewFacade {
|
||||||
addFromComponent(options: { id: string; component: string }): void;
|
addFromComponent(options: {
|
||||||
|
id: string;
|
||||||
|
component: string;
|
||||||
|
params?: { [index: string]: any };
|
||||||
|
}): void;
|
||||||
layout(size: number, orthogonalSize: number): void;
|
layout(size: number, orthogonalSize: number): void;
|
||||||
|
onChange: (cb: (event: { proportions: number[] }) => void) => IDisposable;
|
||||||
|
toJSON: () => any;
|
||||||
|
deserialize: (data: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SplitviewReadyEvent {
|
export interface SplitviewReadyEvent {
|
||||||
api: SplitviewFacade;
|
api: IComponentSplitview;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISplitviewPanelProps {
|
||||||
|
api: IBasePanelApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISplitviewComponentProps {
|
export interface ISplitviewComponentProps {
|
||||||
orientation: Orientation;
|
orientation: Orientation;
|
||||||
onReady?: (event: SplitviewReadyEvent) => void;
|
onReady?: (event: SplitviewReadyEvent) => void;
|
||||||
components: { [index: string]: React.FunctionComponent<{}> };
|
components: {
|
||||||
|
[index: string]: React.FunctionComponent<ISplitviewPanelProps>;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SplitViewComponent = (props: ISplitviewComponentProps) => {
|
export const SplitViewComponent = (props: ISplitviewComponentProps) => {
|
||||||
const domReference = React.useRef<HTMLDivElement>();
|
const domReference = React.useRef<HTMLDivElement>();
|
||||||
const splitview = React.useRef<SplitView>();
|
const splitpanel = React.useRef<IComponentSplitview>();
|
||||||
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
|
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
|
||||||
|
|
||||||
const addPortal = React.useCallback((p: React.ReactPortal) => {
|
const addPortal = React.useCallback((p: React.ReactPortal) => {
|
||||||
@ -32,52 +51,29 @@ export const SplitViewComponent = (props: ISplitviewComponentProps) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
splitview.current = new SplitView(domReference.current, {
|
splitpanel.current = new ComponentSplitview(domReference.current, {
|
||||||
orientation: props.orientation,
|
orientation: props.orientation,
|
||||||
|
frameworkComponents: props.components,
|
||||||
|
frameworkWrapper: {
|
||||||
|
createComponent: (id: string, component: any) => {
|
||||||
|
return new ReactComponentView(id, id, component, { addPortal });
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const createViewWrapper = (
|
|
||||||
id: string,
|
|
||||||
component: React.FunctionComponent<{}>
|
|
||||||
) => {
|
|
||||||
return new ReactView(id, component, { addPortal });
|
|
||||||
};
|
|
||||||
|
|
||||||
const facade: SplitviewFacade = {
|
|
||||||
addFromComponent: (options) => {
|
|
||||||
const component = props.components[options.component];
|
|
||||||
const view = createViewWrapper(options.id, component);
|
|
||||||
|
|
||||||
splitview.current.addView(view, { type: "distribute" });
|
|
||||||
view.init({ params: {} });
|
|
||||||
return {
|
|
||||||
dispose: () => {
|
|
||||||
//
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
layout: (width, height) => {
|
|
||||||
const [size, orthogonalSize] =
|
|
||||||
props.orientation === Orientation.HORIZONTAL
|
|
||||||
? [width, height]
|
|
||||||
: [height, width];
|
|
||||||
splitview.current.layout(size, orthogonalSize);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const { width, height } = domReference.current.getBoundingClientRect();
|
const { width, height } = domReference.current.getBoundingClientRect();
|
||||||
const [size, orthogonalSize] =
|
const [size, orthogonalSize] =
|
||||||
props.orientation === Orientation.HORIZONTAL
|
props.orientation === Orientation.HORIZONTAL
|
||||||
? [width, height]
|
? [width, height]
|
||||||
: [height, width];
|
: [height, width];
|
||||||
splitview.current.layout(size, orthogonalSize);
|
splitpanel.current.layout(size, orthogonalSize);
|
||||||
|
|
||||||
if (props.onReady) {
|
if (props.onReady) {
|
||||||
props.onReady({ api: facade });
|
props.onReady({ api: splitpanel.current });
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
splitview.current.dispose();
|
splitpanel.current.dispose();
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
128
packages/splitview/src/splitview/componentSplitview.ts
Normal file
128
packages/splitview/src/splitview/componentSplitview.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import { IDisposable } from "../lifecycle";
|
||||||
|
import { Orientation, SplitView } from "./splitview";
|
||||||
|
import {
|
||||||
|
createComponent,
|
||||||
|
ISerializableView,
|
||||||
|
SplitPanelOptions,
|
||||||
|
} from "./options";
|
||||||
|
|
||||||
|
export interface IComponentSplitview extends IDisposable {
|
||||||
|
addFromComponent(options: {
|
||||||
|
id: string;
|
||||||
|
component: string;
|
||||||
|
params?: {
|
||||||
|
[index: string]: any;
|
||||||
|
};
|
||||||
|
}): IDisposable;
|
||||||
|
layout(width: number, height: number): void;
|
||||||
|
onChange(cb: (event: { proportions: number[] }) => void): IDisposable;
|
||||||
|
toJSON(): object;
|
||||||
|
deserialize(data: any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A high-level implementation of splitview that works using 'panels'
|
||||||
|
*/
|
||||||
|
export class ComponentSplitview implements IComponentSplitview {
|
||||||
|
private splitview: SplitView;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly element: HTMLElement,
|
||||||
|
private readonly options: SplitPanelOptions
|
||||||
|
) {
|
||||||
|
if (!options.components) {
|
||||||
|
options.components = {};
|
||||||
|
}
|
||||||
|
if (!options.frameworkComponents) {
|
||||||
|
options.frameworkComponents = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.splitview = new SplitView(this.element, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
addFromComponent(options: {
|
||||||
|
id: string;
|
||||||
|
component: string;
|
||||||
|
params?: {
|
||||||
|
[index: string]: any;
|
||||||
|
};
|
||||||
|
}): IDisposable {
|
||||||
|
const view = createComponent(
|
||||||
|
options.component,
|
||||||
|
this.options.components,
|
||||||
|
this.options.frameworkComponents,
|
||||||
|
this.options.frameworkWrapper.createComponent
|
||||||
|
);
|
||||||
|
|
||||||
|
this.registerView(view);
|
||||||
|
|
||||||
|
this.splitview.addView(view, { type: "distribute" });
|
||||||
|
view.init({ params: options.params });
|
||||||
|
return {
|
||||||
|
dispose: () => {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerView(view: ISerializableView) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(width: number, height: number): void {
|
||||||
|
const [size, orthogonalSize] =
|
||||||
|
this.splitview.orientation === Orientation.HORIZONTAL
|
||||||
|
? [width, height]
|
||||||
|
: [height, width];
|
||||||
|
this.splitview.layout(size, orthogonalSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(cb: (event: { proportions: number[] }) => void): IDisposable {
|
||||||
|
return this.splitview.onDidSashEnd(() => {
|
||||||
|
cb({ proportions: this.splitview.proportions });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
toJSON(): object {
|
||||||
|
const views = this.splitview.getViews().map((v: ISerializableView, i) => {
|
||||||
|
const size = this.splitview.getViewSize(i);
|
||||||
|
return { size, data: v.toJSON ? v.toJSON() : {} };
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
views,
|
||||||
|
size: this.splitview.size,
|
||||||
|
orientation: this.splitview.orientation,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
deserialize(data: any): void {
|
||||||
|
const { views, orientation, size } = data;
|
||||||
|
|
||||||
|
this.splitview.dispose();
|
||||||
|
this.splitview = new SplitView(this.element, {
|
||||||
|
orientation,
|
||||||
|
descriptor: {
|
||||||
|
size,
|
||||||
|
views: views.map((v) => {
|
||||||
|
const data = v.data;
|
||||||
|
|
||||||
|
const view = createComponent(
|
||||||
|
data.component,
|
||||||
|
this.options.components,
|
||||||
|
this.options.frameworkComponents,
|
||||||
|
this.options.frameworkWrapper.createComponent
|
||||||
|
);
|
||||||
|
|
||||||
|
view.init({ params: v.props });
|
||||||
|
|
||||||
|
return { size: v.size, view };
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.splitview.orientation = orientation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
this.splitview.dispose();
|
||||||
|
}
|
||||||
|
}
|
58
packages/splitview/src/splitview/options.ts
Normal file
58
packages/splitview/src/splitview/options.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { IView, ISplitViewOptions } from "../splitview/splitview";
|
||||||
|
import { Constructor, FrameworkFactory } from "../types";
|
||||||
|
|
||||||
|
export interface ISerializableView extends IView {
|
||||||
|
toJSON: () => object;
|
||||||
|
init: (params: { params: any }) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SplitPanelOptions extends ISplitViewOptions {
|
||||||
|
components?: {
|
||||||
|
[componentName: string]: ISerializableView;
|
||||||
|
};
|
||||||
|
frameworkComponents?: {
|
||||||
|
[componentName: string]: any;
|
||||||
|
};
|
||||||
|
frameworkWrapper?: FrameworkFactory<ISerializableView>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISerializableViewConstructor
|
||||||
|
extends Constructor<ISerializableView> {}
|
||||||
|
|
||||||
|
export function createComponent(
|
||||||
|
componentName: string | ISerializableViewConstructor | any,
|
||||||
|
components: {
|
||||||
|
[componentName: string]: ISerializableView;
|
||||||
|
},
|
||||||
|
frameworkComponents: {
|
||||||
|
[componentName: string]: any;
|
||||||
|
},
|
||||||
|
createFrameworkComponent: (id: string, component: any) => ISerializableView
|
||||||
|
): ISerializableView {
|
||||||
|
const Component =
|
||||||
|
typeof componentName === "string"
|
||||||
|
? components[componentName]
|
||||||
|
: componentName;
|
||||||
|
const FrameworkComponent =
|
||||||
|
typeof componentName === "string"
|
||||||
|
? frameworkComponents[componentName]
|
||||||
|
: componentName;
|
||||||
|
if (Component && FrameworkComponent) {
|
||||||
|
throw new Error(
|
||||||
|
`cannot register component ${componentName} as both a component and frameworkComponent`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (FrameworkComponent) {
|
||||||
|
if (!createFrameworkComponent) {
|
||||||
|
throw new Error(
|
||||||
|
"you must register a frameworkPanelWrapper to use framework components"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const wrappedComponent = createFrameworkComponent(
|
||||||
|
componentName,
|
||||||
|
FrameworkComponent
|
||||||
|
);
|
||||||
|
return wrappedComponent;
|
||||||
|
}
|
||||||
|
return new Component() as ISerializableView;
|
||||||
|
}
|
@ -68,15 +68,23 @@ export class SplitView {
|
|||||||
private sashContainer: HTMLElement;
|
private sashContainer: HTMLElement;
|
||||||
private views: IViewItem[] = [];
|
private views: IViewItem[] = [];
|
||||||
private sashes: ISashItem[] = [];
|
private sashes: ISashItem[] = [];
|
||||||
private orientation: Orientation;
|
private _orientation: Orientation;
|
||||||
private size: number;
|
private _size: number;
|
||||||
private orthogonalSize: number;
|
private _orthogonalSize: number;
|
||||||
private contentSize: number;
|
private contentSize: number;
|
||||||
private _proportions: number[];
|
private _proportions: number[];
|
||||||
|
|
||||||
private _onDidSashEnd = new Emitter<any>();
|
private _onDidSashEnd = new Emitter<any>();
|
||||||
public onDidSashEnd = this._onDidSashEnd.event;
|
public onDidSashEnd = this._onDidSashEnd.event;
|
||||||
|
|
||||||
|
get size() {
|
||||||
|
return this._size;
|
||||||
|
}
|
||||||
|
|
||||||
|
get orthogonalSize() {
|
||||||
|
return this._orthogonalSize;
|
||||||
|
}
|
||||||
|
|
||||||
public get length() {
|
public get length() {
|
||||||
return this.views.length;
|
return this.views.length;
|
||||||
}
|
}
|
||||||
@ -85,6 +93,10 @@ export class SplitView {
|
|||||||
return [...this._proportions];
|
return [...this._proportions];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get orientation() {
|
||||||
|
return this._orientation;
|
||||||
|
}
|
||||||
|
|
||||||
get minimumSize(): number {
|
get minimumSize(): number {
|
||||||
return this.views.reduce((r, item) => r + item.view.minimumSize, 0);
|
return this.views.reduce((r, item) => r + item.view.minimumSize, 0);
|
||||||
}
|
}
|
||||||
@ -99,7 +111,7 @@ export class SplitView {
|
|||||||
private readonly container: HTMLElement,
|
private readonly container: HTMLElement,
|
||||||
options: ISplitViewOptions
|
options: ISplitViewOptions
|
||||||
) {
|
) {
|
||||||
this.orientation = options.orientation;
|
this._orientation = options.orientation;
|
||||||
this.element = this.createContainer();
|
this.element = this.createContainer();
|
||||||
|
|
||||||
this.viewContainer = this.createViewContainer();
|
this.viewContainer = this.createViewContainer();
|
||||||
@ -112,7 +124,7 @@ export class SplitView {
|
|||||||
|
|
||||||
// We have an existing set of view, add them now
|
// We have an existing set of view, add them now
|
||||||
if (options.descriptor) {
|
if (options.descriptor) {
|
||||||
this.size = options.descriptor.size;
|
this._size = options.descriptor.size;
|
||||||
options.descriptor.views.forEach((viewDescriptor, index) => {
|
options.descriptor.views.forEach((viewDescriptor, index) => {
|
||||||
const sizing = viewDescriptor.size;
|
const sizing = viewDescriptor.size;
|
||||||
|
|
||||||
@ -161,7 +173,7 @@ export class SplitView {
|
|||||||
size = clamp(
|
size = clamp(
|
||||||
size,
|
size,
|
||||||
item.view.minimumSize,
|
item.view.minimumSize,
|
||||||
Math.min(item.view.maximumSize, this.size)
|
Math.min(item.view.maximumSize, this._size)
|
||||||
);
|
);
|
||||||
|
|
||||||
item.size = size;
|
item.size = size;
|
||||||
@ -189,7 +201,7 @@ export class SplitView {
|
|||||||
|
|
||||||
const contentSize = this.views.reduce((r, i) => r + i.size, 0);
|
const contentSize = this.views.reduce((r, i) => r + i.size, 0);
|
||||||
|
|
||||||
this.resize(this.views.length - 1, this.size - contentSize, undefined, [
|
this.resize(this.views.length - 1, this._size - contentSize, undefined, [
|
||||||
index,
|
index,
|
||||||
]);
|
]);
|
||||||
this.distributeEmptySpace();
|
this.distributeEmptySpace();
|
||||||
@ -250,7 +262,7 @@ export class SplitView {
|
|||||||
|
|
||||||
const cb = (event: MouseEvent) => {
|
const cb = (event: MouseEvent) => {
|
||||||
let start =
|
let start =
|
||||||
this.orientation === Orientation.HORIZONTAL
|
this._orientation === Orientation.HORIZONTAL
|
||||||
? event.clientX
|
? event.clientX
|
||||||
: event.clientY;
|
: event.clientY;
|
||||||
const sizes = this.views.map((x) => x.size);
|
const sizes = this.views.map((x) => x.size);
|
||||||
@ -259,7 +271,7 @@ export class SplitView {
|
|||||||
|
|
||||||
const mousemove = (event: MouseEvent) => {
|
const mousemove = (event: MouseEvent) => {
|
||||||
const current =
|
const current =
|
||||||
this.orientation === Orientation.HORIZONTAL
|
this._orientation === Orientation.HORIZONTAL
|
||||||
? event.clientX
|
? event.clientX
|
||||||
: event.clientY;
|
: event.clientY;
|
||||||
const delta = current - start;
|
const delta = current - start;
|
||||||
@ -370,11 +382,11 @@ export class SplitView {
|
|||||||
this.addView(view, sizing, to);
|
this.addView(view, sizing, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setOrientation(orientation: Orientation) {
|
set orientation(orientation: Orientation) {
|
||||||
if (orientation === this.orientation) {
|
if (orientation === this._orientation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.orientation = orientation;
|
this._orientation = orientation;
|
||||||
|
|
||||||
const classname =
|
const classname =
|
||||||
orientation === Orientation.HORIZONTAL ? "horizontal" : "vertical";
|
orientation === Orientation.HORIZONTAL ? "horizontal" : "vertical";
|
||||||
@ -386,8 +398,8 @@ export class SplitView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public layout(size: number, orthogonalSize: number) {
|
public layout(size: number, orthogonalSize: number) {
|
||||||
this.size = size;
|
this._size = size;
|
||||||
this.orthogonalSize = orthogonalSize;
|
this._orthogonalSize = orthogonalSize;
|
||||||
|
|
||||||
for (let i = 0; i < this.views.length; i++) {
|
for (let i = 0; i < this.views.length; i++) {
|
||||||
const item = this.views[i];
|
const item = this.views[i];
|
||||||
@ -412,7 +424,7 @@ export class SplitView {
|
|||||||
|
|
||||||
this.resize(
|
this.resize(
|
||||||
this.views.length - 1,
|
this.views.length - 1,
|
||||||
this.size - contentSize,
|
this._size - contentSize,
|
||||||
undefined,
|
undefined,
|
||||||
lowPriorityIndexes,
|
lowPriorityIndexes,
|
||||||
highPriorityIndexes
|
highPriorityIndexes
|
||||||
@ -423,7 +435,7 @@ export class SplitView {
|
|||||||
|
|
||||||
private distributeEmptySpace() {
|
private distributeEmptySpace() {
|
||||||
let contentSize = this.views.reduce((r, i) => r + i.size, 0);
|
let contentSize = this.views.reduce((r, i) => r + i.size, 0);
|
||||||
let emptyDelta = this.size - contentSize;
|
let emptyDelta = this._size - contentSize;
|
||||||
|
|
||||||
for (let i = this.views.length - 1; emptyDelta !== 0 && i >= 0; i--) {
|
for (let i = this.views.length - 1; emptyDelta !== 0 && i >= 0; i--) {
|
||||||
const item = this.views[i];
|
const item = this.views[i];
|
||||||
@ -448,30 +460,30 @@ export class SplitView {
|
|||||||
for (let i = 0; i < this.views.length - 1; i++) {
|
for (let i = 0; i < this.views.length - 1; i++) {
|
||||||
sum += this.views[i].size;
|
sum += this.views[i].size;
|
||||||
x.push(sum);
|
x.push(sum);
|
||||||
if (this.orientation === Orientation.HORIZONTAL) {
|
if (this._orientation === Orientation.HORIZONTAL) {
|
||||||
this.sashes[i].container.style.left = `${sum - 2}px`;
|
this.sashes[i].container.style.left = `${sum - 2}px`;
|
||||||
this.sashes[i].container.style.top = `0px`;
|
this.sashes[i].container.style.top = `0px`;
|
||||||
}
|
}
|
||||||
if (this.orientation === Orientation.VERTICAL) {
|
if (this._orientation === Orientation.VERTICAL) {
|
||||||
this.sashes[i].container.style.left = `0px`;
|
this.sashes[i].container.style.left = `0px`;
|
||||||
this.sashes[i].container.style.top = `${sum - 2}px`;
|
this.sashes[i].container.style.top = `${sum - 2}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.views.forEach((view, i) => {
|
this.views.forEach((view, i) => {
|
||||||
if (this.orientation === Orientation.HORIZONTAL) {
|
if (this._orientation === Orientation.HORIZONTAL) {
|
||||||
view.container.style.width = `${view.size}px`;
|
view.container.style.width = `${view.size}px`;
|
||||||
view.container.style.left = i == 0 ? "0px" : `${x[i - 1]}px`;
|
view.container.style.left = i == 0 ? "0px" : `${x[i - 1]}px`;
|
||||||
view.container.style.top = "";
|
view.container.style.top = "";
|
||||||
view.container.style.height = "";
|
view.container.style.height = "";
|
||||||
}
|
}
|
||||||
if (this.orientation === Orientation.VERTICAL) {
|
if (this._orientation === Orientation.VERTICAL) {
|
||||||
view.container.style.height = `${view.size}px`;
|
view.container.style.height = `${view.size}px`;
|
||||||
view.container.style.top = i == 0 ? "0px" : `${x[i - 1]}px`;
|
view.container.style.top = i == 0 ? "0px" : `${x[i - 1]}px`;
|
||||||
view.container.style.width = "";
|
view.container.style.width = "";
|
||||||
view.container.style.left = "";
|
view.container.style.left = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
view.view.layout(view.size, this.orthogonalSize);
|
view.view.layout(view.size, this._orthogonalSize);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,12 +600,13 @@ export class SplitView {
|
|||||||
private createContainer() {
|
private createContainer() {
|
||||||
const element = document.createElement("div");
|
const element = document.createElement("div");
|
||||||
const orientationClassname =
|
const orientationClassname =
|
||||||
this.orientation === Orientation.HORIZONTAL ? "horizontal" : "vertical";
|
this._orientation === Orientation.HORIZONTAL ? "horizontal" : "vertical";
|
||||||
element.className = `split-view-container ${orientationClassname}`;
|
element.className = `split-view-container ${orientationClassname}`;
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose() {
|
public dispose() {
|
||||||
|
this.element.remove();
|
||||||
for (let i = 0; i < this.element.children.length; i++) {
|
for (let i = 0; i < this.element.children.length; i++) {
|
||||||
if (this.element.children.item[i] === this.element) {
|
if (this.element.children.item[i] === this.element) {
|
||||||
this.element.removeChild(this.element);
|
this.element.removeChild(this.element);
|
||||||
|
7
packages/splitview/src/types.ts
Normal file
7
packages/splitview/src/types.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface Constructor<T> {
|
||||||
|
new (): T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FrameworkFactory<T> {
|
||||||
|
createComponent: (id: string, component: any) => T;
|
||||||
|
}
|
@ -5,6 +5,7 @@ const merge = require("merge2");
|
|||||||
const header = require("gulp-header");
|
const header = require("gulp-header");
|
||||||
const gulpSass = require("gulp-sass");
|
const gulpSass = require("gulp-sass");
|
||||||
const concat = require("gulp-concat");
|
const concat = require("gulp-concat");
|
||||||
|
const sourcemaps = require("gulp-sourcemaps");
|
||||||
|
|
||||||
const headerTemplate = [
|
const headerTemplate = [
|
||||||
"/**",
|
"/**",
|
||||||
@ -28,7 +29,10 @@ const build = (options) => {
|
|||||||
|
|
||||||
gulp.task("esm", () => {
|
gulp.task("esm", () => {
|
||||||
const ts = gulpTypescript.createProject(tsconfig);
|
const ts = gulpTypescript.createProject(tsconfig);
|
||||||
const tsResult = gulp.src(["src/**/*.ts", "src/**/*.tsx"]).pipe(ts());
|
const tsResult = gulp
|
||||||
|
.src(["src/**/*.ts", "src/**/*.tsx"])
|
||||||
|
.pipe(sourcemaps.init())
|
||||||
|
.pipe(ts());
|
||||||
return merge([
|
return merge([
|
||||||
tsResult.dts
|
tsResult.dts
|
||||||
.pipe(header(dtsHeaderTemplate, { pkg: package }))
|
.pipe(header(dtsHeaderTemplate, { pkg: package }))
|
||||||
@ -36,6 +40,9 @@ const build = (options) => {
|
|||||||
tsResult.js
|
tsResult.js
|
||||||
.pipe(header(headerTemplate, { pkg: package }))
|
.pipe(header(headerTemplate, { pkg: package }))
|
||||||
.pipe(gulp.dest("./dist/esm")),
|
.pipe(gulp.dest("./dist/esm")),
|
||||||
|
tsResult
|
||||||
|
.pipe(sourcemaps.write(".", { includeContent: false }))
|
||||||
|
.pipe(gulp.dest("./dist/esm")),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user