This commit is contained in:
mathuo 2020-09-20 20:00:31 +01:00
parent 62909463ed
commit 9fc0603d61
104 changed files with 27018 additions and 26799 deletions

3
.prettierignore Normal file
View File

@ -0,0 +1,3 @@
typedocs/
dist/
build/

6
.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": false,
"singleQuote": true
}

13
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,13 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"esbenp.prettier-vscode"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
]
}

View File

@ -1 +1 @@
{}
{}

View File

@ -1,16 +1,16 @@
module.exports = {
transform: {
"^.+\\.tsx?$": "ts-jest",
},
testEnvironment: "jsdom",
collectCoverageFrom: ["**/*.{ts,tsx}", "!**/node_modules/**"],
moduleNameMapper: {
"\\.(css|less|sass|scss)$":
"<rootDir>/src/__tests__/__mocks__/styleMock.js",
},
testMatch: [
"<rootDir>/src/__tests__/**/*.spec.ts",
"<rootDir>/src/__tests__/**/*.spec.tsx",
],
setupFilesAfterEnv: ["<rootDir>/src/__tests__/setupTests.ts"],
};
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testEnvironment: 'jsdom',
collectCoverageFrom: ['**/*.{ts,tsx}', '!**/node_modules/**'],
moduleNameMapper: {
'\\.(css|less|sass|scss)$':
'<rootDir>/src/__tests__/__mocks__/styleMock.js',
},
testMatch: [
'<rootDir>/src/__tests__/**/*.spec.ts',
'<rootDir>/src/__tests__/**/*.spec.tsx',
],
setupFilesAfterEnv: ['<rootDir>/src/__tests__/setupTests.ts'],
}

View File

@ -1,6 +1,4 @@
{
"packages": [
"packages/*"
],
"version": "0.0.0"
"packages": ["packages/*"],
"version": "0.0.0"
}

36436
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,44 +1,44 @@
{
"name": "splitview-root",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mathuo/splitview.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/mathuo/splitview/issues"
},
"homepage": "https://github.com/mathuo/splitview#readme",
"devDependencies": {
"@testing-library/jest-dom": "^5.10.1",
"@types/jest": "^26.0.3",
"css-loader": "^3.6.0",
"gulp": "^4.0.2",
"gulp-clean": "^0.4.0",
"gulp-concat": "^2.6.1",
"gulp-header": "^2.0.9",
"gulp-sass": "^4.1.0",
"gulp-sourcemaps": "^2.6.5",
"gulp-typescript": "^6.0.0-alpha.1",
"jest": "^26.1.0",
"jsdom": "^16.2.2",
"lerna": "^3.22.1",
"merge2": "^1.4.1",
"node-sass": "^4.14.1",
"sass-loader": "^8.0.2",
"style-loader": "^1.2.1",
"ts-jest": "^26.1.1",
"ts-loader": "^7.0.5",
"typescript": "^3.9.5",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
}
"name": "splitview-root",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mathuo/splitview.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/mathuo/splitview/issues"
},
"homepage": "https://github.com/mathuo/splitview#readme",
"devDependencies": {
"@testing-library/jest-dom": "^5.10.1",
"@types/jest": "^26.0.3",
"css-loader": "^3.6.0",
"gulp": "^4.0.2",
"gulp-clean": "^0.4.0",
"gulp-concat": "^2.6.1",
"gulp-header": "^2.0.9",
"gulp-sass": "^4.1.0",
"gulp-sourcemaps": "^2.6.5",
"gulp-typescript": "^6.0.0-alpha.1",
"jest": "^26.1.0",
"jsdom": "^16.2.2",
"lerna": "^3.22.1",
"merge2": "^1.4.1",
"node-sass": "^4.14.1",
"sass-loader": "^8.0.2",
"style-loader": "^1.2.1",
"ts-jest": "^26.1.1",
"ts-loader": "^7.0.5",
"typescript": "^3.9.5",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
}
}

View File

@ -1,22 +1,22 @@
{
"name": "splitview-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "bash ../../node_modules/.bin/webpack --config webpack.config.js",
"start": "bash ../../node_modules/.bin/webpack-dev-server --config webpack.config.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1",
"splitview": "*",
"splitview-react": "*"
},
"devDependencies": {
"@types/react": "^16.9.41",
"@types/react-dom": "^16.9.8"
}
"name": "splitview-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "bash ../../node_modules/.bin/webpack --config webpack.config.js",
"start": "../../node_modules/.bin/webpack-dev-server --config webpack.config.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1",
"splitview": "*",
"splitview-react": "*"
},
"devDependencies": {
"@types/react": "^16.9.41",
"@types/react-dom": "^16.9.8"
}
}

View File

@ -1,6 +1,6 @@
<html>
<body>
<div id="app"></div>
<script src="/dist/bundle.js"></script>
</body>
<body>
<div id="app"></div>
<script src="/dist/bundle.js"></script>
</body>
</html>

View File

@ -1,52 +1,52 @@
import * as React from "react";
import * as React from 'react'
// import { LoadFromConfig } from "./loadFromConfig";
// import { FromApi } from "./fromApi";
// import { PaneDemo } from "./pane";
import { TestGrid } from "./layout-grid/reactgrid";
import { Application } from "./layout-grid/application";
import { TestGrid } from './layout-grid/reactgrid'
import { Application } from './layout-grid/application'
const options = [
// { id: "config", component: LoadFromConfig },
// { id: "api", component: FromApi },
// { id: "pane", component: PaneDemo },
{ id: "grid", component: Application },
];
// { id: "config", component: LoadFromConfig },
// { id: "api", component: FromApi },
// { id: "pane", component: PaneDemo },
{ id: 'grid', component: Application },
]
export const App = () => {
const [value, setValue] = React.useState<string>(options[0].id);
const [value, setValue] = React.useState<string>(options[0].id)
const onChange = (event: React.ChangeEvent<HTMLSelectElement>) =>
setValue(event.target.value);
const onChange = (event: React.ChangeEvent<HTMLSelectElement>) =>
setValue(event.target.value)
const Component = React.useMemo(
() => options.find((o) => o.id === value)?.component,
[value]
);
const Component = React.useMemo(
() => options.find((o) => o.id === value)?.component,
[value]
)
return (
<div
style={{
height: "100vh",
width: "100vw",
display: "flex",
flexDirection: "column",
}}
>
<div style={{ height: "20px", flexShrink: 0 }}>
<select onChange={onChange} value={value}>
{options.map((option, i) => (
<option key={i} value={option.id}>
{option.id}{" "}
</option>
))}
</select>
</div>
return (
<div
style={{
height: '100vh',
width: '100vw',
display: 'flex',
flexDirection: 'column',
}}
>
<div style={{ height: '20px', flexShrink: 0 }}>
<select onChange={onChange} value={value}>
{options.map((option, i) => (
<option key={i} value={option.id}>
{option.id}{' '}
</option>
))}
</select>
</div>
{Component && (
<div style={{ width: "100%", flexGrow: 1 }}>
<Component />
{Component && (
<div style={{ width: '100%', flexGrow: 1 }}>
<Component />
</div>
)}
</div>
)}
</div>
);
};
)
}

View File

@ -1,9 +1,9 @@
.header {
padding-left: 10px;
display: flex;
padding-left: 10px;
display: flex;
span {
margin-top: auto;
margin-bottom: auto;
}
span {
margin-top: auto;
margin-bottom: auto;
}
}

View File

@ -1,15 +1,15 @@
@import "~splitview/dist/styles.css";
@import '~splitview/dist/styles.css';
body {
margin: 0;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
overflow: hidden;
margin: 0;
font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
overflow: hidden;
}
*,
*::after,
*::before {
// -webkit-user-drag: none;
-webkit-app-region: no-drag;
-webkit-user-select: none;
// -webkit-user-drag: none;
-webkit-app-region: no-drag;
-webkit-user-select: none;
}

View File

@ -1,6 +1,6 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import { App } from "./app";
import "./index.scss";
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { App } from './app'
import './index.scss'
ReactDOM.render(<App />, document.getElementById("app"));
ReactDOM.render(<App />, document.getElementById('app'))

View File

@ -1,75 +1,83 @@
import * as React from "react";
import * as React from 'react'
import {
Orientation,
GridviewComponent,
LayoutPriority,
GridviewReadyEvent,
ComponentGridview,
IGridviewPanelProps,
} from "splitview";
import { TestGrid } from "./reactgrid";
Orientation,
GridviewComponent,
LayoutPriority,
GridviewReadyEvent,
ComponentGridview,
IGridviewPanelProps,
} from 'splitview'
import { TestGrid } from './reactgrid'
const rootcomponents: {
[index: string]: React.FunctionComponent<IGridviewPanelProps>;
[index: string]: React.FunctionComponent<IGridviewPanelProps>
} = {
sidebar: (props: IGridviewPanelProps) => {
return <div style={{backgroundColor: "rgb(37,37,38)", height:"100%"}}>sidebar</div>;
},
editor: TestGrid,
panel: () => {
return <div style={{backgroundColor: "rgb(30,30,30)", height: "100%"}}>panel</div>;
},
};
sidebar: (props: IGridviewPanelProps) => {
return (
<div style={{ backgroundColor: 'rgb(37,37,38)', height: '100%' }}>
sidebar
</div>
)
},
editor: TestGrid,
panel: () => {
return (
<div style={{ backgroundColor: 'rgb(30,30,30)', height: '100%' }}>
panel
</div>
)
},
}
export const Application = () => {
const api = React.useRef<ComponentGridview>();
const api = React.useRef<ComponentGridview>()
const onReady = (event: GridviewReadyEvent) => {
// event.api.deserialize(rootLayout);
event.api.addComponent({
id: "1",
component: "sidebar",
snap: true,
});
event.api.addComponent({
id: "2",
component: "editor",
snap: true,
position: { reference: "1", direction: "right" },
priority: LayoutPriority.High,
});
const onReady = (event: GridviewReadyEvent) => {
// event.api.deserialize(rootLayout);
event.api.addComponent({
id: '1',
component: 'sidebar',
snap: true,
})
event.api.addComponent({
id: '2',
component: 'editor',
snap: true,
position: { reference: '1', direction: 'right' },
priority: LayoutPriority.High,
})
api.current = event.api as ComponentGridview;
};
api.current = event.api as ComponentGridview
}
React.useEffect(() => {
const callback = (ev: UIEvent) => {
const height = window.innerHeight - 20;
const width = window.innerWidth;
React.useEffect(() => {
const callback = (ev: UIEvent) => {
const height = window.innerHeight - 20
const width = window.innerWidth
api.current?.layout(width, height);
};
window.addEventListener("resize", callback);
callback(undefined);
api.current?.layout(width, height)
}
window.addEventListener('resize', callback)
callback(undefined)
api.current.addComponent({
id: "3",
component: "panel",
position: { reference: "2", direction: "below" },
size: 200,
snap: true,
});
api.current.addComponent({
id: '3',
component: 'panel',
position: { reference: '2', direction: 'below' },
size: 200,
snap: true,
})
return () => {
window.removeEventListener("resize", callback);
};
}, []);
return () => {
window.removeEventListener('resize', callback)
}
}, [])
return (
<GridviewComponent
components={rootcomponents}
onReady={onReady}
orientation={Orientation.HORIZONTAL}
/>
);
};
return (
<GridviewComponent
components={rootcomponents}
onReady={onReady}
orientation={Orientation.HORIZONTAL}
/>
)
}

View File

@ -1,6 +1,6 @@
import * as React from "react";
import { IPanelProps } from "splitview";
import * as React from 'react'
import { IPanelProps } from 'splitview'
export const CustomTab = (props: IPanelProps) => {
return <div>hello</div>;
};
return <div>hello</div>
}

View File

@ -1,33 +1,39 @@
import * as React from "react";
import { Api, IPanelProps } from "splitview";
import * as React from 'react'
import { Api, IPanelProps } from 'splitview'
export const Editor = (props: IPanelProps & { layoutApi: Api }) => {
const [tabHeight, setTabHeight] = React.useState<number>(0);
const [tabHeight, setTabHeight] = React.useState<number>(0)
React.useEffect(() => {
if (props.layoutApi) {
setTabHeight(props.layoutApi.getTabHeight());
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)
}
}
}, [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)
}
};
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>
);
};
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>
)
}

View File

@ -1,410 +1,425 @@
import * as React from "react";
import * as React from 'react'
import {
ReactGrid,
OnReadyEvent,
Api,
IPanelProps,
ClosePanelResult,
CompositeDisposable,
GroupChangeKind,
IGridviewPanelProps,
TabContextMenuEvent
} from "splitview";
import { CustomTab } from "./customTab";
import { Editor } from "./editorPanel";
import { SplitPanel } from "./splitPanel";
ReactGrid,
OnReadyEvent,
Api,
IPanelProps,
ClosePanelResult,
CompositeDisposable,
GroupChangeKind,
IGridviewPanelProps,
TabContextMenuEvent,
} from 'splitview'
import { CustomTab } from './customTab'
import { Editor } from './editorPanel'
import { SplitPanel } from './splitPanel'
const components = {
inner_component: (props: IPanelProps) => {
const _api = React.useRef<Api>();
const [api, setApi] = React.useState<Api>();
inner_component: (props: IPanelProps) => {
const _api = React.useRef<Api>()
const [api, setApi] = React.useState<Api>()
const onReady = (event: OnReadyEvent) => {
_api.current = event.api;
const onReady = (event: OnReadyEvent) => {
_api.current = event.api
const layout = props.api.getStateKey<object>("layout");
if (layout) {
event.api.deserialize(layout);
} else {
event.api.addPanelFromComponent({
componentName: "test_component",
id: "inner-1",
title: "inner-1",
});
event.api.addPanelFromComponent({
componentName: "test_component",
id: "inner-2",
title: "inner-2",
});
event.api.addPanelFromComponent({
componentName: "test_component",
id: nextGuid(),
title: "inner-3",
position: { direction: "within", referencePanel: "inner-1" },
});
event.api.addPanelFromComponent({
componentName: "test_component",
id: nextGuid(),
title: "inner-4",
position: { direction: "within", referencePanel: "inner-2" },
});
}
setApi(event.api);
};
React.useEffect(() => {
const compDis = new CompositeDisposable(
props.api.onDidDimensionsChange((event) => {
_api.current?.layout(event.width, event.height);
}),
_api.current.onDidLayoutChange((event) => {
if (event.kind === GroupChangeKind.LAYOUT_CONFIG_UPDATED) {
props.api.setState("layout", _api.current.toJSON());
}
})
);
return () => {
compDis.dispose();
};
}, []);
React.useEffect(() => {
if (!api) {
return;
}
api.onDidLayoutChange((event) => {
// on inner grid changes
});
}, [api]);
return (
<div
style={{
boxSizing: "border-box",
// borderTop: "1px solid var(--splitview-divider-color)",
}}
>
<ReactGrid
onReady={onReady}
components={components}
tabHeight={20}
debug={true}
/>
</div>
);
},
test_component: (props: IPanelProps & { [key: string]: any }) => {
const [panelState, setPanelState] = React.useState<{
isGroupActive: boolean;
isPanelVisible: boolean;
}>({
isGroupActive: false,
isPanelVisible: false,
});
React.useEffect(() => {
const disposable = new CompositeDisposable(
props.api.onDidFocusChange((event) => {
setPanelState((_) => ({ ..._, isGroupActive: event.isFocused }));
}),
props.api.onDidChangeVisibility((x) => {
setPanelState((_) => ({ ..._, isPanelVisible: x.isVisible }));
})
);
props.api.setClosePanelHook(() => {
if (confirm("close?")) {
return Promise.resolve(ClosePanelResult.CLOSE);
const layout = props.api.getStateKey<object>('layout')
if (layout) {
event.api.deserialize(layout)
} else {
event.api.addPanelFromComponent({
componentName: 'test_component',
id: 'inner-1',
title: 'inner-1',
})
event.api.addPanelFromComponent({
componentName: 'test_component',
id: 'inner-2',
title: 'inner-2',
})
event.api.addPanelFromComponent({
componentName: 'test_component',
id: nextGuid(),
title: 'inner-3',
position: {
direction: 'within',
referencePanel: 'inner-1',
},
})
event.api.addPanelFromComponent({
componentName: 'test_component',
id: nextGuid(),
title: 'inner-4',
position: {
direction: 'within',
referencePanel: 'inner-2',
},
})
}
setApi(event.api)
}
return Promise.resolve(ClosePanelResult.DONT_CLOSE);
});
return () => {
disposable.dispose();
};
}, []);
React.useEffect(() => {
const compDis = new CompositeDisposable(
props.api.onDidDimensionsChange((event) => {
_api.current?.layout(event.width, event.height)
}),
_api.current.onDidLayoutChange((event) => {
if (event.kind === GroupChangeKind.LAYOUT_CONFIG_UPDATED) {
props.api.setState('layout', _api.current.toJSON())
}
})
)
const onClick = () => {
props.api.setState("test_key", "hello");
};
return () => {
compDis.dispose()
}
}, [])
const backgroundColor = React.useMemo(
() =>
// "#1e1e1e",
`rgb(${Math.floor(Math.random() * 256)},${Math.floor(
Math.random() * 256
)},${Math.floor(Math.random() * 256)})`,
[]
);
return (
<div
style={{
backgroundColor,
height: "100%",
}}
>
<div>test component</div>
<button onClick={onClick}>set state</button>
{/* {props.api.getState()["test_key"]} */}
React.useEffect(() => {
if (!api) {
return
}
<div>{`G:${panelState.isGroupActive} P:${panelState.isPanelVisible}`}</div>
<div>{props.text || "-"}</div>
</div>
);
},
editor: Editor,
split_panel: SplitPanel,
};
api.onDidLayoutChange((event) => {
// on inner grid changes
})
}, [api])
return (
<div
style={{
boxSizing: 'border-box',
// borderTop: "1px solid var(--splitview-divider-color)",
}}
>
<ReactGrid
onReady={onReady}
components={components}
tabHeight={20}
debug={true}
/>
</div>
)
},
test_component: (props: IPanelProps & { [key: string]: any }) => {
const [panelState, setPanelState] = React.useState<{
isGroupActive: boolean
isPanelVisible: boolean
}>({
isGroupActive: false,
isPanelVisible: false,
})
React.useEffect(() => {
const disposable = new CompositeDisposable(
props.api.onDidFocusChange((event) => {
setPanelState((_) => ({
..._,
isGroupActive: event.isFocused,
}))
}),
props.api.onDidChangeVisibility((x) => {
setPanelState((_) => ({
..._,
isPanelVisible: x.isVisible,
}))
})
)
props.api.setClosePanelHook(() => {
if (confirm('close?')) {
return Promise.resolve(ClosePanelResult.CLOSE)
}
return Promise.resolve(ClosePanelResult.DONT_CLOSE)
})
return () => {
disposable.dispose()
}
}, [])
const onClick = () => {
props.api.setState('test_key', 'hello')
}
const backgroundColor = React.useMemo(
() =>
// "#1e1e1e",
`rgb(${Math.floor(Math.random() * 256)},${Math.floor(
Math.random() * 256
)},${Math.floor(Math.random() * 256)})`,
[]
)
return (
<div
style={{
backgroundColor,
height: '100%',
}}
>
<div>test component</div>
<button onClick={onClick}>set state</button>
{/* {props.api.getState()["test_key"]} */}
<div>{`G:${panelState.isGroupActive} P:${panelState.isPanelVisible}`}</div>
<div>{props.text || '-'}</div>
</div>
)
},
editor: Editor,
split_panel: SplitPanel,
}
const tabComponents = {
default: CustomTab,
};
default: CustomTab,
}
const nextGuid = (() => {
let counter = 0;
return () => "panel_" + (counter++).toString();
})();
let counter = 0
return () => 'panel_' + (counter++).toString()
})()
export const TestGrid = (props: IGridviewPanelProps) => {
const _api = React.useRef<Api>();
const [api, setApi] = React.useState<Api>();
const _api = React.useRef<Api>()
const [api, setApi] = React.useState<Api>()
const onReady = (event: OnReadyEvent) => {
_api.current = event.api;
setApi(event.api);
};
React.useEffect(() => {
if (!api) {
return;
const onReady = (event: OnReadyEvent) => {
_api.current = event.api
setApi(event.api)
}
const panelReference = api.addPanelFromComponent({
componentName: "test_component",
id: nextGuid(),
title: "Item 1",
params: { text: "how low?" },
});
api.addPanelFromComponent({
componentName: "test_component",
id: "item2",
title: "Item 2",
});
api.addPanelFromComponent({
componentName: "split_panel",
id: nextGuid(),
title: "Item 3 with a long title",
});
api.addPanelFromComponent({
componentName: "test_component",
id: nextGuid(),
title: "Item 3",
position: { direction: "below", referencePanel: "item2" },
suppressClosable: true,
});
React.useEffect(() => {
if (!api) {
return
}
// setInterval(() => {
// panelReference.update({ params: { text: `Tick ${Date.now()}` } });
// // panelReference.remove();
// }, 1000);
const panelReference = api.addPanelFromComponent({
componentName: 'test_component',
id: nextGuid(),
title: 'Item 1',
params: { text: 'how low?' },
})
api.addPanelFromComponent({
componentName: 'test_component',
id: 'item2',
title: 'Item 2',
})
api.addPanelFromComponent({
componentName: 'split_panel',
id: nextGuid(),
title: 'Item 3 with a long title',
})
api.addPanelFromComponent({
componentName: 'test_component',
id: nextGuid(),
title: 'Item 3',
position: { direction: 'below', referencePanel: 'item2' },
suppressClosable: true,
})
api.addDndHandle("text/plain", (ev) => {
const { event } = ev;
// setInterval(() => {
// panelReference.update({ params: { text: `Tick ${Date.now()}` } });
// // panelReference.remove();
// }, 1000);
return {
id: "yellow",
componentName: "test_component",
};
});
api.addDndHandle('text/plain', (ev) => {
const { event } = ev
api.addDndHandle("Files", (ev) => {
const { event } = ev;
return {
id: 'yellow',
componentName: 'test_component',
}
})
ev.event.event.preventDefault();
api.addDndHandle('Files', (ev) => {
const { event } = ev
return {
id: Date.now().toString(),
title: event.event.dataTransfer.files[0].name,
componentName: "test_component",
};
});
}, [api]);
ev.event.event.preventDefault()
const onAdd = () => {
const id = nextGuid();
api.addPanelFromComponent({
componentName: "test_component",
id,
});
};
return {
id: Date.now().toString(),
title: event.event.dataTransfer.files[0].name,
componentName: 'test_component',
}
})
}, [api])
const onAddEmpty = () => {
api.addEmptyGroup();
};
React.useEffect(() => {
// const callback = (ev: UIEvent) => {
// const height = window.innerHeight - 40;
// const width = window.innerWidth;
// _api.current?.layout(width, height);
// };
// window.addEventListener("resize", callback);
// callback(undefined);
props.api.setConstraints({
minimumWidth: () => _api.current.minimumWidth,
minimumHeight: () => _api.current.minimumHeight,
});
const disposable = new CompositeDisposable(
_api.current.onDidLayoutChange((event) => {
console.log(event.kind);
}),
props.api.onDidDimensionsChange((event) => {
const { width, height } = event;
_api.current.layout(width, height - 20);
})
);
return () => {
disposable.dispose();
// window.removeEventListener("resize", callback);
};
}, []);
const onConfig = () => {
const data = api.toJSON();
const stringData = JSON.stringify(data, null, 4);
console.log(stringData);
localStorage.setItem("layout", stringData);
};
const onLoad = async () => {
const didClose = await api.closeAllGroups();
if (!didClose) {
return;
const onAdd = () => {
const id = nextGuid()
api.addPanelFromComponent({
componentName: 'test_component',
id,
})
}
const data = localStorage.getItem("layout");
if (data) {
const jsonData = JSON.parse(data);
api.deserialize(jsonData);
const onAddEmpty = () => {
api.addEmptyGroup()
}
};
const onClear = () => {
api.closeAllGroups();
};
React.useEffect(() => {
// const callback = (ev: UIEvent) => {
// const height = window.innerHeight - 40;
// const width = window.innerWidth;
const onNextGroup = () => {
api.moveToNext({ includePanel: true });
};
// _api.current?.layout(width, height);
// };
// window.addEventListener("resize", callback);
// callback(undefined);
const onPreviousGroup = () => {
api.moveToPrevious({ includePanel: true });
};
props.api.setConstraints({
minimumWidth: () => _api.current.minimumWidth,
minimumHeight: () => _api.current.minimumHeight,
})
const onNextPanel = () => {
api.activeGroup?.moveToNext();
};
const disposable = new CompositeDisposable(
_api.current.onDidLayoutChange((event) => {
console.log(event.kind)
}),
props.api.onDidDimensionsChange((event) => {
const { width, height } = event
_api.current.layout(width, height - 20)
})
)
const onPreviousPanel = () => {
api.activeGroup?.moveToPrevious();
};
return () => {
disposable.dispose()
// window.removeEventListener("resize", callback);
}
}, [])
const dragRef = React.useRef<HTMLDivElement>();
React.useEffect(() => {
if (!api) {
return;
const onConfig = () => {
const data = api.toJSON()
const stringData = JSON.stringify(data, null, 4)
console.log(stringData)
localStorage.setItem('layout', stringData)
}
api.createDragTarget(
{ element: dragRef.current, content: "drag me" },
() => ({
id: "yellow",
componentName: "test_component",
})
);
}, [api]);
const onDragStart = (event: React.DragEvent) => {
event.dataTransfer.setData("text/plain", "Panel2");
};
const onLoad = async () => {
const didClose = await api.closeAllGroups()
if (!didClose) {
return
}
const data = localStorage.getItem('layout')
if (data) {
const jsonData = JSON.parse(data)
api.deserialize(jsonData)
}
}
const onAddEditor = () => {
api.addPanelFromComponent({
id: "editor",
componentName: "editor",
tabComponentName: "default",
params: { layoutApi: api },
});
};
const onClear = () => {
api.closeAllGroups()
}
const onTabContextMenu = React.useMemo(() => (event: TabContextMenuEvent) => {
console.log(event);
},[])
const onNextGroup = () => {
api.moveToNext({ includePanel: true })
}
return (
<div
// className="visual-studio-theme"
style={{ width: "100%", overflow: "hidden" }}
>
<div
style={{
height: "20px",
display: "flex",
maxHeight: "20px",
minHeight: "20px",
}}
>
<button onClick={onAdd}>Add</button>
<button onClick={onAddEditor}>Expr</button>
<button onClick={onAddEmpty}>Add empty</button>
<button onClick={onConfig}>Save</button>
<button onClick={onLoad}>Load</button>
<button onClick={onClear}>Clear</button>
<button onClick={onNextGroup}>Next</button>
<button onClick={onPreviousGroup}>Before</button>
<button onClick={onNextPanel}>NextPanel</button>
<button onClick={onPreviousPanel}>BeforePanel</button>
const onPreviousGroup = () => {
api.moveToPrevious({ includePanel: true })
}
const onNextPanel = () => {
api.activeGroup?.moveToNext()
}
const onPreviousPanel = () => {
api.activeGroup?.moveToPrevious()
}
const dragRef = React.useRef<HTMLDivElement>()
React.useEffect(() => {
if (!api) {
return
}
api.createDragTarget(
{ element: dragRef.current, content: 'drag me' },
() => ({
id: 'yellow',
componentName: 'test_component',
})
)
}, [api])
const onDragStart = (event: React.DragEvent) => {
event.dataTransfer.setData('text/plain', 'Panel2')
}
const onAddEditor = () => {
api.addPanelFromComponent({
id: 'editor',
componentName: 'editor',
tabComponentName: 'default',
params: { layoutApi: api },
})
}
const onTabContextMenu = React.useMemo(
() => (event: TabContextMenuEvent) => {
console.log(event)
},
[]
)
return (
<div
draggable={true}
className="my-dragger"
style={{
backgroundColor: "dodgerblue",
borderRadius: "10px",
color: " white",
}}
ref={dragRef}
// className="visual-studio-theme"
style={{ width: '100%', overflow: 'hidden' }}
>
Drag me
<div
style={{
height: '20px',
display: 'flex',
maxHeight: '20px',
minHeight: '20px',
}}
>
<button onClick={onAdd}>Add</button>
<button onClick={onAddEditor}>Expr</button>
<button onClick={onAddEmpty}>Add empty</button>
<button onClick={onConfig}>Save</button>
<button onClick={onLoad}>Load</button>
<button onClick={onClear}>Clear</button>
<button onClick={onNextGroup}>Next</button>
<button onClick={onPreviousGroup}>Before</button>
<button onClick={onNextPanel}>NextPanel</button>
<button onClick={onPreviousPanel}>BeforePanel</button>
<div
draggable={true}
className="my-dragger"
style={{
backgroundColor: 'dodgerblue',
borderRadius: '10px',
color: ' white',
}}
ref={dragRef}
>
Drag me
</div>
<div
onDragStart={onDragStart}
draggable={true}
className="my-dragger"
style={{
backgroundColor: 'orange',
borderRadius: '10px',
color: ' white',
}}
>
Drag me too
</div>
</div>
<ReactGrid
// autoSizeToFitContainer={true}
onReady={onReady}
components={components}
tabComponents={tabComponents}
debug={true}
// tabHeight={30}
enableExternalDragEvents={true}
// serializedLayout={data}
onTabContextMenu={onTabContextMenu}
/>
</div>
<div
onDragStart={onDragStart}
draggable={true}
className="my-dragger"
style={{
backgroundColor: "orange",
borderRadius: "10px",
color: " white",
}}
>
Drag me too
</div>
</div>
<ReactGrid
// autoSizeToFitContainer={true}
onReady={onReady}
components={components}
tabComponents={tabComponents}
debug={true}
// tabHeight={30}
enableExternalDragEvents={true}
// serializedLayout={data}
onTabContextMenu={onTabContextMenu}
/>
</div>
);
};
)
}

View File

@ -1,88 +1,88 @@
import * as React from "react";
import * as React from 'react'
import {
CompositeDisposable,
IPanelProps,
ISplitviewPanelProps,
Orientation,
SplitviewFacade,
SplitviewReadyEvent,
} from "splitview";
import { SplitViewComponent } from "splitview";
CompositeDisposable,
IPanelProps,
ISplitviewPanelProps,
Orientation,
SplitviewFacade,
SplitviewReadyEvent,
} from 'splitview'
import { SplitViewComponent } from 'splitview'
const components = {
default1: (props: ISplitviewPanelProps) => {
const [focused, setFocused] = React.useState<boolean>(false);
React.useEffect(() => {
const disposable = new CompositeDisposable(
props.api.onDidFocusChange((event) => {
setFocused(event.isFocused);
})
);
default1: (props: ISplitviewPanelProps) => {
const [focused, setFocused] = React.useState<boolean>(false)
React.useEffect(() => {
const disposable = new CompositeDisposable(
props.api.onDidFocusChange((event) => {
setFocused(event.isFocused)
})
)
return () => {
disposable.dispose();
};
}, []);
return () => {
disposable.dispose()
}
}, [])
return (
<div
style={{ height: "100%", width: "100%" }}
>{`component [isFocused: ${focused}]`}</div>
);
},
};
return (
<div
style={{ height: '100%', width: '100%' }}
>{`component [isFocused: ${focused}]`}</div>
)
},
}
export const SplitPanel = (props: IPanelProps) => {
const api = React.useRef<SplitviewFacade>();
const api = React.useRef<SplitviewFacade>()
React.useEffect(() => {
const disposable = new CompositeDisposable(
props.api.onDidDimensionsChange((event) => {
api.current?.layout(event.width, event.height - 20);
}),
api.current.onChange((event) => {
props.api.setState("sview_layout", api.current.toJSON());
})
);
React.useEffect(() => {
const disposable = new CompositeDisposable(
props.api.onDidDimensionsChange((event) => {
api.current?.layout(event.width, event.height - 20)
}),
api.current.onChange((event) => {
props.api.setState('sview_layout', api.current.toJSON())
})
)
return () => {
disposable.dispose();
};
}, []);
return () => {
disposable.dispose()
}
}, [])
const onReady = (event: SplitviewReadyEvent) => {
const existingLayout = props.api.getStateKey("sview_layout");
const onReady = (event: SplitviewReadyEvent) => {
const existingLayout = props.api.getStateKey('sview_layout')
if (existingLayout) {
event.api.deserialize(existingLayout);
} else {
event.api.addFromComponent({ id: "1", component: "default1" });
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());
};
const onSave = () => {
props.api.setState('sview_layout', api.current.toJSON())
}
return (
<div
style={{
display: "flex",
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>
);
};
return (
<div
style={{
display: 'flex',
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>
)
}

View File

@ -1,11 +1,11 @@
{
"extends": "../../tsconfig.build.json",
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}

View File

@ -1,4 +1,4 @@
{
"extends": "../../tsconfig.json",
"exclude": ["**/node_modules", "src/__tests__"]
"extends": "../../tsconfig.json",
"exclude": ["**/node_modules", "src/__tests__"]
}

View File

@ -1,53 +1,53 @@
var path = require("path");
var path = require('path')
module.exports = {
entry: path.resolve(__dirname, "src/index.tsx"),
devtool: "source-map",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
},
mode: "development",
resolve: {
extensions: [".ts", ".js", ".tsx", "jsx"],
alias: {
react: path.resolve(__dirname, "node_modules/react"),
"react-dom": path.resolve(__dirname, "node_modules/react-dom"),
entry: path.resolve(__dirname, 'src/index.tsx'),
devtool: 'source-map',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
options: { configFile: "tsconfig.build.json" },
},
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader",
mode: 'development',
resolve: {
extensions: ['.ts', '.js', '.tsx', 'jsx'],
alias: {
react: path.resolve(__dirname, 'node_modules/react'),
'react-dom': path.resolve(__dirname, 'node_modules/react-dom'),
},
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: { configFile: 'tsconfig.build.json' },
},
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
'style-loader',
// Translates CSS into CommonJS
'css-loader',
// Compiles Sass to CSS
'sass-loader',
],
},
{
test: /\.css$/i,
use: [
// Creates `style` nodes from JS strings
'style-loader',
// Translates CSS into CommonJS
'css-loader',
],
},
],
},
{
test: /\.css$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
],
},
],
},
devServer: {
port: 9000,
compress: true,
contentBase: path.resolve(__dirname, "public"),
publicPath: "/dist",
},
};
},
devServer: {
port: 9000,
compress: true,
contentBase: path.resolve(__dirname, 'public'),
publicPath: '/dist',
},
}

View File

@ -1,7 +1,7 @@
const gulp = require("gulp");
const buildfile = require("../../scripts/build");
const package = require("./package");
const gulp = require('gulp')
const buildfile = require('../../scripts/build')
const package = require('./package')
buildfile.build({ tsconfig: "./tsconfig.build.json", package });
buildfile.build({ tsconfig: './tsconfig.build.json', package })
gulp.task("run", gulp.series(["clean", "esm", "sass"]));
gulp.task('run', gulp.series(['clean', 'esm', 'sass']))

View File

@ -1,433 +1,433 @@
{
"name": "splitview-react",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@babel/code-frame": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
"integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"dev": true,
"requires": {
"@babel/highlight": "^7.10.4"
}
},
"@babel/helper-validator-identifier": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
"integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
"dev": true
},
"@babel/highlight": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
"integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.10.4",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
},
"dependencies": {
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
}
}
},
"@babel/runtime": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
"integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"@babel/runtime-corejs3": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.11.2.tgz",
"integrity": "sha512-qh5IR+8VgFz83VBa6OkaET6uN/mJOhHONuy3m1sgF0CV6mXdPSEBdA7e1eUbVvyNtANjMbg22JUv71BaDXLY6A==",
"dev": true,
"requires": {
"core-js-pure": "^3.0.0",
"regenerator-runtime": "^0.13.4"
}
},
"@jest/types": {
"version": "26.3.0",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz",
"integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==",
"dev": true,
"requires": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
"@types/node": "*",
"@types/yargs": "^15.0.0",
"chalk": "^4.0.0"
}
},
"@testing-library/dom": {
"version": "7.24.2",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.24.2.tgz",
"integrity": "sha512-ERxcZSoHx0EcN4HfshySEWmEf5Kkmgi+J7O79yCJ3xggzVlBJ2w/QjJUC+EBkJJ2OeSw48i3IoePN4w8JlVUIA==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.10.3",
"@types/aria-query": "^4.2.0",
"aria-query": "^4.2.2",
"chalk": "^4.1.0",
"dom-accessibility-api": "^0.5.1",
"pretty-format": "^26.4.2"
}
},
"@testing-library/react": {
"version": "10.4.9",
"resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.9.tgz",
"integrity": "sha512-pHZKkqUy0tmiD81afs8xfiuseXfU/N7rAX3iKjeZYje86t9VaB0LrxYVa+OOsvkrveX5jCK3IjajVn2MbePvqA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.10.3",
"@testing-library/dom": "^7.22.3"
}
},
"@types/aria-query": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz",
"integrity": "sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==",
"dev": true
},
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true
},
"@types/istanbul-lib-coverage": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
"integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==",
"dev": true
},
"@types/istanbul-lib-report": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
"integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
"dev": true,
"requires": {
"@types/istanbul-lib-coverage": "*"
}
},
"@types/istanbul-reports": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz",
"integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==",
"dev": true,
"requires": {
"@types/istanbul-lib-report": "*"
}
},
"@types/node": {
"version": "14.10.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.10.2.tgz",
"integrity": "sha512-IzMhbDYCpv26pC2wboJ4MMOa9GKtjplXfcAqrMeNJpUUwpM/2ATt2w1JPUXwS6spu856TvKZL2AOmeU2rAxskw==",
"dev": true
},
"@types/prop-types": {
"version": "15.7.3",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
"dev": true
},
"@types/react": {
"version": "16.9.49",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.49.tgz",
"integrity": "sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g==",
"dev": true,
"requires": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
}
},
"@types/react-dom": {
"version": "16.9.8",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz",
"integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/yargs": {
"version": "15.0.5",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
"integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
"dev": true,
"requires": {
"@types/yargs-parser": "*"
}
},
"@types/yargs-parser": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz",
"integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==",
"dev": true
},
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"aria-query": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
"integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.10.2",
"@babel/runtime-corejs3": "^7.10.2"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"core-js-pure": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz",
"integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==",
"dev": true
},
"csstype": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz",
"integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==",
"dev": true
},
"dom-accessibility-api": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.2.tgz",
"integrity": "sha512-k7hRNKAiPJXD2aBqfahSo4/01cTsKWXf+LqJgglnkN2Nz8TsxXKQBXHhKe0Ye9fEfHEZY49uSA5Sr3AqP/sWKA==",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true
},
"pretty-format": {
"version": "26.4.2",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz",
"integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==",
"dev": true,
"requires": {
"@jest/types": "^26.3.0",
"ansi-regex": "^5.0.0",
"ansi-styles": "^4.0.0",
"react-is": "^16.12.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
}
}
},
"prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"dev": true,
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
},
"react": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
"integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
}
},
"react-dom": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
"integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.19.1"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
"regenerator-runtime": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
"dev": true
},
"scheduler": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
"integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
"name": "splitview-react",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@babel/code-frame": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
"integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"dev": true,
"requires": {
"@babel/highlight": "^7.10.4"
}
},
"@babel/helper-validator-identifier": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
"integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
"dev": true
},
"@babel/highlight": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
"integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.10.4",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
},
"dependencies": {
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
}
}
},
"@babel/runtime": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
"integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"@babel/runtime-corejs3": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.11.2.tgz",
"integrity": "sha512-qh5IR+8VgFz83VBa6OkaET6uN/mJOhHONuy3m1sgF0CV6mXdPSEBdA7e1eUbVvyNtANjMbg22JUv71BaDXLY6A==",
"dev": true,
"requires": {
"core-js-pure": "^3.0.0",
"regenerator-runtime": "^0.13.4"
}
},
"@jest/types": {
"version": "26.3.0",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz",
"integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==",
"dev": true,
"requires": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
"@types/node": "*",
"@types/yargs": "^15.0.0",
"chalk": "^4.0.0"
}
},
"@testing-library/dom": {
"version": "7.24.2",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.24.2.tgz",
"integrity": "sha512-ERxcZSoHx0EcN4HfshySEWmEf5Kkmgi+J7O79yCJ3xggzVlBJ2w/QjJUC+EBkJJ2OeSw48i3IoePN4w8JlVUIA==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.10.3",
"@types/aria-query": "^4.2.0",
"aria-query": "^4.2.2",
"chalk": "^4.1.0",
"dom-accessibility-api": "^0.5.1",
"pretty-format": "^26.4.2"
}
},
"@testing-library/react": {
"version": "10.4.9",
"resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.9.tgz",
"integrity": "sha512-pHZKkqUy0tmiD81afs8xfiuseXfU/N7rAX3iKjeZYje86t9VaB0LrxYVa+OOsvkrveX5jCK3IjajVn2MbePvqA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.10.3",
"@testing-library/dom": "^7.22.3"
}
},
"@types/aria-query": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz",
"integrity": "sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==",
"dev": true
},
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true
},
"@types/istanbul-lib-coverage": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
"integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==",
"dev": true
},
"@types/istanbul-lib-report": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
"integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
"dev": true,
"requires": {
"@types/istanbul-lib-coverage": "*"
}
},
"@types/istanbul-reports": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz",
"integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==",
"dev": true,
"requires": {
"@types/istanbul-lib-report": "*"
}
},
"@types/node": {
"version": "14.10.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.10.2.tgz",
"integrity": "sha512-IzMhbDYCpv26pC2wboJ4MMOa9GKtjplXfcAqrMeNJpUUwpM/2ATt2w1JPUXwS6spu856TvKZL2AOmeU2rAxskw==",
"dev": true
},
"@types/prop-types": {
"version": "15.7.3",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
"dev": true
},
"@types/react": {
"version": "16.9.49",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.49.tgz",
"integrity": "sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g==",
"dev": true,
"requires": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
}
},
"@types/react-dom": {
"version": "16.9.8",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz",
"integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/yargs": {
"version": "15.0.5",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
"integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
"dev": true,
"requires": {
"@types/yargs-parser": "*"
}
},
"@types/yargs-parser": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz",
"integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==",
"dev": true
},
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"aria-query": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
"integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.10.2",
"@babel/runtime-corejs3": "^7.10.2"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"core-js-pure": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz",
"integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==",
"dev": true
},
"csstype": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz",
"integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==",
"dev": true
},
"dom-accessibility-api": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.2.tgz",
"integrity": "sha512-k7hRNKAiPJXD2aBqfahSo4/01cTsKWXf+LqJgglnkN2Nz8TsxXKQBXHhKe0Ye9fEfHEZY49uSA5Sr3AqP/sWKA==",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true
},
"pretty-format": {
"version": "26.4.2",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz",
"integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==",
"dev": true,
"requires": {
"@jest/types": "^26.3.0",
"ansi-regex": "^5.0.0",
"ansi-styles": "^4.0.0",
"react-is": "^16.12.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
}
}
},
"prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"dev": true,
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
},
"react": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
"integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
}
},
"react-dom": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
"integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.19.1"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
"regenerator-runtime": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
"dev": true
},
"scheduler": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
"integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
}

View File

@ -1,26 +1,26 @@
{
"name": "splitview-react",
"version": "1.0.0",
"description": "",
"main": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"splitview": "*"
},
"devDependencies": {
"@testing-library/react": "^10.4.2",
"@types/react": "^16.9.41",
"@types/react-dom": "^16.9.8",
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"peerDependencies": {
"react": "^16.x",
"react-dom": "^16.x"
}
"name": "splitview-react",
"version": "1.0.0",
"description": "",
"main": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"splitview": "*"
},
"devDependencies": {
"@testing-library/react": "^10.4.2",
"@types/react": "^16.9.41",
"@types/react-dom": "^16.9.8",
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"peerDependencies": {
"react": "^16.x",
"react-dom": "^16.x"
}
}

View File

@ -1,117 +1,121 @@
import * as React from "react";
import { Orientation } from "splitview";
import { IViewWithReactComponent } from "../splitview";
import * as React from 'react'
import { Orientation } from 'splitview'
import { IViewWithReactComponent } from '../splitview'
// component view
export interface IPaneComponentProps extends IViewWithReactComponent {
setExpanded(expanded: boolean): void;
orientation: Orientation;
size: number;
orthogonalSize: number;
userprops?: { [index: string]: any };
setExpanded(expanded: boolean): void
orientation: Orientation
size: number
orthogonalSize: number
userprops?: { [index: string]: any }
}
export interface IPaneComponentRef {
layout: (size: number, orthogonalSize: number) => void;
layout: (size: number, orthogonalSize: number) => void
}
export type PaneComponent = React.ForwardRefRenderFunction<
IPaneComponentRef,
IPaneComponentProps
>;
IPaneComponentRef,
IPaneComponentProps
>
export interface IPaneHeaderComponentProps extends IViewWithReactComponent {
setExpanded(expanded: boolean): void;
isExpanded: boolean;
userprops?: { [index: string]: any };
setExpanded(expanded: boolean): void
isExpanded: boolean
userprops?: { [index: string]: any }
}
export type PaneHeaderComponent = React.ForwardRefRenderFunction<
{},
IPaneHeaderComponentProps
>;
{},
IPaneHeaderComponentProps
>
// component view facade
export interface IPaneRootProps {
component: PaneComponent;
props: {};
component: PaneComponent
props: {}
}
export interface IPaneHeaderRootProps {
component: PaneHeaderComponent;
props: {};
component: PaneHeaderComponent
props: {}
}
export interface IPaneRootRef extends IPaneComponentRef {
updateProps: (props: Partial<IPaneComponentProps>) => void;
updateProps: (props: Partial<IPaneComponentProps>) => void
}
export interface IPaneHeaderRootRef {
updateProps: (props: Partial<IPaneHeaderComponentProps>) => void;
updateProps: (props: Partial<IPaneHeaderComponentProps>) => void
}
export const PaneRoot = React.forwardRef(
(props: IPaneRootProps, facadeRef: React.Ref<IPaneRootRef>) => {
const ref = React.useRef<IPaneComponentRef>();
const [facadeProps, setFacadeProps] = React.useState<IPaneComponentProps>();
(props: IPaneRootProps, facadeRef: React.Ref<IPaneRootRef>) => {
const ref = React.useRef<IPaneComponentRef>()
const [facadeProps, setFacadeProps] = React.useState<
IPaneComponentProps
>()
React.useImperativeHandle(
facadeRef,
() => {
return {
updateProps: (props) => {
setFacadeProps((_props) => ({ ..._props, ...props }));
},
layout: (size, orthogonalSize) => {
ref.current?.layout(size, orthogonalSize);
},
};
},
[ref]
);
React.useImperativeHandle(
facadeRef,
() => {
return {
updateProps: (props) => {
setFacadeProps((_props) => ({ ..._props, ...props }))
},
layout: (size, orthogonalSize) => {
ref.current?.layout(size, orthogonalSize)
},
}
},
[ref]
)
const Component = React.useMemo(() => React.forwardRef(props.component), [
props.component,
]);
const Component = React.useMemo(
() => React.forwardRef(props.component),
[props.component]
)
const _props = React.useMemo(
() => ({ ...props.props, ...facadeProps, ref }),
[props.props, facadeProps]
);
const _props = React.useMemo(
() => ({ ...props.props, ...facadeProps, ref }),
[props.props, facadeProps]
)
return React.createElement(Component, _props);
}
);
return React.createElement(Component, _props)
}
)
export const PaneHeaderRoot = React.forwardRef(
(props: IPaneHeaderRootProps, facadeRef: React.Ref<IPaneHeaderRootRef>) => {
const [facadeProps, setFacadeProps] = React.useState<
IPaneHeaderComponentProps
>();
(props: IPaneHeaderRootProps, facadeRef: React.Ref<IPaneHeaderRootRef>) => {
const [facadeProps, setFacadeProps] = React.useState<
IPaneHeaderComponentProps
>()
React.useImperativeHandle(
facadeRef,
() => {
return {
updateProps: (props) => {
setFacadeProps((_props) => ({ ..._props, ...props }));
},
};
},
[]
);
React.useImperativeHandle(
facadeRef,
() => {
return {
updateProps: (props) => {
setFacadeProps((_props) => ({ ..._props, ...props }))
},
}
},
[]
)
const Component = React.useMemo(() => React.forwardRef(props.component), [
props.component,
]);
const Component = React.useMemo(
() => React.forwardRef(props.component),
[props.component]
)
const _props = React.useMemo(() => ({ ...props.props, ...facadeProps }), [
props.props,
facadeProps,
]);
const _props = React.useMemo(
() => ({ ...props.props, ...facadeProps }),
[props.props, facadeProps]
)
return React.createElement(Component, _props);
}
);
return React.createElement(Component, _props)
}
)

View File

@ -1,64 +1,67 @@
import * as React from "react";
import { IViewWithReactComponent } from "../splitview";
import * as React from 'react'
import { IViewWithReactComponent } from '../splitview'
// component view
export interface IViewComponentProps
extends Omit<IViewWithReactComponent, "component"> {
userprops?: { [index: string]: any };
extends Omit<IViewWithReactComponent, 'component'> {
userprops?: { [index: string]: any }
}
export interface IViewComponentRef {
layout: (size: number, orthogonalSize: number) => void;
layout: (size: number, orthogonalSize: number) => void
}
export type ViewComponent = React.ForwardRefRenderFunction<
IViewComponentRef,
IViewComponentProps
>;
IViewComponentRef,
IViewComponentProps
>
// component view facade
export interface IViewRootProps {
component: ViewComponent;
props: {};
component: ViewComponent
props: {}
}
export interface IViewRootRef extends IViewComponentRef {
updateProps: (props: Partial<IViewComponentProps>) => void;
updateProps: (props: Partial<IViewComponentProps>) => void
}
export const ViewRoot = React.forwardRef(
(props: IViewRootProps, facadeRef: React.Ref<IViewRootRef>) => {
const ref = React.useRef<IViewComponentRef>();
const [facadeProps, setFacadeProps] = React.useState<IViewComponentProps>();
(props: IViewRootProps, facadeRef: React.Ref<IViewRootRef>) => {
const ref = React.useRef<IViewComponentRef>()
const [facadeProps, setFacadeProps] = React.useState<
IViewComponentProps
>()
React.useImperativeHandle(
facadeRef,
() => {
return {
updateProps: (props) => {
setFacadeProps((_props) => ({ ..._props, ...props }));
},
layout: (size, orthogonalSize) => {
ref.current?.layout(size, orthogonalSize);
},
};
},
[ref]
);
React.useImperativeHandle(
facadeRef,
() => {
return {
updateProps: (props) => {
setFacadeProps((_props) => ({ ..._props, ...props }))
},
layout: (size, orthogonalSize) => {
ref.current?.layout(size, orthogonalSize)
},
}
},
[ref]
)
const Component = React.useMemo(() => React.forwardRef(props.component), [
props.component,
]);
const Component = React.useMemo(
() => React.forwardRef(props.component),
[props.component]
)
const _props = React.useMemo(
() => ({ ...props.props, ...facadeProps, ref }),
[props.props, facadeProps]
);
const _props = React.useMemo(
() => ({ ...props.props, ...facadeProps, ref }),
[props.props, facadeProps]
)
return React.createElement(Component, _props);
return React.createElement(Component, _props)
// return <Component ref={ref} {...props.props} {...facadeProps} />;
}
);
// return <Component ref={ref} {...props.props} {...facadeProps} />;
}
)

View File

@ -1,6 +1,6 @@
export * from "./splitview";
export * from "./paneview";
export * from "./bridge/view";
export * from "./panel/view";
export * from "./bridge/pane";
export * from "./panel/pane";
export * from './splitview'
export * from './paneview'
export * from './bridge/view'
export * from './panel/view'
export * from './bridge/pane'
export * from './panel/pane'

View File

@ -1,143 +1,143 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Pane, IDisposable } from "splitview";
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { Pane, IDisposable } from 'splitview'
import {
PaneComponent,
PaneRoot as PaneBodyRoot,
IPaneRootRef,
PaneHeaderComponent,
PaneHeaderRoot,
IPaneHeaderRootRef,
} from "../bridge/pane";
import { IViewWithReactComponent } from "../splitview";
import { IPaneWithReactComponent } from "../paneview";
PaneComponent,
PaneRoot as PaneBodyRoot,
IPaneRootRef,
PaneHeaderComponent,
PaneHeaderRoot,
IPaneHeaderRootRef,
} from '../bridge/pane'
import { IViewWithReactComponent } from '../splitview'
import { IPaneWithReactComponent } from '../paneview'
export class PaneReact extends Pane {
public readonly id: string;
public readonly id: string
private bodyDisposable: IDisposable;
private headerDisposable: IDisposable;
private bodyRef: IPaneRootRef;
private headerRef: IPaneHeaderRootRef;
private disposable: IDisposable;
private bodyDisposable: IDisposable
private headerDisposable: IDisposable
private bodyRef: IPaneRootRef
private headerRef: IPaneHeaderRootRef
private disposable: IDisposable
constructor(
private readonly view: IPaneWithReactComponent,
private readonly bodyComponent: PaneComponent,
private readonly options: {
headerName: string;
addPortal: (portal: React.ReactPortal) => IDisposable;
headerComponent?: PaneHeaderComponent;
}
) {
super({ isExpanded: view.isExpanded });
this.layout = this.layout.bind(this);
this.onDidChange = this.onDidChange.bind(this);
this.setRef = this.setRef.bind(this);
this.setHeaderRef = this.setHeaderRef.bind(this);
this.setExpanded = this.setExpanded.bind(this);
constructor(
private readonly view: IPaneWithReactComponent,
private readonly bodyComponent: PaneComponent,
private readonly options: {
headerName: string
addPortal: (portal: React.ReactPortal) => IDisposable
headerComponent?: PaneHeaderComponent
}
) {
super({ isExpanded: view.isExpanded })
this.layout = this.layout.bind(this)
this.onDidChange = this.onDidChange.bind(this)
this.setRef = this.setRef.bind(this)
this.setHeaderRef = this.setHeaderRef.bind(this)
this.setExpanded = this.setExpanded.bind(this)
this.id = view.id;
this.id = view.id
this.minimumSize = view.minimumSize;
this.maximumSize = view.maximumSize;
this.minimumSize = view.minimumSize
this.maximumSize = view.maximumSize
this.render();
}
public renderBody(element: HTMLElement) {
if (this.bodyDisposable) {
this.bodyDisposable.dispose();
this.bodyDisposable = undefined;
this.render()
}
const bodyPortal = ReactDOM.createPortal(
<PaneBodyRoot
ref={this.setRef}
component={this.bodyComponent}
props={{
minimumSize: this.minimumSize,
maximumSize: this.maximumSize,
snapSize: this.view.snapSize,
userprops: this.view.componentProps,
id: this.id,
}}
/>,
element
);
this.bodyDisposable = this.options.addPortal(bodyPortal);
}
public renderBody(element: HTMLElement) {
if (this.bodyDisposable) {
this.bodyDisposable.dispose()
this.bodyDisposable = undefined
}
public renderHeader(element: HTMLElement) {
if (this.headerDisposable) {
this.headerDisposable.dispose();
this.disposable?.dispose();
this.headerDisposable = undefined;
const bodyPortal = ReactDOM.createPortal(
<PaneBodyRoot
ref={this.setRef}
component={this.bodyComponent}
props={{
minimumSize: this.minimumSize,
maximumSize: this.maximumSize,
snapSize: this.view.snapSize,
userprops: this.view.componentProps,
id: this.id,
}}
/>,
element
)
this.bodyDisposable = this.options.addPortal(bodyPortal)
}
if (this.options.headerComponent) {
this.disposable = this.onDidChangeExpansionState((isExpanded) => {
this.headerRef?.updateProps({ isExpanded });
});
public renderHeader(element: HTMLElement) {
if (this.headerDisposable) {
this.headerDisposable.dispose()
this.disposable?.dispose()
this.headerDisposable = undefined
}
const headerPortal = ReactDOM.createPortal(
<PaneHeaderRoot
ref={this.setHeaderRef}
component={this.options.headerComponent}
props={{
if (this.options.headerComponent) {
this.disposable = this.onDidChangeExpansionState((isExpanded) => {
this.headerRef?.updateProps({ isExpanded })
})
const headerPortal = ReactDOM.createPortal(
<PaneHeaderRoot
ref={this.setHeaderRef}
component={this.options.headerComponent}
props={{
minimumSize: this.minimumSize,
maximumSize: this.maximumSize,
snapSize: this.view.snapSize,
userprops: this.view.headerProps,
id: this.id,
}}
/>,
element
)
this.headerDisposable = this.options.addPortal(headerPortal)
} else {
element.textContent = this.options.headerName
element.onclick = () => {
this.setExpanded(!this.isExpanded())
}
}
}
public update(view: IViewWithReactComponent) {
this.minimumSize = view.minimumSize
this.maximumSize = view.maximumSize
this.render()
this.bodyRef?.updateProps({
minimumSize: this.minimumSize,
maximumSize: this.maximumSize,
snapSize: this.view.snapSize,
userprops: this.view.headerProps,
id: this.id,
}}
/>,
element
);
this.headerDisposable = this.options.addPortal(headerPortal);
} else {
element.textContent = this.options.headerName;
element.onclick = () => {
this.setExpanded(!this.isExpanded());
};
})
}
}
public update(view: IViewWithReactComponent) {
this.minimumSize = view.minimumSize;
this.maximumSize = view.maximumSize;
public layout(size: number, orthogonalSize: number) {
super.layout(size, orthogonalSize)
this.orthogonalSize = orthogonalSize
this.bodyRef?.layout(size, orthogonalSize)
this.bodyRef?.updateProps({ size, orthogonalSize })
}
this.render();
private setRef(ref: IPaneRootRef) {
this.bodyRef = ref
}
this.bodyRef?.updateProps({
minimumSize: this.minimumSize,
maximumSize: this.maximumSize,
});
}
private setHeaderRef(ref: IPaneRootRef) {
this.headerRef = ref
this.headerRef?.updateProps({
isExpanded: this.isExpanded(),
setExpanded: this.setExpanded,
})
}
public layout(size: number, orthogonalSize: number) {
super.layout(size, orthogonalSize);
this.orthogonalSize = orthogonalSize;
this.bodyRef?.layout(size, orthogonalSize);
this.bodyRef?.updateProps({ size, orthogonalSize });
}
private setRef(ref: IPaneRootRef) {
this.bodyRef = ref;
}
private setHeaderRef(ref: IPaneRootRef) {
this.headerRef = ref;
this.headerRef?.updateProps({
isExpanded: this.isExpanded(),
setExpanded: this.setExpanded,
});
}
public dispose() {
this.bodyDisposable?.dispose();
this.headerDisposable?.dispose();
this.disposable?.dispose();
}
public dispose() {
this.bodyDisposable?.dispose()
this.headerDisposable?.dispose()
this.disposable?.dispose()
}
}

View File

@ -1,108 +1,108 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import { IView, Emitter } from "splitview";
import { IViewRootRef, ViewComponent, ViewRoot } from "../bridge/view";
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { IView, Emitter } from 'splitview'
import { IViewRootRef, ViewComponent, ViewRoot } from '../bridge/view'
import { IViewWithReactComponent } from "../splitview";
import { IViewWithReactComponent } from '../splitview'
export class ReactRenderView implements IView {
private ref: IViewRootRef;
private disposable: { dispose: () => void };
private ref: IViewRootRef
private disposable: { dispose: () => void }
public readonly id: string;
private readonly component: ViewComponent;
public readonly props: {};
public readonly id: string
private readonly component: ViewComponent
public readonly props: {}
public element: HTMLElement;
public minimumSize: number;
public maximumSize: number;
public snapSize: number;
public size: number;
public element: HTMLElement
public minimumSize: number
public maximumSize: number
public snapSize: number
public size: number
private readonly _onDidChange = new Emitter<number | undefined>();
public readonly onDidChange = this._onDidChange.event;
private readonly _onDidChange = new Emitter<number | undefined>()
public readonly onDidChange = this._onDidChange.event
private _rendered = false;
private _size: number;
private _orthogonalSize: number;
private _rendered = false
private _size: number
private _orthogonalSize: number
constructor(
view: IViewWithReactComponent,
private readonly addPortal: (
portal: React.ReactPortal
) => { dispose: () => void }
) {
this.layout = this.layout.bind(this);
this.onDidChange = this.onDidChange.bind(this);
this.setRef = this.setRef.bind(this);
constructor(
view: IViewWithReactComponent,
private readonly addPortal: (
portal: React.ReactPortal
) => { dispose: () => void }
) {
this.layout = this.layout.bind(this)
this.onDidChange = this.onDidChange.bind(this)
this.setRef = this.setRef.bind(this)
this.id = view.id;
this.component = view.component;
this.props = view.props;
this.id = view.id
this.component = view.component
this.props = view.props
this.minimumSize = view.minimumSize;
this.maximumSize = view.maximumSize;
this.snapSize = view.snapSize;
this.minimumSize = view.minimumSize
this.maximumSize = view.maximumSize
this.snapSize = view.snapSize
this.element = document.createElement("div");
this.element.id = "react-attachable-view";
}
public update(view: IView) {
this.minimumSize = view.minimumSize;
this.maximumSize = view.maximumSize;
this.snapSize = view.snapSize;
this.ref?.updateProps({
minimumSize: this.minimumSize,
maximumSize: this.maximumSize,
snapSize: this.snapSize,
});
}
public layout(size: number, orthogonalSize: number) {
if (!this._rendered) {
this.attachReactComponent();
this._rendered = true;
this.element = document.createElement('div')
this.element.id = 'react-attachable-view'
}
this._size = size;
this._orthogonalSize = orthogonalSize;
this.ref?.layout(size, orthogonalSize);
}
public update(view: IView) {
this.minimumSize = view.minimumSize
this.maximumSize = view.maximumSize
this.snapSize = view.snapSize
private attachReactComponent() {
const portal = this.createReactElement();
if (this.disposable) {
this.disposable.dispose();
this.disposable = undefined;
this.ref?.updateProps({
minimumSize: this.minimumSize,
maximumSize: this.maximumSize,
snapSize: this.snapSize,
})
}
this.disposable = this.addPortal(portal);
}
private createReactElement() {
return ReactDOM.createPortal(
<ViewRoot
ref={this.setRef}
component={this.component}
props={{
minimumSize: this.minimumSize,
maximumSize: this.maximumSize,
snapSize: this.snapSize,
userprops: this.props,
id: this.id,
}}
/>,
this.element
);
}
public layout(size: number, orthogonalSize: number) {
if (!this._rendered) {
this.attachReactComponent()
this._rendered = true
}
private setRef(ref: IViewRootRef) {
this.ref = ref;
this.ref?.layout(this._size, this._orthogonalSize);
}
this._size = size
this._orthogonalSize = orthogonalSize
this.ref?.layout(size, orthogonalSize)
}
public dispose() {
this.disposable?.dispose();
}
private attachReactComponent() {
const portal = this.createReactElement()
if (this.disposable) {
this.disposable.dispose()
this.disposable = undefined
}
this.disposable = this.addPortal(portal)
}
private createReactElement() {
return ReactDOM.createPortal(
<ViewRoot
ref={this.setRef}
component={this.component}
props={{
minimumSize: this.minimumSize,
maximumSize: this.maximumSize,
snapSize: this.snapSize,
userprops: this.props,
id: this.id,
}}
/>,
this.element
)
}
private setRef(ref: IViewRootRef) {
this.ref = ref
this.ref?.layout(this._size, this._orthogonalSize)
}
public dispose() {
this.disposable?.dispose()
}
}

View File

@ -1,196 +1,215 @@
import * as React from "react";
import { Orientation, IBaseView, PaneView } from "splitview";
import * as React from 'react'
import { Orientation, IBaseView, PaneView } from 'splitview'
import { PaneReact } from "./panel/pane";
import { PaneComponent, PaneHeaderComponent } from "./bridge/pane";
import { PaneReact } from './panel/pane'
import { PaneComponent, PaneHeaderComponent } from './bridge/pane'
export interface IPaneWithReactComponent extends IBaseView {
id: string;
headerId: string;
component: PaneComponent;
headerComponent: PaneHeaderComponent;
isExpanded: boolean;
componentProps: {};
headerProps: {};
id: string
headerId: string
component: PaneComponent
headerComponent: PaneHeaderComponent
isExpanded: boolean
componentProps: {}
headerProps: {}
}
export interface IPaneViewReactProps {
orientation: Orientation;
onReady?: (event: PaneViewReadyEvent) => void;
components?: { [index: string]: PaneComponent };
headerComponents?: { [index: string]: PaneHeaderComponent };
size: number;
orthogonalSize: number;
initialLayout?: PaneViewSerializedConfig;
orientation: Orientation
onReady?: (event: PaneViewReadyEvent) => void
components?: { [index: string]: PaneComponent }
headerComponents?: { [index: string]: PaneHeaderComponent }
size: number
orthogonalSize: number
initialLayout?: PaneViewSerializedConfig
}
export interface PaneViewReadyEvent {
api: PaneviewApi;
api: PaneviewApi
}
export interface PaneViewSerializedConfig {
views: Array<
Omit<IPaneWithReactComponent, "component" | "headerComponent"> & {
size?: number;
}
>;
views: Array<
Omit<IPaneWithReactComponent, 'component' | 'headerComponent'> & {
size?: number
}
>
}
export interface PaneviewApi {
add: (
options: Omit<IPaneWithReactComponent, "component" | "headerComponent"> & {
size?: number;
index?: number;
}
) => void;
moveView: (from: number, to: number) => void;
toJSON: () => {};
add: (
options: Omit<
IPaneWithReactComponent,
'component' | 'headerComponent'
> & {
size?: number
index?: number
}
) => void
moveView: (from: number, to: number) => void
toJSON: () => {}
}
export interface IPaneViewComponentRef {
layout: (size: number, orthogonalSize: number) => void;
layout: (size: number, orthogonalSize: number) => void
}
export const PaneViewComponent = React.forwardRef(
(props: IPaneViewReactProps, _ref: React.Ref<IPaneViewComponentRef>) => {
const ref = React.useRef<HTMLDivElement>();
const dimension = React.useRef<{ size: number; orthogonalSize: number }>();
const paneview = React.useRef<PaneView>();
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
(props: IPaneViewReactProps, _ref: React.Ref<IPaneViewComponentRef>) => {
const ref = React.useRef<HTMLDivElement>()
const dimension = React.useRef<{
size: number
orthogonalSize: number
}>()
const paneview = React.useRef<PaneView>()
const [portals, setPortals] = React.useState<React.ReactPortal[]>([])
const createView = React.useCallback((_view: IPaneWithReactComponent) => {
return new PaneReact(_view, _view.component, {
headerName: "header",
headerComponent: _view.headerComponent,
addPortal: (portal) => {
setPortals((portals) => [...portals, portal]);
return {
dispose: () => {
setPortals((portals) => portals.filter((_) => _ !== portal));
const createView = React.useCallback(
(_view: IPaneWithReactComponent) => {
return new PaneReact(_view, _view.component, {
headerName: 'header',
headerComponent: _view.headerComponent,
addPortal: (portal) => {
setPortals((portals) => [...portals, portal])
return {
dispose: () => {
setPortals((portals) =>
portals.filter((_) => _ !== portal)
)
},
}
},
})
},
};
},
});
}, []);
[]
)
const hydrate = React.useCallback(() => {
if (!props.initialLayout || !paneview.current) {
return;
}
const hydrate = React.useCallback(() => {
if (!props.initialLayout || !paneview.current) {
return
}
const serializedConfig = props.initialLayout;
const serializedConfig = props.initialLayout
serializedConfig.views.forEach((view) => {
const component = props.components[view.id];
const headerComponent = props.headerComponents[view.headerId];
paneview.current.addPane(
createView({ ...view, component, headerComponent }),
view.size
);
});
paneview.current.layout(props.size, props.orthogonalSize);
}, [props.initialLayout]);
serializedConfig.views.forEach((view) => {
const component = props.components[view.id]
const headerComponent = props.headerComponents[view.headerId]
paneview.current.addPane(
createView({ ...view, component, headerComponent }),
view.size
)
})
paneview.current.layout(props.size, props.orthogonalSize)
}, [props.initialLayout])
React.useEffect(() => {
if (paneview.current && dimension?.current) {
paneview.current?.layout(
dimension.current.size,
dimension.current.orthogonalSize
);
dimension.current = undefined;
}
}, [paneview.current]);
React.useEffect(() => {
if (paneview.current && dimension?.current) {
paneview.current?.layout(
dimension.current.size,
dimension.current.orthogonalSize
)
dimension.current = undefined
}
}, [paneview.current])
// if you put this in a hook it's laggy
// paneview.current?.layout(props.size, props.orthogonalSize);
// if you put this in a hook it's laggy
// paneview.current?.layout(props.size, props.orthogonalSize);
React.useImperativeHandle(
_ref,
() => ({
layout: (size, orthogonalSize) => {
if (!paneview.current) {
// handle the case when layout is called and paneview doesn't exist yet
// we cache the values and use them at the first opportunity
dimension.current = { size, orthogonalSize };
}
paneview.current?.layout(size, orthogonalSize);
},
}),
[paneview]
);
React.useImperativeHandle(
_ref,
() => ({
layout: (size, orthogonalSize) => {
if (!paneview.current) {
// handle the case when layout is called and paneview doesn't exist yet
// we cache the values and use them at the first opportunity
dimension.current = { size, orthogonalSize }
}
paneview.current?.layout(size, orthogonalSize)
},
}),
[paneview]
)
React.useEffect(() => {
paneview.current = new PaneView(ref.current, {
orientation: props.orientation,
});
React.useEffect(() => {
paneview.current = new PaneView(ref.current, {
orientation: props.orientation,
})
hydrate();
hydrate()
if (props.onReady) {
props.onReady({
api: {
add: (
options: Omit<
IPaneWithReactComponent,
"component" | "headerComponent"
> & {
props?: {};
size?: number;
index?: number;
}
) => {
const component = props.components[options.id];
const headerComponent = props.headerComponents[options.headerId];
paneview.current.addPane(
createView({ ...options, component, headerComponent }),
options.size,
options.index
);
paneview.current.layout(props.size, props.orthogonalSize);
},
moveView: (from: number, to: number) => {
paneview.current.moveView(from, to);
},
toJSON: () => {
return {
// views: (paneview.current.getViews() as PaneReact[]).map((v) =>
// Object.entries({
// size: v.size,
// id: v.id,
// snapSize: v.snapSize,
// minimumSize: v.minimumSize,
// maximumSize: v.maximumSize,
// props: v.props,
// }).reduce(
// (x, y) =>
// y[1] !== undefined || x !== null
// ? { ...x, [y[0]]: y[1] }
// : x,
// {}
// )
// ),
};
},
},
});
}
if (props.onReady) {
props.onReady({
api: {
add: (
options: Omit<
IPaneWithReactComponent,
'component' | 'headerComponent'
> & {
props?: {}
size?: number
index?: number
}
) => {
const component = props.components[options.id]
const headerComponent =
props.headerComponents[options.headerId]
paneview.current.addPane(
createView({
...options,
component,
headerComponent,
}),
options.size,
options.index
)
paneview.current.layout(
props.size,
props.orthogonalSize
)
},
moveView: (from: number, to: number) => {
paneview.current.moveView(from, to)
},
toJSON: () => {
return {
// views: (paneview.current.getViews() as PaneReact[]).map((v) =>
// Object.entries({
// size: v.size,
// id: v.id,
// snapSize: v.snapSize,
// minimumSize: v.minimumSize,
// maximumSize: v.maximumSize,
// props: v.props,
// }).reduce(
// (x, y) =>
// y[1] !== undefined || x !== null
// ? { ...x, [y[0]]: y[1] }
// : x,
// {}
// )
// ),
}
},
},
})
}
paneview.current.layout(props.size, props.orthogonalSize);
paneview.current.layout(props.size, props.orthogonalSize)
return () => {
paneview.current?.dispose();
paneview.current = undefined;
};
}, []);
return () => {
paneview.current?.dispose()
paneview.current = undefined
}
}, [])
React.useEffect(() => {
paneview.current?.setOrientation(props.orientation);
}, [props.orientation]);
React.useEffect(() => {
paneview.current?.setOrientation(props.orientation)
}, [props.orientation])
return (
<div ref={ref} className="split-view-react-wrapper">
{portals}
</div>
);
}
);
return (
<div ref={ref} className="split-view-react-wrapper">
{portals}
</div>
)
}
)

View File

@ -1,160 +1,168 @@
import * as React from "react";
import { SplitView, Orientation, IBaseView } from "splitview";
import { ReactRenderView } from "./panel/view";
import { ViewComponent } from "./bridge/view";
import * as React from 'react'
import { SplitView, Orientation, IBaseView } from 'splitview'
import { ReactRenderView } from './panel/view'
import { ViewComponent } from './bridge/view'
export interface IViewWithReactComponent extends IBaseView {
id: string;
props?: {};
component: ViewComponent;
id: string
props?: {}
component: ViewComponent
}
export interface OnReadyEvent {
api: SplitviewApi;
api: SplitviewApi
}
export interface SerializedConfig {
views: Array<Omit<IViewWithReactComponent, "component"> & { size?: number }>;
views: Array<Omit<IViewWithReactComponent, 'component'> & { size?: number }>
}
export interface SplitviewApi {
add: (
options: Omit<IViewWithReactComponent, "component"> & {
size?: number;
index?: number;
}
) => void;
moveView: (from: number, to: number) => void;
toJSON: () => {};
add: (
options: Omit<IViewWithReactComponent, 'component'> & {
size?: number
index?: number
}
) => void
moveView: (from: number, to: number) => void
toJSON: () => {}
}
export interface ISplitViewReactProps {
orientation: Orientation;
size: number;
orthogonalSize: number;
onReady?: (event: OnReadyEvent) => void;
components?: { [index: string]: ViewComponent };
initialLayout?: SerializedConfig;
orientation: Orientation
size: number
orthogonalSize: number
onReady?: (event: OnReadyEvent) => void
components?: { [index: string]: ViewComponent }
initialLayout?: SerializedConfig
}
export interface ISplitViewComponentRef {
layout: (size: number, orthogonalSize: number) => void;
layout: (size: number, orthogonalSize: number) => void
}
export const SplitViewComponent = React.forwardRef(
(props: ISplitViewReactProps, ref: React.Ref<ISplitViewComponentRef>) => {
const containerRef = React.useRef<HTMLDivElement>();
const splitview = React.useRef<SplitView>();
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
(props: ISplitViewReactProps, ref: React.Ref<ISplitViewComponentRef>) => {
const containerRef = React.useRef<HTMLDivElement>()
const splitview = React.useRef<SplitView>()
const [portals, setPortals] = React.useState<React.ReactPortal[]>([])
const hydrate = React.useCallback(() => {
if (!props.initialLayout || !splitview.current) {
return;
}
const hydrate = React.useCallback(() => {
if (!props.initialLayout || !splitview.current) {
return
}
const serializedConfig = props.initialLayout;
const serializedConfig = props.initialLayout
serializedConfig.views.forEach((view) => {
const component = props.components[view.id];
splitview.current.addView(
createView({ ...view, component }),
view.size
);
});
splitview.current.layout(props.size, props.orthogonalSize);
}, [props.initialLayout]);
serializedConfig.views.forEach((view) => {
const component = props.components[view.id]
splitview.current.addView(
createView({ ...view, component }),
view.size
)
})
splitview.current.layout(props.size, props.orthogonalSize)
}, [props.initialLayout])
React.useEffect(() => {
splitview.current?.setOrientation(props.orientation);
splitview.current?.layout(props.size, props.orthogonalSize);
}, [props.orientation]);
React.useEffect(() => {
splitview.current?.setOrientation(props.orientation)
splitview.current?.layout(props.size, props.orthogonalSize)
}, [props.orientation])
React.useImperativeHandle(
ref,
() => ({
layout: (size, orthogonalSize) => {
splitview.current?.layout(size, orthogonalSize);
},
}),
[splitview]
);
React.useImperativeHandle(
ref,
() => ({
layout: (size, orthogonalSize) => {
splitview.current?.layout(size, orthogonalSize)
},
}),
[splitview]
)
React.useEffect(() => {
splitview.current = new SplitView(containerRef.current, {
orientation: props.orientation,
});
React.useEffect(() => {
splitview.current = new SplitView(containerRef.current, {
orientation: props.orientation,
})
hydrate();
hydrate()
if (props.onReady) {
props.onReady({
api: {
add: (
options: Omit<IViewWithReactComponent, "component"> & {
props?: {};
size?: number;
index?: number;
}
) => {
const component = props.components[options.id];
splitview.current.addView(
createView({ ...options, component }),
options.size,
options.index
);
splitview.current.layout(props.size, props.orthogonalSize);
},
moveView: (from: number, to: number) => {
splitview.current.moveView(from, to);
},
toJSON: () => {
return {
views: (splitview.current.getViews() as ReactRenderView[]).map(
(v) =>
Object.entries({
size: v.size,
id: v.id,
snapSize: v.snapSize,
minimumSize: v.minimumSize,
maximumSize: v.maximumSize,
props: v.props,
}).reduce(
(x, y) =>
y[1] !== undefined || x !== null
? { ...x, [y[0]]: y[1] }
: x,
{}
)
),
};
},
},
});
}
if (props.onReady) {
props.onReady({
api: {
add: (
options: Omit<
IViewWithReactComponent,
'component'
> & {
props?: {}
size?: number
index?: number
}
) => {
const component = props.components[options.id]
splitview.current.addView(
createView({ ...options, component }),
options.size,
options.index
)
splitview.current.layout(
props.size,
props.orthogonalSize
)
},
moveView: (from: number, to: number) => {
splitview.current.moveView(from, to)
},
toJSON: () => {
return {
views: (splitview.current.getViews() as ReactRenderView[]).map(
(v) =>
Object.entries({
size: v.size,
id: v.id,
snapSize: v.snapSize,
minimumSize: v.minimumSize,
maximumSize: v.maximumSize,
props: v.props,
}).reduce(
(x, y) =>
y[1] !== undefined || x !== null
? { ...x, [y[0]]: y[1] }
: x,
{}
)
),
}
},
},
})
}
splitview.current.layout(props.size, props.orthogonalSize);
splitview.current.layout(props.size, props.orthogonalSize)
return () => {
splitview.current.dispose();
};
}, []);
return () => {
splitview.current.dispose()
}
}, [])
const createView = React.useCallback(
(view: IViewWithReactComponent) =>
new ReactRenderView(view, (portal) => {
setPortals((portals) => [...portals, portal]);
return {
dispose: () =>
void setPortals((portals) => portals.filter((_) => _ !== portal)),
};
}),
[]
);
const createView = React.useCallback(
(view: IViewWithReactComponent) =>
new ReactRenderView(view, (portal) => {
setPortals((portals) => [...portals, portal])
return {
dispose: () =>
void setPortals((portals) =>
portals.filter((_) => _ !== portal)
),
}
}),
[]
)
return (
<div ref={containerRef} className="split-view-container-react">
{portals}
</div>
);
}
);
return (
<div ref={containerRef} className="split-view-container-react">
{portals}
</div>
)
}
)

View File

@ -1,12 +1,12 @@
{
"extends": "../../tsconfig.build.json",
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["**/node_modules"]
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["**/node_modules"]
}

View File

@ -1,3 +1,3 @@
{
"extends": "../../tsconfig.json"
"extends": "../../tsconfig.json"
}

View File

@ -1,7 +1,7 @@
const gulp = require("gulp");
const buildfile = require("../../scripts/build");
const package = require("./package");
const gulp = require('gulp')
const buildfile = require('../../scripts/build')
const package = require('./package')
buildfile.build({ tsconfig: "./tsconfig.build.json", package });
buildfile.build({ tsconfig: './tsconfig.build.json', package })
gulp.task("run", gulp.series(["esm", "sass"]));
gulp.task('run', gulp.series(['esm', 'sass']))

View File

@ -1,368 +1,368 @@
{
"name": "splitview",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/prop-types": {
"version": "15.7.3",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
},
"@types/react": {
"version": "16.9.43",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.43.tgz",
"integrity": "sha512-PxshAFcnJqIWYpJbLPriClH53Z2WlJcVZE+NP2etUtWQs2s7yIMj3/LDKZT/5CHJ/F62iyjVCDu2H3jHEXIxSg==",
"requires": {
"@types/prop-types": "*",
"csstype": "^2.2.0"
}
},
"@types/react-dom": {
"version": "16.9.8",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz",
"integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==",
"requires": {
"@types/react": "*"
}
},
"at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
"dev": true
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"csstype": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.11.tgz",
"integrity": "sha512-l8YyEC9NBkSm783PFTvh0FmJy7s5pFKrDp49ZL7zBGX3fWkO+N4EEyan1qqp8cwPLDcD0OSdyY6hAMoxp34JFw=="
},
"fs-extra": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
"integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
"dev": true,
"requires": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^1.0.0"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"handlebars": {
"version": "4.7.6",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
"integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
"dev": true,
"requires": {
"minimist": "^1.2.5",
"neo-async": "^2.6.0",
"source-map": "^0.6.1",
"uglify-js": "^3.1.4",
"wordwrap": "^1.0.0"
}
},
"highlight.js": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.2.tgz",
"integrity": "sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA==",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"interpret": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
"dev": true
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"jsonfile": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz",
"integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^1.0.0"
}
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"lunr": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
"dev": true
},
"marked": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/marked/-/marked-1.1.1.tgz",
"integrity": "sha512-mJzT8D2yPxoPh7h0UXkB+dBj4FykPJ2OIfxAWeIHrvoHDkFxukV/29QxoFQoPM6RLEwhIFdJpmKBlqVM3s2ZIw==",
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"neo-async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
"prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
},
"react": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
"integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
}
},
"react-dom": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
"integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.19.1"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"rechoir": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
"integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
"dev": true,
"requires": {
"resolve": "^1.1.6"
}
},
"resolve": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
},
"scheduler": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
"integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"shelljs": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
"integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==",
"dev": true,
"requires": {
"glob": "^7.0.0",
"interpret": "^1.0.0",
"rechoir": "^0.6.2"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"typedoc": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.18.0.tgz",
"integrity": "sha512-UgDQwapCGQCCdYhEQzQ+kGutmcedklilgUGf62Vw6RdI29u6FcfAXFQfRTiJEbf16aK3YnkB20ctQK1JusCRbA==",
"dev": true,
"requires": {
"fs-extra": "^9.0.1",
"handlebars": "^4.7.6",
"highlight.js": "^10.0.0",
"lodash": "^4.17.15",
"lunr": "^2.3.8",
"marked": "^1.1.1",
"minimatch": "^3.0.0",
"progress": "^2.0.3",
"shelljs": "^0.8.4",
"typedoc-default-themes": "^0.10.2"
}
},
"typedoc-default-themes": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.10.2.tgz",
"integrity": "sha512-zo09yRj+xwLFE3hyhJeVHWRSPuKEIAsFK5r2u47KL/HBKqpwdUSanoaz5L34IKiSATFrjG5ywmIu98hPVMfxZg==",
"dev": true,
"requires": {
"lunr": "^2.3.8"
}
},
"uglify-js": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.1.tgz",
"integrity": "sha512-RjxApKkrPJB6kjJxQS3iZlf///REXWYxYJxO/MpmlQzVkDWVI3PSnCBWezMecmTU/TRkNxrl8bmsfFQCp+LO+Q==",
"dev": true,
"optional": true
},
"universalify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
"dev": true
},
"wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
"dev": true
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
"name": "splitview",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/prop-types": {
"version": "15.7.3",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
},
"@types/react": {
"version": "16.9.43",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.43.tgz",
"integrity": "sha512-PxshAFcnJqIWYpJbLPriClH53Z2WlJcVZE+NP2etUtWQs2s7yIMj3/LDKZT/5CHJ/F62iyjVCDu2H3jHEXIxSg==",
"requires": {
"@types/prop-types": "*",
"csstype": "^2.2.0"
}
},
"@types/react-dom": {
"version": "16.9.8",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz",
"integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==",
"requires": {
"@types/react": "*"
}
},
"at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
"dev": true
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"csstype": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.11.tgz",
"integrity": "sha512-l8YyEC9NBkSm783PFTvh0FmJy7s5pFKrDp49ZL7zBGX3fWkO+N4EEyan1qqp8cwPLDcD0OSdyY6hAMoxp34JFw=="
},
"fs-extra": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
"integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
"dev": true,
"requires": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^1.0.0"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"handlebars": {
"version": "4.7.6",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
"integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
"dev": true,
"requires": {
"minimist": "^1.2.5",
"neo-async": "^2.6.0",
"source-map": "^0.6.1",
"uglify-js": "^3.1.4",
"wordwrap": "^1.0.0"
}
},
"highlight.js": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.2.tgz",
"integrity": "sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA==",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"interpret": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
"dev": true
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"jsonfile": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz",
"integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^1.0.0"
}
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"lunr": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
"dev": true
},
"marked": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/marked/-/marked-1.1.1.tgz",
"integrity": "sha512-mJzT8D2yPxoPh7h0UXkB+dBj4FykPJ2OIfxAWeIHrvoHDkFxukV/29QxoFQoPM6RLEwhIFdJpmKBlqVM3s2ZIw==",
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"neo-async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
"prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
},
"react": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
"integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
}
},
"react-dom": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
"integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.19.1"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"rechoir": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
"integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
"dev": true,
"requires": {
"resolve": "^1.1.6"
}
},
"resolve": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
},
"scheduler": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
"integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"shelljs": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
"integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==",
"dev": true,
"requires": {
"glob": "^7.0.0",
"interpret": "^1.0.0",
"rechoir": "^0.6.2"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"typedoc": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.18.0.tgz",
"integrity": "sha512-UgDQwapCGQCCdYhEQzQ+kGutmcedklilgUGf62Vw6RdI29u6FcfAXFQfRTiJEbf16aK3YnkB20ctQK1JusCRbA==",
"dev": true,
"requires": {
"fs-extra": "^9.0.1",
"handlebars": "^4.7.6",
"highlight.js": "^10.0.0",
"lodash": "^4.17.15",
"lunr": "^2.3.8",
"marked": "^1.1.1",
"minimatch": "^3.0.0",
"progress": "^2.0.3",
"shelljs": "^0.8.4",
"typedoc-default-themes": "^0.10.2"
}
},
"typedoc-default-themes": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.10.2.tgz",
"integrity": "sha512-zo09yRj+xwLFE3hyhJeVHWRSPuKEIAsFK5r2u47KL/HBKqpwdUSanoaz5L34IKiSATFrjG5ywmIu98hPVMfxZg==",
"dev": true,
"requires": {
"lunr": "^2.3.8"
}
},
"uglify-js": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.1.tgz",
"integrity": "sha512-RjxApKkrPJB6kjJxQS3iZlf///REXWYxYJxO/MpmlQzVkDWVI3PSnCBWezMecmTU/TRkNxrl8bmsfFQCp+LO+Q==",
"dev": true,
"optional": true
},
"universalify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
"dev": true
},
"wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
"dev": true
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
}
}
}
}

View File

@ -1,23 +1,23 @@
{
"name": "splitview",
"version": "1.0.0",
"description": "",
"main": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"module": "dist/esm/index.js",
"scripts": {
"build": "gulp run",
"docs": "typedoc"
},
"author": "",
"license": "ISC",
"dependencies": {
"@types/react": "^16.9.43",
"@types/react-dom": "^16.9.8",
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"devDependencies": {
"typedoc": "^0.18.0"
}
"name": "splitview",
"version": "1.0.0",
"description": "",
"main": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"module": "dist/esm/index.js",
"scripts": {
"build": "gulp run",
"docs": "typedoc"
},
"author": "",
"license": "ISC",
"dependencies": {
"@types/react": "^16.9.43",
"@types/react-dom": "^16.9.8",
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"devDependencies": {
"typedoc": "^0.18.0"
}
}

View File

@ -1,69 +1,69 @@
export function tail<T>(arr: T[]): [T[], T] {
if (arr.length === 0) {
throw new Error("Invalid tail call");
}
if (arr.length === 0) {
throw new Error('Invalid tail call')
}
return [arr.slice(0, arr.length - 1), arr[arr.length - 1]];
return [arr.slice(0, arr.length - 1), arr[arr.length - 1]]
}
export function last<T>(arr: T[]): T {
return arr.length > 0 ? arr[arr.length - 1] : undefined;
return arr.length > 0 ? arr[arr.length - 1] : undefined
}
export function sequenceEquals<T>(arr1: T[], arr2: T[]) {
if (arr1.length !== arr2.length) {
return false;
}
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) {
return false;
if (arr1.length !== arr2.length) {
return false
}
}
return true;
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) {
return false
}
}
return true
}
/**
* Pushes an element to the start of the array, if found.
*/
export function pushToStart<T>(arr: T[], value: T): void {
const index = arr.indexOf(value);
const index = arr.indexOf(value)
if (index > -1) {
arr.splice(index, 1);
arr.unshift(value);
}
if (index > -1) {
arr.splice(index, 1)
arr.unshift(value)
}
}
/**
* Pushes an element to the end of the array, if found.
*/
export function pushToEnd<T>(arr: T[], value: T): void {
const index = arr.indexOf(value);
const index = arr.indexOf(value)
if (index > -1) {
arr.splice(index, 1);
arr.push(value);
}
if (index > -1) {
arr.splice(index, 1)
arr.push(value)
}
}
export const range = (from: number, to: number = undefined) => {
const result: number[] = [];
const result: number[] = []
if (to === undefined) {
to = from;
from = 0;
}
if (from <= to) {
for (let i = from; i < to; i++) {
result.push(i);
if (to === undefined) {
to = from
from = 0
}
} else {
for (let i = from; i > to; i--) {
result.push(i);
}
}
return result;
};
if (from <= to) {
for (let i = from; i < to; i++) {
result.push(i)
}
} else {
for (let i = from; i > to; i--) {
result.push(i)
}
}
return result
}

View File

@ -1,7 +1,7 @@
export function timeoutPromise(timeout: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, timeout);
});
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, timeout)
})
}

View File

@ -1,14 +1,14 @@
import { Event, Emitter, addDisposableListener } from "./events";
import { IDisposable, CompositeDisposable } from "./lifecycle";
import { Event, Emitter, addDisposableListener } from './events'
import { IDisposable, CompositeDisposable } from './lifecycle'
export function getDomNodePagePosition(domNode: HTMLElement) {
const bb = domNode.getBoundingClientRect();
return {
left: bb.left + window.scrollX,
top: bb.top + window.scrollY,
width: bb.width,
height: bb.height,
};
const bb = domNode.getBoundingClientRect()
return {
left: bb.left + window.scrollX,
top: bb.top + window.scrollY,
width: bb.width,
height: bb.height,
}
}
/**
@ -16,213 +16,217 @@ export function getDomNodePagePosition(domNode: HTMLElement) {
* the element is above or below the containers view portal.
*/
export const scrollIntoView = (
element: HTMLElement,
container: HTMLElement
element: HTMLElement,
container: HTMLElement
) => {
const { inView, breachPoint } = isElementInView(element, container, true);
if (!inView) {
const adder = -container.offsetTop;
const isUp = breachPoint === "top";
container.scrollTo({
top: isUp
? adder + element.offsetTop
: adder +
element.offsetTop -
container.clientHeight +
element.clientHeight,
});
}
};
const { inView, breachPoint } = isElementInView(element, container, true)
if (!inView) {
const adder = -container.offsetTop
const isUp = breachPoint === 'top'
container.scrollTo({
top: isUp
? adder + element.offsetTop
: adder +
element.offsetTop -
container.clientHeight +
element.clientHeight,
})
}
}
export const isElementInView = (
element: HTMLElement,
container: HTMLElement,
fullyInView: boolean
): { inView: boolean; breachPoint?: "top" | "bottom" } => {
const containerOfftsetTop = container.offsetTop;
const containerTop = containerOfftsetTop + container.scrollTop;
const containerBottom =
containerTop + container.getBoundingClientRect().height;
const elementTop = element.offsetTop;
const elementBottom = elementTop + element.getBoundingClientRect().height;
element: HTMLElement,
container: HTMLElement,
fullyInView: boolean
): { inView: boolean; breachPoint?: 'top' | 'bottom' } => {
const containerOfftsetTop = container.offsetTop
const containerTop = containerOfftsetTop + container.scrollTop
const containerBottom =
containerTop + container.getBoundingClientRect().height
const elementTop = element.offsetTop
const elementBottom = elementTop + element.getBoundingClientRect().height
const isAbove = fullyInView
? containerTop >= elementTop
: elementTop > containerBottom;
const isBelow = fullyInView
? containerBottom <= elementBottom
: elementBottom < containerTop;
const isAbove = fullyInView
? containerTop >= elementTop
: elementTop > containerBottom
const isBelow = fullyInView
? containerBottom <= elementBottom
: elementBottom < containerTop
if (isAbove) {
return { inView: false, breachPoint: "top" };
}
if (isAbove) {
return { inView: false, breachPoint: 'top' }
}
if (isBelow) {
return { inView: false, breachPoint: "bottom" };
}
if (isBelow) {
return { inView: false, breachPoint: 'bottom' }
}
return { inView: true };
};
return { inView: true }
}
export function isHTMLElement(o: any): o is HTMLElement {
if (typeof HTMLElement === "object") {
return o instanceof HTMLElement;
}
return (
o &&
typeof o === "object" &&
o.nodeType === 1 &&
typeof o.nodeName === "string"
);
if (typeof HTMLElement === 'object') {
return o instanceof HTMLElement
}
return (
o &&
typeof o === 'object' &&
o.nodeType === 1 &&
typeof o.nodeName === 'string'
)
}
export const isInTree = (element: HTMLElement, className: string) => {
let _element = element;
let _element = element
while (_element) {
if (_element.classList.contains(className)) {
return true;
while (_element) {
if (_element.classList.contains(className)) {
return true
}
_element = _element.parentElement
}
_element = _element.parentElement;
}
return false;
};
return false
}
export const removeClasses = (element: HTMLElement, ...classes: string[]) => {
for (const classname of classes) {
if (element.classList.contains(classname)) {
element.classList.remove(classname);
for (const classname of classes) {
if (element.classList.contains(classname)) {
element.classList.remove(classname)
}
}
}
};
}
export const addClasses = (element: HTMLElement, ...classes: string[]) => {
for (const classname of classes) {
if (!element.classList.contains(classname)) {
element.classList.add(classname);
for (const classname of classes) {
if (!element.classList.contains(classname)) {
element.classList.add(classname)
}
}
}
};
}
export const toggleClass = (
element: HTMLElement,
className: string,
isToggled: boolean
element: HTMLElement,
className: string,
isToggled: boolean
) => {
const hasClass = element.classList.contains(className);
if (isToggled && !hasClass) {
element.classList.add(className);
}
if (!isToggled && hasClass) {
element.classList.remove(className);
}
};
const hasClass = element.classList.contains(className)
if (isToggled && !hasClass) {
element.classList.add(className)
}
if (!isToggled && hasClass) {
element.classList.remove(className)
}
}
export function firstIndex<T>(
array: T[] | ReadonlyArray<T>,
fn: (item: T) => boolean
array: T[] | ReadonlyArray<T>,
fn: (item: T) => boolean
): number {
for (let i = 0; i < array.length; i++) {
const element = array[i];
for (let i = 0; i < array.length; i++) {
const element = array[i]
if (fn(element)) {
return i;
if (fn(element)) {
return i
}
}
}
return -1;
return -1
}
export function isAncestor(
testChild: Node | null,
testAncestor: Node | null
testChild: Node | null,
testAncestor: Node | null
): boolean {
while (testChild) {
if (testChild === testAncestor) {
return true;
while (testChild) {
if (testChild === testAncestor) {
return true
}
testChild = testChild.parentNode
}
testChild = testChild.parentNode;
}
return false;
return false
}
export interface IFocusTracker extends IDisposable {
onDidFocus: Event<void>;
onDidBlur: Event<void>;
refreshState?(): void;
onDidFocus: Event<void>
onDidBlur: Event<void>
refreshState?(): void
}
export function trackFocus(element: HTMLElement | Window): IFocusTracker {
return new FocusTracker(element);
return new FocusTracker(element)
}
/**
* Track focus on an element. Ensure tabIndex is set when an HTMLElement is not focusable by default
*/
class FocusTracker extends CompositeDisposable implements IFocusTracker {
private readonly _onDidFocus = new Emitter<void>();
public readonly onDidFocus: Event<void> = this._onDidFocus.event;
private readonly _onDidFocus = new Emitter<void>()
public readonly onDidFocus: Event<void> = this._onDidFocus.event
private readonly _onDidBlur = new Emitter<void>();
public readonly onDidBlur: Event<void> = this._onDidBlur.event;
private readonly _onDidBlur = new Emitter<void>()
public readonly onDidBlur: Event<void> = this._onDidBlur.event
private _refreshStateHandler: () => void;
private _refreshStateHandler: () => void
constructor(element: HTMLElement | Window) {
super();
constructor(element: HTMLElement | Window) {
super()
let hasFocus = isAncestor(document.activeElement, <HTMLElement>element);
let loosingFocus = false;
let hasFocus = isAncestor(document.activeElement, <HTMLElement>element)
let loosingFocus = false
const onFocus = () => {
loosingFocus = false;
if (!hasFocus) {
hasFocus = true;
this._onDidFocus.fire();
}
};
const onBlur = () => {
if (hasFocus) {
loosingFocus = true;
window.setTimeout(() => {
if (loosingFocus) {
loosingFocus = false;
hasFocus = false;
this._onDidBlur.fire();
}
}, 0);
}
};
this._refreshStateHandler = () => {
let currentNodeHasFocus = isAncestor(
document.activeElement,
<HTMLElement>element
);
if (currentNodeHasFocus !== hasFocus) {
if (hasFocus) {
onBlur();
} else {
onFocus();
const onFocus = () => {
loosingFocus = false
if (!hasFocus) {
hasFocus = true
this._onDidFocus.fire()
}
}
}
};
this.addDisposables(addDisposableListener(element, "focus", onFocus, true));
this.addDisposables(addDisposableListener(element, "blur", onBlur, true));
}
const onBlur = () => {
if (hasFocus) {
loosingFocus = true
window.setTimeout(() => {
if (loosingFocus) {
loosingFocus = false
hasFocus = false
this._onDidBlur.fire()
}
}, 0)
}
}
refreshState() {
this._refreshStateHandler();
}
this._refreshStateHandler = () => {
let currentNodeHasFocus = isAncestor(
document.activeElement,
<HTMLElement>element
)
if (currentNodeHasFocus !== hasFocus) {
if (hasFocus) {
onBlur()
} else {
onFocus()
}
}
}
public dispose() {
super.dispose();
this.addDisposables(
addDisposableListener(element, 'focus', onFocus, true)
)
this.addDisposables(
addDisposableListener(element, 'blur', onBlur, true)
)
}
this._onDidBlur.dispose();
this._onDidFocus.dispose();
}
refreshState() {
this._refreshStateHandler()
}
public dispose() {
super.dispose()
this._onDidBlur.dispose()
this._onDidFocus.dispose()
}
}

View File

@ -1,88 +1,88 @@
import { IDisposable } from "./lifecycle";
import { IDisposable } from './lifecycle'
export interface Event<T> {
(listener: (e: T) => any): IDisposable;
(listener: (e: T) => any): IDisposable
}
export interface EmitterOptions {
emitLastValue?: boolean;
emitLastValue?: boolean
}
export namespace Event {
export const any = <T>(...children: Event<T>[]): Event<T> => {
return (listener: (e: T) => void) => {
const disposables = children.map((child) => child(listener));
export const any = <T>(...children: Event<T>[]): Event<T> => {
return (listener: (e: T) => void) => {
const disposables = children.map((child) => child(listener))
return {
dispose: () => {
disposables.forEach((d) => {
d.dispose();
});
},
};
};
};
return {
dispose: () => {
disposables.forEach((d) => {
d.dispose()
})
},
}
}
}
}
// dumb event emitter with better typings than nodes event emitter
// https://github.com/microsoft/vscode/blob/master/src/vs/base/common/event.ts
export class Emitter<T> implements IDisposable {
private _event: Event<T>;
private _event: Event<T>
private _last: T;
private _listeners: Array<(e: T) => any> = [];
private _disposed: boolean = false;
private _last: T
private _listeners: Array<(e: T) => any> = []
private _disposed: boolean = false
constructor(private readonly options?: EmitterOptions) {}
constructor(private readonly options?: EmitterOptions) {}
get event() {
if (!this._event) {
this._event = (listener: (e: T) => void): IDisposable => {
if (this.options?.emitLastValue && this._last !== undefined) {
listener(this._last);
}
get event() {
if (!this._event) {
this._event = (listener: (e: T) => void): IDisposable => {
if (this.options?.emitLastValue && this._last !== undefined) {
listener(this._last)
}
this._listeners.push(listener);
this._listeners.push(listener)
return {
dispose: () => {
const index = this._listeners.indexOf(listener);
if (index > -1) {
this._listeners.splice(index, 1);
return {
dispose: () => {
const index = this._listeners.indexOf(listener)
if (index > -1) {
this._listeners.splice(index, 1)
}
},
}
}
},
};
};
}
return this._event
}
return this._event;
}
public fire(e: T) {
this._last = e;
this._listeners.forEach((listener) => {
listener(e);
});
}
public fire(e: T) {
this._last = e
this._listeners.forEach((listener) => {
listener(e)
})
}
public dispose() {
this._listeners = [];
this._disposed = true;
}
public dispose() {
this._listeners = []
this._disposed = true
}
}
export type EventHandler = HTMLElement | HTMLDocument | Window;
export type EventHandler = HTMLElement | HTMLDocument | Window
export const addDisposableListener = <K extends keyof HTMLElementEventMap>(
element: EventHandler,
type: K,
listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,
options?: boolean | AddEventListenerOptions
element: EventHandler,
type: K,
listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,
options?: boolean | AddEventListenerOptions
): IDisposable => {
element.addEventListener(type, listener, options);
element.addEventListener(type, listener, options)
return {
dispose: () => {
element.removeEventListener(type, listener);
},
};
};
return {
dispose: () => {
element.removeEventListener(type, listener)
},
}
}

View File

@ -1,9 +1,9 @@
export function debounce<T extends Function>(cb: T, wait: number) {
let timeout: NodeJS.Timeout;
let timeout: NodeJS.Timeout
const callable = (...args: any) => {
clearTimeout(timeout);
timeout = setTimeout(() => cb(...args), wait);
};
return <T>(<any>callable);
const callable = (...args: any) => {
clearTimeout(timeout)
timeout = setTimeout(() => cb(...args), wait)
}
return <T>(<any>callable)
}

View File

@ -1,244 +1,246 @@
import {
IView,
SplitView,
Orientation,
Sizing,
LayoutPriority,
} from "../splitview/splitview";
import { Emitter, Event } from "../events";
import { INodeDescriptor } from "./gridview";
import { Node } from "./types";
import { CompositeDisposable, IDisposable, Disposable } from "../lifecycle";
IView,
SplitView,
Orientation,
Sizing,
LayoutPriority,
} from '../splitview/splitview'
import { Emitter, Event } from '../events'
import { INodeDescriptor } from './gridview'
import { Node } from './types'
import { CompositeDisposable, IDisposable, Disposable } from '../lifecycle'
export class BranchNode extends CompositeDisposable implements IView {
readonly element: HTMLElement;
private splitview: SplitView;
private _orthogonalSize: number;
private _size: number;
public readonly children: Node[] = [];
readonly element: HTMLElement
private splitview: SplitView
private _orthogonalSize: number
private _size: number
public readonly children: Node[] = []
private readonly _onDidChange = new Emitter<number | undefined>();
readonly onDidChange: Event<number | undefined> = this._onDidChange.event;
private readonly _onDidChange = new Emitter<number | undefined>()
readonly onDidChange: Event<number | undefined> = this._onDidChange.event
get width(): number {
return this.orientation === Orientation.HORIZONTAL
? this.size
: this.orthogonalSize;
}
get height(): number {
return this.orientation === Orientation.HORIZONTAL
? this.orthogonalSize
: this.size;
}
get minimumSize(): number {
return this.children.length === 0
? 0
: Math.max(...this.children.map((c) => c.minimumOrthogonalSize));
}
get maximumSize(): number {
return Math.min(...this.children.map((c) => c.maximumOrthogonalSize));
}
get minimumOrthogonalSize(): number {
return this.splitview.minimumSize;
}
get maximumOrthogonalSize(): number {
return this.splitview.maximumSize;
}
get orthogonalSize() {
return this._orthogonalSize;
}
get size() {
return this._size;
}
get minimumWidth(): number {
return this.orientation === Orientation.HORIZONTAL
? this.minimumOrthogonalSize
: this.minimumSize;
}
get snapSize() {
return undefined;
}
get minimumHeight(): number {
return this.orientation === Orientation.HORIZONTAL
? this.minimumSize
: this.minimumOrthogonalSize;
}
get maximumWidth(): number {
return this.orientation === Orientation.HORIZONTAL
? this.maximumOrthogonalSize
: this.maximumSize;
}
get maximumHeight(): number {
return this.orientation === Orientation.HORIZONTAL
? this.maximumSize
: this.maximumOrthogonalSize;
}
get priority(): LayoutPriority {
if (this.children.length === 0) {
return LayoutPriority.Normal;
get width(): number {
return this.orientation === Orientation.HORIZONTAL
? this.size
: this.orthogonalSize
}
const priorities = this.children.map((c) =>
typeof c.priority === "undefined" ? LayoutPriority.Normal : c.priority
);
if (priorities.some((p) => p === LayoutPriority.High)) {
return LayoutPriority.High;
} else if (priorities.some((p) => p === LayoutPriority.Low)) {
return LayoutPriority.Low;
get height(): number {
return this.orientation === Orientation.HORIZONTAL
? this.orthogonalSize
: this.size
}
return LayoutPriority.Normal;
}
constructor(
readonly orientation: Orientation,
readonly proportionalLayout: boolean,
size: number = 0,
orthogonalSize: number,
childDescriptors?: INodeDescriptor[]
) {
super();
this._orthogonalSize = orthogonalSize;
this._size = size;
this.element = document.createElement("div");
this.element.className = "branch-node";
if (!childDescriptors) {
this.splitview = new SplitView(this.element, {
orientation: this.orientation,
proportionalLayout,
});
this.splitview.layout(this.size, this.orthogonalSize);
} else {
const descriptor = {
views: childDescriptors.map((childDescriptor) => {
return {
view: childDescriptor.node,
size: childDescriptor.node.size,
};
}),
size: this.orthogonalSize,
};
this.children = childDescriptors.map((c) => c.node);
this.splitview = new SplitView(this.element, {
orientation: this.orientation,
descriptor,
});
get minimumSize(): number {
return this.children.length === 0
? 0
: Math.max(...this.children.map((c) => c.minimumOrthogonalSize))
}
this.addDisposables(
this.splitview.onDidSashEnd(() => {
this._onDidChange.fire(undefined);
})
);
}
moveChild(from: number, to: number): void {
if (from === to) {
return;
get maximumSize(): number {
return Math.min(...this.children.map((c) => c.maximumOrthogonalSize))
}
if (from < 0 || from >= this.children.length) {
throw new Error("Invalid from index");
get minimumOrthogonalSize(): number {
return this.splitview.minimumSize
}
if (from < to) {
to--;
get maximumOrthogonalSize(): number {
return this.splitview.maximumSize
}
this.splitview.moveView(from, to);
const child = this._removeChild(from);
this._addChild(child, to);
}
getChildSize(index: number): number {
if (index < 0 || index >= this.children.length) {
throw new Error("Invalid index");
get orthogonalSize() {
return this._orthogonalSize
}
return this.splitview.getViewSize(index);
}
resizeChild(index: number, size: number): void {
if (index < 0 || index >= this.children.length) {
throw new Error("Invalid index");
get size() {
return this._size
}
this.splitview.resizeView(index, size);
}
public layout(size: number, orthogonalSize: number) {
this._size = orthogonalSize;
this._orthogonalSize = size;
this.splitview.layout(this.size, this.orthogonalSize);
}
public addChild(node: Node, size: number | Sizing, index: number): void {
if (index < 0 || index > this.children.length) {
throw new Error("Invalid index");
get minimumWidth(): number {
return this.orientation === Orientation.HORIZONTAL
? this.minimumOrthogonalSize
: this.minimumSize
}
this.splitview.addView(node, size, index);
this._addChild(node, index);
}
public removeChild(index: number, sizing?: Sizing) {
if (index < 0 || index >= this.children.length) {
throw new Error("Invalid index");
get snapSize() {
return undefined
}
this.splitview.removeView(index, sizing);
this._removeChild(index);
}
get minimumHeight(): number {
return this.orientation === Orientation.HORIZONTAL
? this.minimumSize
: this.minimumOrthogonalSize
}
private _addChild(node: Node, index: number): void {
this.children.splice(index, 0, node);
this.setupChildrenEvents();
}
get maximumWidth(): number {
return this.orientation === Orientation.HORIZONTAL
? this.maximumOrthogonalSize
: this.maximumSize
}
private _removeChild(index: number): Node {
const first = index === 0;
const last = index === this.children.length - 1;
const [child] = this.children.splice(index, 1);
this.setupChildrenEvents();
get maximumHeight(): number {
return this.orientation === Orientation.HORIZONTAL
? this.maximumSize
: this.maximumOrthogonalSize
}
return child;
}
get priority(): LayoutPriority {
if (this.children.length === 0) {
return LayoutPriority.Normal
}
private _childrenDisposable: IDisposable = Disposable.NONE;
const priorities = this.children.map((c) =>
typeof c.priority === 'undefined'
? LayoutPriority.Normal
: c.priority
)
private setupChildrenEvents() {
this._childrenDisposable.dispose();
if (priorities.some((p) => p === LayoutPriority.High)) {
return LayoutPriority.High
} else if (priorities.some((p) => p === LayoutPriority.Low)) {
return LayoutPriority.Low
}
this._childrenDisposable = Event.any(
...this.children.map((c) => c.onDidChange)
)((e) => {
this._onDidChange.fire(e);
});
}
return LayoutPriority.Normal
}
public dispose() {
super.dispose();
this._childrenDisposable.dispose();
this.splitview.dispose();
this.children.forEach((child) => child.dispose());
}
constructor(
readonly orientation: Orientation,
readonly proportionalLayout: boolean,
size: number = 0,
orthogonalSize: number,
childDescriptors?: INodeDescriptor[]
) {
super()
this._orthogonalSize = orthogonalSize
this._size = size
this.element = document.createElement('div')
this.element.className = 'branch-node'
if (!childDescriptors) {
this.splitview = new SplitView(this.element, {
orientation: this.orientation,
proportionalLayout,
})
this.splitview.layout(this.size, this.orthogonalSize)
} else {
const descriptor = {
views: childDescriptors.map((childDescriptor) => {
return {
view: childDescriptor.node,
size: childDescriptor.node.size,
}
}),
size: this.orthogonalSize,
}
this.children = childDescriptors.map((c) => c.node)
this.splitview = new SplitView(this.element, {
orientation: this.orientation,
descriptor,
})
}
this.addDisposables(
this.splitview.onDidSashEnd(() => {
this._onDidChange.fire(undefined)
})
)
}
moveChild(from: number, to: number): void {
if (from === to) {
return
}
if (from < 0 || from >= this.children.length) {
throw new Error('Invalid from index')
}
if (from < to) {
to--
}
this.splitview.moveView(from, to)
const child = this._removeChild(from)
this._addChild(child, to)
}
getChildSize(index: number): number {
if (index < 0 || index >= this.children.length) {
throw new Error('Invalid index')
}
return this.splitview.getViewSize(index)
}
resizeChild(index: number, size: number): void {
if (index < 0 || index >= this.children.length) {
throw new Error('Invalid index')
}
this.splitview.resizeView(index, size)
}
public layout(size: number, orthogonalSize: number) {
this._size = orthogonalSize
this._orthogonalSize = size
this.splitview.layout(this.size, this.orthogonalSize)
}
public addChild(node: Node, size: number | Sizing, index: number): void {
if (index < 0 || index > this.children.length) {
throw new Error('Invalid index')
}
this.splitview.addView(node, size, index)
this._addChild(node, index)
}
public removeChild(index: number, sizing?: Sizing) {
if (index < 0 || index >= this.children.length) {
throw new Error('Invalid index')
}
this.splitview.removeView(index, sizing)
this._removeChild(index)
}
private _addChild(node: Node, index: number): void {
this.children.splice(index, 0, node)
this.setupChildrenEvents()
}
private _removeChild(index: number): Node {
const first = index === 0
const last = index === this.children.length - 1
const [child] = this.children.splice(index, 1)
this.setupChildrenEvents()
return child
}
private _childrenDisposable: IDisposable = Disposable.NONE
private setupChildrenEvents() {
this._childrenDisposable.dispose()
this._childrenDisposable = Event.any(
...this.children.map((c) => c.onDidChange)
)((e) => {
this._onDidChange.fire(e)
})
}
public dispose() {
super.dispose()
this._childrenDisposable.dispose()
this.splitview.dispose()
this.children.forEach((child) => child.dispose())
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,94 +1,94 @@
import { IView, LayoutPriority, Orientation } from "../splitview/splitview";
import { Emitter, Event } from "../events";
import { IGridView } from "./gridview";
import { IView, LayoutPriority, Orientation } from '../splitview/splitview'
import { Emitter, Event } from '../events'
import { IGridView } from './gridview'
export class LeafNode implements IView {
private readonly _onDidChange = new Emitter<number | undefined>();
readonly onDidChange: Event<number | undefined> = this._onDidChange.event;
private _size: number;
private _orthogonalSize: number;
private readonly _onDidChange = new Emitter<number | undefined>()
readonly onDidChange: Event<number | undefined> = this._onDidChange.event
private _size: number
private _orthogonalSize: number
public dispose() {}
public dispose() {}
private get minimumWidth(): number {
return this.view.minimumWidth;
}
private get minimumWidth(): number {
return this.view.minimumWidth
}
private get maximumWidth(): number {
return this.view.maximumWidth;
}
private get maximumWidth(): number {
return this.view.maximumWidth
}
private get minimumHeight(): number {
return this.view.minimumHeight;
}
private get minimumHeight(): number {
return this.view.minimumHeight
}
private get maximumHeight(): number {
return this.view.maximumHeight;
}
private get maximumHeight(): number {
return this.view.maximumHeight
}
get priority(): LayoutPriority | undefined {
return this.view.priority;
}
get priority(): LayoutPriority | undefined {
return this.view.priority
}
get snapSize() {
return this.view.snap ? this.minimumSize / 2 : undefined;
}
get snapSize() {
return this.view.snap ? this.minimumSize / 2 : undefined
}
get minimumSize(): number {
return this.orientation === Orientation.HORIZONTAL
? this.minimumHeight
: this.minimumWidth;
}
get minimumSize(): number {
return this.orientation === Orientation.HORIZONTAL
? this.minimumHeight
: this.minimumWidth
}
get maximumSize(): number {
return this.orientation === Orientation.HORIZONTAL
? this.maximumHeight
: this.maximumWidth;
}
get maximumSize(): number {
return this.orientation === Orientation.HORIZONTAL
? this.maximumHeight
: this.maximumWidth
}
get minimumOrthogonalSize(): number {
return this.orientation === Orientation.HORIZONTAL
? this.minimumWidth
: this.minimumHeight;
}
get minimumOrthogonalSize(): number {
return this.orientation === Orientation.HORIZONTAL
? this.minimumWidth
: this.minimumHeight
}
get maximumOrthogonalSize(): number {
return this.orientation === Orientation.HORIZONTAL
? this.maximumWidth
: this.maximumHeight;
}
get maximumOrthogonalSize(): number {
return this.orientation === Orientation.HORIZONTAL
? this.maximumWidth
: this.maximumHeight
}
get orthogonalSize() {
return this._orthogonalSize;
}
get orthogonalSize() {
return this._orthogonalSize
}
get size() {
return this._size;
}
get size() {
return this._size
}
get element() {
return this.view.element;
}
get element() {
return this.view.element
}
constructor(
public readonly view: IGridView,
readonly orientation: Orientation,
orthogonalSize: number,
size: number = 0
) {
this._orthogonalSize = orthogonalSize;
this._size = size;
}
constructor(
public readonly view: IGridView,
readonly orientation: Orientation,
orthogonalSize: number,
size: number = 0
) {
this._orthogonalSize = orthogonalSize
this._size = size
}
public layout(size: number, orthogonalSize: number) {
this._size = size;
this._orthogonalSize = orthogonalSize;
public layout(size: number, orthogonalSize: number) {
this._size = size
this._orthogonalSize = orthogonalSize
const [width, height] =
this.orientation === Orientation.HORIZONTAL
? [orthogonalSize, size]
: [size, orthogonalSize];
const [width, height] =
this.orientation === Orientation.HORIZONTAL
? [orthogonalSize, size]
: [size, orthogonalSize]
this.view.layout(width, height, 0, 0);
}
this.view.layout(width, height, 0, 0)
}
}

View File

@ -1,4 +1,4 @@
import { BranchNode } from "./branchNode";
import { LeafNode } from "./leafNode";
import { BranchNode } from './branchNode'
import { LeafNode } from './leafNode'
export type Node = BranchNode | LeafNode;
export type Node = BranchNode | LeafNode

View File

@ -1,29 +1,29 @@
.actions-bar {
text-align: right;
width: 28px;
display: flex;
align-items: center;
.actions-container {
text-align: right;
width: 28px;
display: flex;
padding: 0px;
margin: 0px;
justify-content: flex-end;
align-items: center;
a:active {
-webkit-mask-size: 100% 100% !important;
mask-size: 100% 100% !important;
}
.actions-container {
display: flex;
padding: 0px;
margin: 0px;
justify-content: flex-end;
.close-action {
background-color: white;
height: 16px;
width: 16px;
display: block;
-webkit-mask: var(--tab-close-icon) 50% 50% / 90% 90% no-repeat;
mask: var(--tab-close-icon) 50% 50% / 90% 90% no-repeat;
margin-right: "0.5em";
cursor: pointer;
a:active {
-webkit-mask-size: 100% 100% !important;
mask-size: 100% 100% !important;
}
.close-action {
background-color: white;
height: 16px;
width: 16px;
display: block;
-webkit-mask: var(--tab-close-icon) 50% 50% / 90% 90% no-repeat;
mask: var(--tab-close-icon) 50% 50% / 90% 90% no-repeat;
margin-right: '0.5em';
cursor: pointer;
}
}
}
}

View File

@ -1,22 +1,22 @@
export class ActionContainer {
private _element: HTMLElement;
private _list: HTMLElement;
private _element: HTMLElement
private _list: HTMLElement
get element() {
return this._element;
}
get element() {
return this._element
}
constructor() {
this._element = document.createElement("div");
this._element.className = "actions-bar";
constructor() {
this._element = document.createElement('div')
this._element.className = 'actions-bar'
this._list = document.createElement("ul");
this._list.className = "actions-container";
this._list = document.createElement('ul')
this._list.className = 'actions-container'
this._element.appendChild(this._list);
}
this._element.appendChild(this._list)
}
public add(element: HTMLElement) {
this._list.appendChild(element);
}
public add(element: HTMLElement) {
this._list.appendChild(element)
}
}

View File

@ -1,124 +1,124 @@
import { PanelOptions } from "../../layout/options";
import { PanelOptions } from '../../layout/options'
export const DATA_KEY = "splitview/transfer";
export const DATA_KEY = 'splitview/transfer'
export const isPanelTransferEvent = (event: DragEvent) => {
return event.dataTransfer.types.includes(DATA_KEY);
};
return event.dataTransfer.types.includes(DATA_KEY)
}
export enum DragType {
ITEM = "group_drag",
EXTERNAL = "external_group_drag",
ITEM = 'group_drag',
EXTERNAL = 'external_group_drag',
}
export interface DragItem {
itemId: string;
groupId: string;
itemId: string
groupId: string
}
export interface ExternalDragItem extends PanelOptions {}
export type DataObject = DragItem | ExternalDragItem;
export type DataObject = DragItem | ExternalDragItem
/**
* Determine whether this data belong to that of an event that was started by
* dragging a tab component
*/
export const isTabDragEvent = (data: any): data is DragItem => {
return data.type === DragType.ITEM;
};
return data.type === DragType.ITEM
}
/**
* Determine whether this data belong to that of an event that was started by
* a custom drag-enable component
*/
export const isCustomDragEvent = (data: any): data is ExternalDragItem => {
return data.type === DragType.EXTERNAL;
};
export const extractData = (event: DragEvent): DataObject => {
const data = JSON.parse(event.dataTransfer.getData(DATA_KEY));
if (!data) {
console.warn(`[dragEvent] ${DATA_KEY} data is missing`);
}
if (typeof data.type !== "string") {
console.warn(`[dragEvent] invalid type ${data.type}`);
}
return data;
};
class DataTransfer {
private map = new Map<string, string>();
public setData(format: string, data: string) {
this.map.set(format, data);
}
public getData(format: string) {
const data = this.map.get(format);
return data;
}
public has(format: string) {
return this.map.has(format);
}
public removeData(format: string) {
const data = this.getData(format);
this.map.delete(format);
return data;
}
get size() {
return this.map.size;
}
return data.type === DragType.EXTERNAL
}
export const DataTransferSingleton = new DataTransfer();
export const extractData = (event: DragEvent): DataObject => {
const data = JSON.parse(event.dataTransfer.getData(DATA_KEY))
if (!data) {
console.warn(`[dragEvent] ${DATA_KEY} data is missing`)
}
if (typeof data.type !== 'string') {
console.warn(`[dragEvent] invalid type ${data.type}`)
}
return data
}
class DataTransfer {
private map = new Map<string, string>()
public setData(format: string, data: string) {
this.map.set(format, data)
}
public getData(format: string) {
const data = this.map.get(format)
return data
}
public has(format: string) {
return this.map.has(format)
}
public removeData(format: string) {
const data = this.getData(format)
this.map.delete(format)
return data
}
get size() {
return this.map.size
}
}
export const DataTransferSingleton = new DataTransfer()
/**
* A singleton to store transfer data during drag & drop operations that are only valid within the application.
*/
export class LocalSelectionTransfer<T> {
private static readonly INSTANCE = new LocalSelectionTransfer();
private static readonly INSTANCE = new LocalSelectionTransfer()
private data?: T[];
private proto?: T;
private data?: T[]
private proto?: T
private constructor() {
// protect against external instantiation
}
static getInstance<T>(): LocalSelectionTransfer<T> {
return LocalSelectionTransfer.INSTANCE as LocalSelectionTransfer<T>;
}
hasData(proto: T): boolean {
return proto && proto === this.proto;
}
clearData(proto: T): void {
if (this.hasData(proto)) {
this.proto = undefined;
this.data = undefined;
}
}
getData(proto: T): T[] | undefined {
if (this.hasData(proto)) {
return this.data;
private constructor() {
// protect against external instantiation
}
return undefined;
}
setData(data: T[], proto: T): void {
if (proto) {
this.data = data;
this.proto = proto;
static getInstance<T>(): LocalSelectionTransfer<T> {
return LocalSelectionTransfer.INSTANCE as LocalSelectionTransfer<T>
}
hasData(proto: T): boolean {
return proto && proto === this.proto
}
clearData(proto: T): void {
if (this.hasData(proto)) {
this.proto = undefined
this.data = undefined
}
}
getData(proto: T): T[] | undefined {
if (this.hasData(proto)) {
return this.data
}
return undefined
}
setData(data: T[], proto: T): void {
if (proto) {
this.data = data
this.proto = proto
}
}
}
}

View File

@ -1,38 +1,38 @@
.drop-target {
position: relative;
.drop-target-dropzone {
position: absolute;
left: 0px;
top: 0px;
height: 100%;
width: 100%;
z-index: 10000;
}
.drop-target-selection {
position: relative;
pointer-events: none;
height: 100%;
width: 100%;
background-color: var(--drag-over-background-color);
transition-duration: 0.15s;
transition-timing-function: ease-out;
.drop-target-dropzone {
position: absolute;
left: 0px;
top: 0px;
height: 100%;
width: 100%;
z-index: 10000;
}
&.left,
&.right {
width: 50%;
.drop-target-selection {
position: relative;
pointer-events: none;
height: 100%;
width: 100%;
background-color: var(--drag-over-background-color);
transition-duration: 0.15s;
transition-timing-function: ease-out;
&.left,
&.right {
width: 50%;
}
&.right {
transform: translate(100%, 0%);
}
&.bottom {
transform: translate(0%, 100%);
}
&.top,
&.bottom {
height: 50%;
}
}
&.right {
transform: translate(100%, 0%);
}
&.bottom {
transform: translate(0%, 100%);
}
&.top,
&.bottom {
height: 50%;
}
}
}

View File

@ -1,170 +1,170 @@
import { Emitter, Event } from "../../events";
import { DataTransferSingleton } from "./dataTransfer";
import { Emitter, Event } from '../../events'
import { DataTransferSingleton } from './dataTransfer'
export enum Position {
Top = "Top",
Left = "Left",
Bottom = "Bottom",
Right = "Right",
Center = "Center",
Top = 'Top',
Left = 'Left',
Bottom = 'Bottom',
Right = 'Right',
Center = 'Center',
}
export interface DroptargetEvent {
position: Position;
event: DragEvent;
position: Position
event: DragEvent
}
const HAS_PROCESSED_KEY = "__drop_target_processed__";
const HAS_PROCESSED_KEY = '__drop_target_processed__'
export const hasProcessed = (event: DragEvent) =>
!!(event as any)[HAS_PROCESSED_KEY];
!!(event as any)[HAS_PROCESSED_KEY]
// tagging events as processed is better than calling .stopPropagation() which is the root of all evil
const setEventAsProcessed = (event: DragEvent) => {
event[HAS_PROCESSED_KEY] = true;
};
event[HAS_PROCESSED_KEY] = true
}
const toggleClassName = (
element: HTMLElement,
className: string,
addOrRemove: boolean
element: HTMLElement,
className: string,
addOrRemove: boolean
) => {
if (addOrRemove && !element.classList.contains(className)) {
element.classList.add(className);
} else if (!addOrRemove && element.classList.contains(className)) {
element.classList.remove(className);
}
};
if (addOrRemove && !element.classList.contains(className)) {
element.classList.add(className)
} else if (!addOrRemove && element.classList.contains(className)) {
element.classList.remove(className)
}
}
export class Droptarget {
private target: HTMLElement;
private overlay: HTMLElement;
private state: Position | undefined;
private target: HTMLElement
private overlay: HTMLElement
private state: Position | undefined
private readonly _onDidChange = new Emitter<DroptargetEvent>();
readonly onDidChange: Event<DroptargetEvent> = this._onDidChange.event;
private readonly _onDidChange = new Emitter<DroptargetEvent>()
readonly onDidChange: Event<DroptargetEvent> = this._onDidChange.event
constructor(
private element: HTMLElement,
private options: {
isDisabled: () => boolean;
isDirectional: boolean;
id: string;
enableExternalDragEvents?: boolean;
}
) {
this.element.addEventListener("dragenter", this.onDragEnter);
}
public dispose() {
this._onDidChange.dispose();
this.removeDropTarget();
this.element.removeEventListener("dragenter", this.onDragEnter);
}
private onDragEnter = (event: DragEvent) => {
if (
!this.options.enableExternalDragEvents &&
!DataTransferSingleton.has(this.options.id)
constructor(
private element: HTMLElement,
private options: {
isDisabled: () => boolean
isDirectional: boolean
id: string
enableExternalDragEvents?: boolean
}
) {
console.debug("[droptarget] invalid event");
return;
this.element.addEventListener('dragenter', this.onDragEnter)
}
if (this.options.isDisabled()) {
return;
public dispose() {
this._onDidChange.dispose()
this.removeDropTarget()
this.element.removeEventListener('dragenter', this.onDragEnter)
}
event.preventDefault();
if (!this.target) {
console.debug("[droptarget] created");
this.target = document.createElement("div");
this.target.className = "drop-target-dropzone";
this.overlay = document.createElement("div");
this.overlay.className = "drop-target-selection";
//
this.target.addEventListener("dragover", this.onDragOver);
this.target.addEventListener("dragleave", this.onDragLeave);
this.target.addEventListener("drop", this.onDrop);
this.target.appendChild(this.overlay);
private onDragEnter = (event: DragEvent) => {
if (
!this.options.enableExternalDragEvents &&
!DataTransferSingleton.has(this.options.id)
) {
console.debug('[droptarget] invalid event')
return
}
this.element.classList.add("drop-target");
this.element.append(this.target);
}
};
if (this.options.isDisabled()) {
return
}
private onDrop = (event: DragEvent) => {
if (
!this.options.enableExternalDragEvents &&
!DataTransferSingleton.has(this.options.id)
) {
console.debug("[dragtarget] invalid");
return;
event.preventDefault()
if (!this.target) {
console.debug('[droptarget] created')
this.target = document.createElement('div')
this.target.className = 'drop-target-dropzone'
this.overlay = document.createElement('div')
this.overlay.className = 'drop-target-selection'
//
this.target.addEventListener('dragover', this.onDragOver)
this.target.addEventListener('dragleave', this.onDragLeave)
this.target.addEventListener('drop', this.onDrop)
this.target.appendChild(this.overlay)
this.element.classList.add('drop-target')
this.element.append(this.target)
}
}
console.debug("[dragtarget] drop");
this.removeDropTarget();
private onDrop = (event: DragEvent) => {
if (
!this.options.enableExternalDragEvents &&
!DataTransferSingleton.has(this.options.id)
) {
console.debug('[dragtarget] invalid')
return
}
if (!hasProcessed(event)) {
this._onDidChange.fire({ position: this.state, event });
} else {
console.debug("[dragtarget] already processed");
}
this.state = undefined;
console.debug('[dragtarget] drop')
this.removeDropTarget()
setEventAsProcessed(event);
};
if (!hasProcessed(event)) {
this._onDidChange.fire({ position: this.state, event })
} else {
console.debug('[dragtarget] already processed')
}
this.state = undefined
private onDragOver = (event: DragEvent) => {
event.preventDefault();
if (!this.options.isDirectional) {
return;
setEventAsProcessed(event)
}
const width = this.target.clientWidth;
const height = this.target.clientHeight;
const x = event.offsetX;
const y = event.offsetY;
const xp = (100 * x) / width;
const yp = (100 * y) / height;
private onDragOver = (event: DragEvent) => {
event.preventDefault()
const isRight = xp > 80;
const isLeft = xp < 20;
const isTop = !isRight && !isLeft && yp < 20;
const isBottom = !isRight && !isLeft && yp > 80;
if (!this.options.isDirectional) {
return
}
toggleClassName(this.overlay, "right", isRight);
toggleClassName(this.overlay, "left", isLeft);
toggleClassName(this.overlay, "top", isTop);
toggleClassName(this.overlay, "bottom", isBottom);
const width = this.target.clientWidth
const height = this.target.clientHeight
const x = event.offsetX
const y = event.offsetY
const xp = (100 * x) / width
const yp = (100 * y) / height
if (isRight) {
this.state = Position.Right;
} else if (isLeft) {
this.state = Position.Left;
} else if (isTop) {
this.state = Position.Top;
} else if (isBottom) {
this.state = Position.Bottom;
} else {
this.state = Position.Center;
const isRight = xp > 80
const isLeft = xp < 20
const isTop = !isRight && !isLeft && yp < 20
const isBottom = !isRight && !isLeft && yp > 80
toggleClassName(this.overlay, 'right', isRight)
toggleClassName(this.overlay, 'left', isLeft)
toggleClassName(this.overlay, 'top', isTop)
toggleClassName(this.overlay, 'bottom', isBottom)
if (isRight) {
this.state = Position.Right
} else if (isLeft) {
this.state = Position.Left
} else if (isTop) {
this.state = Position.Top
} else if (isBottom) {
this.state = Position.Bottom
} else {
this.state = Position.Center
}
}
};
private onDragLeave = (event: DragEvent) => {
console.debug("[droptarget] leave");
this.removeDropTarget();
};
private removeDropTarget() {
if (this.target) {
this.target.removeEventListener("dragover", this.onDragOver);
this.target.removeEventListener("dragleave", this.onDragLeave);
this.target.removeEventListener("drop", this.onDrop);
this.element.removeChild(this.target);
this.target = undefined;
this.element.classList.remove("drop-target");
private onDragLeave = (event: DragEvent) => {
console.debug('[droptarget] leave')
this.removeDropTarget()
}
private removeDropTarget() {
if (this.target) {
this.target.removeEventListener('dragover', this.onDragOver)
this.target.removeEventListener('dragleave', this.onDragLeave)
this.target.removeEventListener('drop', this.onDrop)
this.element.removeChild(this.target)
this.target = undefined
this.element.classList.remove('drop-target')
}
}
}
}

View File

@ -1,19 +1,19 @@
import { DroptargetEvent } from "./droptarget/droptarget";
import { IGroupPanel } from "./panel/types";
import { DroptargetEvent } from './droptarget/droptarget'
import { IGroupPanel } from './panel/types'
export interface TabDropEvent {
event: DroptargetEvent;
index?: number;
event: DroptargetEvent
index?: number
}
export enum MouseEventKind {
CLICK = "CLICK",
CONTEXT_MENU = "CONTEXT_MENU",
CLICK = 'CLICK',
CONTEXT_MENU = 'CONTEXT_MENU',
}
export interface LayoutMouseEvent {
kind: MouseEventKind;
event: MouseEvent;
panel?: IGroupPanel;
tab?: boolean;
kind: MouseEventKind
event: MouseEvent
panel?: IGroupPanel
tab?: boolean
}

View File

@ -1,22 +1,22 @@
.groupview {
display: flex;
flex-direction: column;
height: 100%;
background-color: var(--group-view-background-color);
display: flex;
flex-direction: column;
height: 100%;
background-color: var(--group-view-background-color);
&:focus {
outline: none;
}
&.empty {
.title-container {
display: none;
&:focus {
outline: none;
}
}
.content-container {
flex-grow: 1;
overflow: hidden;
outline: none;
}
&.empty {
.title-container {
display: none;
}
}
.content-container {
flex-grow: 1;
overflow: hidden;
outline: none;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,76 +1,76 @@
import { IGroupview } from "../groupview";
import { Emitter, Event } from "../../events";
import { ClosePanelResult } from "./parts";
import { IGroupPanel } from "./types";
import { IBaseViewApi, BaseViewApi } from "../../panel/api";
import { IGroupview } from '../groupview'
import { Emitter, Event } from '../../events'
import { ClosePanelResult } from './parts'
import { IGroupPanel } from './types'
import { IBaseViewApi, BaseViewApi } from '../../panel/api'
interface ChangeVisibilityEvent {
isVisible: boolean;
isVisible: boolean
}
export interface IGroupPanelApi extends IBaseViewApi {
// events
onDidDirtyChange: Event<boolean>;
onDidChangeVisibility: Event<ChangeVisibilityEvent>;
// misc
readonly isVisible: boolean;
group: IGroupview;
close: () => Promise<boolean>;
canClose: () => Promise<ClosePanelResult>;
setClosePanelHook(callback: () => Promise<ClosePanelResult>): void;
// events
onDidDirtyChange: Event<boolean>
onDidChangeVisibility: Event<ChangeVisibilityEvent>
// misc
readonly isVisible: boolean
group: IGroupview
close: () => Promise<boolean>
canClose: () => Promise<ClosePanelResult>
setClosePanelHook(callback: () => Promise<ClosePanelResult>): void
}
export class GroupPanelApi extends BaseViewApi implements IGroupPanelApi {
private _isVisible: boolean;
private _group: IGroupview;
private _closePanelCallback: () => Promise<ClosePanelResult>;
private _isVisible: boolean
private _group: IGroupview
private _closePanelCallback: () => Promise<ClosePanelResult>
readonly _onDidDirtyChange = new Emitter<boolean>();
readonly onDidDirtyChange = this._onDidDirtyChange.event;
readonly _onDidChangeVisibility = new Emitter<ChangeVisibilityEvent>({
emitLastValue: true,
});
readonly onDidChangeVisibility: Event<ChangeVisibilityEvent> = this
._onDidChangeVisibility.event;
readonly _onDidDirtyChange = new Emitter<boolean>()
readonly onDidDirtyChange = this._onDidDirtyChange.event
readonly _onDidChangeVisibility = new Emitter<ChangeVisibilityEvent>({
emitLastValue: true,
})
readonly onDidChangeVisibility: Event<ChangeVisibilityEvent> = this
._onDidChangeVisibility.event
get isVisible() {
return this._isVisible;
}
get isVisible() {
return this._isVisible
}
get canClose() {
return this._closePanelCallback;
}
get canClose() {
return this._closePanelCallback
}
set group(value: IGroupview) {
this._group = value;
}
set group(value: IGroupview) {
this._group = value
}
get group() {
return this._group;
}
get group() {
return this._group
}
constructor(private panel: IGroupPanel, group: IGroupview) {
super();
this._group = group;
constructor(private panel: IGroupPanel, group: IGroupview) {
super()
this._group = group
this.addDisposables(
this._onDidChangeVisibility,
this._onDidDirtyChange,
this.onDidChangeVisibility((event) => {
this._isVisible = event.isVisible;
})
);
}
this.addDisposables(
this._onDidChangeVisibility,
this._onDidDirtyChange,
this.onDidChangeVisibility((event) => {
this._isVisible = event.isVisible
})
)
}
public close() {
return this.group.closePanel(this.panel);
}
public close() {
return this.group.closePanel(this.panel)
}
public setClosePanelHook(callback: () => Promise<ClosePanelResult>) {
this._closePanelCallback = callback;
}
public setClosePanelHook(callback: () => Promise<ClosePanelResult>) {
this._closePanelCallback = callback
}
public dispose() {
super.dispose();
}
public dispose() {
super.dispose()
}
}

View File

@ -1,54 +1,55 @@
import { CompositeDisposable, IDisposable } from "../../../lifecycle";
import { Emitter, Event } from "../../../events";
import { trackFocus } from "../../../dom";
import { CompositeDisposable, IDisposable } from '../../../lifecycle'
import { Emitter, Event } from '../../../events'
import { trackFocus } from '../../../dom'
export interface IContentContainer extends IDisposable {
onDidFocus: Event<void>;
element: HTMLElement;
openPanel: (panel: HTMLElement) => void;
closePanel: () => void;
onDidFocus: Event<void>
element: HTMLElement
openPanel: (panel: HTMLElement) => void
closePanel: () => void
}
export class ContentContainer extends CompositeDisposable
implements IContentContainer {
private _element: HTMLElement;
private content: HTMLElement;
export class ContentContainer
extends CompositeDisposable
implements IContentContainer {
private _element: HTMLElement
private content: HTMLElement
private readonly _onDidFocus = new Emitter<void>();
readonly onDidFocus: Event<void> = this._onDidFocus.event;
private readonly _onDidFocus = new Emitter<void>()
readonly onDidFocus: Event<void> = this._onDidFocus.event
get element() {
return this._element;
}
constructor() {
super();
this._element = document.createElement("div");
this._element.className = "content-container";
this._element.tabIndex = -1;
const { onDidBlur, onDidFocus } = trackFocus(this._element);
this.addDisposables(onDidFocus(() => this._onDidFocus.fire()));
}
public openPanel(panel: HTMLElement) {
if (this.content) {
this._element.removeChild(this.content);
this.content = undefined;
get element() {
return this._element
}
this.content = panel;
this._element.appendChild(this.content);
}
public closePanel() {
if (this.content) {
this._element.removeChild(this.content);
this.content = undefined;
constructor() {
super()
this._element = document.createElement('div')
this._element.className = 'content-container'
this._element.tabIndex = -1
const { onDidBlur, onDidFocus } = trackFocus(this._element)
this.addDisposables(onDidFocus(() => this._onDidFocus.fire()))
}
}
public dispose() {
super.dispose();
}
public openPanel(panel: HTMLElement) {
if (this.content) {
this._element.removeChild(this.content)
this.content = undefined
}
this.content = panel
this._element.appendChild(this.content)
}
public closePanel() {
if (this.content) {
this._element.removeChild(this.content)
this.content = undefined
}
}
public dispose() {
super.dispose()
}
}

View File

@ -1,141 +1,141 @@
import { IGroupPanel, PanelInitParameters } from "./types";
import { GroupPanelApi } from "./api";
import { Event } from "../../events";
import { IGroupview, GroupChangeKind } from "../groupview";
import { MutableDisposable, CompositeDisposable } from "../../lifecycle";
import { PanelContentPart, PanelHeaderPart, ClosePanelResult } from "./parts";
import { PanelUpdateEvent } from "../../panel/types";
import { IGroupPanel, PanelInitParameters } from './types'
import { GroupPanelApi } from './api'
import { Event } from '../../events'
import { IGroupview, GroupChangeKind } from '../groupview'
import { MutableDisposable, CompositeDisposable } from '../../lifecycle'
import { PanelContentPart, PanelHeaderPart, ClosePanelResult } from './parts'
import { PanelUpdateEvent } from '../../panel/types'
export class DefaultPanel extends CompositeDisposable implements IGroupPanel {
private readonly mutableDisposable = new MutableDisposable();
private readonly mutableDisposable = new MutableDisposable()
private readonly api: GroupPanelApi;
private _group: IGroupview;
private params: PanelInitParameters;
private readonly api: GroupPanelApi
private _group: IGroupview
private params: PanelInitParameters
readonly onDidStateChange: Event<any>;
readonly onDidStateChange: Event<any>
get group() {
return this._group;
}
get header() {
return this.headerPart;
}
get content() {
return this.contentPart;
}
constructor(
public readonly id: string,
private readonly headerPart: PanelHeaderPart,
private readonly contentPart: PanelContentPart
) {
super();
this.api = new GroupPanelApi(this, this._group);
this.onDidStateChange = this.api.onDidStateChange;
}
public setDirty(isDirty: boolean) {
this.api._onDidDirtyChange.fire(isDirty);
}
public close(): Promise<ClosePanelResult> {
if (this.api.canClose) {
return this.api.canClose();
get group() {
return this._group
}
return Promise.resolve(ClosePanelResult.CLOSE);
}
public toJSON(): object {
return {
id: this.id,
content: this.contentPart.toJSON(),
tab: this.headerPart.toJSON(),
props: this.params.params,
title: this.params.title,
suppressClosable: this.params.suppressClosable,
state: this.api.getState(),
};
}
public fromJSON(data: object) {
//
}
public update(params: PanelUpdateEvent): void {
this.params.params = { ...this.params.params, ...params };
this.contentPart.update(params.params);
this.api._onDidStateChange.fire();
}
public init(params: PanelInitParameters): void {
this.params = params;
this.api.setState(this.params.state);
if (this.content.init) {
this.content.init({ ...params, api: this.api });
get header() {
return this.headerPart
}
if (this.header.init) {
this.header.init({ ...params, api: this.api });
get content() {
return this.contentPart
}
}
public onHide() {
//
}
constructor(
public readonly id: string,
private readonly headerPart: PanelHeaderPart,
private readonly contentPart: PanelContentPart
) {
super()
public focus() {
//
}
this.api = new GroupPanelApi(this, this._group)
this.onDidStateChange = this.api.onDidStateChange
}
public setVisible(isGroupActive: boolean, group: IGroupview) {
this._group = group;
this.api.group = group;
public setDirty(isDirty: boolean) {
this.api._onDidDirtyChange.fire(isDirty)
}
this.mutableDisposable.value = this._group.onDidGroupChange((ev) => {
if (ev.kind === GroupChangeKind.GROUP_ACTIVE) {
public close(): Promise<ClosePanelResult> {
if (this.api.canClose) {
return this.api.canClose()
}
return Promise.resolve(ClosePanelResult.CLOSE)
}
public toJSON(): object {
return {
id: this.id,
content: this.contentPart.toJSON(),
tab: this.headerPart.toJSON(),
props: this.params.params,
title: this.params.title,
suppressClosable: this.params.suppressClosable,
state: this.api.getState(),
}
}
public fromJSON(data: object) {
//
}
public update(params: PanelUpdateEvent): void {
this.params.params = { ...this.params.params, ...params }
this.contentPart.update(params.params)
this.api._onDidStateChange.fire()
}
public init(params: PanelInitParameters): void {
this.params = params
this.api.setState(this.params.state)
if (this.content.init) {
this.content.init({ ...params, api: this.api })
}
if (this.header.init) {
this.header.init({ ...params, api: this.api })
}
}
public onHide() {
//
}
public focus() {
//
}
public setVisible(isGroupActive: boolean, group: IGroupview) {
this._group = group
this.api.group = group
this.mutableDisposable.value = this._group.onDidGroupChange((ev) => {
if (ev.kind === GroupChangeKind.GROUP_ACTIVE) {
this.api._onDidChangeVisibility.fire({
isVisible: this._group.isPanelActive(this),
})
}
})
this.api._onDidChangeFocus.fire({ isFocused: isGroupActive })
this.api._onDidChangeVisibility.fire({
isVisible: this._group.isPanelActive(this),
});
}
});
isVisible: this._group.isPanelActive(this),
})
this.api._onDidChangeFocus.fire({ isFocused: isGroupActive });
this.api._onDidChangeVisibility.fire({
isVisible: this._group.isPanelActive(this),
});
if (this.headerPart.setVisible) {
this.headerPart.setVisible(
this._group.isPanelActive(this),
isGroupActive
);
if (this.headerPart.setVisible) {
this.headerPart.setVisible(
this._group.isPanelActive(this),
isGroupActive
)
}
if (this.contentPart.setVisible) {
this.contentPart.setVisible(
this._group.isPanelActive(this),
isGroupActive
)
}
}
if (this.contentPart.setVisible) {
this.contentPart.setVisible(
this._group.isPanelActive(this),
isGroupActive
);
public layout(width: number, height: number) {
// thw height of the panel excluded the height of the title/tab
this.api._onDidPanelDimensionChange.fire({
width,
height: height - (this.group?.tabHeight || 0),
})
}
}
public layout(width: number, height: number) {
// thw height of the panel excluded the height of the title/tab
this.api._onDidPanelDimensionChange.fire({
width,
height: height - (this.group?.tabHeight || 0),
});
}
public dispose() {
this.api.dispose()
this.mutableDisposable.dispose()
public dispose() {
this.api.dispose();
this.mutableDisposable.dispose();
this.headerPart.dispose();
this.contentPart.dispose();
}
this.headerPart.dispose()
this.contentPart.dispose()
}
}

View File

@ -1,57 +1,57 @@
import { IDisposable } from "../../lifecycle";
import { IGroupview } from "../groupview";
import { IGroupAccessor } from "../../layout";
import { IGroupPanelApi } from "./api";
import { PanelInitParameters } from "./types";
import { Constructor } from "../../types";
import { IDisposable } from '../../lifecycle'
import { IGroupview } from '../groupview'
import { IGroupAccessor } from '../../layout'
import { IGroupPanelApi } from './api'
import { PanelInitParameters } from './types'
import { Constructor } from '../../types'
export enum ClosePanelResult {
CLOSE = "CLOSE",
DONT_CLOSE = "DONT_CLOSE",
CLOSE = 'CLOSE',
DONT_CLOSE = 'DONT_CLOSE',
}
interface BasePart extends IDisposable {
init?(params: PartInitParameters): void;
setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void;
init?(params: PartInitParameters): void
setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void
}
export interface WatermarkPartInitParameters {
accessor: IGroupAccessor;
accessor: IGroupAccessor
}
export interface PartInitParameters extends PanelInitParameters {
api: IGroupPanelApi;
api: IGroupPanelApi
}
export interface PanelHeaderPart extends BasePart {
id: string;
element: HTMLElement;
layout?(height: string): void;
toJSON(): {};
id: string
element: HTMLElement
layout?(height: string): void
toJSON(): {}
}
export interface PanelContentPart extends BasePart {
id: string;
element: HTMLElement;
layout?(width: number, height: number): void;
close?(): Promise<ClosePanelResult>;
focus(): void;
onHide(): void;
update(params: {}): void;
toJSON(): {};
id: string
element: HTMLElement
layout?(width: number, height: number): void
close?(): Promise<ClosePanelResult>
focus(): void
onHide(): void
update(params: {}): void
toJSON(): {}
}
export interface WatermarkPart extends IDisposable {
init?: (params: WatermarkPartInitParameters) => void;
setVisible?(visible: boolean, group: IGroupview): void;
element: HTMLElement;
init?: (params: WatermarkPartInitParameters) => void
setVisible?(visible: boolean, group: IGroupview): void
element: HTMLElement
}
// constructors
export interface PanelHeaderPartConstructor
extends Constructor<PanelHeaderPart> {}
extends Constructor<PanelHeaderPart> {}
export interface PanelContentPartConstructor
extends Constructor<PanelContentPart> {}
extends Constructor<PanelContentPart> {}
export interface WatermarkConstructor extends Constructor<WatermarkPart> {}

View File

@ -1,155 +1,161 @@
import { addDisposableListener, Emitter, Event } from "../../../events";
import { Droptarget, DroptargetEvent } from "../../droptarget/droptarget";
import { CompositeDisposable } from "../../../lifecycle";
import { IGroupview } from "../../groupview";
import { addDisposableListener, Emitter, Event } from '../../../events'
import { Droptarget, DroptargetEvent } from '../../droptarget/droptarget'
import { CompositeDisposable } from '../../../lifecycle'
import { IGroupview } from '../../groupview'
import {
DataTransferSingleton,
DATA_KEY,
DragType,
} from "../../droptarget/dataTransfer";
import { toggleClass } from "../../../dom";
import { IGroupAccessor } from "../../../layout";
import {LayoutMouseEvent, MouseEventKind} from "../../events"
DataTransferSingleton,
DATA_KEY,
DragType,
} from '../../droptarget/dataTransfer'
import { toggleClass } from '../../../dom'
import { IGroupAccessor } from '../../../layout'
import { LayoutMouseEvent, MouseEventKind } from '../../events'
export interface ITab {
id: string;
element: HTMLElement;
hasActiveDragEvent: boolean;
setContent: (element: HTMLElement) => void;
onChanged: Event<LayoutMouseEvent>;
onDropped: Event<DroptargetEvent>;
setActive(isActive: boolean): void;
startDragEvent(): void;
stopDragEvent(): void;
id: string
element: HTMLElement
hasActiveDragEvent: boolean
setContent: (element: HTMLElement) => void
onChanged: Event<LayoutMouseEvent>
onDropped: Event<DroptargetEvent>
setActive(isActive: boolean): void
startDragEvent(): void
stopDragEvent(): void
}
export class Tab extends CompositeDisposable implements ITab {
private _element: HTMLElement;
private dragInPlayDetails: { id?: string; isDragging: boolean } = {
isDragging: false,
};
private droptarget: Droptarget;
private content: HTMLElement;
private readonly _onChanged = new Emitter<LayoutMouseEvent>();
readonly onChanged: Event<LayoutMouseEvent> = this._onChanged.event;
private readonly _onDropped = new Emitter<DroptargetEvent>();
readonly onDropped: Event<DroptargetEvent> = this._onDropped.event;
public get element() {
return this._element;
}
public get hasActiveDragEvent() {
return this.dragInPlayDetails?.isDragging;
}
public startDragEvent() {
this.dragInPlayDetails = { isDragging: true, id: this.accessor.id };
}
public stopDragEvent() {
this.dragInPlayDetails = { isDragging: false, id: undefined };
}
constructor(
public id: string,
private readonly accessor: IGroupAccessor,
private group: IGroupview
) {
super();
this.addDisposables(this._onChanged, this._onDropped);
this._element = document.createElement("div");
this._element.className = "tab";
this._element.draggable = true;
this.addDisposables(
addDisposableListener(this._element, "mousedown", (event) => {
if (event.defaultPrevented) {
return;
}
this._onChanged.fire({ kind: MouseEventKind.CLICK,event });
}),
addDisposableListener(this._element, "contextmenu", (event) => {
this._onChanged.fire({ kind: MouseEventKind.CONTEXT_MENU, event });
}),
addDisposableListener(this._element, "dragstart", (event) => {
this.dragInPlayDetails = { isDragging: true, id: this.accessor.id };
// set up a custom ghost image
const dragImage = this._element.cloneNode(true) as HTMLElement;
const box = this._element.getBoundingClientRect();
// if the style of the tab is determined by CSS by a parent element that style will lost
// therefore we must explicility re-add the style features that we know will be lost
dragImage.style.height = `${box.height}px`;
dragImage.style.width = `${box.width}px`;
dragImage.style.position = "absolute";
dragImage.classList.add("dragging");
document.body.appendChild(dragImage);
event.dataTransfer.setDragImage(
dragImage,
event.offsetX,
event.offsetY
);
setTimeout(() => document.body.removeChild(dragImage), 0);
// configure the data-transfer object
const data = JSON.stringify({
type: DragType.ITEM,
itemId: this.id,
groupId: this.group.id,
});
DataTransferSingleton.setData(this.dragInPlayDetails.id, data);
event.dataTransfer.setData(DATA_KEY, data);
event.dataTransfer.effectAllowed = "move";
}),
addDisposableListener(this._element, "dragend", (ev) => {
// drop events fire before dragend so we can remove this safely
DataTransferSingleton.removeData(this.dragInPlayDetails.id);
this.dragInPlayDetails = {
isDragging: false,
id: undefined,
};
})
);
this.droptarget = new Droptarget(this._element, {
isDirectional: false,
isDisabled: () => this.dragInPlayDetails.isDragging,
id: this.accessor.id,
enableExternalDragEvents: this.accessor.options.enableExternalDragEvents,
});
this.addDisposables(
this.droptarget.onDidChange((event) => {
this._onDropped.fire(event);
})
);
}
public setActive(isActive: boolean) {
toggleClass(this.element, "active-tab", isActive);
toggleClass(this.element, "inactive-tab", !isActive);
}
public setContent(element: HTMLElement) {
if (this.content) {
this._element.removeChild(this.content);
private _element: HTMLElement
private dragInPlayDetails: { id?: string; isDragging: boolean } = {
isDragging: false,
}
this.content = element;
this._element.appendChild(this.content);
}
private droptarget: Droptarget
private content: HTMLElement
public dispose() {
super.dispose();
this.droptarget.dispose();
}
private readonly _onChanged = new Emitter<LayoutMouseEvent>()
readonly onChanged: Event<LayoutMouseEvent> = this._onChanged.event
private readonly _onDropped = new Emitter<DroptargetEvent>()
readonly onDropped: Event<DroptargetEvent> = this._onDropped.event
public get element() {
return this._element
}
public get hasActiveDragEvent() {
return this.dragInPlayDetails?.isDragging
}
public startDragEvent() {
this.dragInPlayDetails = { isDragging: true, id: this.accessor.id }
}
public stopDragEvent() {
this.dragInPlayDetails = { isDragging: false, id: undefined }
}
constructor(
public id: string,
private readonly accessor: IGroupAccessor,
private group: IGroupview
) {
super()
this.addDisposables(this._onChanged, this._onDropped)
this._element = document.createElement('div')
this._element.className = 'tab'
this._element.draggable = true
this.addDisposables(
addDisposableListener(this._element, 'mousedown', (event) => {
if (event.defaultPrevented) {
return
}
this._onChanged.fire({ kind: MouseEventKind.CLICK, event })
}),
addDisposableListener(this._element, 'contextmenu', (event) => {
this._onChanged.fire({
kind: MouseEventKind.CONTEXT_MENU,
event,
})
}),
addDisposableListener(this._element, 'dragstart', (event) => {
this.dragInPlayDetails = {
isDragging: true,
id: this.accessor.id,
}
// set up a custom ghost image
const dragImage = this._element.cloneNode(true) as HTMLElement
const box = this._element.getBoundingClientRect()
// if the style of the tab is determined by CSS by a parent element that style will lost
// therefore we must explicility re-add the style features that we know will be lost
dragImage.style.height = `${box.height}px`
dragImage.style.width = `${box.width}px`
dragImage.style.position = 'absolute'
dragImage.classList.add('dragging')
document.body.appendChild(dragImage)
event.dataTransfer.setDragImage(
dragImage,
event.offsetX,
event.offsetY
)
setTimeout(() => document.body.removeChild(dragImage), 0)
// configure the data-transfer object
const data = JSON.stringify({
type: DragType.ITEM,
itemId: this.id,
groupId: this.group.id,
})
DataTransferSingleton.setData(this.dragInPlayDetails.id, data)
event.dataTransfer.setData(DATA_KEY, data)
event.dataTransfer.effectAllowed = 'move'
}),
addDisposableListener(this._element, 'dragend', (ev) => {
// drop events fire before dragend so we can remove this safely
DataTransferSingleton.removeData(this.dragInPlayDetails.id)
this.dragInPlayDetails = {
isDragging: false,
id: undefined,
}
})
)
this.droptarget = new Droptarget(this._element, {
isDirectional: false,
isDisabled: () => this.dragInPlayDetails.isDragging,
id: this.accessor.id,
enableExternalDragEvents: this.accessor.options
.enableExternalDragEvents,
})
this.addDisposables(
this.droptarget.onDidChange((event) => {
this._onDropped.fire(event)
})
)
}
public setActive(isActive: boolean) {
toggleClass(this.element, 'active-tab', isActive)
toggleClass(this.element, 'inactive-tab', !isActive)
}
public setContent(element: HTMLElement) {
if (this.content) {
this._element.removeChild(this.content)
}
this.content = element
this._element.appendChild(this.content)
}
public dispose() {
super.dispose()
this.droptarget.dispose()
}
}

View File

@ -1,14 +1,14 @@
import { IGroupview } from "../groupview";
import { IDisposable, ISerializable } from "../../lifecycle";
import { Event } from "../../events";
import { PanelHeaderPart, PanelContentPart, ClosePanelResult } from "./parts";
import { InitParameters, IPanel } from "../../panel/types";
import { IGroupview } from '../groupview'
import { IDisposable, ISerializable } from '../../lifecycle'
import { Event } from '../../events'
import { PanelHeaderPart, PanelContentPart, ClosePanelResult } from './parts'
import { InitParameters, IPanel } from '../../panel/types'
// init parameters
export interface PanelInitParameters extends InitParameters {
title: string;
suppressClosable?: boolean;
title: string
suppressClosable?: boolean
}
// constructors
@ -16,15 +16,15 @@ export interface PanelInitParameters extends InitParameters {
// panel
export interface IGroupPanel extends IDisposable, ISerializable, IPanel {
id: string;
header: PanelHeaderPart;
content: PanelContentPart;
group: IGroupview;
focus(): void;
onHide(): void;
setVisible(isGroupActive: boolean, group: IGroupview): void;
setDirty(isDirty: boolean): void;
close?(): Promise<ClosePanelResult>;
init?(params: PanelInitParameters & { [index: string]: string }): void;
onDidStateChange: Event<any>;
id: string
header: PanelHeaderPart
content: PanelContentPart
group: IGroupview
focus(): void
onHide(): void
setVisible(isGroupActive: boolean, group: IGroupview): void
setDirty(isDirty: boolean): void
close?(): Promise<ClosePanelResult>
init?(params: PanelInitParameters & { [index: string]: string }): void
onDidStateChange: Event<any>
}

View File

@ -1,57 +1,57 @@
.title-container {
display: flex;
background-color: var(--title-bar-background-color);
overflow: overlay;
flex-shrink: 0;
box-sizing: border-box;
&.hidden {
display: none;
}
&::-webkit-scrollbar {
height: 3px;
}
/* Track */
&::-webkit-scrollbar-track {
background: transparent;
}
/* Handle */
&::-webkit-scrollbar-thumb {
background: var(--title-bar-scroll-bar-color);
}
.tab-container {
flex-shrink: 0;
flex-grow: 1;
display: flex;
font-size: 13px;
overflow-x: overlay;
overflow-y: hidden;
background-color: var(--title-bar-background-color);
overflow: overlay;
flex-shrink: 0;
box-sizing: border-box;
&.drag-over-target {
background-color: var(--drag-over-background-color);
&.hidden {
display: none;
}
.tab {
min-width: 75px;
cursor: pointer;
position: relative;
box-sizing: border-box;
&:not(:first-child)::before {
content: " ";
position: absolute;
top: 0;
left: 0;
z-index: 5;
pointer-events: none;
background-color: var(--tab-divider-color);
width: 1px;
height: 100%;
}
&::-webkit-scrollbar {
height: 3px;
}
/* Track */
&::-webkit-scrollbar-track {
background: transparent;
}
/* Handle */
&::-webkit-scrollbar-thumb {
background: var(--title-bar-scroll-bar-color);
}
.tab-container {
flex-shrink: 0;
flex-grow: 1;
display: flex;
font-size: 13px;
overflow-x: overlay;
overflow-y: hidden;
&.drag-over-target {
background-color: var(--drag-over-background-color);
}
.tab {
min-width: 75px;
cursor: pointer;
position: relative;
box-sizing: border-box;
&:not(:first-child)::before {
content: ' ';
position: absolute;
top: 0;
left: 0;
z-index: 5;
pointer-events: none;
background-color: var(--tab-divider-color);
width: 1px;
height: 100%;
}
}
}
}
}

View File

@ -1,243 +1,252 @@
import {
IDisposable,
CompositeDisposable,
IValueDisposable,
} from "../../lifecycle";
import { addDisposableListener, Emitter, Event } from "../../events";
import { ITab, Tab } from "../panel/tab/tab";
import { removeClasses, addClasses, toggleClass } from "../../dom";
import { hasProcessed, Position } from "../droptarget/droptarget";
import { TabDropEvent } from "../events";
IDisposable,
CompositeDisposable,
IValueDisposable,
} from '../../lifecycle'
import { addDisposableListener, Emitter, Event } from '../../events'
import { ITab, Tab } from '../panel/tab/tab'
import { removeClasses, addClasses, toggleClass } from '../../dom'
import { hasProcessed, Position } from '../droptarget/droptarget'
import { TabDropEvent } from '../events'
import { IGroupview } from "../groupview";
import { IGroupAccessor } from "../../layout";
import { last } from "../../array";
import { DataTransferSingleton } from "../droptarget/dataTransfer";
import { IGroupPanel } from "../panel/types";
import {MouseEventKind} from "../events"
import { IGroupview } from '../groupview'
import { IGroupAccessor } from '../../layout'
import { last } from '../../array'
import { DataTransferSingleton } from '../droptarget/dataTransfer'
import { IGroupPanel } from '../panel/types'
import { MouseEventKind } from '../events'
export interface ITabContainer extends IDisposable {
element: HTMLElement;
visible: boolean;
height: number;
hasActiveDragEvent: boolean;
delete: (id: string) => void;
indexOf: (tabOrId: ITab | string) => number;
at: (index: number) => ITab;
onDropEvent: Event<TabDropEvent>;
setActive: (isGroupActive: boolean) => void;
setActivePanel: (panel: IGroupPanel) => void;
isActive: (tab: ITab) => boolean;
closePanel: (panel: IGroupPanel) => void;
openPanel: (panel: IGroupPanel, index?: number) => void;
element: HTMLElement
visible: boolean
height: number
hasActiveDragEvent: boolean
delete: (id: string) => void
indexOf: (tabOrId: ITab | string) => number
at: (index: number) => ITab
onDropEvent: Event<TabDropEvent>
setActive: (isGroupActive: boolean) => void
setActivePanel: (panel: IGroupPanel) => void
isActive: (tab: ITab) => boolean
closePanel: (panel: IGroupPanel) => void
openPanel: (panel: IGroupPanel, index?: number) => void
}
export class TabContainer extends CompositeDisposable implements ITabContainer {
private tabContainer: HTMLElement;
private _element: HTMLElement;
private actionContainer: HTMLElement;
private tabContainer: HTMLElement
private _element: HTMLElement
private actionContainer: HTMLElement
private tabs: IValueDisposable<ITab>[] = [];
private selectedIndex: number = -1;
private active: boolean;
private activePanel: IGroupPanel;
private tabs: IValueDisposable<ITab>[] = []
private selectedIndex: number = -1
private active: boolean
private activePanel: IGroupPanel
private _visible: boolean = true;
private _height: number;
private _visible: boolean = true
private _height: number
private readonly _onDropped = new Emitter<TabDropEvent>();
readonly onDropEvent: Event<TabDropEvent> = this._onDropped.event;
private readonly _onDropped = new Emitter<TabDropEvent>()
readonly onDropEvent: Event<TabDropEvent> = this._onDropped.event
get visible() {
return this._visible;
}
set visible(value: boolean) {
this._visible = value;
toggleClass(this.element, "hidden", !this._visible);
}
get height() {
return this._height;
}
set height(value: number) {
this._height = value;
this._element.style.height = `${this.height}px`;
}
public get element() {
return this._element;
}
public isActive(tab: ITab) {
return (
this.selectedIndex > -1 && this.tabs[this.selectedIndex].value === tab
);
}
public get hasActiveDragEvent() {
return !!this.tabs.find((tab) => tab.value.hasActiveDragEvent);
}
public at(index: number) {
return this.tabs[index]?.value;
}
public indexOf(tabOrId: ITab) {
const id = typeof tabOrId === "string" ? tabOrId : tabOrId.id;
return this.tabs.findIndex((tab) => tab.value.id === id);
}
constructor(private accessor: IGroupAccessor, private group: IGroupview) {
super();
this.addDisposables(this._onDropped);
this._element = document.createElement("div");
this._element.className = "title-container";
this.height = 35;
this.actionContainer = document.createElement("div");
this.actionContainer.className = "action-container";
const list = document.createElement("ul");
list.className = "action-list";
this.tabContainer = document.createElement("div");
this.tabContainer.className = "tab-container";
this._element.appendChild(this.tabContainer);
this._element.appendChild(this.actionContainer);
this.addDisposables(
addDisposableListener(this.tabContainer, "dragenter", (event) => {
if (!DataTransferSingleton.has(this.accessor.id)) {
console.debug("[tabs] invalid drop event");
return;
}
if (!last(this.tabs).value.hasActiveDragEvent) {
addClasses(this.tabContainer, "drag-over-target");
}
}),
addDisposableListener(this.tabContainer, "dragover", (event) => {
event.preventDefault();
}),
addDisposableListener(this.tabContainer, "dragleave", (event) => {
removeClasses(this.tabContainer, "drag-over-target");
}),
addDisposableListener(this.tabContainer, "drop", (event) => {
if (!DataTransferSingleton.has(this.accessor.id)) {
console.debug("[tabs] invalid drop event");
return;
}
if (hasProcessed(event)) {
console.debug("[tab] drop event already processed");
return;
}
removeClasses(this.tabContainer, "drag-over-target");
const activetab = this.tabs.find((tab) => tab.value.hasActiveDragEvent);
const ignore = !!(
activetab &&
event.composedPath().find((x) => activetab.value.element === x)
);
if (ignore) {
console.debug("[tabs] ignore event");
return;
}
this._onDropped.fire({
event: { event, position: Position.Center },
index: this.tabs.length - 1,
});
})
);
}
public setActive(isGroupActive: boolean) {
this.active = isGroupActive;
}
private addTab(
tab: IValueDisposable<ITab>,
index: number = this.tabs.length
) {
if (index < 0 || index > this.tabs.length) {
throw new Error("invalid location");
get visible() {
return this._visible
}
this.tabContainer.insertBefore(
tab.value.element,
this.tabContainer.children[index]
);
set visible(value: boolean) {
this._visible = value
this.tabs = [...this.tabs.slice(0, index), tab, ...this.tabs.slice(index)];
if (this.selectedIndex < 0) {
this.selectedIndex = index;
toggleClass(this.element, 'hidden', !this._visible)
}
}
public delete(id: string) {
const index = this.tabs.findIndex((tab) => tab.value.id === id);
const tab = this.tabs.splice(index, 1)[0];
const { value, disposable } = tab;
disposable.dispose();
value.element.remove();
}
public setActivePanel(panel: IGroupPanel) {
this.tabs.forEach((tab) => {
const isActivePanel = panel.id === tab.value.id;
tab.value.setActive(isActivePanel);
});
}
public openPanel(panel: IGroupPanel, index: number = this.tabs.length) {
if (this.tabs.find((tab) => tab.value.id === panel.id)) {
return;
get height() {
return this._height
}
const tab = new Tab(panel.id, this.accessor, this.group);
tab.setContent(panel.header.element);
const disposable = CompositeDisposable.from(
tab.onChanged((event) => {
switch (event.kind) {
case MouseEventKind.CLICK:
this.group.openPanel(panel);
break;
set height(value: number) {
this._height = value
this._element.style.height = `${this.height}px`
}
public get element() {
return this._element
}
public isActive(tab: ITab) {
return (
this.selectedIndex > -1 &&
this.tabs[this.selectedIndex].value === tab
)
}
public get hasActiveDragEvent() {
return !!this.tabs.find((tab) => tab.value.hasActiveDragEvent)
}
public at(index: number) {
return this.tabs[index]?.value
}
public indexOf(tabOrId: ITab) {
const id = typeof tabOrId === 'string' ? tabOrId : tabOrId.id
return this.tabs.findIndex((tab) => tab.value.id === id)
}
constructor(private accessor: IGroupAccessor, private group: IGroupview) {
super()
this.addDisposables(this._onDropped)
this._element = document.createElement('div')
this._element.className = 'title-container'
this.height = 35
this.actionContainer = document.createElement('div')
this.actionContainer.className = 'action-container'
const list = document.createElement('ul')
list.className = 'action-list'
this.tabContainer = document.createElement('div')
this.tabContainer.className = 'tab-container'
this._element.appendChild(this.tabContainer)
this._element.appendChild(this.actionContainer)
this.addDisposables(
addDisposableListener(this.tabContainer, 'dragenter', (event) => {
if (!DataTransferSingleton.has(this.accessor.id)) {
console.debug('[tabs] invalid drop event')
return
}
if (!last(this.tabs).value.hasActiveDragEvent) {
addClasses(this.tabContainer, 'drag-over-target')
}
}),
addDisposableListener(this.tabContainer, 'dragover', (event) => {
event.preventDefault()
}),
addDisposableListener(this.tabContainer, 'dragleave', (event) => {
removeClasses(this.tabContainer, 'drag-over-target')
}),
addDisposableListener(this.tabContainer, 'drop', (event) => {
if (!DataTransferSingleton.has(this.accessor.id)) {
console.debug('[tabs] invalid drop event')
return
}
if (hasProcessed(event)) {
console.debug('[tab] drop event already processed')
return
}
removeClasses(this.tabContainer, 'drag-over-target')
const activetab = this.tabs.find(
(tab) => tab.value.hasActiveDragEvent
)
const ignore = !!(
activetab &&
event
.composedPath()
.find((x) => activetab.value.element === x)
)
if (ignore) {
console.debug('[tabs] ignore event')
return
}
this._onDropped.fire({
event: { event, position: Position.Center },
index: this.tabs.length - 1,
})
})
)
}
public setActive(isGroupActive: boolean) {
this.active = isGroupActive
}
private addTab(
tab: IValueDisposable<ITab>,
index: number = this.tabs.length
) {
if (index < 0 || index > this.tabs.length) {
throw new Error('invalid location')
}
this.accessor.fireMouseEvent({...event, panel, tab:true});
}),
tab.onDropped((event) => {
this._onDropped.fire({ event, index: this.indexOf(tab) });
})
);
const value: IValueDisposable<ITab> = { value: tab, disposable };
this.tabContainer.insertBefore(
tab.value.element,
this.tabContainer.children[index]
)
this.addTab(value, index);
this.activePanel = panel;
}
this.tabs = [
...this.tabs.slice(0, index),
tab,
...this.tabs.slice(index),
]
public closePanel(panel: IGroupPanel) {
this.delete(panel.id);
}
if (this.selectedIndex < 0) {
this.selectedIndex = index
}
}
public dispose() {
super.dispose();
public delete(id: string) {
const index = this.tabs.findIndex((tab) => tab.value.id === id)
this.tabs.forEach((tab) => {
tab.disposable.dispose();
});
this.tabs = [];
}
const tab = this.tabs.splice(index, 1)[0]
const { value, disposable } = tab
disposable.dispose()
value.element.remove()
}
public setActivePanel(panel: IGroupPanel) {
this.tabs.forEach((tab) => {
const isActivePanel = panel.id === tab.value.id
tab.value.setActive(isActivePanel)
})
}
public openPanel(panel: IGroupPanel, index: number = this.tabs.length) {
if (this.tabs.find((tab) => tab.value.id === panel.id)) {
return
}
const tab = new Tab(panel.id, this.accessor, this.group)
tab.setContent(panel.header.element)
const disposable = CompositeDisposable.from(
tab.onChanged((event) => {
switch (event.kind) {
case MouseEventKind.CLICK:
this.group.openPanel(panel)
break
}
this.accessor.fireMouseEvent({ ...event, panel, tab: true })
}),
tab.onDropped((event) => {
this._onDropped.fire({ event, index: this.indexOf(tab) })
})
)
const value: IValueDisposable<ITab> = { value: tab, disposable }
this.addTab(value, index)
this.activePanel = panel
}
public closePanel(panel: IGroupPanel) {
this.delete(panel.id)
}
public dispose() {
super.dispose()
this.tabs.forEach((tab) => {
tab.disposable.dispose()
})
this.tabs = []
}
}

View File

@ -1,21 +1,21 @@
export * from "./splitview/splitview";
export * from "./paneview/paneview";
export * from "./gridview/gridview";
export * from "./groupview/groupview";
export * from "./groupview/panel/content/content";
export * from "./groupview/panel/tab/tab";
export * from "./events";
export * from "./lifecycle";
export * from "./groupview/panel/panel";
export * from "./groupview/panel/api";
export * from "./react/react";
export * from "./groupview/panel/types";
export * from "./groupview/panel/parts";
export * from "./react/layout";
export * from "./react/splitview";
export * from "./react/gridview";
export * from "./react/reactContentPart";
export * from "./react/reactHeaderPart";
export * from "./react/reactComponentGridView";
export * from './splitview/splitview'
export * from './paneview/paneview'
export * from './gridview/gridview'
export * from './groupview/groupview'
export * from './groupview/panel/content/content'
export * from './groupview/panel/tab/tab'
export * from './events'
export * from './lifecycle'
export * from './groupview/panel/panel'
export * from './groupview/panel/api'
export * from './react/react'
export * from './groupview/panel/types'
export * from './groupview/panel/parts'
export * from './react/layout'
export * from './react/splitview'
export * from './react/gridview'
export * from './react/reactContentPart'
export * from './react/reactHeaderPart'
export * from './react/reactComponentGridView'
export * from "./layout";
export * from './layout'

View File

@ -1,89 +1,89 @@
import {
PanelContentPart,
PanelContentPartConstructor,
PanelHeaderPart,
PanelHeaderPartConstructor,
} from "../groupview/panel/parts";
import { FrameworkFactory } from "../types";
import { DefaultTab } from "./components/tab/defaultTab";
PanelContentPart,
PanelContentPartConstructor,
PanelHeaderPart,
PanelHeaderPartConstructor,
} from '../groupview/panel/parts'
import { FrameworkFactory } from '../types'
import { DefaultTab } from './components/tab/defaultTab'
export function createContentComponent(
componentName: string | PanelContentPartConstructor | any,
components: {
[componentName: string]: PanelContentPartConstructor;
},
frameworkComponents: {
[componentName: string]: any;
},
createFrameworkComponent: FrameworkFactory<PanelContentPart>
componentName: string | PanelContentPartConstructor | any,
components: {
[componentName: string]: PanelContentPartConstructor
},
frameworkComponents: {
[componentName: string]: any
},
createFrameworkComponent: FrameworkFactory<PanelContentPart>
): PanelContentPart {
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 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`
)
}
const wrappedComponent = createFrameworkComponent.createComponent(
componentName,
FrameworkComponent
);
return wrappedComponent;
}
return new Component() as PanelContentPart;
if (FrameworkComponent) {
if (!createFrameworkComponent) {
throw new Error(
'you must register a frameworkPanelWrapper to use framework components'
)
}
const wrappedComponent = createFrameworkComponent.createComponent(
componentName,
FrameworkComponent
)
return wrappedComponent
}
return new Component() as PanelContentPart
}
export function createTabComponent(
componentName: string | PanelHeaderPartConstructor | any,
components: {
[componentName: string]: PanelHeaderPartConstructor;
},
frameworkComponents: {
[componentName: string]: any;
},
createFrameworkComponent: FrameworkFactory<PanelHeaderPart>
componentName: string | PanelHeaderPartConstructor | any,
components: {
[componentName: string]: PanelHeaderPartConstructor
},
frameworkComponents: {
[componentName: string]: any
},
createFrameworkComponent: FrameworkFactory<PanelHeaderPart>
): PanelHeaderPart {
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 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.createComponent(
componentName,
FrameworkComponent
)
return wrappedComponent
}
const wrappedComponent = createFrameworkComponent.createComponent(
componentName,
FrameworkComponent
);
return wrappedComponent;
}
if (!Component) {
return new DefaultTab();
}
if (!Component) {
return new DefaultTab()
}
return new Component() as PanelHeaderPart;
return new Component() as PanelHeaderPart
}

View File

@ -1,408 +1,410 @@
import { Gridview, getRelativeLocation, IGridView } from "../gridview/gridview";
import { Position } from "../groupview/droptarget/droptarget";
import { getGridLocation } from "../gridview/gridview";
import { tail, sequenceEquals } from "../array";
import { Gridview, getRelativeLocation, IGridView } from '../gridview/gridview'
import { Position } from '../groupview/droptarget/droptarget'
import { getGridLocation } from '../gridview/gridview'
import { tail, sequenceEquals } from '../array'
import {
GroupChangeKind,
GroupChangeEvent,
GroupDropEvent,
} from "../groupview/groupview";
import {
CompositeDisposable,
Disposable,
IValueDisposable,
} from "../lifecycle";
import { Event, Emitter } from "../events";
GroupChangeKind,
GroupChangeEvent,
GroupDropEvent,
} from '../groupview/groupview'
import { CompositeDisposable, Disposable, IValueDisposable } from '../lifecycle'
import { Event, Emitter } from '../events'
import { DebugWidget } from "./components/debug/debug";
import { DebugWidget } from './components/debug/debug'
import { sequentialNumberGenerator } from "../math";
import { DefaultDeserializer, IPanelDeserializer } from "./deserializer";
import { MovementOptions } from "./options";
import { sequentialNumberGenerator } from '../math'
import { IPanelDeserializer } from './deserializer'
import { createComponent } from "../splitview/options";
import { LayoutPriority, Orientation } from "../splitview/splitview";
import { createComponent } from '../splitview/options'
import { LayoutPriority, Orientation } from '../splitview/splitview'
const nextGroupId = sequentialNumberGenerator();
const nextLayoutId = sequentialNumberGenerator();
const nextLayoutId = sequentialNumberGenerator()
export interface AddComponentOptions {
component: string;
params?: { [key: string]: any };
id: string;
position?: {
direction?: "left" | "right" | "above" | "below" | "within";
reference: string;
};
size?: number;
priority?: LayoutPriority;
snap?: boolean;
component: string
params?: { [key: string]: any }
id: string
position?: {
direction?: 'left' | 'right' | 'above' | 'below' | 'within'
reference: string
}
size?: number
priority?: LayoutPriority
snap?: boolean
}
export interface GridComponentOptions {
orientation: Orientation;
components?: {
[componentName: string]: IComponentGridview;
};
frameworkComponents?: {
[componentName: string]: any;
};
frameworkComponentFactory: any;
tabHeight?: number;
orientation: Orientation
components?: {
[componentName: string]: IComponentGridview
}
frameworkComponents?: {
[componentName: string]: any
}
frameworkComponentFactory: any
tabHeight?: number
}
export interface IComponentGridview extends IGridView {
id: string;
init: (params: { params: any }) => void;
priority?: LayoutPriority;
id: string
init: (params: { params: any }) => void
priority?: LayoutPriority
}
export interface MovementOptions2 {
group?: IComponentGridview;
group?: IComponentGridview
}
export interface IComponentGridviewLayout {
addComponent(options: AddComponentOptions): void;
addComponent(options: AddComponentOptions): void
}
export class ComponentGridview
extends CompositeDisposable
implements IComponentGridviewLayout {
private readonly _id = nextLayoutId.next();
private readonly groups = new Map<
string,
IValueDisposable<IComponentGridview>
>();
private readonly gridview: Gridview = new Gridview(false);
// events
private readonly _onDidLayoutChange = new Emitter<GroupChangeEvent>();
readonly onDidLayoutChange: Event<GroupChangeEvent> = this._onDidLayoutChange
.event;
// everything else
private _size: number;
private _orthogonalSize: number;
private _activeGroup: IComponentGridview;
private _deserializer: IPanelDeserializer;
private resizeTimer: NodeJS.Timer;
private debugContainer: DebugWidget;
extends CompositeDisposable
implements IComponentGridviewLayout {
private readonly _id = nextLayoutId.next()
private readonly groups = new Map<
string,
IValueDisposable<IComponentGridview>
>()
private readonly gridview: Gridview = new Gridview(false)
// events
private readonly _onDidLayoutChange = new Emitter<GroupChangeEvent>()
readonly onDidLayoutChange: Event<GroupChangeEvent> = this
._onDidLayoutChange.event
// everything else
private _size: number
private _orthogonalSize: number
private _activeGroup: IComponentGridview
private _deserializer: IPanelDeserializer
private resizeTimer: NodeJS.Timer
private debugContainer: DebugWidget
constructor(
private readonly element: HTMLElement,
public readonly options: GridComponentOptions
) {
super();
constructor(
private readonly element: HTMLElement,
public readonly options: GridComponentOptions
) {
super()
this.element.appendChild(this.gridview.element);
this.element.appendChild(this.gridview.element)
if (!this.options.components) {
this.options.components = {};
}
if (!this.options.frameworkComponents) {
this.options.frameworkComponents = {};
if (!this.options.components) {
this.options.components = {}
}
if (!this.options.frameworkComponents) {
this.options.frameworkComponents = {}
}
this.addDisposables(
this.gridview.onDidChange((e) => {
this._onDidLayoutChange.fire({ kind: GroupChangeKind.LAYOUT })
})
)
}
this.addDisposables(
this.gridview.onDidChange((e) => {
this._onDidLayoutChange.fire({ kind: GroupChangeKind.LAYOUT });
})
);
}
get minimumHeight() {
return this.gridview.minimumHeight;
}
get maximumHeight() {
return this.gridview.maximumHeight;
}
get minimumWidth() {
return this.gridview.maximumWidth;
}
get maximumWidth() {
return this.gridview.maximumWidth;
}
get activeGroup() {
return this._activeGroup;
}
get deserializer() {
return this._deserializer;
}
set deserializer(value: IPanelDeserializer) {
this._deserializer = value;
}
get id() {
return this._id;
}
get size() {
return this.groups.size;
}
public moveToNext(options?: MovementOptions2) {
if (!options) {
options = {};
get minimumHeight() {
return this.gridview.minimumHeight
}
if (!options.group) {
options.group = this.activeGroup;
get maximumHeight() {
return this.gridview.maximumHeight
}
get minimumWidth() {
return this.gridview.maximumWidth
}
get maximumWidth() {
return this.gridview.maximumWidth
}
const location = getGridLocation(options.group.element);
const next = this.gridview.next(location)?.view as IComponentGridview;
this.doSetGroupActive(next);
}
public moveToPrevious(options?: MovementOptions2) {
if (!options) {
options = {};
}
if (!options.group) {
options.group = this.activeGroup;
get activeGroup() {
return this._activeGroup
}
const location = getGridLocation(options.group.element);
const next = this.gridview.preivous(location)?.view as IComponentGridview;
this.doSetGroupActive(next);
}
/**
* Serialize the current state of the layout
*
* @returns A JSON respresentation of the layout
*/
public toJSON() {
const data = this.gridview.serialize();
return { grid: data };
}
public deserialize(data: any) {
this.gridview.clear();
this.groups.clear();
this.fromJSON(data, this.deserializer);
this.gridview.layout(this._size, this._orthogonalSize);
}
public fromJSON(data: any, deserializer: IPanelDeserializer) {
const { grid, panels } = data;
// this.gridview.deserialize(
// grid,
// new DefaultDeserializer(this, {
// createPanel: (id) => {
// const panelData = panels[id];
// const panel = deserializer.fromJSON(panelData);
// this.registerPanel(panel);
// return panel;
// },
// })
// );
this._onDidLayoutChange.fire({ kind: GroupChangeKind.NEW_LAYOUT });
}
public setAutoResizeToFit(enabled: boolean) {
if (this.resizeTimer) {
clearInterval(this.resizeTimer);
}
if (enabled) {
this.resizeTimer = setInterval(() => {
this.resizeToFit();
}, 500);
}
}
/**
* Resize the layout to fit the parent container
*/
public resizeToFit() {
const {
width,
height,
} = this.element.parentElement.getBoundingClientRect();
this.layout(width, height);
}
public addComponent(options: AddComponentOptions) {
let relativeLocation: number[] = [0];
if (options.position?.reference) {
const referenceGroup = this.groups.get(options.position.reference).value;
const target = this.toTarget(options.position.direction);
if (target === Position.Center) {
throw new Error(`${target} not supported as an option`);
} else {
const location = getGridLocation(referenceGroup.element);
relativeLocation = getRelativeLocation(
this.gridview.orientation,
location,
target
);
}
get deserializer() {
return this._deserializer
}
const view = createComponent(
options.component,
this.options.components,
this.options.frameworkComponents,
this.options.frameworkComponentFactory.createComponent
);
view.init({ params: {} });
view.priority = options.priority;
view.snap = options.snap;
this.groups.set(options.id, { value: view, disposable: Disposable.NONE });
this.doAddGroup(view, relativeLocation, options.size);
}
public getGroup(id: string) {
return this.groups.get(id)?.value;
}
public removeGroup(group: IComponentGridview) {
if (group === this._activeGroup) {
this._activeGroup = undefined;
}
this.doRemoveGroup(group);
}
private doAddGroup(
group: IComponentGridview,
location: number[],
size?: number
) {
this.gridview.addView(group, size ?? { type: "distribute" }, location);
this._onDidLayoutChange.fire({ kind: GroupChangeKind.ADD_GROUP });
this.doSetGroupActive(group);
}
private doRemoveGroup(
group: IComponentGridview,
options?: { skipActive?: boolean; skipDispose?: boolean }
) {
if (!this.groups.has(group.id)) {
throw new Error("invalid operation");
set deserializer(value: IPanelDeserializer) {
this._deserializer = value
}
const { disposable } = this.groups.get(group.id);
if (!options?.skipDispose) {
disposable.dispose();
this.groups.delete(group.id);
get id() {
return this._id
}
const view = this.gridview.remove(group, { type: "distribute" });
this._onDidLayoutChange.fire({ kind: GroupChangeKind.REMOVE_GROUP });
if (!options?.skipActive && this.groups.size > 0) {
this.doSetGroupActive(Array.from(this.groups.values())[0].value);
get size() {
return this.groups.size
}
return view;
}
public moveToNext(options?: MovementOptions2) {
if (!options) {
options = {}
}
if (!options.group) {
options.group = this.activeGroup
}
public doSetGroupActive(group: IComponentGridview) {
if (this._activeGroup && this._activeGroup !== group) {
// this._activeGroup.setActive(false);
}
// group.setActive(true);
this._activeGroup = group;
}
public moveGroup(
referenceGroup: IComponentGridview,
groupId: string,
itemId: string,
target: Position
) {
const sourceGroup = groupId ? this.groups.get(groupId).value : undefined;
const referenceLocation = getGridLocation(referenceGroup.element);
const targetLocation = getRelativeLocation(
this.gridview.orientation,
referenceLocation,
target
);
const [targetParentLocation, to] = tail(targetLocation);
const sourceLocation = getGridLocation(sourceGroup.element);
const [sourceParentLocation, from] = tail(sourceLocation);
if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
// special case when 'swapping' two views within same grid location
// if a group has one tab - we are essentially moving the 'group'
// which is equivalent to swapping two views in this case
this.gridview.moveView(sourceParentLocation, from, to);
return;
const location = getGridLocation(options.group.element)
const next = this.gridview.next(location)?.view as IComponentGridview
this.doSetGroupActive(next)
}
// source group will become empty so delete the group
const targetGroup = this.doRemoveGroup(sourceGroup, {
skipActive: true,
skipDispose: true,
}) as IComponentGridview;
public moveToPrevious(options?: MovementOptions2) {
if (!options) {
options = {}
}
if (!options.group) {
options.group = this.activeGroup
}
// after deleting the group we need to re-evaulate the ref location
const updatedReferenceLocation = getGridLocation(referenceGroup.element);
const location = getRelativeLocation(
this.gridview.orientation,
updatedReferenceLocation,
target
);
this.doAddGroup(targetGroup, location);
}
public layout(size: number, orthogonalSize: number, force?: boolean) {
const different =
force || size !== this._size || orthogonalSize !== this._orthogonalSize;
if (!different) {
return;
const location = getGridLocation(options.group.element)
const next = this.gridview.preivous(location)
?.view as IComponentGridview
this.doSetGroupActive(next)
}
this.element.style.height = `${orthogonalSize}px`;
this.element.style.width = `${size}px`;
/**
* Serialize the current state of the layout
*
* @returns A JSON respresentation of the layout
*/
public toJSON() {
const data = this.gridview.serialize()
this._size = size;
this._orthogonalSize = orthogonalSize;
this.gridview.layout(size, orthogonalSize);
}
private toTarget(direction: "left" | "right" | "above" | "below" | "within") {
switch (direction) {
case "left":
return Position.Left;
case "right":
return Position.Right;
case "above":
return Position.Top;
case "below":
return Position.Bottom;
case "within":
default:
return Position.Center;
}
}
public dispose() {
super.dispose();
this.gridview.dispose();
this.debugContainer?.dispose();
if (this.resizeTimer) {
clearInterval(this.resizeTimer);
this.resizeTimer = undefined;
return { grid: data }
}
this._onDidLayoutChange.dispose();
}
public deserialize(data: any) {
this.gridview.clear()
this.groups.clear()
this.fromJSON(data, this.deserializer)
this.gridview.layout(this._size, this._orthogonalSize)
}
public fromJSON(data: any, deserializer: IPanelDeserializer) {
const { grid, panels } = data
// this.gridview.deserialize(
// grid,
// new DefaultDeserializer(this, {
// createPanel: (id) => {
// const panelData = panels[id];
// const panel = deserializer.fromJSON(panelData);
// this.registerPanel(panel);
// return panel;
// },
// })
// );
this._onDidLayoutChange.fire({ kind: GroupChangeKind.NEW_LAYOUT })
}
public setAutoResizeToFit(enabled: boolean) {
if (this.resizeTimer) {
clearInterval(this.resizeTimer)
}
if (enabled) {
this.resizeTimer = setInterval(() => {
this.resizeToFit()
}, 500)
}
}
/**
* Resize the layout to fit the parent container
*/
public resizeToFit() {
const {
width,
height,
} = this.element.parentElement.getBoundingClientRect()
this.layout(width, height)
}
public addComponent(options: AddComponentOptions) {
let relativeLocation: number[] = [0]
if (options.position?.reference) {
const referenceGroup = this.groups.get(options.position.reference)
.value
const target = this.toTarget(options.position.direction)
if (target === Position.Center) {
throw new Error(`${target} not supported as an option`)
} else {
const location = getGridLocation(referenceGroup.element)
relativeLocation = getRelativeLocation(
this.gridview.orientation,
location,
target
)
}
}
const view = createComponent(
options.component,
this.options.components,
this.options.frameworkComponents,
this.options.frameworkComponentFactory.createComponent
)
view.init({ params: {} })
view.priority = options.priority
view.snap = options.snap
this.groups.set(options.id, {
value: view,
disposable: Disposable.NONE,
})
this.doAddGroup(view, relativeLocation, options.size)
}
public getGroup(id: string) {
return this.groups.get(id)?.value
}
public removeGroup(group: IComponentGridview) {
if (group === this._activeGroup) {
this._activeGroup = undefined
}
this.doRemoveGroup(group)
}
private doAddGroup(
group: IComponentGridview,
location: number[],
size?: number
) {
this.gridview.addView(group, size ?? { type: 'distribute' }, location)
this._onDidLayoutChange.fire({ kind: GroupChangeKind.ADD_GROUP })
this.doSetGroupActive(group)
}
private doRemoveGroup(
group: IComponentGridview,
options?: { skipActive?: boolean; skipDispose?: boolean }
) {
if (!this.groups.has(group.id)) {
throw new Error('invalid operation')
}
const { disposable } = this.groups.get(group.id)
if (!options?.skipDispose) {
disposable.dispose()
this.groups.delete(group.id)
}
const view = this.gridview.remove(group, { type: 'distribute' })
this._onDidLayoutChange.fire({ kind: GroupChangeKind.REMOVE_GROUP })
if (!options?.skipActive && this.groups.size > 0) {
this.doSetGroupActive(Array.from(this.groups.values())[0].value)
}
return view
}
public doSetGroupActive(group: IComponentGridview) {
if (this._activeGroup && this._activeGroup !== group) {
// this._activeGroup.setActive(false);
}
// group.setActive(true);
this._activeGroup = group
}
public moveGroup(
referenceGroup: IComponentGridview,
groupId: string,
itemId: string,
target: Position
) {
const sourceGroup = groupId ? this.groups.get(groupId).value : undefined
const referenceLocation = getGridLocation(referenceGroup.element)
const targetLocation = getRelativeLocation(
this.gridview.orientation,
referenceLocation,
target
)
const [targetParentLocation, to] = tail(targetLocation)
const sourceLocation = getGridLocation(sourceGroup.element)
const [sourceParentLocation, from] = tail(sourceLocation)
if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
// special case when 'swapping' two views within same grid location
// if a group has one tab - we are essentially moving the 'group'
// which is equivalent to swapping two views in this case
this.gridview.moveView(sourceParentLocation, from, to)
return
}
// source group will become empty so delete the group
const targetGroup = this.doRemoveGroup(sourceGroup, {
skipActive: true,
skipDispose: true,
}) as IComponentGridview
// after deleting the group we need to re-evaulate the ref location
const updatedReferenceLocation = getGridLocation(referenceGroup.element)
const location = getRelativeLocation(
this.gridview.orientation,
updatedReferenceLocation,
target
)
this.doAddGroup(targetGroup, location)
}
public layout(size: number, orthogonalSize: number, force?: boolean) {
const different =
force ||
size !== this._size ||
orthogonalSize !== this._orthogonalSize
if (!different) {
return
}
this.element.style.height = `${orthogonalSize}px`
this.element.style.width = `${size}px`
this._size = size
this._orthogonalSize = orthogonalSize
this.gridview.layout(size, orthogonalSize)
}
private toTarget(
direction: 'left' | 'right' | 'above' | 'below' | 'within'
) {
switch (direction) {
case 'left':
return Position.Left
case 'right':
return Position.Right
case 'above':
return Position.Top
case 'below':
return Position.Bottom
case 'within':
default:
return Position.Center
}
}
public dispose() {
super.dispose()
this.gridview.dispose()
this.debugContainer?.dispose()
if (this.resizeTimer) {
clearInterval(this.resizeTimer)
this.resizeTimer = undefined
}
this._onDidLayoutChange.dispose()
}
}

View File

@ -1,21 +1,21 @@
.layout-debug-container {
color: white;
position: fixed;
top: 0px;
right: 0px;
z-index: 9999;
font-size: 11px;
width: 100px;
color: white;
position: fixed;
top: 0px;
right: 0px;
z-index: 9999;
font-size: 11px;
width: 100px;
.layout-debug-widget {
background-color: black;
margin: 1px;
.layout-debug-widget-row {
padding: 0px 5px;
display: flex;
span:first-child {
flex-grow: 1;
}
.layout-debug-widget {
background-color: black;
margin: 1px;
.layout-debug-widget-row {
padding: 0px 5px;
display: flex;
span:first-child {
flex-grow: 1;
}
}
}
}
}

View File

@ -1,59 +1,59 @@
import { CompositeDisposable } from "../../../lifecycle";
import { Layout } from "../../layout";
import { GroupChangeKind } from "../../../groupview/groupview";
import { CompositeDisposable } from '../../../lifecycle'
import { Layout } from '../../layout'
import { GroupChangeKind } from '../../../groupview/groupview'
export class DebugWidget extends CompositeDisposable {
private _element: HTMLElement;
private _element: HTMLElement
constructor(private layout: Layout) {
super();
constructor(private layout: Layout) {
super()
let container = document.getElementById("layout-debug-container");
let container = document.getElementById('layout-debug-container')
if (!container) {
container = document.createElement("div");
container.id = "layout-debug-container";
container.className = "layout-debug-container";
document.body.appendChild(container);
}
this._element = document.createElement("div");
this._element.innerHTML =
`<div class='layout-debug-widget'>` +
`<div class='layout-debug-widget-row'><span>Groups:</span><span id='group-count'>0</span></div>` +
`<div class='layout-debug-widget-row'><span>Panels:</span><span id='panel-count'>0</span></div>` +
`</div>`;
container.appendChild(this._element);
const gc = this._element.querySelector("#group-count");
const pc = this._element.querySelector("#panel-count");
const events = [
GroupChangeKind.PANEL_CREATED,
GroupChangeKind.PANEL_DESTROYED,
GroupChangeKind.ADD_GROUP,
GroupChangeKind.REMOVE_GROUP,
];
this.addDisposables(
this.layout.onDidLayoutChange((event) => {
if (events.includes(event.kind)) {
gc.textContent = this.layout.size.toString();
pc.textContent = this.layout.totalPanels.toString();
if (!container) {
container = document.createElement('div')
container.id = 'layout-debug-container'
container.className = 'layout-debug-container'
document.body.appendChild(container)
}
})
);
}
public dispose() {
super.dispose();
this._element = document.createElement('div')
this._element.innerHTML =
`<div class='layout-debug-widget'>` +
`<div class='layout-debug-widget-row'><span>Groups:</span><span id='group-count'>0</span></div>` +
`<div class='layout-debug-widget-row'><span>Panels:</span><span id='panel-count'>0</span></div>` +
`</div>`
this._element.remove();
container.appendChild(this._element)
const container = document.getElementById("layout-debug-container");
if (container && container.children.length === 0) {
container.remove();
const gc = this._element.querySelector('#group-count')
const pc = this._element.querySelector('#panel-count')
const events = [
GroupChangeKind.PANEL_CREATED,
GroupChangeKind.PANEL_DESTROYED,
GroupChangeKind.ADD_GROUP,
GroupChangeKind.REMOVE_GROUP,
]
this.addDisposables(
this.layout.onDidLayoutChange((event) => {
if (events.includes(event.kind)) {
gc.textContent = this.layout.size.toString()
pc.textContent = this.layout.totalPanels.toString()
}
})
)
}
public dispose() {
super.dispose()
this._element.remove()
const container = document.getElementById('layout-debug-container')
if (container && container.children.length === 0) {
container.remove()
}
}
}
}

View File

@ -1,77 +1,79 @@
.tab {
&.dragging {
.tab-action {
background-color: var(--active-group-visible-panel-color);
}
}
&.active-tab > .default-tab {
.tab-action {
visibility: visible;
}
}
&.inactive-tab > .default-tab {
.tab-action:not(.dirty) {
visibility: hidden;
}
&:hover {
.tab-action {
visibility: visible;
}
}
}
.default-tab {
position: relative;
height: 100%;
display: flex;
min-width: 80px;
align-items: center;
padding-left: 10px;
white-space: nowrap;
text-overflow: elipsis;
font-size: 13px;
.tab-content {
flex-grow: 1;
}
.action-container {
text-align: right;
width: 28px;
display: flex;
.tab-list {
display: flex;
padding: 0px;
margin: 0px;
justify-content: flex-end;
a:active:hover {
-webkit-mask-size: 100% 100% !important;
mask-size: 100% 100% !important;
}
&.dragging {
.tab-action {
height: 16px;
width: 16px;
display: block;
-webkit-mask: var(--tab-close-icon) 50% 50% / 90% 90% no-repeat;
mask: var(--tab-close-icon) 50% 50% / 90% 90% no-repeat;
margin-right: "0.5em";
&.disable-close {
display: none;
}
&.dirty:not(:hover) {
display: block;
-webkit-mask: var(--tab-dirty-icon) 50% 50% / 60% 60% no-repeat;
mask: var(--tab-dirty-icon) 50% 50% / 60% 60% no-repeat;
}
background-color: var(--active-group-visible-panel-color);
}
}
&.active-tab > .default-tab {
.tab-action {
visibility: visible;
}
}
&.inactive-tab > .default-tab {
.tab-action:not(.dirty) {
visibility: hidden;
}
&:hover {
.tab-action {
visibility: visible;
}
}
}
.default-tab {
position: relative;
height: 100%;
display: flex;
min-width: 80px;
align-items: center;
padding-left: 10px;
white-space: nowrap;
text-overflow: elipsis;
font-size: 13px;
.tab-content {
flex-grow: 1;
}
.action-container {
text-align: right;
width: 28px;
display: flex;
.tab-list {
display: flex;
padding: 0px;
margin: 0px;
justify-content: flex-end;
a:active:hover {
-webkit-mask-size: 100% 100% !important;
mask-size: 100% 100% !important;
}
.tab-action {
height: 16px;
width: 16px;
display: block;
-webkit-mask: var(--tab-close-icon) 50% 50% / 90% 90%
no-repeat;
mask: var(--tab-close-icon) 50% 50% / 90% 90% no-repeat;
margin-right: '0.5em';
&.disable-close {
display: none;
}
&.dirty:not(:hover) {
display: block;
-webkit-mask: var(--tab-dirty-icon) 50% 50% / 60% 60%
no-repeat;
mask: var(--tab-dirty-icon) 50% 50% / 60% 60% no-repeat;
}
}
}
}
}
}
}
}

View File

@ -1,97 +1,99 @@
import { CompositeDisposable, MutableDisposable } from "../../../lifecycle";
import { CompositeDisposable, MutableDisposable } from '../../../lifecycle'
import {
PanelHeaderPart,
PartInitParameters,
} from "../../../groupview/panel/parts";
import { addDisposableListener } from "../../../events";
import { toggleClass } from "../../../dom";
PanelHeaderPart,
PartInitParameters,
} from '../../../groupview/panel/parts'
import { addDisposableListener } from '../../../events'
import { toggleClass } from '../../../dom'
export class DefaultTab extends CompositeDisposable implements PanelHeaderPart {
private _element: HTMLElement;
private _isGroupActive: boolean;
private _isPanelVisible: boolean;
private _element: HTMLElement
private _isGroupActive: boolean
private _isPanelVisible: boolean
//
private _content: HTMLElement;
private _actionContainer: HTMLElement;
private _list: HTMLElement;
private action: HTMLElement;
//
private params: PartInitParameters;
//
private isDirtyDisposable = new MutableDisposable();
get element() {
return this._element;
}
get id() {
return "__DEFAULT_TAB__";
}
constructor() {
super();
this._element = document.createElement("div");
this._element.className = "default-tab";
//
this._content = document.createElement("div");
this._content.className = "tab-content";
private _content: HTMLElement
private _actionContainer: HTMLElement
private _list: HTMLElement
private action: HTMLElement
//
this._actionContainer = document.createElement("div");
this._actionContainer.className = "action-container";
private params: PartInitParameters
//
this._list = document.createElement("ul");
this._list.className = "tab-list";
//
this.action = document.createElement("a");
this.action.className = "tab-action";
//
this._element.appendChild(this._content);
this._element.appendChild(this._actionContainer);
this._actionContainer.appendChild(this._list);
this._list.appendChild(this.action);
//
this.addDisposables(
addDisposableListener(this._actionContainer, "mousedown", (ev) => {
ev.preventDefault();
})
);
private isDirtyDisposable = new MutableDisposable()
this.render();
}
public toJSON() {
return { id: this.id };
}
public init(params: PartInitParameters) {
this.params = params;
this._content.textContent = params.title;
this.isDirtyDisposable.value = this.params.api.onDidDirtyChange((event) => {
const isDirty = event;
toggleClass(this.action, "dirty", isDirty);
});
if (!this.params.suppressClosable) {
addDisposableListener(this.action, "click", (ev) => {
ev.preventDefault(); //
this.params.api.close();
});
} else {
this.action.classList.add("disable-close");
get element() {
return this._element
}
}
public setVisible(isPanelVisible: boolean, isGroupVisible: boolean) {
this._isPanelVisible = isPanelVisible;
this._isGroupActive = isGroupVisible;
get id() {
return '__DEFAULT_TAB__'
}
this.render();
}
constructor() {
super()
private render() {
//
}
this._element = document.createElement('div')
this._element.className = 'default-tab'
//
this._content = document.createElement('div')
this._content.className = 'tab-content'
//
this._actionContainer = document.createElement('div')
this._actionContainer.className = 'action-container'
//
this._list = document.createElement('ul')
this._list.className = 'tab-list'
//
this.action = document.createElement('a')
this.action.className = 'tab-action'
//
this._element.appendChild(this._content)
this._element.appendChild(this._actionContainer)
this._actionContainer.appendChild(this._list)
this._list.appendChild(this.action)
//
this.addDisposables(
addDisposableListener(this._actionContainer, 'mousedown', (ev) => {
ev.preventDefault()
})
)
this.render()
}
public toJSON() {
return { id: this.id }
}
public init(params: PartInitParameters) {
this.params = params
this._content.textContent = params.title
this.isDirtyDisposable.value = this.params.api.onDidDirtyChange(
(event) => {
const isDirty = event
toggleClass(this.action, 'dirty', isDirty)
}
)
if (!this.params.suppressClosable) {
addDisposableListener(this.action, 'click', (ev) => {
ev.preventDefault() //
this.params.api.close()
})
} else {
this.action.classList.add('disable-close')
}
}
public setVisible(isPanelVisible: boolean, isGroupVisible: boolean) {
this._isPanelVisible = isPanelVisible
this._isGroupActive = isGroupVisible
this.render()
}
private render() {
//
}
}

View File

@ -1,21 +1,21 @@
.watermark {
display: flex;
width: 100%;
&.has-actions {
.watermark-title {
.actions-bar {
display: none;
}
}
}
.watermark-title {
height: 35px;
width: 100%;
display: flex;
}
.watermark-content {
flex-grow: 1;
}
width: 100%;
&.has-actions {
.watermark-title {
.actions-bar {
display: none;
}
}
}
.watermark-title {
height: 35px;
width: 100%;
display: flex;
}
.watermark-content {
flex-grow: 1;
}
}

View File

@ -1,81 +1,81 @@
import {
WatermarkPart,
WatermarkPartInitParameters,
} from "../../../groupview/panel/parts";
import { IGroupAccessor } from "../../layout";
import { IGroupview } from "../../../groupview/groupview";
import { ActionContainer } from "../../../groupview/actions/actionsContainer";
import { addDisposableListener } from "../../../events";
import { toggleClass } from "../../../dom";
import { CompositeDisposable } from "../../../lifecycle";
WatermarkPart,
WatermarkPartInitParameters,
} from '../../../groupview/panel/parts'
import { IGroupAccessor } from '../../layout'
import { IGroupview } from '../../../groupview/groupview'
import { ActionContainer } from '../../../groupview/actions/actionsContainer'
import { addDisposableListener } from '../../../events'
import { toggleClass } from '../../../dom'
import { CompositeDisposable } from '../../../lifecycle'
export class Watermark extends CompositeDisposable implements WatermarkPart {
private _element: HTMLElement;
private accessor: IGroupAccessor;
private _element: HTMLElement
private accessor: IGroupAccessor
private _visible: boolean;
private _group: IGroupview;
private _visible: boolean
private _group: IGroupview
constructor() {
super();
this._element = document.createElement("div");
this._element.className = "watermark";
constructor() {
super()
this._element = document.createElement('div')
this._element.className = 'watermark'
const title = document.createElement("div");
title.className = "watermark-title";
const title = document.createElement('div')
title.className = 'watermark-title'
const emptySpace = document.createElement("span");
emptySpace.style.flexGrow = "1";
const emptySpace = document.createElement('span')
emptySpace.style.flexGrow = '1'
const content = document.createElement("div");
content.className = "watermark-content";
const content = document.createElement('div')
content.className = 'watermark-content'
this._element.appendChild(title);
this._element.appendChild(content);
this._element.appendChild(title)
this._element.appendChild(content)
const actions = new ActionContainer();
title.appendChild(emptySpace);
title.appendChild(actions.element);
const actions = new ActionContainer()
title.appendChild(emptySpace)
title.appendChild(actions.element)
const closeAnchor = document.createElement("a");
closeAnchor.className = "close-action";
const closeAnchor = document.createElement('a')
closeAnchor.className = 'close-action'
actions.add(closeAnchor);
actions.add(closeAnchor)
addDisposableListener(closeAnchor, "click", (ev) => {
ev.preventDefault(); //
this.accessor.removeGroup(this._group);
});
}
addDisposableListener(closeAnchor, 'click', (ev) => {
ev.preventDefault() //
this.accessor.removeGroup(this._group)
})
}
public init(params: WatermarkPartInitParameters) {
this.accessor = params.accessor;
public init(params: WatermarkPartInitParameters) {
this.accessor = params.accessor
this.addDisposables(
this.accessor.onDidLayoutChange((event) => {
this.render();
})
);
this.addDisposables(
this.accessor.onDidLayoutChange((event) => {
this.render()
})
)
this.render();
}
this.render()
}
public setVisible(visible: boolean, group: IGroupview): void {
this._visible = visible;
this._group = group;
this.render();
}
public setVisible(visible: boolean, group: IGroupview): void {
this._visible = visible
this._group = group
this.render()
}
get element() {
return this._element;
}
get element() {
return this._element
}
private render() {
const isOneGroup = this.accessor.size <= 1;
toggleClass(this.element, "has-actions", isOneGroup);
}
private render() {
const isOneGroup = this.accessor.size <= 1
toggleClass(this.element, 'has-actions', isOneGroup)
}
public dispose() {
super.dispose();
}
public dispose() {
super.dispose()
}
}

View File

@ -1,34 +1,34 @@
import { IGridView, IViewDeserializer } from "../gridview/gridview";
import { IGroupPanel } from "../groupview/panel/types";
import { Layout } from "./layout";
import { IGridView, IViewDeserializer } from '../gridview/gridview'
import { IGroupPanel } from '../groupview/panel/types'
import { Layout } from './layout'
export interface IPanelDeserializer {
fromJSON(panelData: { [index: string]: any }): IGroupPanel;
fromJSON(panelData: { [index: string]: any }): IGroupPanel
}
export class DefaultDeserializer implements IViewDeserializer {
constructor(
private readonly layout: Layout,
private panelDeserializer: { createPanel: (id: string) => IGroupPanel }
) {}
constructor(
private readonly layout: Layout,
private panelDeserializer: { createPanel: (id: string) => IGroupPanel }
) {}
public fromJSON(data: { [key: string]: any }): IGridView {
const children = data.views;
const active = data.activeView;
public fromJSON(data: { [key: string]: any }): IGridView {
const children = data.views
const active = data.activeView
const panels: IGroupPanel[] = [];
const panels: IGroupPanel[] = []
for (const child of children) {
const panel = this.panelDeserializer.createPanel(child);
for (const child of children) {
const panel = this.panelDeserializer.createPanel(child)
panels.push(panel);
panels.push(panel)
}
const group = this.layout.createGroup({
panels,
activePanel: panels.find((p) => p.id === active),
})
return group
}
const group = this.layout.createGroup({
panels,
activePanel: panels.find((p) => p.id === active),
});
return group;
}
}

View File

@ -1,3 +1,3 @@
export * from "./layout";
export * from "./componentGridview";
export * from "./options";
export * from './layout'
export * from './componentGridview'
export * from './options'

View File

@ -1,63 +1,63 @@
.custom-dragging {
height: 24px;
line-height: 24px;
font-size: 11px;
width: 100px;
background-color: dodgerblue;
color: ghostwhite;
border-radius: 11px;
position: absolute;
padding-left: 10px;
height: 24px;
line-height: 24px;
font-size: 11px;
width: 100px;
background-color: dodgerblue;
color: ghostwhite;
border-radius: 11px;
position: absolute;
padding-left: 10px;
}
.groupview {
&.active-group {
> .title-container > .tab-container > .tab {
&.active-tab {
background-color: var(--active-tab-background-visible);
color: var(--active-group-visible-panel-color);
&.active-group {
> .title-container > .tab-container > .tab {
&.active-tab {
background-color: var(--active-tab-background-visible);
color: var(--active-group-visible-panel-color);
.tab-action {
background-color: var(--active-group-visible-panel-color);
}
}
&.inactive-tab {
background-color: var(--active-tab-background-hidden);
color: var(--active-group-hidden-panel-color);
.tab-action {
background-color: var(--active-group-visible-panel-color);
}
}
&.inactive-tab {
background-color: var(--active-tab-background-hidden);
color: var(--active-group-hidden-panel-color);
.tab-action {
background-color: var(--active-group-hidden-panel-color);
.tab-action {
background-color: var(--active-group-hidden-panel-color);
}
}
}
}
}
}
&.inactive-group {
> .title-container > .tab-container > .tab {
&.active-tab {
background-color: var(--inactive-tab-background-visible);
color: var(--inactive-group-visible-panel-color);
&.inactive-group {
> .title-container > .tab-container > .tab {
&.active-tab {
background-color: var(--inactive-tab-background-visible);
color: var(--inactive-group-visible-panel-color);
.tab-action {
background-color: var(--inactive-group-visible-panel-color);
}
}
&.inactive-tab {
background-color: var(--inactive-tab-background-hidden);
color: var(--inactive-group-hidden-panel-color);
.tab-action {
background-color: var(--inactive-group-visible-panel-color);
}
}
&.inactive-tab {
background-color: var(--inactive-tab-background-hidden);
color: var(--inactive-group-hidden-panel-color);
.tab-action {
background-color: var(--inactive-group-hidden-panel-color);
.tab-action {
background-color: var(--inactive-group-hidden-panel-color);
}
}
}
}
}
}
}
// when a tab is dragged we loss the above stylings because they are conditional on parent elements
// there we also set some stylings for the dragging event
.tab {
&.dragging {
background-color: var(--active-tab-background-visible);
color: var(--active-group-visible-panel-color);
}
&.dragging {
background-color: var(--active-tab-background-visible);
color: var(--active-group-visible-panel-color);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,72 +1,72 @@
import { IGroupview } from "../groupview/groupview";
import { IGroupview } from '../groupview/groupview'
import {
PanelContentPart,
PanelContentPartConstructor,
PanelHeaderPart,
PanelHeaderPartConstructor,
WatermarkConstructor,
} from "../groupview/panel/parts";
import { IGroupPanel } from "../groupview/panel/types";
import { FrameworkFactory } from "../types";
import { Api } from "./layout";
PanelContentPart,
PanelContentPartConstructor,
PanelHeaderPart,
PanelHeaderPartConstructor,
WatermarkConstructor,
} from '../groupview/panel/parts'
import { IGroupPanel } from '../groupview/panel/types'
import { FrameworkFactory } from '../types'
import { Api } from './layout'
export interface GroupPanelFrameworkComponentFactory {
content: FrameworkFactory<PanelContentPart>;
tab: FrameworkFactory<PanelHeaderPart>;
content: FrameworkFactory<PanelContentPart>
tab: FrameworkFactory<PanelHeaderPart>
}
export interface TabContextMenuEvent {
event: MouseEvent;
api: Api;
panel: IGroupPanel;
event: MouseEvent
api: Api
panel: IGroupPanel
}
export interface LayoutOptions {
tabComponents?: {
[componentName: string]: PanelHeaderPartConstructor;
};
components?: {
[componentName: string]: PanelContentPartConstructor;
};
frameworkTabComponents?: {
[componentName: string]: any;
};
frameworkComponents?: {
[componentName: string]: any;
};
watermarkComponent?: WatermarkConstructor;
watermarkFrameworkComponent?: any;
frameworkComponentFactory: GroupPanelFrameworkComponentFactory;
tabHeight?: number;
debug?: boolean;
enableExternalDragEvents?: boolean;
tabComponents?: {
[componentName: string]: PanelHeaderPartConstructor
}
components?: {
[componentName: string]: PanelContentPartConstructor
}
frameworkTabComponents?: {
[componentName: string]: any
}
frameworkComponents?: {
[componentName: string]: any
}
watermarkComponent?: WatermarkConstructor
watermarkFrameworkComponent?: any
frameworkComponentFactory: GroupPanelFrameworkComponentFactory
tabHeight?: number
debug?: boolean
enableExternalDragEvents?: boolean
}
export interface PanelOptions {
componentName: string;
tabComponentName?: string;
params?: { [key: string]: any };
id: string;
title?: string;
suppressClosable?: boolean;
componentName: string
tabComponentName?: string
params?: { [key: string]: any }
id: string
title?: string
suppressClosable?: boolean
}
export interface AddPanelOptions
extends Omit<PanelOptions, "componentName" | "tabComponentName"> {
componentName: string | PanelContentPartConstructor;
tabComponentName?: string | PanelHeaderPartConstructor;
position?: {
direction?: "left" | "right" | "above" | "below" | "within";
referencePanel: string;
};
extends Omit<PanelOptions, 'componentName' | 'tabComponentName'> {
componentName: string | PanelContentPartConstructor
tabComponentName?: string | PanelHeaderPartConstructor
position?: {
direction?: 'left' | 'right' | 'above' | 'below' | 'within'
referencePanel: string
}
}
export interface AddGroupOptions {
direction?: "left" | "right" | "above" | "below";
referencePanel: string;
direction?: 'left' | 'right' | 'above' | 'below'
referencePanel: string
}
export interface MovementOptions {
group?: IGroupview;
includePanel?: boolean;
group?: IGroupview
includePanel?: boolean
}

View File

@ -1,56 +1,56 @@
export interface IDisposable {
dispose: () => void;
dispose: () => void
}
export interface IValueDisposable<T> {
value: T;
disposable: IDisposable;
value: T
disposable: IDisposable
}
export interface ISerializable {
toJSON(): object;
fromJSON(data: object): void;
toJSON(): object
fromJSON(data: object): void
}
export namespace Disposable {
export const NONE: IDisposable = { dispose: () => {} };
export const NONE: IDisposable = { dispose: () => {} }
}
export class CompositeDisposable {
private disposables: IDisposable[];
private disposables: IDisposable[]
public static from(...args: IDisposable[]) {
return new CompositeDisposable(...args);
}
public static from(...args: IDisposable[]) {
return new CompositeDisposable(...args)
}
constructor(...args: IDisposable[]) {
this.disposables = args;
}
constructor(...args: IDisposable[]) {
this.disposables = args
}
public addDisposables(...args: IDisposable[]) {
args?.forEach((arg) => this.disposables.push(arg));
}
public addDisposables(...args: IDisposable[]) {
args?.forEach((arg) => this.disposables.push(arg))
}
public dispose() {
this.disposables.forEach((arg) => arg.dispose());
}
public dispose() {
this.disposables.forEach((arg) => arg.dispose())
}
}
export class MutableDisposable implements IDisposable {
private _disposable: IDisposable;
private _disposable: IDisposable
constructor() {}
constructor() {}
set value(disposable: IDisposable) {
if (this._disposable) {
this._disposable.dispose();
set value(disposable: IDisposable) {
if (this._disposable) {
this._disposable.dispose()
}
this._disposable = disposable
}
this._disposable = disposable;
}
public dispose() {
if (this._disposable) {
this._disposable.dispose();
public dispose() {
if (this._disposable) {
this._disposable.dispose()
}
}
}
}

View File

@ -1,8 +1,8 @@
export const clamp = (value: number, min: number, max: number) => {
return Math.min(max, Math.max(value, min));
};
return Math.min(max, Math.max(value, min))
}
export const sequentialNumberGenerator = () => {
let value = 1;
return { next: () => (value++).toString() };
};
let value = 1
return { next: () => (value++).toString() }
}

View File

@ -1,161 +1,163 @@
import { PanelDimensionChangeEvent } from "./types";
import { Emitter, Event } from "../events";
import { CompositeDisposable, IDisposable } from "../lifecycle";
import { FunctionOrValue } from "../types";
import { PanelDimensionChangeEvent } from './types'
import { Emitter, Event } from '../events'
import { CompositeDisposable, IDisposable } from '../lifecycle'
import { FunctionOrValue } from '../types'
// we've tried to do a bit better than the 'any' type.
// anything that is serializable JSON should be valid here
type StateObject =
| number
| string
| boolean
| undefined
| null
| object
| StateObject[]
| { [key: string]: StateObject };
| number
| string
| boolean
| undefined
| null
| object
| StateObject[]
| { [key: string]: StateObject }
interface State {
[key: string]: StateObject;
[key: string]: StateObject
}
interface ChangeFocusEvent {
isFocused: boolean;
isFocused: boolean
}
interface PanelConstraintChangeEvent {
minimumSize?: number | (() => number);
maximumSize?: number | (() => number);
minimumSize?: number | (() => number)
maximumSize?: number | (() => number)
}
export interface IBaseViewApi extends IDisposable {
// events
onDidDimensionsChange: Event<PanelDimensionChangeEvent>;
onDidStateChange: Event<void>;
onDidFocusChange: Event<ChangeFocusEvent>;
// state
setState(key: string, value: StateObject): void;
setState(state: State): void;
getState: () => State;
getStateKey: <T extends StateObject>(key: string) => T;
//
readonly isFocused: boolean;
// events
onDidDimensionsChange: Event<PanelDimensionChangeEvent>
onDidStateChange: Event<void>
onDidFocusChange: Event<ChangeFocusEvent>
// state
setState(key: string, value: StateObject): void
setState(state: State): void
getState: () => State
getStateKey: <T extends StateObject>(key: string) => T
//
readonly isFocused: boolean
}
/**
* A core api implementation that should be used across all panel-like objects
*/
export class BaseViewApi extends CompositeDisposable implements IBaseViewApi {
private _state: State = {};
private _isFocused: boolean;
private _state: State = {}
private _isFocused: boolean
readonly _onDidStateChange = new Emitter<void>();
readonly onDidStateChange: Event<void> = this._onDidStateChange.event;
//
readonly _onDidPanelDimensionChange = new Emitter<PanelDimensionChangeEvent>({
emitLastValue: true,
});
readonly onDidDimensionsChange = this._onDidPanelDimensionChange.event;
//
readonly _onDidChangeFocus = new Emitter<ChangeFocusEvent>({
emitLastValue: true,
});
readonly onDidFocusChange: Event<ChangeFocusEvent> = this._onDidChangeFocus
.event;
//
readonly _onDidStateChange = new Emitter<void>()
readonly onDidStateChange: Event<void> = this._onDidStateChange.event
//
readonly _onDidPanelDimensionChange = new Emitter<
PanelDimensionChangeEvent
>({
emitLastValue: true,
})
readonly onDidDimensionsChange = this._onDidPanelDimensionChange.event
//
readonly _onDidChangeFocus = new Emitter<ChangeFocusEvent>({
emitLastValue: true,
})
readonly onDidFocusChange: Event<ChangeFocusEvent> = this._onDidChangeFocus
.event
//
get isFocused() {
return this._isFocused;
}
constructor() {
super();
this.addDisposables(
this._onDidStateChange,
this._onDidChangeFocus,
this._onDidPanelDimensionChange,
this.onDidFocusChange((event) => {
this._isFocused = event.isFocused;
})
);
}
public setState(
key: string | { [key: string]: StateObject },
value?: StateObject
) {
if (typeof key === "object") {
this._state = key;
} else {
this._state[key] = value;
get isFocused() {
return this._isFocused
}
this._onDidStateChange.fire(undefined);
}
public getState(): State {
return this._state;
}
constructor() {
super()
public getStateKey<T extends StateObject>(key: string): T {
return this._state[key] as T;
}
this.addDisposables(
this._onDidStateChange,
this._onDidChangeFocus,
this._onDidPanelDimensionChange,
this.onDidFocusChange((event) => {
this._isFocused = event.isFocused
})
)
}
public dispose() {
super.dispose();
}
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(): State {
return this._state
}
public getStateKey<T extends StateObject>(key: string): T {
return this._state[key] as T
}
public dispose() {
super.dispose()
}
}
interface PanelConstraintChangeEvent {
minimumSize?: FunctionOrValue<number>;
maximumSize?: FunctionOrValue<number>;
minimumSize?: FunctionOrValue<number>
maximumSize?: FunctionOrValue<number>
}
export interface IPanelApi extends IBaseViewApi {
onDidConstraintsChange: Event<PanelConstraintChangeEvent>;
setConstraints(value: PanelConstraintChangeEvent): void;
onDidConstraintsChange: Event<PanelConstraintChangeEvent>
setConstraints(value: PanelConstraintChangeEvent): void
}
export class PanelApi extends BaseViewApi implements IBaseViewApi {
readonly _onDidConstraintsChange = new Emitter<PanelConstraintChangeEvent>({
emitLastValue: true,
});
readonly onDidConstraintsChange: Event<PanelConstraintChangeEvent> = this
._onDidConstraintsChange.event;
readonly _onDidConstraintsChange = new Emitter<PanelConstraintChangeEvent>({
emitLastValue: true,
})
readonly onDidConstraintsChange: Event<PanelConstraintChangeEvent> = this
._onDidConstraintsChange.event
constructor() {
super();
}
constructor() {
super()
}
public setConstraints(value: PanelConstraintChangeEvent) {
this._onDidConstraintsChange.fire(value);
}
public setConstraints(value: PanelConstraintChangeEvent) {
this._onDidConstraintsChange.fire(value)
}
}
interface GridConstraintChangeEvent {
minimumWidth?: FunctionOrValue<number>;
minimumHeight?: FunctionOrValue<number>;
maximumWidth?: FunctionOrValue<number>;
maximumHeight?: FunctionOrValue<number>;
minimumWidth?: FunctionOrValue<number>
minimumHeight?: FunctionOrValue<number>
maximumWidth?: FunctionOrValue<number>
maximumHeight?: FunctionOrValue<number>
}
export interface IGridApi extends IBaseViewApi {
onDidConstraintsChange: Event<GridConstraintChangeEvent>;
setConstraints(value: GridConstraintChangeEvent): void;
onDidConstraintsChange: Event<GridConstraintChangeEvent>
setConstraints(value: GridConstraintChangeEvent): void
}
export class GridApi extends BaseViewApi implements IBaseViewApi {
readonly _onDidConstraintsChange = new Emitter<GridConstraintChangeEvent>({
emitLastValue: true,
});
readonly onDidConstraintsChange: Event<GridConstraintChangeEvent> = this
._onDidConstraintsChange.event;
readonly _onDidConstraintsChange = new Emitter<GridConstraintChangeEvent>({
emitLastValue: true,
})
readonly onDidConstraintsChange: Event<GridConstraintChangeEvent> = this
._onDidConstraintsChange.event
constructor() {
super();
}
constructor() {
super()
}
public setConstraints(value: GridConstraintChangeEvent) {
this._onDidConstraintsChange.fire(value);
}
public setConstraints(value: GridConstraintChangeEvent) {
this._onDidConstraintsChange.fire(value)
}
}

View File

@ -1,19 +1,19 @@
export interface InitParameters {
params: { [index: string]: any };
state?: { [index: string]: any };
params: { [index: string]: any }
state?: { [index: string]: any }
}
export interface PanelUpdateEvent {
params: { [index: string]: any };
params: { [index: string]: any }
}
export interface IPanel {
init?(params: InitParameters): void;
layout?(width: number, height: number): void;
update?(event: PanelUpdateEvent): void;
init?(params: InitParameters): void
layout?(width: number, height: number): void
update?(event: PanelUpdateEvent): void
}
export interface PanelDimensionChangeEvent {
width: number;
height: number;
width: number
height: number
}

View File

@ -1,41 +1,41 @@
.pane-container {
&.animated {
&.animated {
.view {
transition-duration: 0.15s;
transition-timing-function: ease-out;
}
}
.view {
transition-duration: 0.15s;
transition-timing-function: ease-out;
}
}
.view {
overflow: hidden;
display: flex;
flex-direction: column;
overflow: hidden;
display: flex;
flex-direction: column;
&:not(:first-child)::before {
background-color: transparent !important;
&:not(:first-child)::before {
background-color: transparent !important;
}
}
}
.pane {
display: flex;
flex-direction: column;
overflow: hidden;
.pane {
display: flex;
flex-direction: column;
overflow: hidden;
.pane-header {
background-color: #383838;
color: white;
.pane-header {
background-color: #383838;
color: white;
&:focus,
:focus-within {
outline-width: 1px;
outline-style: solid;
outline-offset: -1px;
opacity: 1 !important;
outline-color: dodgerblue;
}
&:focus,
:focus-within {
outline-width: 1px;
outline-style: solid;
outline-offset: -1px;
opacity: 1 !important;
outline-color: dodgerblue;
}
}
.pane-body {
overflow: auto;
background-color: #252526;
}
}
.pane-body {
overflow: auto;
background-color: #252526;
}
}
}

View File

@ -1,274 +1,276 @@
import { SplitView, IView, Orientation } from "../splitview/splitview";
import { IDisposable } from "../lifecycle";
import { Emitter } from "../events";
import { addClasses, removeClasses } from "../dom";
import { SplitView, IView, Orientation } from '../splitview/splitview'
import { IDisposable } from '../lifecycle'
import { Emitter } from '../events'
import { addClasses, removeClasses } from '../dom'
export interface IPaneOptions {
minimumBodySize?: number;
maximumBodySize?: number;
orientation?: Orientation;
isExpanded?: boolean;
minimumBodySize?: number
maximumBodySize?: number
orientation?: Orientation
isExpanded?: boolean
}
export abstract class Pane implements IView {
public element: HTMLElement;
private header: HTMLElement;
private body: HTMLElement;
public element: HTMLElement
private header: HTMLElement
private body: HTMLElement
private _onDidChangeExpansionState: Emitter<boolean> = new Emitter<boolean>();
public onDidChangeExpansionState = this._onDidChangeExpansionState.event;
private _onDidChangeExpansionState: Emitter<boolean> = new Emitter<
boolean
>()
public onDidChangeExpansionState = this._onDidChangeExpansionState.event
private _onDidChange: Emitter<number | undefined> = new Emitter<
number | undefined
>();
public onDidChange = this._onDidChange.event;
private _onDidChange: Emitter<number | undefined> = new Emitter<
number | undefined
>()
public onDidChange = this._onDidChange.event
private _minimumBodySize: number;
private _maximumBodySize: number;
private _minimumBodySize: number
private _maximumBodySize: number
private _minimumSize: number;
private _maximumSize: number;
private _isExpanded: boolean;
private _orientation: Orientation;
private _orthogonalSize: number;
private animationTimer: NodeJS.Timeout;
private expandedSize: number;
private headerSize = 22;
private _minimumSize: number
private _maximumSize: number
private _isExpanded: boolean
private _orientation: Orientation
private _orthogonalSize: number
private animationTimer: NodeJS.Timeout
private expandedSize: number
private headerSize = 22
constructor(options: IPaneOptions) {
this.element = document.createElement("div");
this.element.className = "pane";
constructor(options: IPaneOptions) {
this.element = document.createElement('div')
this.element.className = 'pane'
this._minimumBodySize =
typeof options.minimumBodySize === "number"
? options.minimumBodySize
: 120;
this._maximumBodySize =
typeof options.maximumBodySize === "number"
? options.maximumBodySize
: Number.POSITIVE_INFINITY;
this._minimumBodySize =
typeof options.minimumBodySize === 'number'
? options.minimumBodySize
: 120
this._maximumBodySize =
typeof options.maximumBodySize === 'number'
? options.maximumBodySize
: Number.POSITIVE_INFINITY
this._isExpanded = options.isExpanded;
this.orientation = options.orientation;
}
public get minimumSize(): number {
const headerSize = this.headerSize;
const expanded = this.isExpanded();
const minimumBodySize = expanded
? this._minimumBodySize
: this._orientation === Orientation.HORIZONTAL
? 50
: 0;
return headerSize + minimumBodySize;
}
public get maximumSize(): number {
const headerSize = this.headerSize;
const expanded = this.isExpanded();
const maximumBodySize = expanded
? this._maximumBodySize
: this._orientation === Orientation.HORIZONTAL
? 50
: 0;
return headerSize + maximumBodySize;
}
public isExpanded() {
return this._isExpanded;
}
public get orientation() {
return this._orientation;
}
public get orthogonalSize() {
return this._orthogonalSize;
}
public set minimumSize(size: number) {
this._minimumSize = size;
this._onDidChange.fire(undefined);
}
public set maximumSize(size: number) {
this._maximumSize = size;
this._onDidChange.fire(undefined);
}
public setExpanded(expanded: boolean) {
this._isExpanded = expanded;
if (expanded) {
if (this.animationTimer) {
clearTimeout(this.animationTimer);
}
this.element.appendChild(this.body);
} else {
this.animationTimer = setTimeout(() => {
this.body.remove();
}, 200);
this._isExpanded = options.isExpanded
this.orientation = options.orientation
}
this._onDidChangeExpansionState.fire(expanded);
this._onDidChange.fire(expanded ? this.expandedSize : undefined);
}
public get minimumSize(): number {
const headerSize = this.headerSize
const expanded = this.isExpanded()
const minimumBodySize = expanded
? this._minimumBodySize
: this._orientation === Orientation.HORIZONTAL
? 50
: 0
public set orientation(orientation: Orientation) {
this._orientation = orientation;
}
public set orthogonalSize(size: number) {
this._orthogonalSize = size;
}
public layout(size: number, orthogonalSize: number) {
if (this.isExpanded()) {
this.expandedSize = size;
return headerSize + minimumBodySize
}
}
public render() {
this.header = document.createElement("div");
this.header.className = "pane-header";
this.header.style.height = `${this.headerSize}px`;
this.header.style.lineHeight = `${this.headerSize}px`;
this.element.appendChild(this.header);
this.renderHeader(this.header);
public get maximumSize(): number {
const headerSize = this.headerSize
const expanded = this.isExpanded()
const maximumBodySize = expanded
? this._maximumBodySize
: this._orientation === Orientation.HORIZONTAL
? 50
: 0
// this.updateHeader();
return headerSize + maximumBodySize
}
this.body = document.createElement("div");
this.body.className = "pane-body";
this.element.appendChild(this.body);
this.renderBody(this.body);
public isExpanded() {
return this._isExpanded
}
// if (!this.isExpanded()) {
// this.body.remove();
public get orientation() {
return this._orientation
}
public get orthogonalSize() {
return this._orthogonalSize
}
public set minimumSize(size: number) {
this._minimumSize = size
this._onDidChange.fire(undefined)
}
public set maximumSize(size: number) {
this._maximumSize = size
this._onDidChange.fire(undefined)
}
public setExpanded(expanded: boolean) {
this._isExpanded = expanded
if (expanded) {
if (this.animationTimer) {
clearTimeout(this.animationTimer)
}
this.element.appendChild(this.body)
} else {
this.animationTimer = setTimeout(() => {
this.body.remove()
}, 200)
}
this._onDidChangeExpansionState.fire(expanded)
this._onDidChange.fire(expanded ? this.expandedSize : undefined)
}
public set orientation(orientation: Orientation) {
this._orientation = orientation
}
public set orthogonalSize(size: number) {
this._orthogonalSize = size
}
public layout(size: number, orthogonalSize: number) {
if (this.isExpanded()) {
this.expandedSize = size
}
}
public render() {
this.header = document.createElement('div')
this.header.className = 'pane-header'
this.header.style.height = `${this.headerSize}px`
this.header.style.lineHeight = `${this.headerSize}px`
this.element.appendChild(this.header)
this.renderHeader(this.header)
// this.updateHeader();
this.body = document.createElement('div')
this.body.className = 'pane-body'
this.element.appendChild(this.body)
this.renderBody(this.body)
// if (!this.isExpanded()) {
// this.body.remove();
// }
}
// protected updateHeader(): void {
// const expanded = this.isExpanded();
// this.header.style.height = `${this.headerSize}px`;
// this.header.style.lineHeight = `${this.headerSize}px`;
// toggleClass(this.header, "hidden", !this.headerVisible);
// toggleClass(this.header, "expanded", expanded);
// this.header.setAttribute("aria-expanded", String(expanded));
// this.header.style.color = this.styles.headerForeground
// ? this.styles.headerForeground.toString()
// : "";
// this.header.style.backgroundColor = this.styles.headerBackground
// ? this.styles.headerBackground.toString()
// : "";
// this.header.style.borderTop =
// this.styles.headerBorder && this.orientation === Orientation.VERTICAL
// ? `1px solid ${this.styles.headerBorder}`
// : "";
// this._dropBackground = this.styles.dropBackground;
// }
}
// protected updateHeader(): void {
// const expanded = this.isExpanded();
// this.header.style.height = `${this.headerSize}px`;
// this.header.style.lineHeight = `${this.headerSize}px`;
// toggleClass(this.header, "hidden", !this.headerVisible);
// toggleClass(this.header, "expanded", expanded);
// this.header.setAttribute("aria-expanded", String(expanded));
// this.header.style.color = this.styles.headerForeground
// ? this.styles.headerForeground.toString()
// : "";
// this.header.style.backgroundColor = this.styles.headerBackground
// ? this.styles.headerBackground.toString()
// : "";
// this.header.style.borderTop =
// this.styles.headerBorder && this.orientation === Orientation.VERTICAL
// ? `1px solid ${this.styles.headerBorder}`
// : "";
// this._dropBackground = this.styles.dropBackground;
// }
protected abstract renderHeader(container: HTMLElement): void;
protected abstract renderBody(container: HTMLElement): void;
protected abstract renderHeader(container: HTMLElement): void
protected abstract renderBody(container: HTMLElement): void
}
interface PaneItem {
pane: Pane;
disposable: IDisposable;
pane: Pane
disposable: IDisposable
}
export class PaneView implements IDisposable {
private element: HTMLElement;
private splitview: SplitView;
private paneItems: PaneItem[] = [];
private _orientation: Orientation;
private animationTimer: NodeJS.Timeout;
private orthogonalSize: number;
private size: number;
private element: HTMLElement
private splitview: SplitView
private paneItems: PaneItem[] = []
private _orientation: Orientation
private animationTimer: NodeJS.Timeout
private orthogonalSize: number
private size: number
constructor(container: HTMLElement, options: { orientation: Orientation }) {
this._orientation = options.orientation ?? Orientation.VERTICAL;
constructor(container: HTMLElement, options: { orientation: Orientation }) {
this._orientation = options.orientation ?? Orientation.VERTICAL
this.element = document.createElement("div");
this.element.className = "pane-container";
this.element = document.createElement('div')
this.element.className = 'pane-container'
this.setupAnimation = this.setupAnimation.bind(this);
this.setupAnimation = this.setupAnimation.bind(this)
container.appendChild(this.element);
this.splitview = new SplitView(this.element, {
orientation: this._orientation,
});
}
public setOrientation(orientation: Orientation) {
this._orientation = orientation;
}
public addPane(pane: Pane, size?: number, index = this.splitview.length) {
const disposable = pane.onDidChangeExpansionState(this.setupAnimation);
const paneItem: PaneItem = {
pane,
disposable: {
dispose: () => {
disposable.dispose();
},
},
};
this.paneItems.splice(index, 0, paneItem);
pane.orientation = this._orientation;
pane.orthogonalSize = this.orthogonalSize;
this.splitview.addView(pane, size, index);
}
public getPanes() {
return this.splitview.getViews() as Pane[];
}
public removePane(index: number) {
this.splitview.removeView(index);
const paneItem = this.paneItems.splice(index, 1)[0];
paneItem.disposable.dispose();
return paneItem;
}
public moveView(from: number, to: number) {
const view = this.removePane(from);
this.addPane(view.pane, to);
}
public layout(size: number, orthogonalSize: number): void {
this.orthogonalSize = orthogonalSize;
this.size = size;
for (const paneItem of this.paneItems) {
paneItem.pane.orthogonalSize = this.orthogonalSize;
container.appendChild(this.element)
this.splitview = new SplitView(this.element, {
orientation: this._orientation,
})
}
this.splitview.layout(this.size, this.orthogonalSize);
}
private setupAnimation() {
if (this.animationTimer) {
clearTimeout(this.animationTimer);
public setOrientation(orientation: Orientation) {
this._orientation = orientation
}
addClasses(this.element, "animated");
public addPane(pane: Pane, size?: number, index = this.splitview.length) {
const disposable = pane.onDidChangeExpansionState(this.setupAnimation)
this.animationTimer = setTimeout(() => {
this.animationTimer = undefined;
removeClasses(this.element, "animated");
}, 200);
}
const paneItem: PaneItem = {
pane,
disposable: {
dispose: () => {
disposable.dispose()
},
},
}
public dispose() {
this.paneItems.forEach((paneItem) => {
paneItem.disposable.dispose();
});
}
this.paneItems.splice(index, 0, paneItem)
pane.orientation = this._orientation
pane.orthogonalSize = this.orthogonalSize
this.splitview.addView(pane, size, index)
}
public getPanes() {
return this.splitview.getViews() as Pane[]
}
public removePane(index: number) {
this.splitview.removeView(index)
const paneItem = this.paneItems.splice(index, 1)[0]
paneItem.disposable.dispose()
return paneItem
}
public moveView(from: number, to: number) {
const view = this.removePane(from)
this.addPane(view.pane, to)
}
public layout(size: number, orthogonalSize: number): void {
this.orthogonalSize = orthogonalSize
this.size = size
for (const paneItem of this.paneItems) {
paneItem.pane.orthogonalSize = this.orthogonalSize
}
this.splitview.layout(this.size, this.orthogonalSize)
}
private setupAnimation() {
if (this.animationTimer) {
clearTimeout(this.animationTimer)
}
addClasses(this.element, 'animated')
this.animationTimer = setTimeout(() => {
this.animationTimer = undefined
removeClasses(this.element, 'animated')
}, 200)
}
public dispose() {
this.paneItems.forEach((paneItem) => {
paneItem.disposable.dispose()
})
}
}

View File

@ -1,48 +1,48 @@
import { IGroupPanel } from "../groupview/panel/types";
import { Layout } from "../layout/layout";
import { DefaultPanel } from "../groupview/panel/panel";
import { PanelContentPart, PanelHeaderPart } from "../groupview/panel/parts";
import { IPanelDeserializer } from "../layout/deserializer";
import { IGroupPanel } from '../groupview/panel/types'
import { Layout } from '../layout/layout'
import { DefaultPanel } from '../groupview/panel/panel'
import { PanelContentPart, PanelHeaderPart } from '../groupview/panel/parts'
import { IPanelDeserializer } from '../layout/deserializer'
import {
createContentComponent,
createTabComponent,
} from "../layout/componentFactory";
createContentComponent,
createTabComponent,
} from '../layout/componentFactory'
export class ReactPanelDeserialzier implements IPanelDeserializer {
constructor(private readonly layout: Layout) {}
constructor(private readonly layout: Layout) {}
public fromJSON(panelData: { [index: string]: any }): IGroupPanel {
const panelId = panelData.id;
const content = panelData.content;
const tab = panelData.tab;
const props = panelData.props;
const title = panelData.title;
const state = panelData.state;
const suppressClosable = panelData.suppressClosable;
public fromJSON(panelData: { [index: string]: any }): IGroupPanel {
const panelId = panelData.id
const content = panelData.content
const tab = panelData.tab
const props = panelData.props
const title = panelData.title
const state = panelData.state
const suppressClosable = panelData.suppressClosable
const contentPart = createContentComponent(
content.id,
this.layout.options.components,
this.layout.options.frameworkComponents,
this.layout.options.frameworkComponentFactory.content
) as PanelContentPart;
const contentPart = createContentComponent(
content.id,
this.layout.options.components,
this.layout.options.frameworkComponents,
this.layout.options.frameworkComponentFactory.content
) as PanelContentPart
const headerPart = createTabComponent(
tab.id,
this.layout.options.tabComponents,
this.layout.options.frameworkComponentFactory,
this.layout.options.frameworkComponentFactory.tab
) as PanelHeaderPart;
const headerPart = createTabComponent(
tab.id,
this.layout.options.tabComponents,
this.layout.options.frameworkComponentFactory,
this.layout.options.frameworkComponentFactory.tab
) as PanelHeaderPart
const panel = new DefaultPanel(panelId, headerPart, contentPart);
const panel = new DefaultPanel(panelId, headerPart, contentPart)
panel.init({
title,
suppressClosable,
params: props || {},
state: state || {},
});
panel.init({
title,
suppressClosable,
params: props || {},
state: state || {},
})
return panel;
}
return panel
}
}

View File

@ -1,67 +1,71 @@
import * as React from "react";
import * as React from 'react'
import {
ComponentGridview,
IComponentGridviewLayout,
} from "../layout/componentGridview";
import { IGridApi } from "../panel/api";
import { Orientation } from "../splitview/splitview";
import { ReactComponentGridView } from "./reactComponentGridView";
ComponentGridview,
IComponentGridviewLayout,
} from '../layout/componentGridview'
import { IGridApi } from '../panel/api'
import { Orientation } from '../splitview/splitview'
import { ReactComponentGridView } from './reactComponentGridView'
export interface GridviewReadyEvent {
api: IComponentGridviewLayout;
api: IComponentGridviewLayout
}
export interface IGridviewPanelProps {
api: IGridApi;
api: IGridApi
}
export interface IGridviewComponentProps {
orientation: Orientation;
onReady?: (event: GridviewReadyEvent) => void;
components: {
[index: string]: React.FunctionComponent<IGridviewPanelProps>;
};
orientation: Orientation
onReady?: (event: GridviewReadyEvent) => void
components: {
[index: string]: React.FunctionComponent<IGridviewPanelProps>
}
}
export const GridviewComponent = (props: IGridviewComponentProps) => {
const domReference = React.useRef<HTMLDivElement>();
const gridview = React.useRef<IComponentGridviewLayout>();
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
const domReference = React.useRef<HTMLDivElement>()
const gridview = React.useRef<IComponentGridviewLayout>()
const [portals, setPortals] = React.useState<React.ReactPortal[]>([])
const addPortal = React.useCallback((p: React.ReactPortal) => {
setPortals((portals) => [...portals, p]);
return {
dispose: () => {
setPortals((portals) => portals.filter((portal) => portal !== p));
},
};
}, []);
const addPortal = React.useCallback((p: React.ReactPortal) => {
setPortals((portals) => [...portals, p])
return {
dispose: () => {
setPortals((portals) =>
portals.filter((portal) => portal !== p)
)
},
}
}, [])
React.useEffect(() => {
gridview.current = new ComponentGridview(domReference.current, {
orientation: props.orientation,
frameworkComponents: props.components,
frameworkComponentFactory: {
createComponent: (id: string, component: any) => {
return new ReactComponentGridView(id, id, component, { addPortal });
},
},
});
React.useEffect(() => {
gridview.current = new ComponentGridview(domReference.current, {
orientation: props.orientation,
frameworkComponents: props.components,
frameworkComponentFactory: {
createComponent: (id: string, component: any) => {
return new ReactComponentGridView(id, id, component, {
addPortal,
})
},
},
})
if (props.onReady) {
props.onReady({ api: gridview.current });
}
}, []);
if (props.onReady) {
props.onReady({ api: gridview.current })
}
}, [])
return (
<div
style={{
height: "100%",
width: "100%",
}}
ref={domReference}
>
{portals}
</div>
);
};
return (
<div
style={{
height: '100%',
width: '100%',
}}
ref={domReference}
>
{portals}
</div>
)
}

View File

@ -1,134 +1,142 @@
import * as React from "react";
import { IDisposable } from "../lifecycle";
import { Layout, Api } from "../layout/layout";
import { ReactPanelContentPart } from "./reactContentPart";
import { ReactPanelHeaderPart } from "./reactHeaderPart";
import { IPanelProps } from "./react";
import { ReactPanelDeserialzier } from "./deserializer";
import { GroupPanelFrameworkComponentFactory, TabContextMenuEvent } from "../layout/options";
import * as React from 'react'
import { IDisposable } from '../lifecycle'
import { Layout, Api } from '../layout/layout'
import { ReactPanelContentPart } from './reactContentPart'
import { ReactPanelHeaderPart } from './reactHeaderPart'
import { IPanelProps } from './react'
import { ReactPanelDeserialzier } from './deserializer'
import {
GroupPanelFrameworkComponentFactory,
TabContextMenuEvent,
} from '../layout/options'
export interface OnReadyEvent {
api: Api;
api: Api
}
export interface ReactLayout {
addPortal: (portal: React.ReactPortal) => IDisposable;
addPortal: (portal: React.ReactPortal) => IDisposable
}
export interface IReactGridProps {
components?: {
[componentName: string]: React.FunctionComponent<IPanelProps>;
};
tabComponents?: {
[componentName: string]: React.FunctionComponent<IPanelProps>;
};
watermarkComponent?: React.FunctionComponent;
onReady?: (event: OnReadyEvent) => void;
autoSizeToFitContainer?: boolean;
serializedLayout?: {};
deserializer?: {
fromJSON: (
data: any
) => {
component: React.FunctionComponent<IPanelProps>;
tabComponent?: React.FunctionComponent<IPanelProps>;
props?: { [key: string]: any };
};
};
debug?: boolean;
tabHeight?: number;
enableExternalDragEvents?: boolean;
onTabContextMenu?: (event: TabContextMenuEvent) => void;
components?: {
[componentName: string]: React.FunctionComponent<IPanelProps>
}
tabComponents?: {
[componentName: string]: React.FunctionComponent<IPanelProps>
}
watermarkComponent?: React.FunctionComponent
onReady?: (event: OnReadyEvent) => void
autoSizeToFitContainer?: boolean
serializedLayout?: {}
deserializer?: {
fromJSON: (
data: any
) => {
component: React.FunctionComponent<IPanelProps>
tabComponent?: React.FunctionComponent<IPanelProps>
props?: { [key: string]: any }
}
}
debug?: boolean
tabHeight?: number
enableExternalDragEvents?: boolean
onTabContextMenu?: (event: TabContextMenuEvent) => void
}
export const ReactGrid = (props: IReactGridProps) => {
const domReference = React.useRef<HTMLDivElement>();
const layoutReference = React.useRef<Layout>();
const domReference = React.useRef<HTMLDivElement>()
const layoutReference = React.useRef<Layout>()
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
const [portals, setPortals] = React.useState<React.ReactPortal[]>([])
React.useEffect(() => {
const addPortal = (p: React.ReactPortal) => {
setPortals((portals) => [...portals, p]);
return {
dispose: () => {
setPortals((portals) => portals.filter((portal) => portal !== p));
},
};
};
React.useEffect(() => {
const addPortal = (p: React.ReactPortal) => {
setPortals((portals) => [...portals, p])
return {
dispose: () => {
setPortals((portals) =>
portals.filter((portal) => portal !== p)
)
},
}
}
const factory: GroupPanelFrameworkComponentFactory = {
content: {
createComponent: (
id: string,
component: React.FunctionComponent<IPanelProps>
) => {
return new ReactPanelContentPart(id, component, { addPortal });
},
},
tab: {
createComponent: (
id: string,
component: React.FunctionComponent<IPanelProps>
) => {
return new ReactPanelHeaderPart(id, component, { addPortal });
},
},
};
const factory: GroupPanelFrameworkComponentFactory = {
content: {
createComponent: (
id: string,
component: React.FunctionComponent<IPanelProps>
) => {
return new ReactPanelContentPart(id, component, {
addPortal,
})
},
},
tab: {
createComponent: (
id: string,
component: React.FunctionComponent<IPanelProps>
) => {
return new ReactPanelHeaderPart(id, component, {
addPortal,
})
},
},
}
const layout = new Layout({
frameworkComponentFactory: factory,
frameworkComponents: props.components,
frameworkTabComponents: props.tabComponents,
tabHeight: props.tabHeight,
debug: props.debug,
enableExternalDragEvents: props.enableExternalDragEvents,
});
const layout = new Layout({
frameworkComponentFactory: factory,
frameworkComponents: props.components,
frameworkTabComponents: props.tabComponents,
tabHeight: props.tabHeight,
debug: props.debug,
enableExternalDragEvents: props.enableExternalDragEvents,
})
layoutReference.current = layout;
domReference.current.appendChild(layoutReference.current.element);
layoutReference.current = layout
domReference.current.appendChild(layoutReference.current.element)
layout.deserializer = new ReactPanelDeserialzier(layout);
layout.deserializer = new ReactPanelDeserialzier(layout)
layout.resizeToFit()
layout.resizeToFit();
if (props.serializedLayout) {
layout.deserialize(props.serializedLayout)
}
if (props.serializedLayout) {
layout.deserialize(props.serializedLayout);
}
if (props.onReady) {
props.onReady({ api: layout })
}
if (props.onReady) {
props.onReady({ api: layout });
}
return () => {
layout.dispose()
}
}, [])
return () => {
layout.dispose();
};
}, []);
React.useEffect(() => {
const disposable = layoutReference.current.onTabContextMenu((event) => {
props.onTabContextMenu(event)
})
React.useEffect(() => {
const disposable = layoutReference.current.onTabContextMenu((event) => {
props.onTabContextMenu(event);
});
return () => {
disposable.dispose()
}
}, [props.onTabContextMenu])
return () => {
disposable.dispose()
}
}, [props.onTabContextMenu])
React.useEffect(() => {
layoutReference.current.setAutoResizeToFit(props.autoSizeToFitContainer)
}, [props.autoSizeToFitContainer])
React.useEffect(() => {
layoutReference.current.setAutoResizeToFit(props.autoSizeToFitContainer);
}, [props.autoSizeToFitContainer]);
return (
<div
style={{
// height: "100%",
width: "100%",
}}
ref={domReference}
>
{portals}
</div>
);
};
return (
<div
style={{
// height: "100%",
width: '100%',
}}
ref={domReference}
>
{portals}
</div>
)
}

View File

@ -1,103 +1,112 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import { IDisposable } from "../lifecycle";
import { IGroupPanelApi } from "../groupview/panel/api";
import { sequentialNumberGenerator } from "../math";
import { IBaseViewApi } from "../panel/api";
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { IDisposable } from '../lifecycle'
import { IGroupPanelApi } from '../groupview/panel/api'
import { sequentialNumberGenerator } from '../math'
import { IBaseViewApi } from '../panel/api'
export interface IPanelProps {
api: IGroupPanelApi;
api: IGroupPanelApi
}
interface IPanelWrapperProps {
component: React.FunctionComponent<IPanelProps>;
componentProps: any;
component: React.FunctionComponent<IPanelProps>
componentProps: any
}
interface IPanelWrapperRef {
update: (props: { [key: string]: any }) => void;
update: (props: { [key: string]: any }) => void
}
const PanelWrapper = React.forwardRef(
(props: IPanelWrapperProps, ref: React.RefObject<IPanelWrapperRef>) => {
const [_, triggerRender] = React.useState<number>();
const _props = React.useRef<{ [key: string]: any }>(props.componentProps);
(props: IPanelWrapperProps, ref: React.RefObject<IPanelWrapperRef>) => {
const [_, triggerRender] = React.useState<number>()
const _props = React.useRef<{ [key: string]: any }>(
props.componentProps
)
React.useImperativeHandle(
ref,
() => ({
update: (props: { [key: string]: any }) => {
_props.current = { ..._props.current, ...props };
triggerRender(Date.now());
},
}),
[]
);
React.useImperativeHandle(
ref,
() => ({
update: (props: { [key: string]: any }) => {
_props.current = { ..._props.current, ...props }
triggerRender(Date.now())
},
}),
[]
)
React.useEffect(() => {
console.debug("[reactwrapper] component mounted ");
return () => {
console.debug("[reactwrapper] component unmounted ");
};
}, []);
React.useEffect(() => {
console.debug('[reactwrapper] component mounted ')
return () => {
console.debug('[reactwrapper] component unmounted ')
}
}, [])
return React.createElement(props.component, _props.current as IPanelProps);
}
);
return React.createElement(
props.component,
_props.current as IPanelProps
)
}
)
const counter = sequentialNumberGenerator();
const counter = sequentialNumberGenerator()
export class ReactPart implements IDisposable {
private componentInstance: IPanelWrapperRef;
private ref: { portal: React.ReactPortal; disposable: IDisposable };
private disposed: boolean;
private componentInstance: IPanelWrapperRef
private ref: { portal: React.ReactPortal; disposable: IDisposable }
private disposed: boolean
constructor(
private readonly parent: HTMLElement,
private readonly api: IBaseViewApi,
private readonly addPortal: (portal: React.ReactPortal) => IDisposable,
private readonly component: React.FunctionComponent<{}>,
private readonly parameters: { [key: string]: any }
) {
this.createPortal();
}
public update(props: {}) {
if (this.disposed) {
throw new Error("invalid operation");
constructor(
private readonly parent: HTMLElement,
private readonly api: IBaseViewApi,
private readonly addPortal: (portal: React.ReactPortal) => IDisposable,
private readonly component: React.FunctionComponent<{}>,
private readonly parameters: { [key: string]: any }
) {
this.createPortal()
}
this.componentInstance?.update(props);
}
public update(props: {}) {
if (this.disposed) {
throw new Error('invalid operation')
}
private createPortal() {
if (this.disposed) {
throw new Error("invalid operation");
this.componentInstance?.update(props)
}
let props = {
api: this.api,
...this.parameters,
} as any;
private createPortal() {
if (this.disposed) {
throw new Error('invalid operation')
}
const wrapper = React.createElement(PanelWrapper, {
component: this.component,
componentProps: props,
ref: (element: any) => {
this.componentInstance = element;
},
});
const portal = ReactDOM.createPortal(wrapper, this.parent, counter.next());
let props = {
api: this.api,
...this.parameters,
} as any
this.ref = {
portal,
disposable: this.addPortal(portal),
};
}
const wrapper = React.createElement(PanelWrapper, {
component: this.component,
componentProps: props,
ref: (element: any) => {
this.componentInstance = element
},
})
const portal = ReactDOM.createPortal(
wrapper,
this.parent,
counter.next()
)
public dispose() {
this.ref?.disposable?.dispose();
this.ref = undefined;
this.disposed = true;
}
this.ref = {
portal,
disposable: this.addPortal(portal),
}
}
public dispose() {
this.ref?.disposable?.dispose()
this.ref = undefined
this.disposed = true
}
}

View File

@ -1,142 +1,144 @@
import { trackFocus } from "../dom";
import { Emitter } from "../events";
import { GridApi } from "../panel/api";
import { CompositeDisposable } from "../lifecycle";
import { ReactLayout } from "./layout";
import { ReactPart } from "./react";
import { ISplitviewPanelProps } from "./splitview";
import { PanelUpdateEvent, InitParameters, IPanel } from "../panel/types";
import { IComponentGridview } from "../layout/componentGridview";
import { FunctionOrValue } from "../types";
import { trackFocus } from '../dom'
import { Emitter } from '../events'
import { GridApi } from '../panel/api'
import { CompositeDisposable } from '../lifecycle'
import { ReactLayout } from './layout'
import { ReactPart } from './react'
import { ISplitviewPanelProps } from './splitview'
import { PanelUpdateEvent, InitParameters, IPanel } from '../panel/types'
import { IComponentGridview } from '../layout/componentGridview'
import { FunctionOrValue } from '../types'
export class ReactComponentGridView
extends CompositeDisposable
implements IComponentGridview, IPanel {
private _element: HTMLElement;
private part: ReactPart;
private params: { params: any };
private api: GridApi;
extends CompositeDisposable
implements IComponentGridview, IPanel {
private _element: HTMLElement
private part: ReactPart
private params: { params: any }
private api: GridApi
private _onDidChange: Emitter<number | undefined> = new Emitter<
number | undefined
>();
public onDidChange = this._onDidChange.event;
private _onDidChange: Emitter<number | undefined> = new Emitter<
number | undefined
>()
public onDidChange = this._onDidChange.event
get element() {
return this._element;
}
private _minimumWidth: FunctionOrValue<number> = 200;
private _minimumHeight: FunctionOrValue<number> = 200;
private _maximumWidth: FunctionOrValue<number> = Number.MAX_SAFE_INTEGER;
private _maximumHeight: FunctionOrValue<number> = Number.MAX_SAFE_INTEGER;
get minimumWidth() {
return typeof this._minimumWidth === "function"
? this._minimumWidth()
: this._minimumWidth;
}
get minimumHeight() {
return typeof this._minimumHeight === "function"
? this._minimumHeight()
: this._minimumHeight;
}
get maximumHeight() {
return typeof this._maximumHeight === "function"
? this._maximumHeight()
: this._maximumHeight;
}
get maximumWidth() {
return typeof this._maximumWidth === "function"
? this._maximumWidth()
: this._maximumWidth;
}
constructor(
public readonly id: string,
private readonly componentName: string,
private readonly component: React.FunctionComponent<ISplitviewPanelProps>,
private readonly parent: ReactLayout
) {
super();
this.api = new GridApi();
if (!this.component) {
throw new Error("React.FunctionalComponent cannot be undefined");
get element() {
return this._element
}
this._element = document.createElement("div");
this._element.tabIndex = -1;
this._element.style.outline = "none";
private _minimumWidth: FunctionOrValue<number> = 200
private _minimumHeight: FunctionOrValue<number> = 200
private _maximumWidth: FunctionOrValue<number> = Number.MAX_SAFE_INTEGER
private _maximumHeight: FunctionOrValue<number> = Number.MAX_SAFE_INTEGER
const { onDidFocus, onDidBlur } = trackFocus(this._element);
get minimumWidth() {
return typeof this._minimumWidth === 'function'
? this._minimumWidth()
: this._minimumWidth
}
get minimumHeight() {
return typeof this._minimumHeight === 'function'
? this._minimumHeight()
: this._minimumHeight
}
get maximumHeight() {
return typeof this._maximumHeight === 'function'
? this._maximumHeight()
: this._maximumHeight
}
get maximumWidth() {
return typeof this._maximumWidth === 'function'
? this._maximumWidth()
: this._maximumWidth
}
this.addDisposables(
this.api.onDidConstraintsChange((event) => {
if (
typeof event.minimumWidth === "number" ||
typeof event.minimumWidth === "function"
) {
this._minimumWidth = event.minimumWidth;
constructor(
public readonly id: string,
private readonly componentName: string,
private readonly component: React.FunctionComponent<
ISplitviewPanelProps
>,
private readonly parent: ReactLayout
) {
super()
this.api = new GridApi()
if (!this.component) {
throw new Error('React.FunctionalComponent cannot be undefined')
}
if (
typeof event.minimumHeight === "number" ||
typeof event.minimumHeight === "function"
) {
this._minimumHeight = event.minimumHeight;
this._element = document.createElement('div')
this._element.tabIndex = -1
this._element.style.outline = 'none'
const { onDidFocus, onDidBlur } = trackFocus(this._element)
this.addDisposables(
this.api.onDidConstraintsChange((event) => {
if (
typeof event.minimumWidth === 'number' ||
typeof event.minimumWidth === 'function'
) {
this._minimumWidth = event.minimumWidth
}
if (
typeof event.minimumHeight === 'number' ||
typeof event.minimumHeight === 'function'
) {
this._minimumHeight = event.minimumHeight
}
if (
typeof event.maximumWidth === 'number' ||
typeof event.maximumWidth === 'function'
) {
this._maximumWidth = event.maximumWidth
}
if (
typeof event.maximumHeight === 'number' ||
typeof event.maximumHeight === 'function'
) {
this._maximumHeight = event.maximumHeight
}
}),
onDidFocus(() => {
this.api._onDidChangeFocus.fire({ isFocused: true })
}),
onDidBlur(() => {
this.api._onDidChangeFocus.fire({ isFocused: false })
})
)
}
layout(width: number, height: number) {
this.api._onDidPanelDimensionChange.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(),
}
if (
typeof event.maximumWidth === "number" ||
typeof event.maximumWidth === "function"
) {
this._maximumWidth = event.maximumWidth;
}
if (
typeof event.maximumHeight === "number" ||
typeof event.maximumHeight === "function"
) {
this._maximumHeight = event.maximumHeight;
}
}),
onDidFocus(() => {
this.api._onDidChangeFocus.fire({ isFocused: true });
}),
onDidBlur(() => {
this.api._onDidChangeFocus.fire({ isFocused: false });
})
);
}
}
layout(width: number, height: number) {
this.api._onDidPanelDimensionChange.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() {
super.dispose();
this.api.dispose();
}
dispose() {
super.dispose()
this.api.dispose()
}
}

View File

@ -1,118 +1,120 @@
import { trackFocus } from "../dom";
import { Emitter } from "../events";
import { PanelApi } from "../panel/api";
import { PanelDimensionChangeEvent } from "../panel/types";
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";
import { trackFocus } from '../dom'
import { Emitter } from '../events'
import { PanelApi } from '../panel/api'
import { PanelDimensionChangeEvent } from '../panel/types'
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: PanelApi;
extends CompositeDisposable
implements IView, IPanel {
private _element: HTMLElement
private part: ReactPart
private params: { params: any }
private api: PanelApi
private _onDidChange: Emitter<number | undefined> = new Emitter<
number | undefined
>();
public onDidChange = this._onDidChange.event;
private _onDidChange: Emitter<number | undefined> = new Emitter<
number | undefined
>()
public onDidChange = this._onDidChange.event
get element() {
return this._element;
}
private _minimumSize: number = 200;
private _maximumSize: number = Number.MAX_SAFE_INTEGER;
private _snapSize: number;
get minimumSize() {
return this._minimumSize;
}
set minimumSize(value: number) {
this._minimumSize = value;
}
get snapSize() {
return this._snapSize;
}
set snapSize(value: number) {
this._snapSize = value;
}
get maximumSize() {
return this._maximumSize;
}
set maximumSize(value: number) {
this._maximumSize = value;
}
constructor(
public readonly id: string,
private readonly componentName: string,
private readonly component: React.FunctionComponent<ISplitviewPanelProps>,
private readonly parent: ReactLayout
) {
super();
this.api = new PanelApi();
if (!this.component) {
throw new Error("React.FunctionalComponent cannot be undefined");
get element() {
return this._element
}
this._element = document.createElement("div");
this._element.tabIndex = -1;
this._element.style.outline = "none";
private _minimumSize: number = 200
private _maximumSize: number = Number.MAX_SAFE_INTEGER
private _snapSize: number
const { onDidFocus, onDidBlur } = trackFocus(this._element);
get minimumSize() {
return this._minimumSize
}
set minimumSize(value: number) {
this._minimumSize = value
}
this.addDisposables(
onDidFocus(() => {
this.api._onDidChangeFocus.fire({ isFocused: true });
}),
onDidBlur(() => {
this.api._onDidChangeFocus.fire({ isFocused: false });
})
);
}
get snapSize() {
return this._snapSize
}
set snapSize(value: number) {
this._snapSize = value
}
layout(width: number, height: number) {
this.api._onDidPanelDimensionChange.fire({ width, height });
}
get maximumSize() {
return this._maximumSize
}
set maximumSize(value: number) {
this._maximumSize = value
}
init(parameters: InitParameters): void {
this.params = parameters;
this.part = new ReactPart(
this.element,
this.api,
this.parent.addPortal,
this.component,
parameters.params
);
}
constructor(
public readonly id: string,
private readonly componentName: string,
private readonly component: React.FunctionComponent<
ISplitviewPanelProps
>,
private readonly parent: ReactLayout
) {
super()
this.api = new PanelApi()
if (!this.component) {
throw new Error('React.FunctionalComponent cannot be undefined')
}
update(params: PanelUpdateEvent) {
this.params = { ...this.params.params, ...params };
this.part.update(params);
}
this._element = document.createElement('div')
this._element.tabIndex = -1
this._element.style.outline = 'none'
toJSON(): object {
return {
id: this.id,
component: this.componentName,
props: this.params.params,
state: this.api.getState(),
};
}
const { onDidFocus, onDidBlur } = trackFocus(this._element)
dispose() {
super.dispose();
this.api.dispose();
}
this.addDisposables(
onDidFocus(() => {
this.api._onDidChangeFocus.fire({ isFocused: true })
}),
onDidBlur(() => {
this.api._onDidChangeFocus.fire({ isFocused: false })
})
)
}
layout(width: number, height: number) {
this.api._onDidPanelDimensionChange.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() {
super.dispose()
this.api.dispose()
}
}

View File

@ -1,62 +1,62 @@
import * as React from "react";
import * as React from 'react'
import {
PanelContentPart,
PartInitParameters,
ClosePanelResult,
} from "../groupview/panel/parts";
import { ReactPart, IPanelProps } from "./react";
import { ReactLayout } from "./layout";
PanelContentPart,
PartInitParameters,
ClosePanelResult,
} from '../groupview/panel/parts'
import { ReactPart, IPanelProps } from './react'
import { ReactLayout } from './layout'
export class ReactPanelContentPart implements PanelContentPart {
private _element: HTMLElement;
private part: ReactPart;
private _element: HTMLElement
private part: ReactPart
get element() {
return this._element;
}
get element() {
return this._element
}
constructor(
public readonly id: string,
private readonly component: React.FunctionComponent<IPanelProps>,
private readonly parent: ReactLayout
) {
this._element = document.createElement("div");
}
constructor(
public readonly id: string,
private readonly component: React.FunctionComponent<IPanelProps>,
private readonly parent: ReactLayout
) {
this._element = document.createElement('div')
}
public init(parameters: PartInitParameters): void {
this.part = new ReactPart(
this.element,
parameters.api,
this.parent.addPortal,
this.component,
parameters.params
);
}
public init(parameters: PartInitParameters): void {
this.part = new ReactPart(
this.element,
parameters.api,
this.parent.addPortal,
this.component,
parameters.params
)
}
public toJSON() {
return {
id: this.id,
};
}
public toJSON() {
return {
id: this.id,
}
}
public update(params: {}) {
this.part.update(params);
}
public update(params: {}) {
this.part.update(params)
}
public setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void {
// noop - retrieval from api
}
public setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void {
// noop - retrieval from api
}
public layout(width: number, height: number): void {}
public layout(width: number, height: number): void {}
public close(): Promise<ClosePanelResult> {
return Promise.resolve(ClosePanelResult.CLOSE);
}
public close(): Promise<ClosePanelResult> {
return Promise.resolve(ClosePanelResult.CLOSE)
}
public focus(): void {}
public onHide(): void {}
public focus(): void {}
public onHide(): void {}
public dispose() {
this.part?.dispose();
}
public dispose() {
this.part?.dispose()
}
}

View File

@ -1,49 +1,49 @@
import * as React from "react";
import { PanelHeaderPart, PartInitParameters } from "../groupview/panel/parts";
import { ReactPart, IPanelProps } from "./react";
import { ReactLayout } from "./layout";
import * as React from 'react'
import { PanelHeaderPart, PartInitParameters } from '../groupview/panel/parts'
import { ReactPart, IPanelProps } from './react'
import { ReactLayout } from './layout'
export class ReactPanelHeaderPart implements PanelHeaderPart {
private _element: HTMLElement;
private part: ReactPart;
private _element: HTMLElement
private part: ReactPart
get element() {
return this._element;
}
get element() {
return this._element
}
constructor(
public readonly id: string,
private readonly component: React.FunctionComponent<IPanelProps>,
private readonly parent: ReactLayout
) {
this._element = document.createElement("div");
}
constructor(
public readonly id: string,
private readonly component: React.FunctionComponent<IPanelProps>,
private readonly parent: ReactLayout
) {
this._element = document.createElement('div')
}
public init(parameters: PartInitParameters): void {
this.part = new ReactPart(
this.element,
parameters.api,
this.parent.addPortal,
this.component,
parameters.params
);
}
public init(parameters: PartInitParameters): void {
this.part = new ReactPart(
this.element,
parameters.api,
this.parent.addPortal,
this.component,
parameters.params
)
}
public toJSON() {
return {
id: this.id,
};
}
public toJSON() {
return {
id: this.id,
}
}
public layout(height: string) {
// noop - retrieval from api
}
public layout(height: string) {
// noop - retrieval from api
}
public setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void {
// noop - retrieval from api
}
public setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void {
// noop - retrieval from api
}
public dispose() {
this.part?.dispose();
}
public dispose() {
this.part?.dispose()
}
}

View File

@ -1,93 +1,97 @@
import * as React from "react";
import { IPanelApi } from "../panel/api";
import { IDisposable } from "../lifecycle";
import * as React from 'react'
import { IPanelApi } from '../panel/api'
import { IDisposable } from '../lifecycle'
import {
IComponentSplitview,
ComponentSplitview,
} from "../splitview/componentSplitview";
import { Orientation } from "../splitview/splitview";
import { ReactComponentView } from "./reactComponentView";
IComponentSplitview,
ComponentSplitview,
} from '../splitview/componentSplitview'
import { Orientation } from '../splitview/splitview'
import { ReactComponentView } from './reactComponentView'
export interface SplitviewFacade {
addFromComponent(options: {
id: string;
component: string;
params?: { [index: string]: any };
}): void;
layout(size: number, orthogonalSize: number): void;
onChange: (cb: (event: { proportions: number[] }) => void) => IDisposable;
toJSON: () => any;
deserialize: (data: any) => void;
minimumSize: number;
addFromComponent(options: {
id: string
component: string
params?: { [index: string]: any }
}): void
layout(size: number, orthogonalSize: number): void
onChange: (cb: (event: { proportions: number[] }) => void) => IDisposable
toJSON: () => any
deserialize: (data: any) => void
minimumSize: number
}
export interface SplitviewReadyEvent {
api: IComponentSplitview;
api: IComponentSplitview
}
export interface ISplitviewPanelProps {
api: IPanelApi;
api: IPanelApi
}
export interface ISplitviewComponentProps {
orientation: Orientation;
onReady?: (event: SplitviewReadyEvent) => void;
components: {
[index: string]: React.FunctionComponent<ISplitviewPanelProps>;
};
orientation: Orientation
onReady?: (event: SplitviewReadyEvent) => void
components: {
[index: string]: React.FunctionComponent<ISplitviewPanelProps>
}
}
export const SplitViewComponent = (props: ISplitviewComponentProps) => {
const domReference = React.useRef<HTMLDivElement>();
const splitpanel = React.useRef<IComponentSplitview>();
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
const domReference = React.useRef<HTMLDivElement>()
const splitpanel = React.useRef<IComponentSplitview>()
const [portals, setPortals] = React.useState<React.ReactPortal[]>([])
const addPortal = React.useCallback((p: React.ReactPortal) => {
setPortals((portals) => [...portals, p]);
return {
dispose: () => {
setPortals((portals) => portals.filter((portal) => portal !== p));
},
};
}, []);
const addPortal = React.useCallback((p: React.ReactPortal) => {
setPortals((portals) => [...portals, p])
return {
dispose: () => {
setPortals((portals) =>
portals.filter((portal) => portal !== p)
)
},
}
}, [])
React.useEffect(() => {
splitpanel.current = new ComponentSplitview(domReference.current, {
orientation: props.orientation,
frameworkComponents: props.components,
frameworkWrapper: {
createComponent: (id: string, component: any) => {
return new ReactComponentView(id, id, component, { addPortal });
},
},
proportionalLayout: false,
});
React.useEffect(() => {
splitpanel.current = new ComponentSplitview(domReference.current, {
orientation: props.orientation,
frameworkComponents: props.components,
frameworkWrapper: {
createComponent: (id: string, component: any) => {
return new ReactComponentView(id, id, component, {
addPortal,
})
},
},
proportionalLayout: false,
})
const { width, height } = domReference.current.getBoundingClientRect();
const [size, orthogonalSize] =
props.orientation === Orientation.HORIZONTAL
? [width, height]
: [height, width];
splitpanel.current.layout(size, orthogonalSize);
const { width, height } = domReference.current.getBoundingClientRect()
const [size, orthogonalSize] =
props.orientation === Orientation.HORIZONTAL
? [width, height]
: [height, width]
splitpanel.current.layout(size, orthogonalSize)
if (props.onReady) {
props.onReady({ api: splitpanel.current });
}
if (props.onReady) {
props.onReady({ api: splitpanel.current })
}
return () => {
splitpanel.current.dispose();
};
}, []);
return () => {
splitpanel.current.dispose()
}
}, [])
return (
<div
style={{
height: "100%",
width: "100%",
}}
ref={domReference}
>
{portals}
</div>
);
};
return (
<div
style={{
height: '100%',
width: '100%',
}}
ref={domReference}
>
{portals}
</div>
)
}

View File

@ -1,156 +1,158 @@
import { IDisposable } from "../lifecycle";
import { LayoutPriority, Orientation, SplitView } from "./splitview";
import { IDisposable } from '../lifecycle'
import { LayoutPriority, Orientation, SplitView } from './splitview'
import {
createComponent,
ISerializableView,
SplitPanelOptions,
} from "./options";
createComponent,
ISerializableView,
SplitPanelOptions,
} from './options'
export interface IComponentSplitview extends IDisposable {
addFromComponent(options: {
id: string;
component: string;
params?: {
[index: string]: any;
};
priority?: LayoutPriority;
}): IDisposable;
layout(width: number, height: number): void;
onChange(cb: (event: { proportions: number[] }) => void): IDisposable;
toJSON(): object;
deserialize(data: any): void;
minimumSize: number;
addFromComponent(options: {
id: string
component: string
params?: {
[index: string]: any
}
priority?: LayoutPriority
}): IDisposable
layout(width: number, height: number): void
onChange(cb: (event: { proportions: number[] }) => void): IDisposable
toJSON(): object
deserialize(data: any): void
minimumSize: number
}
/**
* A high-level implementation of splitview that works using 'panels'
*/
export class ComponentSplitview implements IComponentSplitview {
private splitview: SplitView;
private splitview: SplitView
constructor(
private readonly element: HTMLElement,
private readonly options: SplitPanelOptions
) {
if (!options.components) {
options.components = {};
}
if (!options.frameworkComponents) {
options.frameworkComponents = {};
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)
}
this.splitview = new SplitView(this.element, options);
}
get minimumSize() {
return this.splitview.minimumSize
}
get minimumSize() {
return this.splitview.minimumSize;
}
addFromComponent(options: {
id: string;
component: string;
params?: {
[index: string]: any;
};
priority?: LayoutPriority;
}): 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 });
view.priority = options.priority;
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() : {},
minimumSize: v.minimumSize,
maximumSize: v.maximumSize,
snapSize: v.snapSize,
};
});
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,
proportionalLayout: false,
descriptor: {
size,
views: views.map((v) => {
const data = v.data;
const view = createComponent(
data.component,
addFromComponent(options: {
id: string
component: string
params?: {
[index: string]: any
}
priority?: LayoutPriority
}): IDisposable {
const view = createComponent(
options.component,
this.options.components,
this.options.frameworkComponents,
this.options.frameworkWrapper.createComponent
);
)
if (typeof v.minimumSize === "number") {
view.minimumSize = v.minimumSize;
}
if (typeof v.maximumSize === "number") {
view.maximumSize = v.maximumSize;
}
if (typeof v.snapSize === "number") {
view.snapSize = v.snapSize;
}
this.registerView(view)
view.init({ params: v.props });
this.splitview.addView(view, { type: 'distribute' })
view.init({ params: options.params })
view.priority = options.priority
view.priority = v.priority;
return {
dispose: () => {
//
},
}
}
return { size: v.size, view };
}),
},
});
private registerView(view: ISerializableView) {
//
}
this.splitview.orientation = orientation;
}
layout(width: number, height: number): void {
const [size, orthogonalSize] =
this.splitview.orientation === Orientation.HORIZONTAL
? [width, height]
: [height, width]
this.splitview.layout(size, orthogonalSize)
}
public dispose() {
this.splitview.dispose();
}
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() : {},
minimumSize: v.minimumSize,
maximumSize: v.maximumSize,
snapSize: v.snapSize,
}
})
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,
proportionalLayout: false,
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
)
if (typeof v.minimumSize === 'number') {
view.minimumSize = v.minimumSize
}
if (typeof v.maximumSize === 'number') {
view.maximumSize = v.maximumSize
}
if (typeof v.snapSize === 'number') {
view.snapSize = v.snapSize
}
view.init({ params: v.props })
view.priority = v.priority
return { size: v.size, view }
}),
},
})
this.splitview.orientation = orientation
}
public dispose() {
this.splitview.dispose()
}
}

View File

@ -1,58 +1,58 @@
import { IView, ISplitViewOptions } from "../splitview/splitview";
import { Constructor, FrameworkFactory } from "../types";
import { IView, ISplitViewOptions } from '../splitview/splitview'
import { Constructor, FrameworkFactory } from '../types'
export interface ISerializableView extends IView {
toJSON: () => object;
init: (params: { params: any }) => void;
toJSON: () => object
init: (params: { params: any }) => void
}
export interface SplitPanelOptions extends ISplitViewOptions {
components?: {
[componentName: string]: ISerializableView;
};
frameworkComponents?: {
[componentName: string]: any;
};
frameworkWrapper?: FrameworkFactory<ISerializableView>;
components?: {
[componentName: string]: ISerializableView
}
frameworkComponents?: {
[componentName: string]: any
}
frameworkWrapper?: FrameworkFactory<ISerializableView>
}
export interface ISerializableViewConstructor
extends Constructor<ISerializableView> {}
extends Constructor<ISerializableView> {}
export function createComponent<T>(
componentName: string | Constructor<T> | any,
components: {
[componentName: string]: T;
},
frameworkComponents: {
[componentName: string]: any;
},
createFrameworkComponent: (id: string, component: any) => T
componentName: string | Constructor<T> | any,
components: {
[componentName: string]: T
},
frameworkComponents: {
[componentName: string]: any
},
createFrameworkComponent: (id: string, component: any) => T
): T {
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 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`
)
}
const wrappedComponent = createFrameworkComponent(
componentName,
FrameworkComponent
);
return wrappedComponent;
}
return new Component() as T;
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 T
}

View File

@ -1,130 +1,130 @@
.split-view-container {
position: relative;
overflow: hidden;
height: 100%;
width: 100%;
&.animation {
.view,
.sash {
transition-duration: 0.15s;
transition-timing-function: ease-out;
}
}
// debug
// .sash {
// &.enabled {
// background-color: black;
// }
// &.disabled {
// background-color: orange;
// }
// &.maximum {
// background-color: green;
// }
// &.minimum {
// background-color: red;
// }
// }
&.horizontal {
height: 100%;
& > .sash-container > .sash {
height: 100%;
width: 4px;
&.enabled {
cursor: ew-resize;
}
&.disabled {
cursor: none;
}
&.maximum {
cursor: w-resize;
}
&.minimum {
cursor: e-resize;
}
}
& > .view-container > .view {
&:not(:first-child) {
padding-left: 1px;
&::before {
height: 100%;
width: 1px;
}
}
}
}
&.vertical {
width: 100%;
& > .sash-container > .sash {
width: 100%;
height: 4px;
&.enabled {
cursor: ns-resize;
}
&.disabled {
cursor: none;
}
&.maximum {
cursor: n-resize;
}
&.minimum {
cursor: s-resize;
}
}
& > .view-container > .view {
width: 100%;
&:not(:first-child) {
padding-top: 1px;
&::before {
height: 1px;
width: 100%;
}
}
}
}
.sash-container {
height: 100%;
width: 100%;
position: absolute;
.sash {
position: absolute;
z-index: 99;
outline: none;
}
}
.view-container {
position: relative;
overflow: hidden;
height: 100%;
width: 100%;
.view:not(:first-child)::before {
content: " ";
position: absolute;
top: 0;
left: 0;
z-index: 5;
pointer-events: none;
background-color: var(--splitview-divider-color);
&.animation {
.view,
.sash {
transition-duration: 0.15s;
transition-timing-function: ease-out;
}
}
.view {
height: 100%;
box-sizing: border-box;
overflow: auto;
position: absolute;
// debug
// .sash {
// &.enabled {
// background-color: black;
// }
// &.disabled {
// background-color: orange;
// }
// &.maximum {
// background-color: green;
// }
// &.minimum {
// background-color: red;
// }
// }
&.horizontal {
height: 100%;
& > .sash-container > .sash {
height: 100%;
width: 4px;
&.enabled {
cursor: ew-resize;
}
&.disabled {
cursor: none;
}
&.maximum {
cursor: w-resize;
}
&.minimum {
cursor: e-resize;
}
}
& > .view-container > .view {
&:not(:first-child) {
padding-left: 1px;
&::before {
height: 100%;
width: 1px;
}
}
}
}
&.vertical {
width: 100%;
& > .sash-container > .sash {
width: 100%;
height: 4px;
&.enabled {
cursor: ns-resize;
}
&.disabled {
cursor: none;
}
&.maximum {
cursor: n-resize;
}
&.minimum {
cursor: s-resize;
}
}
& > .view-container > .view {
width: 100%;
&:not(:first-child) {
padding-top: 1px;
&::before {
height: 1px;
width: 100%;
}
}
}
}
.sash-container {
height: 100%;
width: 100%;
position: absolute;
.sash {
position: absolute;
z-index: 99;
outline: none;
}
}
.view-container {
position: relative;
height: 100%;
width: 100%;
.view:not(:first-child)::before {
content: ' ';
position: absolute;
top: 0;
left: 0;
z-index: 5;
pointer-events: none;
background-color: var(--splitview-divider-color);
}
.view {
height: 100%;
box-sizing: border-box;
overflow: auto;
position: absolute;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,41 +1,41 @@
:root {
--group-view-background-color: #252526;
//
--title-bar-background-color: #252526;
--title-bar-scroll-bar-color: #888;
//
--active-tab-background-visible: #1e1e1e;
--active-tab-background-hidden: #2d2d2d;
--inactive-tab-background-visible: #1e1e1e;
--inactive-tab-background-hidden: #2d2d2d;
--tab-divider-color: #1e1e1e;
//
--drag-over-background-color: rgba(83, 89, 93, 0.5);
//
--active-group-visible-panel-color: white;
--active-group-hidden-panel-color: #969696;
--inactive-group-visible-panel-color: #8f8f8f;
--inactive-group-hidden-panel-color: #626262;
//
--tab-close-icon: url("https://fonts.gstatic.com/s/i/materialicons/close/v8/24px.svg");
--tab-dirty-icon: url("https://fonts.gstatic.com/s/i/materialicons/lens/v6/24px.svg");
//
--splitview-divider-color: rgb(68, 68, 68);
--group-view-background-color: #252526;
//
--title-bar-background-color: #252526;
--title-bar-scroll-bar-color: #888;
//
--active-tab-background-visible: #1e1e1e;
--active-tab-background-hidden: #2d2d2d;
--inactive-tab-background-visible: #1e1e1e;
--inactive-tab-background-hidden: #2d2d2d;
--tab-divider-color: #1e1e1e;
//
--drag-over-background-color: rgba(83, 89, 93, 0.5);
//
--active-group-visible-panel-color: white;
--active-group-hidden-panel-color: #969696;
--inactive-group-visible-panel-color: #8f8f8f;
--inactive-group-hidden-panel-color: #626262;
//
--tab-close-icon: url('https://fonts.gstatic.com/s/i/materialicons/close/v8/24px.svg');
--tab-dirty-icon: url('https://fonts.gstatic.com/s/i/materialicons/lens/v6/24px.svg');
//
--splitview-divider-color: rgb(68, 68, 68);
}
.visual-studio-theme {
--active-tab-background-visible: dodgerblue;
--active-tab-background-visible: dodgerblue;
.groupview {
&.active-group {
> .title-container {
border-bottom: 2px solid var(--active-tab-background-visible);
}
.groupview {
&.active-group {
> .title-container {
border-bottom: 2px solid var(--active-tab-background-visible);
}
}
&.inactive-group {
> .title-container {
border-bottom: 2px solid var(--inactive-tab-background-visible);
}
}
}
&.inactive-group {
> .title-container {
border-bottom: 2px solid var(--inactive-tab-background-visible);
}
}
}
}

View File

@ -1,9 +1,9 @@
export interface Constructor<T> {
new (): T;
new (): T
}
export interface FrameworkFactory<T> {
createComponent: (id: string, component: any) => T;
createComponent: (id: string, component: any) => T
}
export type FunctionOrValue<T> = (() => T) | T;
export type FunctionOrValue<T> = (() => T) | T

View File

@ -1,10 +1,10 @@
{
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}

View File

@ -1,3 +1,3 @@
{
"extends": "../../tsconfig.json"
"extends": "../../tsconfig.json"
}

View File

@ -1,11 +1,11 @@
{
"out": "typedocs",
"mode": "file",
"inputFiles": ["./src"],
"exclude": ["**/_test/**/*.*", "**/index.ts"],
"ignoreCompilerErrors": true,
"disableOutputCheck": true,
"excludeExternals": true,
"excludePrivate": true,
"excludeNotExported": true
"out": "typedocs",
"mode": "file",
"inputFiles": ["./src"],
"exclude": ["**/_test/**/*.*", "**/index.ts"],
"ignoreCompilerErrors": true,
"disableOutputCheck": true,
"excludeExternals": true,
"excludePrivate": true,
"excludeNotExported": true
}

Some files were not shown because too many files have changed in this diff Show More