diff --git a/.prettierrc b/.prettierrc index e74ed9ff3..f91488470 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,6 @@ { "trailingComma": "es5", "tabWidth": 4, - "semi": false, + "semi": true, "singleQuote": true } diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 1663a7914..84a7327fe 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,13 +1,9 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // 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": [ - - ] -} \ No newline at end of file + // 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": [] +} diff --git a/jest.config.js b/jest.config.js index 672aebb23..78bbcde42 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,4 +13,4 @@ module.exports = { '/src/__tests__/**/*.spec.tsx', ], setupFilesAfterEnv: ['/src/__tests__/setupTests.ts'], -} +}; diff --git a/package.json b/package.json index dc11acea6..35a6b14b3 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "format": "prettier --write ." }, "repository": { "type": "git", diff --git a/packages/splitview-demo/src/app.tsx b/packages/splitview-demo/src/app.tsx index c7c56bfda..9b98ede72 100644 --- a/packages/splitview-demo/src/app.tsx +++ b/packages/splitview-demo/src/app.tsx @@ -1,27 +1,27 @@ -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 }, -] +]; export const App = () => { - const [value, setValue] = React.useState(options[0].id) + const [value, setValue] = React.useState(options[0].id); const onChange = (event: React.ChangeEvent) => - setValue(event.target.value) + setValue(event.target.value); const Component = React.useMemo( () => options.find((o) => o.id === value)?.component, [value] - ) + ); return (
{
)} - ) -} + ); +}; diff --git a/packages/splitview-demo/src/index.tsx b/packages/splitview-demo/src/index.tsx index 13b51dfd2..9b634b98a 100644 --- a/packages/splitview-demo/src/index.tsx +++ b/packages/splitview-demo/src/index.tsx @@ -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(, document.getElementById('app')) +ReactDOM.render(, document.getElementById('app')); diff --git a/packages/splitview-demo/src/layout-grid/application.tsx b/packages/splitview-demo/src/layout-grid/application.tsx index e0e73e742..954b23ffd 100644 --- a/packages/splitview-demo/src/layout-grid/application.tsx +++ b/packages/splitview-demo/src/layout-grid/application.tsx @@ -1,4 +1,4 @@ -import * as React from 'react' +import * as React from 'react'; import { Orientation, GridviewComponent, @@ -6,18 +6,18 @@ import { GridviewReadyEvent, ComponentGridview, IGridviewPanelProps, -} from 'splitview' -import { TestGrid } from './reactgrid' +} from 'splitview'; +import { TestGrid } from './reactgrid'; const rootcomponents: { - [index: string]: React.FunctionComponent + [index: string]: React.FunctionComponent; } = { sidebar: (props: IGridviewPanelProps) => { return (
sidebar
- ) + ); }, editor: TestGrid, panel: () => { @@ -25,12 +25,12 @@ const rootcomponents: {
panel
- ) + ); }, -} +}; export const Application = () => { - const api = React.useRef() + const api = React.useRef(); const onReady = (event: GridviewReadyEvent) => { // event.api.deserialize(rootLayout); @@ -38,27 +38,27 @@ export const Application = () => { 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 + 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', @@ -66,12 +66,12 @@ export const Application = () => { position: { reference: '2', direction: 'below' }, size: 200, snap: true, - }) + }); return () => { - window.removeEventListener('resize', callback) - } - }, []) + window.removeEventListener('resize', callback); + }; + }, []); return ( { onReady={onReady} orientation={Orientation.HORIZONTAL} /> - ) -} + ); +}; diff --git a/packages/splitview-demo/src/layout-grid/customTab.tsx b/packages/splitview-demo/src/layout-grid/customTab.tsx index 5fe43a353..ba6d5667d 100644 --- a/packages/splitview-demo/src/layout-grid/customTab.tsx +++ b/packages/splitview-demo/src/layout-grid/customTab.tsx @@ -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
hello
-} + return
hello
; +}; diff --git a/packages/splitview-demo/src/layout-grid/editorPanel.tsx b/packages/splitview-demo/src/layout-grid/editorPanel.tsx index 7635d0c77..006be4b1b 100644 --- a/packages/splitview-demo/src/layout-grid/editorPanel.tsx +++ b/packages/splitview-demo/src/layout-grid/editorPanel.tsx @@ -1,25 +1,25 @@ -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(0) + const [tabHeight, setTabHeight] = React.useState(0); React.useEffect(() => { if (props.layoutApi) { - setTabHeight(props.layoutApi.getTabHeight()) + setTabHeight(props.layoutApi.getTabHeight()); } - }, [props.layoutApi]) + }, [props.layoutApi]); const onTabHeightChange = (event: React.ChangeEvent) => { - const value = Number(event.target.value) + const value = Number(event.target.value); if (!Number.isNaN(value)) { - setTabHeight(value) + setTabHeight(value); } - } + }; const onClick = () => { - props.layoutApi.setTabHeight(tabHeight) - } + props.layoutApi.setTabHeight(tabHeight); + }; return (
{
- ) -} + ); +}; diff --git a/packages/splitview-demo/src/layout-grid/reactgrid.tsx b/packages/splitview-demo/src/layout-grid/reactgrid.tsx index c88f55364..fdbf55a62 100644 --- a/packages/splitview-demo/src/layout-grid/reactgrid.tsx +++ b/packages/splitview-demo/src/layout-grid/reactgrid.tsx @@ -1,4 +1,4 @@ -import * as React from 'react' +import * as React from 'react'; import { ReactGrid, OnReadyEvent, @@ -9,33 +9,33 @@ import { GroupChangeKind, IGridviewPanelProps, TabContextMenuEvent, -} from 'splitview' -import { CustomTab } from './customTab' -import { Editor } from './editorPanel' -import { SplitPanel } from './splitPanel' +} from 'splitview'; +import { CustomTab } from './customTab'; +import { Editor } from './editorPanel'; +import { SplitPanel } from './splitPanel'; const components = { inner_component: (props: IPanelProps) => { - const _api = React.useRef() - const [api, setApi] = React.useState() + const _api = React.useRef(); + const [api, setApi] = React.useState(); const onReady = (event: OnReadyEvent) => { - _api.current = event.api + _api.current = event.api; - const layout = props.api.getStateKey('layout') + const layout = props.api.getStateKey('layout'); if (layout) { - event.api.deserialize(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(), @@ -44,7 +44,7 @@ const components = { direction: 'within', referencePanel: 'inner-1', }, - }) + }); event.api.addPanelFromComponent({ componentName: 'test_component', id: nextGuid(), @@ -53,37 +53,37 @@ const components = { direction: 'within', referencePanel: 'inner-2', }, - }) + }); } - setApi(event.api) - } + setApi(event.api); + }; React.useEffect(() => { const compDis = new CompositeDisposable( props.api.onDidDimensionsChange((event) => { - _api.current?.layout(event.width, event.height) + _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()) + props.api.setState('layout', _api.current.toJSON()); } }) - ) + ); return () => { - compDis.dispose() - } - }, []) + compDis.dispose(); + }; + }, []); React.useEffect(() => { if (!api) { - return + return; } api.onDidLayoutChange((event) => { // on inner grid changes - }) - }, [api]) + }); + }, [api]); return (
- ) + ); }, test_component: (props: IPanelProps & { [key: string]: any }) => { const [panelState, setPanelState] = React.useState<{ - isGroupActive: boolean - isPanelVisible: boolean + isGroupActive: boolean; + isPanelVisible: boolean; }>({ isGroupActive: false, isPanelVisible: false, - }) + }); React.useEffect(() => { const disposable = new CompositeDisposable( @@ -116,31 +116,31 @@ const components = { 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.CLOSE); } - return Promise.resolve(ClosePanelResult.DONT_CLOSE) - }) + return Promise.resolve(ClosePanelResult.DONT_CLOSE); + }); return () => { - disposable.dispose() - } - }, []) + disposable.dispose(); + }; + }, []); const onClick = () => { - props.api.setState('test_key', 'hello') - } + props.api.setState('test_key', 'hello'); + }; const backgroundColor = React.useMemo( () => @@ -149,7 +149,7 @@ const components = { Math.random() * 256 )},${Math.floor(Math.random() * 256)})`, [] - ) + ); return (
{`G:${panelState.isGroupActive} P:${panelState.isPanelVisible}`}
{props.text || '-'}
- ) + ); }, editor: Editor, split_panel: SplitPanel, -} +}; const tabComponents = { 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() - const [api, setApi] = React.useState() + const _api = React.useRef(); + const [api, setApi] = React.useState(); const onReady = (event: OnReadyEvent) => { - _api.current = event.api - setApi(event.api) - } + _api.current = event.api; + setApi(event.api); + }; React.useEffect(() => { if (!api) { - return + return; } const panelReference = api.addPanelFromComponent({ @@ -198,24 +198,24 @@ export const TestGrid = (props: IGridviewPanelProps) => { 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, - }) + }); // setInterval(() => { // panelReference.update({ params: { text: `Tick ${Date.now()}` } }); @@ -223,38 +223,38 @@ export const TestGrid = (props: IGridviewPanelProps) => { // }, 1000); api.addDndHandle('text/plain', (ev) => { - const { event } = ev + const { event } = ev; return { id: 'yellow', componentName: 'test_component', - } - }) + }; + }); api.addDndHandle('Files', (ev) => { - const { event } = ev + const { event } = ev; - ev.event.event.preventDefault() + ev.event.event.preventDefault(); return { id: Date.now().toString(), title: event.event.dataTransfer.files[0].name, componentName: 'test_component', - } - }) - }, [api]) + }; + }); + }, [api]); const onAdd = () => { - const id = nextGuid() + const id = nextGuid(); api.addPanelFromComponent({ componentName: 'test_component', id, - }) - } + }); + }; const onAddEmpty = () => { - api.addEmptyGroup() - } + api.addEmptyGroup(); + }; React.useEffect(() => { // const callback = (ev: UIEvent) => { @@ -269,68 +269,68 @@ export const TestGrid = (props: IGridviewPanelProps) => { props.api.setConstraints({ minimumWidth: () => _api.current.minimumWidth, minimumHeight: () => _api.current.minimumHeight, - }) + }); const disposable = new CompositeDisposable( _api.current.onDidLayoutChange((event) => { - console.log(event.kind) + console.log(event.kind); }), props.api.onDidDimensionsChange((event) => { - const { width, height } = event - _api.current.layout(width, height - 20) + const { width, height } = event; + _api.current.layout(width, height - 20); }) - ) + ); return () => { - disposable.dispose() + 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 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() + const didClose = await api.closeAllGroups(); if (!didClose) { - return + return; } - const data = localStorage.getItem('layout') + const data = localStorage.getItem('layout'); if (data) { - const jsonData = JSON.parse(data) - api.deserialize(jsonData) + const jsonData = JSON.parse(data); + api.deserialize(jsonData); } - } + }; const onClear = () => { - api.closeAllGroups() - } + api.closeAllGroups(); + }; const onNextGroup = () => { - api.moveToNext({ includePanel: true }) - } + api.moveToNext({ includePanel: true }); + }; const onPreviousGroup = () => { - api.moveToPrevious({ includePanel: true }) - } + api.moveToPrevious({ includePanel: true }); + }; const onNextPanel = () => { - api.activeGroup?.moveToNext() - } + api.activeGroup?.moveToNext(); + }; const onPreviousPanel = () => { - api.activeGroup?.moveToPrevious() - } + api.activeGroup?.moveToPrevious(); + }; - const dragRef = React.useRef() + const dragRef = React.useRef(); React.useEffect(() => { if (!api) { - return + return; } api.createDragTarget( { element: dragRef.current, content: 'drag me' }, @@ -338,12 +338,12 @@ export const TestGrid = (props: IGridviewPanelProps) => { id: 'yellow', componentName: 'test_component', }) - ) - }, [api]) + ); + }, [api]); const onDragStart = (event: React.DragEvent) => { - event.dataTransfer.setData('text/plain', 'Panel2') - } + event.dataTransfer.setData('text/plain', 'Panel2'); + }; const onAddEditor = () => { api.addPanelFromComponent({ @@ -351,15 +351,15 @@ export const TestGrid = (props: IGridviewPanelProps) => { componentName: 'editor', tabComponentName: 'default', params: { layoutApi: api }, - }) - } + }); + }; const onTabContextMenu = React.useMemo( () => (event: TabContextMenuEvent) => { - console.log(event) + console.log(event); }, [] - ) + ); return (
{ onTabContextMenu={onTabContextMenu} />
- ) -} + ); +}; diff --git a/packages/splitview-demo/src/layout-grid/splitPanel.tsx b/packages/splitview-demo/src/layout-grid/splitPanel.tsx index d6e864c60..85625a6e5 100644 --- a/packages/splitview-demo/src/layout-grid/splitPanel.tsx +++ b/packages/splitview-demo/src/layout-grid/splitPanel.tsx @@ -1,4 +1,4 @@ -import * as React from 'react' +import * as React from 'react'; import { CompositeDisposable, IPanelProps, @@ -6,65 +6,65 @@ import { Orientation, SplitviewFacade, SplitviewReadyEvent, -} from 'splitview' -import { SplitViewComponent } from 'splitview' +} from 'splitview'; +import { SplitViewComponent } from 'splitview'; const components = { default1: (props: ISplitviewPanelProps) => { - const [focused, setFocused] = React.useState(false) + const [focused, setFocused] = React.useState(false); React.useEffect(() => { const disposable = new CompositeDisposable( props.api.onDidFocusChange((event) => { - setFocused(event.isFocused) + setFocused(event.isFocused); }) - ) + ); return () => { - disposable.dispose() - } - }, []) + disposable.dispose(); + }; + }, []); return (
{`component [isFocused: ${focused}]`}
- ) + ); }, -} +}; export const SplitPanel = (props: IPanelProps) => { - const api = React.useRef() + const api = React.useRef(); React.useEffect(() => { const disposable = new CompositeDisposable( props.api.onDidDimensionsChange((event) => { - api.current?.layout(event.width, event.height - 20) + api.current?.layout(event.width, event.height - 20); }), api.current.onChange((event) => { - props.api.setState('sview_layout', api.current.toJSON()) + props.api.setState('sview_layout', api.current.toJSON()); }) - ) + ); return () => { - disposable.dispose() - } - }, []) + disposable.dispose(); + }; + }, []); const onReady = (event: SplitviewReadyEvent) => { - const existingLayout = props.api.getStateKey('sview_layout') + const existingLayout = props.api.getStateKey('sview_layout'); if (existingLayout) { - event.api.deserialize(existingLayout) + event.api.deserialize(existingLayout); } else { - event.api.addFromComponent({ id: '1', component: 'default1' }) - event.api.addFromComponent({ id: '2', component: 'default1' }) + 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()) - } + props.api.setState('sview_layout', api.current.toJSON()); + }; return (
{ orientation={Orientation.VERTICAL} />
- ) -} + ); +}; diff --git a/packages/splitview-demo/webpack.config.js b/packages/splitview-demo/webpack.config.js index d98b7c6ae..953c83b3c 100644 --- a/packages/splitview-demo/webpack.config.js +++ b/packages/splitview-demo/webpack.config.js @@ -1,4 +1,4 @@ -var path = require('path') +var path = require('path'); module.exports = { entry: path.resolve(__dirname, 'src/index.tsx'), @@ -50,4 +50,4 @@ module.exports = { contentBase: path.resolve(__dirname, 'public'), publicPath: '/dist', }, -} +}; diff --git a/packages/splitview-react/gulpfile.js b/packages/splitview-react/gulpfile.js index 4f324791f..08717da94 100644 --- a/packages/splitview-react/gulpfile.js +++ b/packages/splitview-react/gulpfile.js @@ -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'])); diff --git a/packages/splitview-react/src/bridge/pane.tsx b/packages/splitview-react/src/bridge/pane.tsx index cc1d8507e..77b9e892e 100644 --- a/packages/splitview-react/src/bridge/pane.tsx +++ b/packages/splitview-react/src/bridge/pane.tsx @@ -1,121 +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 -> +>; 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 -> +>; // 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) => void + updateProps: (props: Partial) => void; } export interface IPaneHeaderRootRef { - updateProps: (props: Partial) => void + updateProps: (props: Partial) => void; } export const PaneRoot = React.forwardRef( (props: IPaneRootProps, facadeRef: React.Ref) => { - const ref = React.useRef() + const ref = React.useRef(); const [facadeProps, setFacadeProps] = React.useState< IPaneComponentProps - >() + >(); React.useImperativeHandle( facadeRef, () => { return { updateProps: (props) => { - setFacadeProps((_props) => ({ ..._props, ...props })) + setFacadeProps((_props) => ({ ..._props, ...props })); }, layout: (size, orthogonalSize) => { - ref.current?.layout(size, orthogonalSize) + ref.current?.layout(size, orthogonalSize); }, - } + }; }, [ref] - ) + ); const Component = React.useMemo( () => React.forwardRef(props.component), [props.component] - ) + ); 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) => { const [facadeProps, setFacadeProps] = React.useState< IPaneHeaderComponentProps - >() + >(); React.useImperativeHandle( facadeRef, () => { return { updateProps: (props) => { - setFacadeProps((_props) => ({ ..._props, ...props })) + setFacadeProps((_props) => ({ ..._props, ...props })); }, - } + }; }, [] - ) + ); const Component = React.useMemo( () => React.forwardRef(props.component), [props.component] - ) + ); const _props = React.useMemo( () => ({ ...props.props, ...facadeProps }), [props.props, facadeProps] - ) + ); - return React.createElement(Component, _props) + return React.createElement(Component, _props); } -) +); diff --git a/packages/splitview-react/src/bridge/view.tsx b/packages/splitview-react/src/bridge/view.tsx index 92bc010eb..42d5bbc54 100644 --- a/packages/splitview-react/src/bridge/view.tsx +++ b/packages/splitview-react/src/bridge/view.tsx @@ -1,67 +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 { - userprops?: { [index: string]: any } + 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 -> +>; // component view facade export interface IViewRootProps { - component: ViewComponent - props: {} + component: ViewComponent; + props: {}; } export interface IViewRootRef extends IViewComponentRef { - updateProps: (props: Partial) => void + updateProps: (props: Partial) => void; } export const ViewRoot = React.forwardRef( (props: IViewRootProps, facadeRef: React.Ref) => { - const ref = React.useRef() + const ref = React.useRef(); const [facadeProps, setFacadeProps] = React.useState< IViewComponentProps - >() + >(); React.useImperativeHandle( facadeRef, () => { return { updateProps: (props) => { - setFacadeProps((_props) => ({ ..._props, ...props })) + setFacadeProps((_props) => ({ ..._props, ...props })); }, layout: (size, orthogonalSize) => { - ref.current?.layout(size, orthogonalSize) + ref.current?.layout(size, orthogonalSize); }, - } + }; }, [ref] - ) + ); const Component = React.useMemo( () => React.forwardRef(props.component), [props.component] - ) + ); const _props = React.useMemo( () => ({ ...props.props, ...facadeProps, ref }), [props.props, facadeProps] - ) + ); - return React.createElement(Component, _props) + return React.createElement(Component, _props); // return ; } -) +); diff --git a/packages/splitview-react/src/index.tsx b/packages/splitview-react/src/index.tsx index 625f37efc..4cdfea424 100644 --- a/packages/splitview-react/src/index.tsx +++ b/packages/splitview-react/src/index.tsx @@ -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'; diff --git a/packages/splitview-react/src/panel/pane.tsx b/packages/splitview-react/src/panel/pane.tsx index 4d58f9308..8b2db5c04 100644 --- a/packages/splitview-react/src/panel/pane.tsx +++ b/packages/splitview-react/src/panel/pane.tsx @@ -1,6 +1,6 @@ -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, @@ -9,47 +9,47 @@ import { PaneHeaderComponent, PaneHeaderRoot, IPaneHeaderRootRef, -} from '../bridge/pane' -import { IViewWithReactComponent } from '../splitview' -import { IPaneWithReactComponent } from '../paneview' +} 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 + 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) + 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() + this.render(); } public renderBody(element: HTMLElement) { if (this.bodyDisposable) { - this.bodyDisposable.dispose() - this.bodyDisposable = undefined + this.bodyDisposable.dispose(); + this.bodyDisposable = undefined; } const bodyPortal = ReactDOM.createPortal( @@ -65,21 +65,21 @@ export class PaneReact extends Pane { }} />, element - ) - this.bodyDisposable = this.options.addPortal(bodyPortal) + ); + this.bodyDisposable = this.options.addPortal(bodyPortal); } public renderHeader(element: HTMLElement) { if (this.headerDisposable) { - this.headerDisposable.dispose() - this.disposable?.dispose() - this.headerDisposable = undefined + this.headerDisposable.dispose(); + this.disposable?.dispose(); + this.headerDisposable = undefined; } if (this.options.headerComponent) { this.disposable = this.onDidChangeExpansionState((isExpanded) => { - this.headerRef?.updateProps({ isExpanded }) - }) + this.headerRef?.updateProps({ isExpanded }); + }); const headerPortal = ReactDOM.createPortal( , element - ) - this.headerDisposable = this.options.addPortal(headerPortal) + ); + this.headerDisposable = this.options.addPortal(headerPortal); } else { - element.textContent = this.options.headerName + element.textContent = this.options.headerName; element.onclick = () => { - this.setExpanded(!this.isExpanded()) - } + this.setExpanded(!this.isExpanded()); + }; } } public update(view: IViewWithReactComponent) { - this.minimumSize = view.minimumSize - this.maximumSize = view.maximumSize + this.minimumSize = view.minimumSize; + this.maximumSize = view.maximumSize; - this.render() + this.render(); this.bodyRef?.updateProps({ minimumSize: this.minimumSize, maximumSize: this.maximumSize, - }) + }); } public layout(size: number, orthogonalSize: number) { - super.layout(size, orthogonalSize) - this.orthogonalSize = orthogonalSize - this.bodyRef?.layout(size, orthogonalSize) - this.bodyRef?.updateProps({ size, orthogonalSize }) + super.layout(size, orthogonalSize); + this.orthogonalSize = orthogonalSize; + this.bodyRef?.layout(size, orthogonalSize); + this.bodyRef?.updateProps({ size, orthogonalSize }); } private setRef(ref: IPaneRootRef) { - this.bodyRef = ref + this.bodyRef = ref; } private setHeaderRef(ref: IPaneRootRef) { - this.headerRef = ref + this.headerRef = ref; this.headerRef?.updateProps({ isExpanded: this.isExpanded(), setExpanded: this.setExpanded, - }) + }); } public dispose() { - this.bodyDisposable?.dispose() - this.headerDisposable?.dispose() - this.disposable?.dispose() + this.bodyDisposable?.dispose(); + this.headerDisposable?.dispose(); + this.disposable?.dispose(); } } diff --git a/packages/splitview-react/src/panel/view.tsx b/packages/splitview-react/src/panel/view.tsx index 7c5a6966d..8516b78f1 100644 --- a/packages/splitview-react/src/panel/view.tsx +++ b/packages/splitview-react/src/panel/view.tsx @@ -1,30 +1,30 @@ -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() - public readonly onDidChange = this._onDidChange.event + private readonly _onDidChange = new Emitter(); + 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, @@ -32,52 +32,52 @@ export class ReactRenderView implements IView { portal: React.ReactPortal ) => { dispose: () => void } ) { - this.layout = this.layout.bind(this) - this.onDidChange = this.onDidChange.bind(this) - this.setRef = this.setRef.bind(this) + 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' + 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.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.attachReactComponent(); + this._rendered = true; } - this._size = size - this._orthogonalSize = orthogonalSize - this.ref?.layout(size, orthogonalSize) + this._size = size; + this._orthogonalSize = orthogonalSize; + this.ref?.layout(size, orthogonalSize); } private attachReactComponent() { - const portal = this.createReactElement() + const portal = this.createReactElement(); if (this.disposable) { - this.disposable.dispose() - this.disposable = undefined + this.disposable.dispose(); + this.disposable = undefined; } - this.disposable = this.addPortal(portal) + this.disposable = this.addPortal(portal); } private createReactElement() { @@ -94,15 +94,15 @@ export class ReactRenderView implements IView { }} />, this.element - ) + ); } private setRef(ref: IViewRootRef) { - this.ref = ref - this.ref?.layout(this._size, this._orthogonalSize) + this.ref = ref; + this.ref?.layout(this._size, this._orthogonalSize); } public dispose() { - this.disposable?.dispose() + this.disposable?.dispose(); } } diff --git a/packages/splitview-react/src/paneview.tsx b/packages/splitview-react/src/paneview.tsx index c077413eb..cdb8c5668 100644 --- a/packages/splitview-react/src/paneview.tsx +++ b/packages/splitview-react/src/paneview.tsx @@ -1,39 +1,39 @@ -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 & { - size?: number + size?: number; } - > + >; } export interface PaneviewApi { @@ -42,27 +42,27 @@ export interface PaneviewApi { IPaneWithReactComponent, 'component' | 'headerComponent' > & { - size?: number - index?: number + size?: number; + index?: number; } - ) => void - moveView: (from: number, to: number) => void - toJSON: () => {} + ) => 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) => { - const ref = React.useRef() + const ref = React.useRef(); const dimension = React.useRef<{ - size: number - orthogonalSize: number - }>() - const paneview = React.useRef() - const [portals, setPortals] = React.useState([]) + size: number; + orthogonalSize: number; + }>(); + const paneview = React.useRef(); + const [portals, setPortals] = React.useState([]); const createView = React.useCallback( (_view: IPaneWithReactComponent) => { @@ -70,47 +70,47 @@ export const PaneViewComponent = React.forwardRef( headerName: 'header', headerComponent: _view.headerComponent, addPortal: (portal) => { - setPortals((portals) => [...portals, portal]) + setPortals((portals) => [...portals, portal]); return { dispose: () => { setPortals((portals) => portals.filter((_) => _ !== portal) - ) + ); }, - } + }; }, - }) + }); }, [] - ) + ); const hydrate = React.useCallback(() => { if (!props.initialLayout || !paneview.current) { - return + 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] + 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]) + ); + }); + 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 + ); + dimension.current = undefined; } - }, [paneview.current]) + }, [paneview.current]); // if you put this in a hook it's laggy // paneview.current?.layout(props.size, props.orthogonalSize); @@ -122,20 +122,20 @@ export const PaneViewComponent = React.forwardRef( 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 } + dimension.current = { size, orthogonalSize }; } - paneview.current?.layout(size, orthogonalSize) + paneview.current?.layout(size, orthogonalSize); }, }), [paneview] - ) + ); React.useEffect(() => { paneview.current = new PaneView(ref.current, { orientation: props.orientation, - }) + }); - hydrate() + hydrate(); if (props.onReady) { props.onReady({ @@ -145,14 +145,14 @@ export const PaneViewComponent = React.forwardRef( IPaneWithReactComponent, 'component' | 'headerComponent' > & { - props?: {} - size?: number - index?: number + props?: {}; + size?: number; + index?: number; } ) => { - const component = props.components[options.id] + const component = props.components[options.id]; const headerComponent = - props.headerComponents[options.headerId] + props.headerComponents[options.headerId]; paneview.current.addPane( createView({ ...options, @@ -161,14 +161,14 @@ export const PaneViewComponent = React.forwardRef( }), options.size, options.index - ) + ); paneview.current.layout( props.size, props.orthogonalSize - ) + ); }, moveView: (from: number, to: number) => { - paneview.current.moveView(from, to) + paneview.current.moveView(from, to); }, toJSON: () => { return { @@ -188,28 +188,28 @@ export const PaneViewComponent = React.forwardRef( // {} // ) // ), - } + }; }, }, - }) + }); } - paneview.current.layout(props.size, props.orthogonalSize) + paneview.current.layout(props.size, props.orthogonalSize); return () => { - paneview.current?.dispose() - paneview.current = undefined - } - }, []) + paneview.current?.dispose(); + paneview.current = undefined; + }; + }, []); React.useEffect(() => { - paneview.current?.setOrientation(props.orientation) - }, [props.orientation]) + paneview.current?.setOrientation(props.orientation); + }, [props.orientation]); return (
{portals}
- ) + ); } -) +); diff --git a/packages/splitview-react/src/splitview.tsx b/packages/splitview-react/src/splitview.tsx index b23c5b848..82b6cfe53 100644 --- a/packages/splitview-react/src/splitview.tsx +++ b/packages/splitview-react/src/splitview.tsx @@ -1,90 +1,92 @@ -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 & { size?: number }> + views: Array< + Omit & { size?: number } + >; } export interface SplitviewApi { add: ( options: Omit & { - size?: number - index?: number + size?: number; + index?: number; } - ) => void - moveView: (from: number, to: number) => void - toJSON: () => {} + ) => 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) => { - const containerRef = React.useRef() - const splitview = React.useRef() - const [portals, setPortals] = React.useState([]) + const containerRef = React.useRef(); + const splitview = React.useRef(); + const [portals, setPortals] = React.useState([]); const hydrate = React.useCallback(() => { if (!props.initialLayout || !splitview.current) { - return + return; } - const serializedConfig = props.initialLayout + const serializedConfig = props.initialLayout; serializedConfig.views.forEach((view) => { - const component = props.components[view.id] + const component = props.components[view.id]; splitview.current.addView( createView({ ...view, component }), view.size - ) - }) - splitview.current.layout(props.size, props.orthogonalSize) - }, [props.initialLayout]) + ); + }); + 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]) + 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.current?.layout(size, orthogonalSize); }, }), [splitview] - ) + ); React.useEffect(() => { splitview.current = new SplitView(containerRef.current, { orientation: props.orientation, - }) + }); - hydrate() + hydrate(); if (props.onReady) { props.onReady({ @@ -94,24 +96,24 @@ export const SplitViewComponent = React.forwardRef( IViewWithReactComponent, 'component' > & { - props?: {} - size?: number - index?: number + props?: {}; + size?: number; + index?: number; } ) => { - const component = props.components[options.id] + 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) + splitview.current.moveView(from, to); }, toJSON: () => { return { @@ -132,37 +134,37 @@ export const SplitViewComponent = React.forwardRef( {} ) ), - } + }; }, }, - }) + }); } - splitview.current.layout(props.size, props.orthogonalSize) + splitview.current.layout(props.size, props.orthogonalSize); return () => { - splitview.current.dispose() - } - }, []) + splitview.current.dispose(); + }; + }, []); const createView = React.useCallback( (view: IViewWithReactComponent) => new ReactRenderView(view, (portal) => { - setPortals((portals) => [...portals, portal]) + setPortals((portals) => [...portals, portal]); return { dispose: () => void setPortals((portals) => portals.filter((_) => _ !== portal) ), - } + }; }), [] - ) + ); return (
{portals}
- ) + ); } -) +); diff --git a/packages/splitview/gulpfile.js b/packages/splitview/gulpfile.js index f92abcc44..c0c0ab352 100644 --- a/packages/splitview/gulpfile.js +++ b/packages/splitview/gulpfile.js @@ -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'])); diff --git a/packages/splitview/src/array.ts b/packages/splitview/src/array.ts index f457a429e..5039c439d 100644 --- a/packages/splitview/src/array.ts +++ b/packages/splitview/src/array.ts @@ -1,37 +1,37 @@ export function tail(arr: T[]): [T[], T] { if (arr.length === 0) { - throw new Error('Invalid tail call') + 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(arr: T[]): T { - return arr.length > 0 ? arr[arr.length - 1] : undefined + return arr.length > 0 ? arr[arr.length - 1] : undefined; } export function sequenceEquals(arr1: T[], arr2: T[]) { if (arr1.length !== arr2.length) { - return false + return false; } for (let i = 0; i < arr1.length; i++) { if (arr1[i] !== arr2[i]) { - return false + return false; } } - return true + return true; } /** * Pushes an element to the start of the array, if found. */ export function pushToStart(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) + arr.splice(index, 1); + arr.unshift(value); } } @@ -39,31 +39,31 @@ export function pushToStart(arr: T[], value: T): void { * Pushes an element to the end of the array, if found. */ export function pushToEnd(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) + 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 + to = from; + from = 0; } if (from <= to) { for (let i = from; i < to; i++) { - result.push(i) + result.push(i); } } else { for (let i = from; i > to; i--) { - result.push(i) + result.push(i); } } - return result -} + return result; +}; diff --git a/packages/splitview/src/async.ts b/packages/splitview/src/async.ts index 1593755ee..3943765b9 100644 --- a/packages/splitview/src/async.ts +++ b/packages/splitview/src/async.ts @@ -1,7 +1,7 @@ export function timeoutPromise(timeout: number): Promise { return new Promise((resolve) => { setTimeout(() => { - resolve() - }, timeout) - }) + resolve(); + }, timeout); + }); } diff --git a/packages/splitview/src/dom.ts b/packages/splitview/src/dom.ts index a55da2c24..7be391114 100644 --- a/packages/splitview/src/dom.ts +++ b/packages/splitview/src/dom.ts @@ -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() + const bb = domNode.getBoundingClientRect(); return { left: bb.left + window.scrollX, top: bb.top + window.scrollY, width: bb.width, height: bb.height, - } + }; } /** @@ -19,10 +19,10 @@ export const scrollIntoView = ( element: HTMLElement, container: HTMLElement ) => { - const { inView, breachPoint } = isElementInView(element, container, true) + const { inView, breachPoint } = isElementInView(element, container, true); if (!inView) { - const adder = -container.offsetTop - const isUp = breachPoint === 'top' + const adder = -container.offsetTop; + const isUp = breachPoint === 'top'; container.scrollTo({ top: isUp ? adder + element.offsetTop @@ -30,108 +30,108 @@ export const scrollIntoView = ( 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 containerOfftsetTop = container.offsetTop; + const containerTop = containerOfftsetTop + container.scrollTop; const containerBottom = - containerTop + container.getBoundingClientRect().height - const elementTop = element.offsetTop - const elementBottom = elementTop + element.getBoundingClientRect().height + containerTop + container.getBoundingClientRect().height; + const elementTop = element.offsetTop; + const elementBottom = elementTop + element.getBoundingClientRect().height; const isAbove = fullyInView ? containerTop >= elementTop - : elementTop > containerBottom + : elementTop > containerBottom; const isBelow = fullyInView ? containerBottom <= elementBottom - : elementBottom < containerTop + : elementBottom < containerTop; if (isAbove) { - return { inView: false, breachPoint: 'top' } + return { inView: false, breachPoint: 'top' }; } if (isBelow) { - return { inView: false, breachPoint: 'bottom' } + 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 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 + 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) + 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) + element.classList.add(classname); } } -} +}; export const toggleClass = ( element: HTMLElement, className: string, isToggled: boolean ) => { - const hasClass = element.classList.contains(className) + const hasClass = element.classList.contains(className); if (isToggled && !hasClass) { - element.classList.add(className) + element.classList.add(className); } if (!isToggled && hasClass) { - element.classList.remove(className) + element.classList.remove(className); } -} +}; export function firstIndex( array: T[] | ReadonlyArray, fn: (item: T) => boolean ): number { for (let i = 0; i < array.length; i++) { - const element = array[i] + const element = array[i]; if (fn(element)) { - return i + return i; } } - return -1 + return -1; } export function isAncestor( @@ -140,93 +140,93 @@ export function isAncestor( ): boolean { while (testChild) { if (testChild === testAncestor) { - return true + return true; } - testChild = testChild.parentNode + testChild = testChild.parentNode; } - return false + return false; } export interface IFocusTracker extends IDisposable { - onDidFocus: Event - onDidBlur: Event - refreshState?(): void + onDidFocus: Event; + onDidBlur: Event; + 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() - public readonly onDidFocus: Event = this._onDidFocus.event + private readonly _onDidFocus = new Emitter(); + public readonly onDidFocus: Event = this._onDidFocus.event; - private readonly _onDidBlur = new Emitter() - public readonly onDidBlur: Event = this._onDidBlur.event + private readonly _onDidBlur = new Emitter(); + public readonly onDidBlur: Event = this._onDidBlur.event; - private _refreshStateHandler: () => void + private _refreshStateHandler: () => void; constructor(element: HTMLElement | Window) { - super() + super(); - let hasFocus = isAncestor(document.activeElement, element) - let loosingFocus = false + let hasFocus = isAncestor(document.activeElement, element); + let loosingFocus = false; const onFocus = () => { - loosingFocus = false + loosingFocus = false; if (!hasFocus) { - hasFocus = true - this._onDidFocus.fire() + hasFocus = true; + this._onDidFocus.fire(); } - } + }; const onBlur = () => { if (hasFocus) { - loosingFocus = true + loosingFocus = true; window.setTimeout(() => { if (loosingFocus) { - loosingFocus = false - hasFocus = false - this._onDidBlur.fire() + loosingFocus = false; + hasFocus = false; + this._onDidBlur.fire(); } - }, 0) + }, 0); } - } + }; this._refreshStateHandler = () => { let currentNodeHasFocus = isAncestor( document.activeElement, element - ) + ); if (currentNodeHasFocus !== hasFocus) { if (hasFocus) { - onBlur() + onBlur(); } else { - onFocus() + onFocus(); } } - } + }; this.addDisposables( addDisposableListener(element, 'focus', onFocus, true) - ) + ); this.addDisposables( addDisposableListener(element, 'blur', onBlur, true) - ) + ); } refreshState() { - this._refreshStateHandler() + this._refreshStateHandler(); } public dispose() { - super.dispose() + super.dispose(); - this._onDidBlur.dispose() - this._onDidFocus.dispose() + this._onDidBlur.dispose(); + this._onDidFocus.dispose(); } } diff --git a/packages/splitview/src/events.ts b/packages/splitview/src/events.ts index 886d6a7ae..e61765c4a 100644 --- a/packages/splitview/src/events.ts +++ b/packages/splitview/src/events.ts @@ -1,37 +1,37 @@ -import { IDisposable } from './lifecycle' +import { IDisposable } from './lifecycle'; export interface Event { - (listener: (e: T) => any): IDisposable + (listener: (e: T) => any): IDisposable; } export interface EmitterOptions { - emitLastValue?: boolean + emitLastValue?: boolean; } export namespace Event { export const any = (...children: Event[]): Event => { return (listener: (e: T) => void) => { - const disposables = children.map((child) => child(listener)) + const disposables = children.map((child) => child(listener)); return { dispose: () => { disposables.forEach((d) => { - d.dispose() - }) + 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 implements IDisposable { - private _event: Event + private _event: Event; - 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) {} @@ -39,38 +39,38 @@ export class Emitter implements IDisposable { if (!this._event) { this._event = (listener: (e: T) => void): IDisposable => { if (this.options?.emitLastValue && this._last !== undefined) { - listener(this._last) + listener(this._last); } - this._listeners.push(listener) + this._listeners.push(listener); return { dispose: () => { - const index = this._listeners.indexOf(listener) + const index = this._listeners.indexOf(listener); if (index > -1) { - this._listeners.splice(index, 1) + this._listeners.splice(index, 1); } }, - } - } + }; + }; } - return this._event + return this._event; } public fire(e: T) { - this._last = e + this._last = e; this._listeners.forEach((listener) => { - listener(e) - }) + listener(e); + }); } public dispose() { - this._listeners = [] - this._disposed = true + this._listeners = []; + this._disposed = true; } } -export type EventHandler = HTMLElement | HTMLDocument | Window +export type EventHandler = HTMLElement | HTMLDocument | Window; export const addDisposableListener = ( element: EventHandler, @@ -78,11 +78,11 @@ export const addDisposableListener = ( 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) + element.removeEventListener(type, listener); }, - } -} + }; +}; diff --git a/packages/splitview/src/functions.ts b/packages/splitview/src/functions.ts index cb4b6ea97..e2ff5a953 100644 --- a/packages/splitview/src/functions.ts +++ b/packages/splitview/src/functions.ts @@ -1,9 +1,9 @@ export function debounce(cb: T, wait: number) { - let timeout: NodeJS.Timeout + let timeout: NodeJS.Timeout; const callable = (...args: any) => { - clearTimeout(timeout) - timeout = setTimeout(() => cb(...args), wait) - } - return (callable) + clearTimeout(timeout); + timeout = setTimeout(() => cb(...args), wait); + }; + return (callable); } diff --git a/packages/splitview/src/gridview/branchNode.ts b/packages/splitview/src/gridview/branchNode.ts index 9bb25e2eb..5e11effbd 100644 --- a/packages/splitview/src/gridview/branchNode.ts +++ b/packages/splitview/src/gridview/branchNode.ts @@ -4,106 +4,106 @@ import { 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' +} 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() - readonly onDidChange: Event = this._onDidChange.event + private readonly _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; get width(): number { return this.orientation === Orientation.HORIZONTAL ? this.size - : this.orthogonalSize + : this.orthogonalSize; } get height(): number { return this.orientation === Orientation.HORIZONTAL ? this.orthogonalSize - : this.size + : this.size; } get minimumSize(): number { return this.children.length === 0 ? 0 - : Math.max(...this.children.map((c) => c.minimumOrthogonalSize)) + : Math.max(...this.children.map((c) => c.minimumOrthogonalSize)); } get maximumSize(): number { - return Math.min(...this.children.map((c) => c.maximumOrthogonalSize)) + return Math.min(...this.children.map((c) => c.maximumOrthogonalSize)); } get minimumOrthogonalSize(): number { - return this.splitview.minimumSize + return this.splitview.minimumSize; } get maximumOrthogonalSize(): number { - return this.splitview.maximumSize + return this.splitview.maximumSize; } get orthogonalSize() { - return this._orthogonalSize + return this._orthogonalSize; } get size() { - return this._size + return this._size; } get minimumWidth(): number { return this.orientation === Orientation.HORIZONTAL ? this.minimumOrthogonalSize - : this.minimumSize + : this.minimumSize; } get snapSize() { - return undefined + return undefined; } get minimumHeight(): number { return this.orientation === Orientation.HORIZONTAL ? this.minimumSize - : this.minimumOrthogonalSize + : this.minimumOrthogonalSize; } get maximumWidth(): number { return this.orientation === Orientation.HORIZONTAL ? this.maximumOrthogonalSize - : this.maximumSize + : this.maximumSize; } get maximumHeight(): number { return this.orientation === Orientation.HORIZONTAL ? this.maximumSize - : this.maximumOrthogonalSize + : this.maximumOrthogonalSize; } get priority(): LayoutPriority { if (this.children.length === 0) { - return LayoutPriority.Normal + return LayoutPriority.Normal; } const priorities = this.children.map((c) => typeof c.priority === 'undefined' ? LayoutPriority.Normal : c.priority - ) + ); if (priorities.some((p) => p === LayoutPriority.High)) { - return LayoutPriority.High + return LayoutPriority.High; } else if (priorities.some((p) => p === LayoutPriority.Low)) { - return LayoutPriority.Low + return LayoutPriority.Low; } - return LayoutPriority.Normal + return LayoutPriority.Normal; } constructor( @@ -114,133 +114,133 @@ export class BranchNode extends CompositeDisposable implements IView { childDescriptors?: INodeDescriptor[] ) { - super() - this._orthogonalSize = orthogonalSize - this._size = size - this.element = document.createElement('div') - this.element.className = 'branch-node' + 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) + }); + 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.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) + this._onDidChange.fire(undefined); }) - ) + ); } moveChild(from: number, to: number): void { if (from === to) { - return + return; } if (from < 0 || from >= this.children.length) { - throw new Error('Invalid from index') + throw new Error('Invalid from index'); } if (from < to) { - to-- + to--; } - this.splitview.moveView(from, to) + this.splitview.moveView(from, to); - const child = this._removeChild(from) - this._addChild(child, 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') + throw new Error('Invalid index'); } - return this.splitview.getViewSize(index) + return this.splitview.getViewSize(index); } resizeChild(index: number, size: number): void { if (index < 0 || index >= this.children.length) { - throw new Error('Invalid index') + throw new Error('Invalid index'); } - this.splitview.resizeView(index, size) + this.splitview.resizeView(index, size); } public layout(size: number, orthogonalSize: number) { - this._size = orthogonalSize - this._orthogonalSize = size + this._size = orthogonalSize; + this._orthogonalSize = size; - this.splitview.layout(this.size, this.orthogonalSize) + 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') + throw new Error('Invalid index'); } - this.splitview.addView(node, size, index) - this._addChild(node, 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') + throw new Error('Invalid index'); } - this.splitview.removeView(index, sizing) - this._removeChild(index) + this.splitview.removeView(index, sizing); + this._removeChild(index); } private _addChild(node: Node, index: number): void { - this.children.splice(index, 0, node) - this.setupChildrenEvents() + 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() + const first = index === 0; + const last = index === this.children.length - 1; + const [child] = this.children.splice(index, 1); + this.setupChildrenEvents(); - return child + return child; } - private _childrenDisposable: IDisposable = Disposable.NONE + private _childrenDisposable: IDisposable = Disposable.NONE; private setupChildrenEvents() { - this._childrenDisposable.dispose() + this._childrenDisposable.dispose(); this._childrenDisposable = Event.any( ...this.children.map((c) => c.onDidChange) )((e) => { - this._onDidChange.fire(e) - }) + this._onDidChange.fire(e); + }); } public dispose() { - super.dispose() - this._childrenDisposable.dispose() - this.splitview.dispose() - this.children.forEach((child) => child.dispose()) + super.dispose(); + this._childrenDisposable.dispose(); + this.splitview.dispose(); + this.children.forEach((child) => child.dispose()); } } diff --git a/packages/splitview/src/gridview/gridview.ts b/packages/splitview/src/gridview/gridview.ts index 98e25c8cb..1bdafeae9 100644 --- a/packages/splitview/src/gridview/gridview.ts +++ b/packages/splitview/src/gridview/gridview.ts @@ -1,11 +1,11 @@ -import { LayoutPriority, Orientation, Sizing } from '../splitview/splitview' -import { Position } from '../groupview/droptarget/droptarget' -import { tail } from '../array' -import { LeafNode } from './leafNode' -import { BranchNode } from './branchNode' -import { Node } from './types' -import { Emitter, Event } from '../events' -import { IDisposable } from '../lifecycle' +import { LayoutPriority, Orientation, Sizing } from '../splitview/splitview'; +import { Position } from '../groupview/droptarget/droptarget'; +import { tail } from '../array'; +import { LeafNode } from './leafNode'; +import { BranchNode } from './branchNode'; +import { Node } from './types'; +import { Emitter, Event } from '../events'; +import { IDisposable } from '../lifecycle'; function flipNode( node: T, @@ -18,57 +18,59 @@ function flipNode( node.proportionalLayout, size, orthogonalSize - ) + ); - let totalSize = 0 + let totalSize = 0; for (let i = node.children.length - 1; i >= 0; i--) { - const child = node.children[i] + const child = node.children[i]; const childSize = - child instanceof BranchNode ? child.orthogonalSize : child.size + child instanceof BranchNode ? child.orthogonalSize : child.size; let newSize = - node.size === 0 ? 0 : Math.round((size * childSize) / node.size) - totalSize += newSize + node.size === 0 + ? 0 + : Math.round((size * childSize) / node.size); + totalSize += newSize; // The last view to add should adjust to rounding errors if (i === 0) { - newSize += size - totalSize + newSize += size - totalSize; } result.addChild( flipNode(child, orthogonalSize, newSize), newSize, 0 - ) + ); } - return result as T + return result as T; } else { return new LeafNode( (node as LeafNode).view, orthogonal(node.orientation), orthogonalSize - ) as T + ) as T; } } export function indexInParent(element: HTMLElement): number { - const parentElement = element.parentElement + const parentElement = element.parentElement; if (!parentElement) { - throw new Error('Invalid grid element') + throw new Error('Invalid grid element'); } - let el = parentElement.firstElementChild - let index = 0 + let el = parentElement.firstElementChild; + let index = 0; while (el !== element && el !== parentElement.lastElementChild && el) { - el = el.nextElementSibling - index++ + el = el.nextElementSibling; + index++; } - return index + return index; } /** @@ -78,19 +80,19 @@ export function indexInParent(element: HTMLElement): number { * This will break as soon as DOM structures of the Splitview or Gridview change. */ export function getGridLocation(element: HTMLElement): number[] { - const parentElement = element.parentElement + const parentElement = element.parentElement; if (!parentElement) { - throw new Error('Invalid grid element') + throw new Error('Invalid grid element'); } if (/\bgrid-view\b/.test(parentElement.className)) { - return [] + return []; } - const index = indexInParent(parentElement) - const ancestor = parentElement.parentElement!.parentElement!.parentElement! - return [...getGridLocation(ancestor), index] + const index = indexInParent(parentElement); + const ancestor = parentElement.parentElement!.parentElement!.parentElement!; + return [...getGridLocation(ancestor), index]; } export function getRelativeLocation( @@ -98,30 +100,30 @@ export function getRelativeLocation( location: number[], direction: Position ): number[] { - const orientation = getLocationOrientation(rootOrientation, location) - const directionOrientation = getDirectionOrientation(direction) + const orientation = getLocationOrientation(rootOrientation, location); + const directionOrientation = getDirectionOrientation(direction); if (orientation === directionOrientation) { - let [rest, index] = tail(location) + let [rest, index] = tail(location); if (direction === Position.Right || direction === Position.Bottom) { - index += 1 + index += 1; } - return [...rest, index] + return [...rest, index]; } else { const index = direction === Position.Right || direction === Position.Bottom ? 1 - : 0 - return [...location, index] + : 0; + return [...location, index]; } } export function getDirectionOrientation(direction: Position): Orientation { return direction === Position.Top || direction === Position.Bottom ? Orientation.VERTICAL - : Orientation.HORIZONTAL + : Orientation.HORIZONTAL; } export function getLocationOrientation( @@ -130,88 +132,88 @@ export function getLocationOrientation( ): Orientation { return location.length % 2 === 0 ? orthogonal(rootOrientation) - : rootOrientation + : rootOrientation; } export interface IGridView { - readonly element: HTMLElement - readonly minimumWidth: number - readonly maximumWidth: number - readonly minimumHeight: number - readonly maximumHeight: number - readonly priority?: LayoutPriority - layout(width: number, height: number, top: number, left: number): void - toJSON?(): object - fromJSON?(json: object): void - snap?: boolean + readonly element: HTMLElement; + readonly minimumWidth: number; + readonly maximumWidth: number; + readonly minimumHeight: number; + readonly maximumHeight: number; + readonly priority?: LayoutPriority; + layout(width: number, height: number, top: number, left: number): void; + toJSON?(): object; + fromJSON?(json: object): void; + snap?: boolean; } const orthogonal = (orientation: Orientation) => orientation === Orientation.HORIZONTAL ? Orientation.VERTICAL - : Orientation.HORIZONTAL + : Orientation.HORIZONTAL; const serializeLeafNode = (node: LeafNode) => { const size = node.orientation === Orientation.HORIZONTAL ? node.size - : node.orthogonalSize + : node.orthogonalSize; return { size: node.size, data: node.view.toJSON ? node.view.toJSON() : {}, type: 'leaf', - } -} + }; +}; const serializeBranchNode = (node: BranchNode) => { const size = node.orientation === Orientation.HORIZONTAL ? node.size - : node.orthogonalSize + : node.orthogonalSize; return { orientation: node.orientation, size, data: node.children.map((child) => { if (child instanceof LeafNode) { - return serializeLeafNode(child) + return serializeLeafNode(child); } - return serializeBranchNode(child as BranchNode) + return serializeBranchNode(child as BranchNode); }), type: 'branch', - } -} + }; +}; export interface ISerializedLeafNode { - type: 'leaf' - data: any - size: number - visible?: boolean + type: 'leaf'; + data: any; + size: number; + visible?: boolean; } export interface ISerializedBranchNode { - type: 'branch' - data: ISerializedNode[] - size: number + type: 'branch'; + data: ISerializedNode[]; + size: number; } -export type ISerializedNode = ISerializedLeafNode | ISerializedBranchNode +export type ISerializedNode = ISerializedLeafNode | ISerializedBranchNode; export interface INodeDescriptor { - node: Node - visible?: boolean + node: Node; + visible?: boolean; } export interface IViewDeserializer { - fromJSON: (data: {}) => IGridView + fromJSON: (data: {}) => IGridView; } export class Gridview { - private _root: BranchNode - public readonly element: HTMLElement + private _root: BranchNode; + public readonly element: HTMLElement; - private readonly _onDidChange = new Emitter() - readonly onDidChange: Event = this._onDidChange.event + private readonly _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; public serialize() { return { @@ -219,34 +221,34 @@ export class Gridview { height: this.height, width: this.width, orientation: this.orientation, - } + }; } public dispose() { - this.root.dispose() + this.root.dispose(); } public clear() { - this.root.dispose() + this.root.dispose(); this.root = new BranchNode( Orientation.HORIZONTAL, this.proportionalLayout, 0, 0 - ) + ); } public deserialize(json: any, deserializer: IViewDeserializer) { - const orientation = json.orientation - const height = json.height + const orientation = json.orientation; + const height = json.height; - this.orientation = orientation + this.orientation = orientation; this._deserialize( json.root as ISerializedBranchNode, orientation, deserializer, height - ) + ); } private _deserialize( @@ -260,7 +262,7 @@ export class Gridview { orientation, deserializer, orthogonalSize - ) as BranchNode + ) as BranchNode; } private _deserializeNode( @@ -269,9 +271,9 @@ export class Gridview { deserializer: IViewDeserializer, orthogonalSize: number ): Node { - let result: Node + let result: Node; if (node.type === 'branch') { - const serializedChildren = node.data as ISerializedNode[] + const serializedChildren = node.data as ISerializedNode[]; const children = serializedChildren.map((serializedChild) => { return { node: this._deserializeNode( @@ -280,8 +282,8 @@ export class Gridview { deserializer, node.size ), - } as INodeDescriptor - }) + } as INodeDescriptor; + }); result = new BranchNode( orientation, @@ -289,276 +291,280 @@ export class Gridview { node.size, orthogonalSize, children - ) + ); } else { result = new LeafNode( deserializer.fromJSON(node.data), orientation, orthogonalSize, node.size - ) + ); } - return result + return result; } public get orientation() { - return this.root.orientation + return this.root.orientation; } public set orientation(orientation: Orientation) { if (this._root.orientation === orientation) { - return + return; } - const { size, orthogonalSize } = this._root - this.root = flipNode(this._root, orthogonalSize, size) - this.root.layout(size, orthogonalSize) + const { size, orthogonalSize } = this._root; + this.root = flipNode(this._root, orthogonalSize, size); + this.root.layout(size, orthogonalSize); } private get root(): BranchNode { - return this._root + return this._root; } - private disposable: IDisposable + private disposable: IDisposable; private set root(root: BranchNode) { - const oldRoot = this._root + const oldRoot = this._root; if (oldRoot) { - this.disposable?.dispose() - oldRoot.dispose() - this.element.removeChild(oldRoot.element) + this.disposable?.dispose(); + oldRoot.dispose(); + this.element.removeChild(oldRoot.element); } - this._root = root + this._root = root; this.disposable = this._root.onDidChange((e) => { - this._onDidChange.fire(e) - }) - this.element.appendChild(root.element) + this._onDidChange.fire(e); + }); + this.element.appendChild(root.element); } public next(location: number[]) { - return this.progmaticSelect(location) + return this.progmaticSelect(location); } public preivous(location: number[]) { - return this.progmaticSelect(location, true) + return this.progmaticSelect(location, true); } private progmaticSelect(location: number[], reverse = false) { - const [rest, index] = tail(location) - const [path, node] = this.getNode(location) + const [rest, index] = tail(location); + const [path, node] = this.getNode(location); if (!(node instanceof LeafNode)) { - throw new Error('invalid location') + throw new Error('invalid location'); } const findLeaf = (node: Node, last: boolean): LeafNode => { if (node instanceof LeafNode) { - return node + return node; } if (node instanceof BranchNode) { return findLeaf( node.children[last ? node.children.length - 1 : 0], last - ) + ); } - throw new Error('invalid node') - } + throw new Error('invalid node'); + }; for (let i = path.length - 1; i > -1; i--) { - const n = path[i] - const l = location[i] || 0 + const n = path[i]; + const l = location[i] || 0; const canProgressInCurrentLevel = reverse ? l - 1 > -1 - : l + 1 < n.children.length + : l + 1 < n.children.length; if (canProgressInCurrentLevel) { - return findLeaf(n.children[reverse ? l - 1 : l + 1], reverse) + return findLeaf(n.children[reverse ? l - 1 : l + 1], reverse); } } - return findLeaf(this.root, reverse) + return findLeaf(this.root, reverse); } get width(): number { - return this.root.width + return this.root.width; } get height(): number { - return this.root.height + return this.root.height; } get minimumWidth(): number { - return this.root.minimumWidth + return this.root.minimumWidth; } get minimumHeight(): number { - return this.root.minimumHeight + return this.root.minimumHeight; } get maximumWidth(): number { - return this.root.maximumHeight + return this.root.maximumHeight; } get maximumHeight(): number { - return this.root.maximumHeight + return this.root.maximumHeight; } constructor(readonly proportionalLayout: boolean) { - this.element = document.createElement('div') - this.element.className = 'grid-view' + this.element = document.createElement('div'); + this.element.className = 'grid-view'; this.root = new BranchNode( Orientation.HORIZONTAL, proportionalLayout, 0, 0 - ) + ); - this.element.appendChild(this.root.element) + this.element.appendChild(this.root.element); } public moveView(parentLocation: number[], from: number, to: number): void { - const [, parent] = this.getNode(parentLocation) + const [, parent] = this.getNode(parentLocation); if (!(parent instanceof BranchNode)) { - throw new Error('Invalid location') + throw new Error('Invalid location'); } - parent.moveChild(from, to) + parent.moveChild(from, to); } public addView(view: IGridView, size: number | Sizing, location: number[]) { - const [rest, index] = tail(location) + const [rest, index] = tail(location); - const [pathToParent, parent] = this.getNode(rest) + const [pathToParent, parent] = this.getNode(rest); if (parent instanceof BranchNode) { const node = new LeafNode( view, orthogonal(parent.orientation), parent.orthogonalSize - ) - parent.addChild(node, size, index) + ); + parent.addChild(node, size, index); } else { - const [grandParent, ..._] = [...pathToParent].reverse() - const [parentIndex, ...__] = [...rest].reverse() + const [grandParent, ..._] = [...pathToParent].reverse(); + const [parentIndex, ...__] = [...rest].reverse(); - let newSiblingSize: number | Sizing = 0 + let newSiblingSize: number | Sizing = 0; - grandParent.removeChild(parentIndex) + grandParent.removeChild(parentIndex); const newParent = new BranchNode( parent.orientation, this.proportionalLayout, parent.size, parent.orthogonalSize - ) - grandParent.addChild(newParent, parent.size, parentIndex) + ); + grandParent.addChild(newParent, parent.size, parentIndex); const newSibling = new LeafNode( parent.view, grandParent.orientation, parent.size - ) - newParent.addChild(newSibling, newSiblingSize, 0) + ); + newParent.addChild(newSibling, newSiblingSize, 0); if (typeof size !== 'number' && size.type === 'split') { - size = { type: 'split', index: 0 } + size = { type: 'split', index: 0 }; } const node = new LeafNode( view, grandParent.orientation, parent.size - ) - newParent.addChild(node, size, index) + ); + newParent.addChild(node, size, index); } } public remove(view: IGridView, sizing?: Sizing) { - const location = getGridLocation(view.element) - return this.removeView(location, sizing) + const location = getGridLocation(view.element); + return this.removeView(location, sizing); } removeView(location: number[], sizing?: Sizing): IGridView { - const [rest, index] = tail(location) - const [pathToParent, parent] = this.getNode(rest) + const [rest, index] = tail(location); + const [pathToParent, parent] = this.getNode(rest); if (!(parent instanceof BranchNode)) { - throw new Error('Invalid location') + throw new Error('Invalid location'); } - const node = parent.children[index] + const node = parent.children[index]; if (!(node instanceof LeafNode)) { - throw new Error('Invalid location') + throw new Error('Invalid location'); } - parent.removeChild(index, sizing) + parent.removeChild(index, sizing); if (parent.children.length === 0) { - throw new Error('Invalid grid state') + throw new Error('Invalid grid state'); } if (parent.children.length > 1) { - return node.view + return node.view; } if (pathToParent.length === 0) { // parent is root - const sibling = parent.children[0] + const sibling = parent.children[0]; if (sibling instanceof LeafNode) { - return node.view + return node.view; } // we must promote sibling to be the new root - parent.removeChild(0, sizing) - this.root = sibling - return node.view + parent.removeChild(0, sizing); + this.root = sibling; + return node.view; } - const [grandParent, ..._] = [...pathToParent].reverse() - const [parentIndex, ...__] = [...rest].reverse() + const [grandParent, ..._] = [...pathToParent].reverse(); + const [parentIndex, ...__] = [...rest].reverse(); - const sibling = parent.children[0] - parent.removeChild(0, sizing) + const sibling = parent.children[0]; + parent.removeChild(0, sizing); const sizes = grandParent.children.map((_, i) => grandParent.getChildSize(i) - ) - grandParent.removeChild(parentIndex, sizing) + ); + grandParent.removeChild(parentIndex, sizing); if (sibling instanceof BranchNode) { - sizes.splice(parentIndex, 1, ...sibling.children.map((c) => c.size)) + sizes.splice( + parentIndex, + 1, + ...sibling.children.map((c) => c.size) + ); for (let i = 0; i < sibling.children.length; i++) { - const child = sibling.children[i] - grandParent.addChild(child, child.size, parentIndex + i) + const child = sibling.children[i]; + grandParent.addChild(child, child.size, parentIndex + i); } } else { const newSibling = new LeafNode( sibling.view, orthogonal(sibling.orientation), sibling.size - ) + ); grandParent.addChild( newSibling, sibling.orthogonalSize, parentIndex - ) + ); } for (let i = 0; i < sizes.length; i++) { - grandParent.resizeChild(i, sizes[i]) + grandParent.resizeChild(i, sizes[i]); } - return node.view + return node.view; } public layout(width: number, height: number) { const [size, orthogonalSize] = this.root.orientation === Orientation.HORIZONTAL ? [height, width] - : [width, height] - this.root.layout(size, orthogonalSize) + : [width, height]; + this.root.layout(size, orthogonalSize); } private getNode( @@ -567,22 +573,22 @@ export class Gridview { path: BranchNode[] = [] ): [BranchNode[], Node] { if (location.length === 0) { - return [path, node] + return [path, node]; } if (!(node instanceof BranchNode)) { - throw new Error('Invalid location') + throw new Error('Invalid location'); } - const [index, ...rest] = location + const [index, ...rest] = location; if (index < 0 || index >= node.children.length) { - throw new Error('Invalid location') + throw new Error('Invalid location'); } - const child = node.children[index] - path.push(node) + const child = node.children[index]; + path.push(node); - return this.getNode(rest, child, path) + return this.getNode(rest, child, path); } } diff --git a/packages/splitview/src/gridview/leafNode.ts b/packages/splitview/src/gridview/leafNode.ts index af2a384ae..86f859ac9 100644 --- a/packages/splitview/src/gridview/leafNode.ts +++ b/packages/splitview/src/gridview/leafNode.ts @@ -1,73 +1,73 @@ -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() - readonly onDidChange: Event = this._onDidChange.event - private _size: number - private _orthogonalSize: number + private readonly _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; + private _size: number; + private _orthogonalSize: number; public dispose() {} private get minimumWidth(): number { - return this.view.minimumWidth + return this.view.minimumWidth; } private get maximumWidth(): number { - return this.view.maximumWidth + return this.view.maximumWidth; } private get minimumHeight(): number { - return this.view.minimumHeight + return this.view.minimumHeight; } private get maximumHeight(): number { - return this.view.maximumHeight + return this.view.maximumHeight; } get priority(): LayoutPriority | undefined { - return this.view.priority + return this.view.priority; } get snapSize() { - return this.view.snap ? this.minimumSize / 2 : undefined + return this.view.snap ? this.minimumSize / 2 : undefined; } get minimumSize(): number { return this.orientation === Orientation.HORIZONTAL ? this.minimumHeight - : this.minimumWidth + : this.minimumWidth; } get maximumSize(): number { return this.orientation === Orientation.HORIZONTAL ? this.maximumHeight - : this.maximumWidth + : this.maximumWidth; } get minimumOrthogonalSize(): number { return this.orientation === Orientation.HORIZONTAL ? this.minimumWidth - : this.minimumHeight + : this.minimumHeight; } get maximumOrthogonalSize(): number { return this.orientation === Orientation.HORIZONTAL ? this.maximumWidth - : this.maximumHeight + : this.maximumHeight; } get orthogonalSize() { - return this._orthogonalSize + return this._orthogonalSize; } get size() { - return this._size + return this._size; } get element() { - return this.view.element + return this.view.element; } constructor( @@ -76,19 +76,19 @@ export class LeafNode implements IView { orthogonalSize: number, size: number = 0 ) { - this._orthogonalSize = orthogonalSize - this._size = size + this._orthogonalSize = orthogonalSize; + this._size = size; } public layout(size: number, orthogonalSize: number) { - this._size = size - this._orthogonalSize = orthogonalSize + this._size = size; + this._orthogonalSize = orthogonalSize; const [width, height] = this.orientation === Orientation.HORIZONTAL ? [orthogonalSize, size] - : [size, orthogonalSize] + : [size, orthogonalSize]; - this.view.layout(width, height, 0, 0) + this.view.layout(width, height, 0, 0); } } diff --git a/packages/splitview/src/gridview/types.ts b/packages/splitview/src/gridview/types.ts index e493b5d80..2af426f17 100644 --- a/packages/splitview/src/gridview/types.ts +++ b/packages/splitview/src/gridview/types.ts @@ -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; diff --git a/packages/splitview/src/groupview/actions/actionsContainer.ts b/packages/splitview/src/groupview/actions/actionsContainer.ts index 87a5c7539..cc91c2c07 100644 --- a/packages/splitview/src/groupview/actions/actionsContainer.ts +++ b/packages/splitview/src/groupview/actions/actionsContainer.ts @@ -1,22 +1,22 @@ export class ActionContainer { - private _element: HTMLElement - private _list: HTMLElement + private _element: HTMLElement; + private _list: HTMLElement; get element() { - return this._element + return this._element; } constructor() { - this._element = document.createElement('div') - this._element.className = 'actions-bar' + 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) + this._list.appendChild(element); } } diff --git a/packages/splitview/src/groupview/droptarget/dataTransfer.ts b/packages/splitview/src/groupview/droptarget/dataTransfer.ts index 0a81520b3..84c4e62e6 100644 --- a/packages/splitview/src/groupview/droptarget/dataTransfer.ts +++ b/packages/splitview/src/groupview/droptarget/dataTransfer.ts @@ -1,10 +1,10 @@ -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', @@ -12,113 +12,113 @@ export enum DragType { } 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 -} + return data.type === DragType.EXTERNAL; +}; export const extractData = (event: DragEvent): DataObject => { - const data = JSON.parse(event.dataTransfer.getData(DATA_KEY)) + const data = JSON.parse(event.dataTransfer.getData(DATA_KEY)); if (!data) { - console.warn(`[dragEvent] ${DATA_KEY} data is missing`) + console.warn(`[dragEvent] ${DATA_KEY} data is missing`); } if (typeof data.type !== 'string') { - console.warn(`[dragEvent] invalid type ${data.type}`) + console.warn(`[dragEvent] invalid type ${data.type}`); } - return data -} + return data; +}; class DataTransfer { - private map = new Map() + private map = new Map(); public setData(format: string, data: string) { - this.map.set(format, data) + this.map.set(format, data); } public getData(format: string) { - const data = this.map.get(format) - return data + const data = this.map.get(format); + return data; } public has(format: string) { - return this.map.has(format) + return this.map.has(format); } public removeData(format: string) { - const data = this.getData(format) - this.map.delete(format) - return data + const data = this.getData(format); + this.map.delete(format); + return data; } get size() { - return this.map.size + return this.map.size; } } -export const DataTransferSingleton = new DataTransfer() +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 { - 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(): LocalSelectionTransfer { - return LocalSelectionTransfer.INSTANCE as LocalSelectionTransfer + return LocalSelectionTransfer.INSTANCE as LocalSelectionTransfer; } hasData(proto: T): boolean { - return proto && proto === this.proto + return proto && proto === this.proto; } clearData(proto: T): void { if (this.hasData(proto)) { - this.proto = undefined - this.data = undefined + this.proto = undefined; + this.data = undefined; } } getData(proto: T): T[] | undefined { if (this.hasData(proto)) { - return this.data + return this.data; } - return undefined + return undefined; } setData(data: T[], proto: T): void { if (proto) { - this.data = data - this.proto = proto + this.data = data; + this.proto = proto; } } } diff --git a/packages/splitview/src/groupview/droptarget/droptarget.ts b/packages/splitview/src/groupview/droptarget/droptarget.ts index 3f53779f6..1e353a41a 100644 --- a/packages/splitview/src/groupview/droptarget/droptarget.ts +++ b/packages/splitview/src/groupview/droptarget/droptarget.ts @@ -1,5 +1,5 @@ -import { Emitter, Event } from '../../events' -import { DataTransferSingleton } from './dataTransfer' +import { Emitter, Event } from '../../events'; +import { DataTransferSingleton } from './dataTransfer'; export enum Position { Top = 'Top', @@ -10,19 +10,19 @@ export enum Position { } 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, @@ -30,36 +30,36 @@ const toggleClassName = ( addOrRemove: boolean ) => { if (addOrRemove && !element.classList.contains(className)) { - element.classList.add(className) + element.classList.add(className); } else if (!addOrRemove && element.classList.contains(className)) { - element.classList.remove(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() - readonly onDidChange: Event = this._onDidChange.event + private readonly _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; constructor( private element: HTMLElement, private options: { - isDisabled: () => boolean - isDirectional: boolean - id: string - enableExternalDragEvents?: boolean + isDisabled: () => boolean; + isDirectional: boolean; + id: string; + enableExternalDragEvents?: boolean; } ) { - this.element.addEventListener('dragenter', this.onDragEnter) + this.element.addEventListener('dragenter', this.onDragEnter); } public dispose() { - this._onDidChange.dispose() - this.removeDropTarget() - this.element.removeEventListener('dragenter', this.onDragEnter) + this._onDidChange.dispose(); + this.removeDropTarget(); + this.element.removeEventListener('dragenter', this.onDragEnter); } private onDragEnter = (event: DragEvent) => { @@ -67,104 +67,104 @@ export class Droptarget { !this.options.enableExternalDragEvents && !DataTransferSingleton.has(this.options.id) ) { - console.debug('[droptarget] invalid event') - return + console.debug('[droptarget] invalid event'); + return; } if (this.options.isDisabled()) { - return + return; } - event.preventDefault() + 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' + 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.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) + this.element.classList.add('drop-target'); + this.element.append(this.target); } - } + }; private onDrop = (event: DragEvent) => { if ( !this.options.enableExternalDragEvents && !DataTransferSingleton.has(this.options.id) ) { - console.debug('[dragtarget] invalid') - return + console.debug('[dragtarget] invalid'); + return; } - console.debug('[dragtarget] drop') - this.removeDropTarget() + console.debug('[dragtarget] drop'); + this.removeDropTarget(); if (!hasProcessed(event)) { - this._onDidChange.fire({ position: this.state, event }) + this._onDidChange.fire({ position: this.state, event }); } else { - console.debug('[dragtarget] already processed') + console.debug('[dragtarget] already processed'); } - this.state = undefined + this.state = undefined; - setEventAsProcessed(event) - } + setEventAsProcessed(event); + }; private onDragOver = (event: DragEvent) => { - event.preventDefault() + event.preventDefault(); if (!this.options.isDirectional) { - return + return; } - 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 + 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; - const isRight = xp > 80 - const isLeft = xp < 20 - const isTop = !isRight && !isLeft && yp < 20 - const isBottom = !isRight && !isLeft && yp > 80 + 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) + 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 + this.state = Position.Right; } else if (isLeft) { - this.state = Position.Left + this.state = Position.Left; } else if (isTop) { - this.state = Position.Top + this.state = Position.Top; } else if (isBottom) { - this.state = Position.Bottom + this.state = Position.Bottom; } else { - this.state = Position.Center + this.state = Position.Center; } - } + }; private onDragLeave = (event: DragEvent) => { - console.debug('[droptarget] leave') - this.removeDropTarget() - } + 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') + 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'); } } } diff --git a/packages/splitview/src/groupview/events.ts b/packages/splitview/src/groupview/events.ts index dbb5c4cd6..c86ff77cd 100644 --- a/packages/splitview/src/groupview/events.ts +++ b/packages/splitview/src/groupview/events.ts @@ -1,9 +1,9 @@ -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 { @@ -12,8 +12,8 @@ export enum MouseEventKind { } export interface LayoutMouseEvent { - kind: MouseEventKind - event: MouseEvent - panel?: IGroupPanel - tab?: boolean + kind: MouseEventKind; + event: MouseEvent; + panel?: IGroupPanel; + tab?: boolean; } diff --git a/packages/splitview/src/groupview/groupview.ts b/packages/splitview/src/groupview/groupview.ts index 74031aabd..fce1a5407 100644 --- a/packages/splitview/src/groupview/groupview.ts +++ b/packages/splitview/src/groupview/groupview.ts @@ -1,20 +1,20 @@ -import { IDisposable, CompositeDisposable, Disposable } from '../lifecycle' -import { ITabContainer, TabContainer } from './titlebar/tabContainer' -import { IContentContainer, ContentContainer } from './panel/content/content' -import { IGridView } from '../gridview/gridview' -import { Position, Droptarget, DroptargetEvent } from './droptarget/droptarget' -import { Event, Emitter, addDisposableListener } from '../events' -import { IGroupAccessor, Layout } from '../layout' -import { toggleClass } from '../dom' -import { ClosePanelResult, WatermarkPart } from './panel/parts' -import { IGroupPanel } from './panel/types' -import { timeoutPromise } from '../async' +import { IDisposable, CompositeDisposable, Disposable } from '../lifecycle'; +import { ITabContainer, TabContainer } from './titlebar/tabContainer'; +import { IContentContainer, ContentContainer } from './panel/content/content'; +import { Position, Droptarget, DroptargetEvent } from './droptarget/droptarget'; +import { Event, Emitter, addDisposableListener } from '../events'; +import { IComponentGridview, IGroupAccessor, Layout } from '../layout'; +import { toggleClass } from '../dom'; +import { ClosePanelResult, WatermarkPart } from './panel/parts'; +import { IGroupPanel } from './panel/types'; +import { timeoutPromise } from '../async'; import { extractData, isTabDragEvent, isCustomDragEvent, isPanelTransferEvent, -} from './droptarget/dataTransfer' +} from './droptarget/dataTransfer'; +import { IBaseGridView } from '../layout/baseGrid'; export const enum GroupChangeKind { GROUP_ACTIVE = 'GROUP_ACTIVE', @@ -39,221 +39,219 @@ export const enum GroupChangeKind { } export interface IGroupItem { - id: string - header: { element: HTMLElement } - body: { element: HTMLElement } + id: string; + header: { element: HTMLElement }; + body: { element: HTMLElement }; } interface GroupMoveEvent { - groupId: string - itemId: string - target: Position - index?: number + groupId: string; + itemId: string; + target: Position; + index?: number; } export interface GroupOptions { - panels: IGroupPanel[] - activePanel?: IGroupPanel + panels: IGroupPanel[]; + activePanel?: IGroupPanel; } export interface GroupChangeEvent { - kind: GroupChangeKind - panel?: IGroupPanel + kind: GroupChangeKind; + panel?: IGroupPanel; } -export interface IGroupview extends IDisposable, IGridView { - id: string - size: number - panels: IGroupPanel[] - tabHeight: number - setActive: (isActive: boolean) => void +export interface IGroupview extends IDisposable, IBaseGridView { + size: number; + panels: IGroupPanel[]; + tabHeight: number; // state - isPanelActive: (panel: IGroupPanel) => boolean - isActive: boolean - activePanel: IGroupPanel - indexOf(panel: IGroupPanel): number + isPanelActive: (panel: IGroupPanel) => boolean; + isActive: boolean; + activePanel: IGroupPanel; + indexOf(panel: IGroupPanel): number; // panel lifecycle - openPanel(panel: IGroupPanel, index?: number): void - closePanel(panel: IGroupPanel): Promise - closeAllPanels(): Promise - containsPanel(panel: IGroupPanel): boolean - removePanel: (panelOrId: IGroupPanel | string) => IGroupPanel + openPanel(panel: IGroupPanel, index?: number): void; + closePanel(panel: IGroupPanel): Promise; + closeAllPanels(): Promise; + containsPanel(panel: IGroupPanel): boolean; + removePanel: (panelOrId: IGroupPanel | string) => IGroupPanel; // events - onDidGroupChange: Event<{ kind: GroupChangeKind }> - onMove: Event + onDidGroupChange: Event<{ kind: GroupChangeKind }>; + onMove: Event; // - startActiveDrag(panel: IGroupPanel): IDisposable + startActiveDrag(panel: IGroupPanel): IDisposable; // - moveToNext(options?: { panel?: IGroupPanel; suppressRoll?: boolean }): void + moveToNext(options?: { panel?: IGroupPanel; suppressRoll?: boolean }): void; moveToPrevious(options?: { - panel?: IGroupPanel - suppressRoll?: boolean - }): void + panel?: IGroupPanel; + suppressRoll?: boolean; + }): void; } export interface GroupDropEvent { - event: DragEvent - target: Position - index?: number + event: DragEvent; + target: Position; + index?: number; } export class Groupview extends CompositeDisposable implements IGroupview { - private _element: HTMLElement + private _element: HTMLElement; - private tabContainer: ITabContainer - private contentContainer: IContentContainer - private _active: boolean - private _activePanel: IGroupPanel - private dropTarget: Droptarget - private watermark: WatermarkPart + private tabContainer: ITabContainer; + private contentContainer: IContentContainer; + private _active: boolean; + private _activePanel: IGroupPanel; + private dropTarget: Droptarget; + private watermark: WatermarkPart; - private _width: number - private _height: number + private _width: number; + private _height: number; - private _panels: IGroupPanel[] = [] + private _panels: IGroupPanel[] = []; - private readonly _onMove = new Emitter() - readonly onMove: Event = this._onMove.event + private readonly _onMove = new Emitter(); + readonly onMove: Event = this._onMove.event; - private readonly _onDrop = new Emitter() - readonly onDrop: Event = this._onDrop.event + private readonly _onDrop = new Emitter(); + readonly onDrop: Event = this._onDrop.event; - private readonly _onDidGroupChange = new Emitter() + private readonly _onDidGroupChange = new Emitter(); readonly onDidGroupChange: Event<{ kind: GroupChangeKind }> = this - ._onDidGroupChange.event + ._onDidGroupChange.event; get activePanel() { - return this._activePanel + return this._activePanel; } get tabHeight() { - return this.tabContainer.height + return this.tabContainer.height; } set tabHeight(height: number) { - this.tabContainer.height = height - this.layout(this._width, this._height) + this.tabContainer.height = height; + this.layout(this._width, this._height); } get isActive() { - return this._active + return this._active; } get panels() { - return this._panels + return this._panels; } get element() { - return this._element + return this._element; } get size() { - return this._panels.length + return this._panels.length; } get isEmpty() { - return this._panels.length === 0 + return this._panels.length === 0; } get minimumHeight() { - return 100 + return 100; } get maximumHeight() { - return Number.MAX_SAFE_INTEGER + return Number.MAX_SAFE_INTEGER; } get minimumWidth() { - return 100 + return 100; } get maximumWidth() { - return Number.MAX_SAFE_INTEGER + return Number.MAX_SAFE_INTEGER; } public indexOf(panel: IGroupPanel) { - return this.tabContainer.indexOf(panel.id) + return this.tabContainer.indexOf(panel.id); } public toJSON(): object { return { views: this.panels.map((panel) => panel.id), activeView: this._activePanel?.id, - } + }; } public startActiveDrag(panel: IGroupPanel): IDisposable { - const index = this.tabContainer.indexOf(panel.id) + const index = this.tabContainer.indexOf(panel.id); if (index > -1) { - const tab = this.tabContainer.at(index) - tab.startDragEvent() + const tab = this.tabContainer.at(index); + tab.startDragEvent(); return { dispose: () => { - tab.stopDragEvent() + tab.stopDragEvent(); }, - } + }; } - return Disposable.NONE + return Disposable.NONE; } public moveToNext(options?: { - panel?: IGroupPanel - suppressRoll?: boolean + panel?: IGroupPanel; + suppressRoll?: boolean; }) { if (!options) { - options = {} + options = {}; } if (!options.panel) { - options.panel = this.activePanel + options.panel = this.activePanel; } - const index = this.panels.indexOf(options.panel) + const index = this.panels.indexOf(options.panel); - let normalizedIndex: number = undefined + let normalizedIndex: number = undefined; if (index < this.panels.length - 1) { - normalizedIndex = index + 1 + normalizedIndex = index + 1; } else if (!options.suppressRoll) { - normalizedIndex = 0 + normalizedIndex = 0; } if (normalizedIndex === undefined) { - return + return; } - this.openPanel(this.panels[normalizedIndex]) + this.openPanel(this.panels[normalizedIndex]); } public moveToPrevious(options?: { - panel?: IGroupPanel - suppressRoll?: boolean + panel?: IGroupPanel; + suppressRoll?: boolean; }) { if (!options) { - options = {} + options = {}; } if (!options.panel) { - options.panel = this.activePanel + options.panel = this.activePanel; } - const index = this.panels.indexOf(options.panel) + const index = this.panels.indexOf(options.panel); - let normalizedIndex: number = undefined + let normalizedIndex: number = undefined; if (index > 0) { - normalizedIndex = index - 1 + normalizedIndex = index - 1; } else if (!options.suppressRoll) { - normalizedIndex = this.panels.length - 1 + normalizedIndex = this.panels.length - 1; } if (normalizedIndex === undefined) { - return + return; } - this.openPanel(this.panels[normalizedIndex]) + this.openPanel(this.panels[normalizedIndex]); } public containsPanel(panel: IGroupPanel) { - return this.panels.includes(panel) + return this.panels.includes(panel); } constructor( @@ -261,16 +259,16 @@ export class Groupview extends CompositeDisposable implements IGroupview { public id: string, private options?: GroupOptions ) { - super() + super(); - this.addDisposables(this._onMove, this._onDidGroupChange, this._onDrop) + this.addDisposables(this._onMove, this._onDidGroupChange, this._onDrop); - this._element = document.createElement('div') - this._element.className = 'groupview' - this._element.tabIndex = -1 + this._element = document.createElement('div'); + this._element.className = 'groupview'; + this._element.tabIndex = -1; - this.tabContainer = new TabContainer(this.accessor, this) - this.contentContainer = new ContentContainer() + this.tabContainer = new TabContainer(this.accessor, this); + this.contentContainer = new ContentContainer(); this.dropTarget = new Droptarget(this.contentContainer.element, { isDirectional: true, id: this.accessor.id, @@ -279,16 +277,16 @@ export class Groupview extends CompositeDisposable implements IGroupview { return ( this._panels.length === 1 && this.tabContainer.hasActiveDragEvent - ) + ); }, enableExternalDragEvents: this.accessor.options .enableExternalDragEvents, - }) + }); this._element.append( this.tabContainer.element, this.contentContainer.element - ) + ); this.addDisposables( this._onMove, @@ -297,7 +295,7 @@ export class Groupview extends CompositeDisposable implements IGroupview { this.handleDropEvent(event.event, event.index) ), this.contentContainer.onDidFocus(() => { - this.accessor.doSetGroupActive(this) + this.accessor.doSetGroupActive(this); }), this.dropTarget.onDidChange((event) => { // if we've center dropped on ourself then ignore @@ -305,97 +303,99 @@ export class Groupview extends CompositeDisposable implements IGroupview { event.position === Position.Center && this.tabContainer.hasActiveDragEvent ) { - return + return; } - this.handleDropEvent(event) + this.handleDropEvent(event); }) - ) + ); if (options?.panels) { options.panels.forEach((panel) => { - this.openPanel(panel) - }) + this.openPanel(panel); + }); } if (options?.activePanel) { - this.openPanel(options?.activePanel) + this.openPanel(options?.activePanel); } - this.updateContainer() + this.updateContainer(); } public openPanel(panel: IGroupPanel, index: number = this.panels.length) { if (this._activePanel === panel) { - this.accessor.doSetGroupActive(this) - return + this.accessor.doSetGroupActive(this); + return; } - this.doAddPanel(panel, index) + this.doAddPanel(panel, index); - this.tabContainer.openPanel(panel, index) - this.contentContainer.openPanel(panel.content.element) + this.tabContainer.openPanel(panel, index); + this.contentContainer.openPanel(panel.content.element); - this.doSetActivePanel(panel) - this.accessor.doSetGroupActive(this) + this.doSetActivePanel(panel); + this.accessor.doSetGroupActive(this); - this.updateContainer() + this.updateContainer(); } public removePanel(groupItemOrId: IGroupPanel | string): IGroupPanel { const id = - typeof groupItemOrId === 'string' ? groupItemOrId : groupItemOrId.id + typeof groupItemOrId === 'string' + ? groupItemOrId + : groupItemOrId.id; - const panel = this._panels.find((panel) => panel.id === id) + const panel = this._panels.find((panel) => panel.id === id); if (!panel) { - throw new Error('invalid operation') + throw new Error('invalid operation'); } - return this._removePanel(panel) + return this._removePanel(panel); } public async closeAllPanels() { - const index = this.panels.indexOf(this._activePanel) + const index = this.panels.indexOf(this._activePanel); if (index > -1) { if (this.panels.indexOf(this._activePanel) < 0) { - console.warn('active panel not tracked') + console.warn('active panel not tracked'); } const canClose = !this._activePanel.close || - (await this._activePanel.close()) === ClosePanelResult.CLOSE + (await this._activePanel.close()) === ClosePanelResult.CLOSE; if (!canClose) { - return false + return false; } } for (let i = 0; i < this.panels.length; i++) { if (i === index) { - continue + continue; } - const panel = this.panels[i] - this.openPanel(panel) + const panel = this.panels[i]; + this.openPanel(panel); if (panel.close) { - await timeoutPromise(0) + await timeoutPromise(0); const canClose = - (await panel.close()) === ClosePanelResult.CLOSE + (await panel.close()) === ClosePanelResult.CLOSE; if (!canClose) { - return false + return false; } } } if (this.panels.length > 0) { // take a copy since we will be edting the array as we iterate through - const arrPanelCpy = [...this.panels] - await Promise.all(arrPanelCpy.map((p) => this.doClose(p))) + const arrPanelCpy = [...this.panels]; + await Promise.all(arrPanelCpy.map((p) => this.doClose(p))); } else { - this.accessor.removeGroup(this) + this.accessor.removeGroup(this); } - return true + return true; } public closePanel = async (panel: IGroupPanel) => { @@ -403,159 +403,163 @@ export class Groupview extends CompositeDisposable implements IGroupview { panel.close && (await panel.close()) === ClosePanelResult.DONT_CLOSE ) { - return false + return false; } - this.doClose(panel) - return true - } + this.doClose(panel); + return true; + }; private doClose(panel: IGroupPanel) { - this._removePanel(panel) - ;(this.accessor as Layout).unregisterPanel(panel) + this._removePanel(panel); + (this.accessor as Layout).unregisterPanel(panel); - panel.dispose() + panel.dispose(); if (this.panels.length === 0) { - this.accessor.removeGroup(this) + this.accessor.removeGroup(this); } } public isPanelActive(panel: IGroupPanel) { - return this._activePanel === panel + return this._activePanel === panel; } public setActive(isActive: boolean) { if (this._active === isActive) { - return + return; } - this._active = isActive + this._active = isActive; - toggleClass(this.element, 'active-group', isActive) - toggleClass(this.element, 'inactive-group', !isActive) + toggleClass(this.element, 'active-group', isActive); + toggleClass(this.element, 'inactive-group', !isActive); - this.tabContainer.setActive(this._active) + this.tabContainer.setActive(this._active); if (!this._activePanel && this.panels.length > 0) { - this.doSetActivePanel(this.panels[0]) + this.doSetActivePanel(this.panels[0]); } - this.panels.forEach((panel) => panel.setVisible(this._active, this)) + this.panels.forEach((panel) => panel.setVisible(this._active, this)); if (this.watermark?.setVisible) { - this.watermark.setVisible(this._active, this) + this.watermark.setVisible(this._active, this); } if (isActive) { - this._onDidGroupChange.fire({ kind: GroupChangeKind.GROUP_ACTIVE }) + this._onDidGroupChange.fire({ kind: GroupChangeKind.GROUP_ACTIVE }); } } public layout(width: number, height: number) { - this._width = width - this._height = height + this._width = width; + this._height = height; if (this._activePanel?.layout) { - this._activePanel.layout(this._width, this._height) + this._activePanel.layout(this._width, this._height); } } private _removePanel(panel: IGroupPanel) { - const index = this._panels.indexOf(panel) + const index = this._panels.indexOf(panel); - const isActivePanel = this._activePanel === panel + const isActivePanel = this._activePanel === panel; - this.doRemovePanel(panel) + this.doRemovePanel(panel); if (isActivePanel && this.panels.length > 0) { - const nextPanel = this.panels[Math.max(0, index - 1)] - this.openPanel(nextPanel) + const nextPanel = this.panels[Math.max(0, index - 1)]; + this.openPanel(nextPanel); } if (this._activePanel && this.panels.length === 0) { - this._activePanel = undefined + this._activePanel = undefined; } - this.updateContainer() - return panel + this.updateContainer(); + return panel; } private doRemovePanel(panel: IGroupPanel) { - const index = this.panels.indexOf(panel) + const index = this.panels.indexOf(panel); if (this._activePanel === panel) { - this.contentContainer.closePanel() + this.contentContainer.closePanel(); } - this.tabContainer.delete(panel.id) - this._panels.splice(index, 1) + this.tabContainer.delete(panel.id); + this._panels.splice(index, 1); this._onDidGroupChange.fire({ kind: GroupChangeKind.REMOVE_PANEL, panel, - }) + }); } private doAddPanel(panel: IGroupPanel, index: number) { - const existingPanel = this._panels.indexOf(panel) - const hasExistingPabel = existingPanel > -1 + const existingPanel = this._panels.indexOf(panel); + const hasExistingPabel = existingPanel > -1; if (hasExistingPabel) { // TODO - need to ensure ordering hasn't changed and if it has need to re-order this.panels - return + return; } - this.panels.splice(index, 0, panel) + this.panels.splice(index, 0, panel); - this._onDidGroupChange.fire({ kind: GroupChangeKind.ADD_PANEL }) + this._onDidGroupChange.fire({ kind: GroupChangeKind.ADD_PANEL }); } private doSetActivePanel(panel: IGroupPanel) { - this._activePanel = panel - this.tabContainer.setActivePanel(panel) - panel.layout(this._width, this._height) - this._onDidGroupChange.fire({ kind: GroupChangeKind.PANEL_ACTIVE }) + this._activePanel = panel; + this.tabContainer.setActivePanel(panel); + panel.layout(this._width, this._height); + this._onDidGroupChange.fire({ kind: GroupChangeKind.PANEL_ACTIVE }); } private updateContainer() { - toggleClass(this.element, 'empty', this.isEmpty) + toggleClass(this.element, 'empty', this.isEmpty); if (this.accessor.options.watermarkComponent && !this.watermark) { - const WatermarkComponent = this.accessor.options.watermarkComponent - this.watermark = new WatermarkComponent() - this.watermark.init({ accessor: this.accessor }) + const WatermarkComponent = this.accessor.options.watermarkComponent; + this.watermark = new WatermarkComponent(); + this.watermark.init({ accessor: this.accessor }); } - this.panels.forEach((panel) => panel.setVisible(this._active, this)) + this.panels.forEach((panel) => panel.setVisible(this._active, this)); if (this.isEmpty && !this.watermark?.element.parentNode) { addDisposableListener(this.watermark.element, 'click', () => { if (!this._active) { - this.accessor.doSetGroupActive(this) + this.accessor.doSetGroupActive(this); } - }) + }); - this.contentContainer.openPanel(this.watermark.element) + this.contentContainer.openPanel(this.watermark.element); - this.watermark.setVisible(true, this) + this.watermark.setVisible(true, this); } if (!this.isEmpty && this.watermark.element.parentNode) { - this.watermark.dispose() - this.watermark = undefined - this.contentContainer.closePanel() + this.watermark.dispose(); + this.watermark = undefined; + this.contentContainer.closePanel(); } } private handleDropEvent(event: DroptargetEvent, index?: number) { if (isPanelTransferEvent(event.event)) { - this.handlePanelDropEvent(event.event, event.position, index) - return + this.handlePanelDropEvent(event.event, event.position, index); + return; } - this._onDrop.fire({ event: event.event, target: event.position, index }) + this._onDrop.fire({ + event: event.event, + target: event.position, + index, + }); - console.debug('[customDropEvent]') + console.debug('[customDropEvent]'); } private handlePanelDropEvent( @@ -563,16 +567,18 @@ export class Groupview extends CompositeDisposable implements IGroupview { target: Position, index?: number ) { - const dataObject = extractData(event) + const dataObject = extractData(event); if (isTabDragEvent(dataObject)) { - const { groupId, itemId } = dataObject - const isSameGroup = this.id === groupId + const { groupId, itemId } = dataObject; + const isSameGroup = this.id === groupId; if (isSameGroup && !target) { - const oldIndex = this.tabContainer.indexOf(itemId) + const oldIndex = this.tabContainer.indexOf(itemId); if (oldIndex === index) { - console.debug('[tabs] drop indicates no change in position') - return + console.debug( + '[tabs] drop indicates no change in position' + ); + return; } } @@ -581,14 +587,14 @@ export class Groupview extends CompositeDisposable implements IGroupview { groupId: dataObject.groupId, itemId: dataObject.itemId, index, - }) + }); } if (isCustomDragEvent(dataObject)) { - let panel = this.accessor.getPanel(dataObject.id) + let panel = this.accessor.getPanel(dataObject.id); if (!panel) { - panel = this.accessor.addPanel(dataObject) + panel = this.accessor.addPanel(dataObject); } this._onMove.fire({ @@ -596,19 +602,19 @@ export class Groupview extends CompositeDisposable implements IGroupview { groupId: panel.group?.id, itemId: panel.id, index, - }) + }); } } public dispose() { for (const panel of this.panels) { - panel.dispose() + panel.dispose(); } - super.dispose() + super.dispose(); - this.dropTarget.dispose() - this.tabContainer.dispose() - this.contentContainer.dispose() + this.dropTarget.dispose(); + this.tabContainer.dispose(); + this.contentContainer.dispose(); } } diff --git a/packages/splitview/src/groupview/panel/api.ts b/packages/splitview/src/groupview/panel/api.ts index 830898cc2..ca10e39fb 100644 --- a/packages/splitview/src/groupview/panel/api.ts +++ b/packages/splitview/src/groupview/panel/api.ts @@ -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 - onDidChangeVisibility: Event + onDidDirtyChange: Event; + onDidChangeVisibility: Event; // misc - readonly isVisible: boolean - group: IGroupview - close: () => Promise - canClose: () => Promise - setClosePanelHook(callback: () => Promise): void + readonly isVisible: boolean; + group: IGroupview; + close: () => Promise; + canClose: () => Promise; + setClosePanelHook(callback: () => Promise): void; } export class GroupPanelApi extends BaseViewApi implements IGroupPanelApi { - private _isVisible: boolean - private _group: IGroupview - private _closePanelCallback: () => Promise + private _isVisible: boolean; + private _group: IGroupview; + private _closePanelCallback: () => Promise; - readonly _onDidDirtyChange = new Emitter() - readonly onDidDirtyChange = this._onDidDirtyChange.event + readonly _onDidDirtyChange = new Emitter(); + readonly onDidDirtyChange = this._onDidDirtyChange.event; readonly _onDidChangeVisibility = new Emitter({ emitLastValue: true, - }) + }); readonly onDidChangeVisibility: Event = this - ._onDidChangeVisibility.event + ._onDidChangeVisibility.event; get isVisible() { - return this._isVisible + return this._isVisible; } get canClose() { - return this._closePanelCallback + return this._closePanelCallback; } set group(value: IGroupview) { - this._group = value + this._group = value; } get group() { - return this._group + return this._group; } constructor(private panel: IGroupPanel, group: IGroupview) { - super() - this._group = group + super(); + this._group = group; this.addDisposables( this._onDidChangeVisibility, this._onDidDirtyChange, this.onDidChangeVisibility((event) => { - this._isVisible = event.isVisible + this._isVisible = event.isVisible; }) - ) + ); } public close() { - return this.group.closePanel(this.panel) + return this.group.closePanel(this.panel); } public setClosePanelHook(callback: () => Promise) { - this._closePanelCallback = callback + this._closePanelCallback = callback; } public dispose() { - super.dispose() + super.dispose(); } } diff --git a/packages/splitview/src/groupview/panel/content/content.ts b/packages/splitview/src/groupview/panel/content/content.ts index cb60b24a6..8662bf4e5 100644 --- a/packages/splitview/src/groupview/panel/content/content.ts +++ b/packages/splitview/src/groupview/panel/content/content.ts @@ -1,55 +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 - element: HTMLElement - openPanel: (panel: HTMLElement) => void - closePanel: () => void + onDidFocus: Event; + element: HTMLElement; + openPanel: (panel: HTMLElement) => void; + closePanel: () => void; } export class ContentContainer extends CompositeDisposable implements IContentContainer { - private _element: HTMLElement - private content: HTMLElement + private _element: HTMLElement; + private content: HTMLElement; - private readonly _onDidFocus = new Emitter() - readonly onDidFocus: Event = this._onDidFocus.event + private readonly _onDidFocus = new Emitter(); + readonly onDidFocus: Event = this._onDidFocus.event; get element() { - return this._element + return this._element; } constructor() { - super() - this._element = document.createElement('div') - this._element.className = 'content-container' - this._element.tabIndex = -1 + super(); + this._element = document.createElement('div'); + this._element.className = 'content-container'; + this._element.tabIndex = -1; - const { onDidBlur, onDidFocus } = trackFocus(this._element) + const { onDidBlur, onDidFocus } = trackFocus(this._element); - this.addDisposables(onDidFocus(() => this._onDidFocus.fire())) + this.addDisposables(onDidFocus(() => this._onDidFocus.fire())); } public openPanel(panel: HTMLElement) { if (this.content) { - this._element.removeChild(this.content) - this.content = undefined + this._element.removeChild(this.content); + this.content = undefined; } - this.content = panel - this._element.appendChild(this.content) + this.content = panel; + this._element.appendChild(this.content); } public closePanel() { if (this.content) { - this._element.removeChild(this.content) - this.content = undefined + this._element.removeChild(this.content); + this.content = undefined; } } public dispose() { - super.dispose() + super.dispose(); } } diff --git a/packages/splitview/src/groupview/panel/panel.ts b/packages/splitview/src/groupview/panel/panel.ts index 6b7430f94..89d358f3a 100644 --- a/packages/splitview/src/groupview/panel/panel.ts +++ b/packages/splitview/src/groupview/panel/panel.ts @@ -1,30 +1,30 @@ -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 + readonly onDidStateChange: Event; get group() { - return this._group + return this._group; } get header() { - return this.headerPart + return this.headerPart; } get content() { - return this.contentPart + return this.contentPart; } constructor( @@ -32,22 +32,22 @@ export class DefaultPanel extends CompositeDisposable implements IGroupPanel { private readonly headerPart: PanelHeaderPart, private readonly contentPart: PanelContentPart ) { - super() + super(); - this.api = new GroupPanelApi(this, this._group) - this.onDidStateChange = this.api.onDidStateChange + this.api = new GroupPanelApi(this, this._group); + this.onDidStateChange = this.api.onDidStateChange; } public setDirty(isDirty: boolean) { - this.api._onDidDirtyChange.fire(isDirty) + this.api._onDidDirtyChange.fire(isDirty); } public close(): Promise { if (this.api.canClose) { - return this.api.canClose() + return this.api.canClose(); } - return Promise.resolve(ClosePanelResult.CLOSE) + return Promise.resolve(ClosePanelResult.CLOSE); } public toJSON(): object { @@ -59,7 +59,7 @@ export class DefaultPanel extends CompositeDisposable implements IGroupPanel { title: this.params.title, suppressClosable: this.params.suppressClosable, state: this.api.getState(), - } + }; } public fromJSON(data: object) { @@ -67,20 +67,20 @@ export class DefaultPanel extends CompositeDisposable implements IGroupPanel { } public update(params: PanelUpdateEvent): void { - this.params.params = { ...this.params.params, ...params } + this.params.params = { ...this.params.params, ...params }; - this.contentPart.update(params.params) - this.api._onDidStateChange.fire() + this.contentPart.update(params.params); + this.api._onDidStateChange.fire(); } public init(params: PanelInitParameters): void { - this.params = params - this.api.setState(this.params.state) + this.params = params; + this.api.setState(this.params.state); if (this.content.init) { - this.content.init({ ...params, api: this.api }) + this.content.init({ ...params, api: this.api }); } if (this.header.init) { - this.header.init({ ...params, api: this.api }) + this.header.init({ ...params, api: this.api }); } } @@ -93,33 +93,33 @@ export class DefaultPanel extends CompositeDisposable implements IGroupPanel { } public setVisible(isGroupActive: boolean, group: IGroupview) { - this._group = group - this.api.group = group + 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._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.contentPart.setVisible) { this.contentPart.setVisible( this._group.isPanelActive(this), isGroupActive - ) + ); } } @@ -128,14 +128,14 @@ export class DefaultPanel extends CompositeDisposable implements IGroupPanel { this.api._onDidPanelDimensionChange.fire({ width, height: height - (this.group?.tabHeight || 0), - }) + }); } public dispose() { - this.api.dispose() - this.mutableDisposable.dispose() + this.api.dispose(); + this.mutableDisposable.dispose(); - this.headerPart.dispose() - this.contentPart.dispose() + this.headerPart.dispose(); + this.contentPart.dispose(); } } diff --git a/packages/splitview/src/groupview/panel/parts.ts b/packages/splitview/src/groupview/panel/parts.ts index 95c89ea13..dc3b14a59 100644 --- a/packages/splitview/src/groupview/panel/parts.ts +++ b/packages/splitview/src/groupview/panel/parts.ts @@ -1,9 +1,9 @@ -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', @@ -11,40 +11,40 @@ export enum ClosePanelResult { } 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 - focus(): void - onHide(): void - update(params: {}): void - toJSON(): {} + id: string; + element: HTMLElement; + layout?(width: number, height: number): void; + close?(): Promise; + 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 diff --git a/packages/splitview/src/groupview/panel/tab/tab.ts b/packages/splitview/src/groupview/panel/tab/tab.ts index 05ef4c6b3..d5651628d 100644 --- a/packages/splitview/src/groupview/panel/tab/tab.ts +++ b/packages/splitview/src/groupview/panel/tab/tab.ts @@ -1,56 +1,56 @@ -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' +} 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 - onDropped: Event - setActive(isActive: boolean): void - startDragEvent(): void - stopDragEvent(): void + id: string; + element: HTMLElement; + hasActiveDragEvent: boolean; + setContent: (element: HTMLElement) => void; + onChanged: Event; + onDropped: Event; + setActive(isActive: boolean): void; + startDragEvent(): void; + stopDragEvent(): void; } export class Tab extends CompositeDisposable implements ITab { - private _element: HTMLElement + private _element: HTMLElement; private dragInPlayDetails: { id?: string; isDragging: boolean } = { isDragging: false, - } - private droptarget: Droptarget - private content: HTMLElement + }; + private droptarget: Droptarget; + private content: HTMLElement; - private readonly _onChanged = new Emitter() - readonly onChanged: Event = this._onChanged.event + private readonly _onChanged = new Emitter(); + readonly onChanged: Event = this._onChanged.event; - private readonly _onDropped = new Emitter() - readonly onDropped: Event = this._onDropped.event + private readonly _onDropped = new Emitter(); + readonly onDropped: Event = this._onDropped.event; public get element() { - return this._element + return this._element; } public get hasActiveDragEvent() { - return this.dragInPlayDetails?.isDragging + return this.dragInPlayDetails?.isDragging; } public startDragEvent() { - this.dragInPlayDetails = { isDragging: true, id: this.accessor.id } + this.dragInPlayDetails = { isDragging: true, id: this.accessor.id }; } public stopDragEvent() { - this.dragInPlayDetails = { isDragging: false, id: undefined } + this.dragInPlayDetails = { isDragging: false, id: undefined }; } constructor( @@ -58,73 +58,73 @@ export class Tab extends CompositeDisposable implements ITab { private readonly accessor: IGroupAccessor, private group: IGroupview ) { - super() + super(); - this.addDisposables(this._onChanged, this._onDropped) + this.addDisposables(this._onChanged, this._onDropped); - this._element = document.createElement('div') - this._element.className = 'tab' - this._element.draggable = true + this._element = document.createElement('div'); + this._element.className = 'tab'; + this._element.draggable = true; this.addDisposables( addDisposableListener(this._element, 'mousedown', (event) => { if (event.defaultPrevented) { - return + return; } - this._onChanged.fire({ kind: MouseEventKind.CLICK, event }) + 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 dragImage = this._element.cloneNode(true) as HTMLElement; - const box = this._element.getBoundingClientRect() + 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') + dragImage.style.height = `${box.height}px`; + dragImage.style.width = `${box.width}px`; + dragImage.style.position = 'absolute'; + dragImage.classList.add('dragging'); - document.body.appendChild(dragImage) + document.body.appendChild(dragImage); event.dataTransfer.setDragImage( dragImage, event.offsetX, event.offsetY - ) - setTimeout(() => document.body.removeChild(dragImage), 0) + ); + 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) + }); + DataTransferSingleton.setData(this.dragInPlayDetails.id, data); - event.dataTransfer.setData(DATA_KEY, data) - event.dataTransfer.effectAllowed = 'move' + 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) + DataTransferSingleton.removeData(this.dragInPlayDetails.id); this.dragInPlayDetails = { isDragging: false, id: undefined, - } + }; }) - ) + ); this.droptarget = new Droptarget(this._element, { isDirectional: false, @@ -132,30 +132,30 @@ export class Tab extends CompositeDisposable implements ITab { id: this.accessor.id, enableExternalDragEvents: this.accessor.options .enableExternalDragEvents, - }) + }); this.addDisposables( this.droptarget.onDidChange((event) => { - this._onDropped.fire(event) + this._onDropped.fire(event); }) - ) + ); } public setActive(isActive: boolean) { - toggleClass(this.element, 'active-tab', isActive) - toggleClass(this.element, 'inactive-tab', !isActive) + 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._element.removeChild(this.content); } - this.content = element - this._element.appendChild(this.content) + this.content = element; + this._element.appendChild(this.content); } public dispose() { - super.dispose() - this.droptarget.dispose() + super.dispose(); + this.droptarget.dispose(); } } diff --git a/packages/splitview/src/groupview/panel/types.ts b/packages/splitview/src/groupview/panel/types.ts index c75f90e71..fd23b3dc0 100644 --- a/packages/splitview/src/groupview/panel/types.ts +++ b/packages/splitview/src/groupview/panel/types.ts @@ -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 - init?(params: PanelInitParameters & { [index: string]: string }): void - onDidStateChange: Event + id: string; + header: PanelHeaderPart; + content: PanelContentPart; + group: IGroupview; + focus(): void; + onHide(): void; + setVisible(isGroupActive: boolean, group: IGroupview): void; + setDirty(isDirty: boolean): void; + close?(): Promise; + init?(params: PanelInitParameters & { [index: string]: string }): void; + onDidStateChange: Event; } diff --git a/packages/splitview/src/groupview/titlebar/tabContainer.ts b/packages/splitview/src/groupview/titlebar/tabContainer.ts index 540f3cdac..f2f903422 100644 --- a/packages/splitview/src/groupview/titlebar/tabContainer.ts +++ b/packages/splitview/src/groupview/titlebar/tabContainer.ts @@ -2,170 +2,170 @@ 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' +} 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 - 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; + 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[] = [] - private selectedIndex: number = -1 - private active: boolean - private activePanel: IGroupPanel + private tabs: IValueDisposable[] = []; + 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() - readonly onDropEvent: Event = this._onDropped.event + private readonly _onDropped = new Emitter(); + readonly onDropEvent: Event = this._onDropped.event; get visible() { - return this._visible + return this._visible; } set visible(value: boolean) { - this._visible = value + this._visible = value; - toggleClass(this.element, 'hidden', !this._visible) + toggleClass(this.element, 'hidden', !this._visible); } get height() { - return this._height + return this._height; } set height(value: number) { - this._height = value - this._element.style.height = `${this.height}px` + this._height = value; + this._element.style.height = `${this.height}px`; } public get element() { - return this._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) + return !!this.tabs.find((tab) => tab.value.hasActiveDragEvent); } public at(index: number) { - return this.tabs[index]?.value + 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) + 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() + super(); - this.addDisposables(this._onDropped) + this.addDisposables(this._onDropped); - this._element = document.createElement('div') - this._element.className = 'title-container' + this._element = document.createElement('div'); + this._element.className = 'title-container'; - this.height = 35 + this.height = 35; - this.actionContainer = document.createElement('div') - this.actionContainer.className = 'action-container' + this.actionContainer = document.createElement('div'); + this.actionContainer.className = 'action-container'; - const list = document.createElement('ul') - list.className = 'action-list' + const list = document.createElement('ul'); + list.className = 'action-list'; - this.tabContainer = document.createElement('div') - this.tabContainer.className = 'tab-container' + this.tabContainer = document.createElement('div'); + this.tabContainer.className = 'tab-container'; - this._element.appendChild(this.tabContainer) - this._element.appendChild(this.actionContainer) + 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 + console.debug('[tabs] invalid drop event'); + return; } if (!last(this.tabs).value.hasActiveDragEvent) { - addClasses(this.tabContainer, 'drag-over-target') + addClasses(this.tabContainer, 'drag-over-target'); } }), addDisposableListener(this.tabContainer, 'dragover', (event) => { - event.preventDefault() + event.preventDefault(); }), addDisposableListener(this.tabContainer, 'dragleave', (event) => { - removeClasses(this.tabContainer, 'drag-over-target') + removeClasses(this.tabContainer, 'drag-over-target'); }), addDisposableListener(this.tabContainer, 'drop', (event) => { if (!DataTransferSingleton.has(this.accessor.id)) { - console.debug('[tabs] invalid drop event') - return + console.debug('[tabs] invalid drop event'); + return; } if (hasProcessed(event)) { - console.debug('[tab] drop event already processed') - return + console.debug('[tab] drop event already processed'); + return; } - removeClasses(this.tabContainer, 'drag-over-target') + 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 + 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 + this.active = isGroupActive; } private addTab( @@ -173,80 +173,80 @@ export class TabContainer extends CompositeDisposable implements ITabContainer { index: number = this.tabs.length ) { if (index < 0 || index > this.tabs.length) { - throw new Error('invalid location') + throw new Error('invalid location'); } this.tabContainer.insertBefore( tab.value.element, this.tabContainer.children[index] - ) + ); this.tabs = [ ...this.tabs.slice(0, index), tab, ...this.tabs.slice(index), - ] + ]; if (this.selectedIndex < 0) { - this.selectedIndex = index + this.selectedIndex = index; } } public delete(id: string) { - const index = this.tabs.findIndex((tab) => tab.value.id === id) + const index = this.tabs.findIndex((tab) => tab.value.id === id); - const tab = this.tabs.splice(index, 1)[0] + const tab = this.tabs.splice(index, 1)[0]; - const { value, disposable } = tab + const { value, disposable } = tab; - disposable.dispose() - value.element.remove() + disposable.dispose(); + value.element.remove(); } public setActivePanel(panel: IGroupPanel) { this.tabs.forEach((tab) => { - const isActivePanel = panel.id === tab.value.id - tab.value.setActive(isActivePanel) - }) + 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 + return; } - const tab = new Tab(panel.id, this.accessor, this.group) - tab.setContent(panel.header.element) + 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.group.openPanel(panel); + break; } - this.accessor.fireMouseEvent({ ...event, panel, tab: true }) + this.accessor.fireMouseEvent({ ...event, panel, tab: true }); }), tab.onDropped((event) => { - this._onDropped.fire({ event, index: this.indexOf(tab) }) + this._onDropped.fire({ event, index: this.indexOf(tab) }); }) - ) + ); - const value: IValueDisposable = { value: tab, disposable } + const value: IValueDisposable = { value: tab, disposable }; - this.addTab(value, index) - this.activePanel = panel + this.addTab(value, index); + this.activePanel = panel; } public closePanel(panel: IGroupPanel) { - this.delete(panel.id) + this.delete(panel.id); } public dispose() { - super.dispose() + super.dispose(); this.tabs.forEach((tab) => { - tab.disposable.dispose() - }) - this.tabs = [] + tab.disposable.dispose(); + }); + this.tabs = []; } } diff --git a/packages/splitview/src/index.ts b/packages/splitview/src/index.ts index 2efa3969d..3ad01c9d8 100644 --- a/packages/splitview/src/index.ts +++ b/packages/splitview/src/index.ts @@ -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'; diff --git a/packages/splitview/src/layout/baseGrid.ts b/packages/splitview/src/layout/baseGrid.ts new file mode 100644 index 000000000..b30271455 --- /dev/null +++ b/packages/splitview/src/layout/baseGrid.ts @@ -0,0 +1,173 @@ +import { MovementOptions2 } from '.'; +import { getGridLocation, Gridview, IGridView } from '../gridview/gridview'; +import { CompositeDisposable, IValueDisposable } from '../lifecycle'; +import { sequentialNumberGenerator } from '../math'; + +const nextLayoutId = sequentialNumberGenerator(); + +export interface BaseGridOptions { + readonly proportionalLayout?: boolean; +} + +export interface IBaseGridView extends IGridView { + id: string; + setActive(isActive: boolean): void; +} + +export interface IBaseGrid { + readonly element: HTMLElement; + readonly id: string; + readonly minimumHeight: number; + readonly maximumHeight: number; + readonly minimumWidth: number; + readonly maximumWidth: number; + readonly activeGroup: T; + readonly size: number; + getGroup(id: string): T | undefined; +} + +export class BaseGrid + extends CompositeDisposable + implements IBaseGrid { + private readonly _id = nextLayoutId.next(); + protected readonly groups = new Map>(); + protected readonly gridview: Gridview; + // + private resizeTimer: NodeJS.Timer; + protected _activeGroup: T; + // + protected _size: number; + protected _orthogonalSize: number; + + get id() { + return this._id; + } + + get element() { + return this._element; + } + + get size() { + return this.groups.size; + } + + 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; + } + + constructor( + private readonly _element: HTMLElement, + options: BaseGridOptions + ) { + super(); + + this.gridview = new Gridview(!!options.proportionalLayout); + this.element.appendChild(this.gridview.element); + } + + public getGroup(id: string): T | undefined { + return this.groups.get(id)?.value; + } + + public doSetGroupActive(group: T) { + if (this._activeGroup && this._activeGroup !== group) { + this._activeGroup.setActive(false); + } + group.setActive(true); + this._activeGroup = group; + } + + public moveToNext(options?: MovementOptions2) { + if (!options) { + options = {}; + } + if (!options.group) { + options.group = this.activeGroup; + } + + const location = getGridLocation(options.group.element); + const next = this.gridview.next(location)?.view; + this.doSetGroupActive(next as T); + } + + public moveToPrevious(options?: MovementOptions2) { + if (!options) { + options = {}; + } + if (!options.group) { + options.group = this.activeGroup; + } + + const location = getGridLocation(options.group.element); + const next = this.gridview.preivous(location)?.view; + this.doSetGroupActive(next as T); + } + + public layout( + size: number, + orthogonalSize: number, + forceResize?: boolean + ): void { + const different = + forceResize || + 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); + } + + public setAutoResizeToFit(enabled: boolean): void { + if (this.resizeTimer) { + clearInterval(this.resizeTimer); + } + if (enabled) { + this.resizeTimer = setInterval(() => { + this.resizeToFit(); + }, 500); + } + } + + /** + * Resize the layout to fit the parent container + */ + public resizeToFit(): void { + const { + width, + height, + } = this.element.parentElement.getBoundingClientRect(); + this.layout(width, height); + } + + public dispose(): void { + super.dispose(); + + if (this.resizeTimer) { + clearInterval(this.resizeTimer); + this.resizeTimer = undefined; + } + + this.gridview.dispose(); + } +} diff --git a/packages/splitview/src/layout/componentFactory.ts b/packages/splitview/src/layout/componentFactory.ts index 9822be7f7..f8021065e 100644 --- a/packages/splitview/src/layout/componentFactory.ts +++ b/packages/splitview/src/layout/componentFactory.ts @@ -3,87 +3,87 @@ import { PanelContentPartConstructor, PanelHeaderPart, PanelHeaderPartConstructor, -} from '../groupview/panel/parts' -import { FrameworkFactory } from '../types' -import { DefaultTab } from './components/tab/defaultTab' +} 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 + [componentName: string]: PanelContentPartConstructor; }, frameworkComponents: { - [componentName: string]: any + [componentName: string]: any; }, createFrameworkComponent: FrameworkFactory ): PanelContentPart { const Component = typeof componentName === 'string' ? components[componentName] - : componentName + : componentName; const FrameworkComponent = typeof componentName === 'string' ? frameworkComponents[componentName] - : 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 + ); + return wrappedComponent; } - return new Component() as PanelContentPart + return new Component() as PanelContentPart; } export function createTabComponent( componentName: string | PanelHeaderPartConstructor | any, components: { - [componentName: string]: PanelHeaderPartConstructor + [componentName: string]: PanelHeaderPartConstructor; }, frameworkComponents: { - [componentName: string]: any + [componentName: string]: any; }, createFrameworkComponent: FrameworkFactory ): PanelHeaderPart { const Component = typeof componentName === 'string' ? components[componentName] - : componentName + : componentName; const FrameworkComponent = typeof componentName === 'string' ? frameworkComponents[componentName] - : 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 + ); + return wrappedComponent; } if (!Component) { - return new DefaultTab() + return new DefaultTab(); } - return new Component() as PanelHeaderPart + return new Component() as PanelHeaderPart; } diff --git a/packages/splitview/src/layout/componentGridview.ts b/packages/splitview/src/layout/componentGridview.ts index 399bc1a00..c04991f0e 100644 --- a/packages/splitview/src/layout/componentGridview.ts +++ b/packages/splitview/src/layout/componentGridview.ts @@ -1,165 +1,85 @@ -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' +import { getRelativeLocation, IGridView } from '../gridview/gridview'; +import { Position } from '../groupview/droptarget/droptarget'; +import { getGridLocation } from '../gridview/gridview'; +import { tail, sequenceEquals } from '../array'; +import { GroupChangeKind, GroupChangeEvent } from '../groupview/groupview'; +import { 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 { IPanelDeserializer } from './deserializer' +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'; +import { MovementOptions2 } from './options'; +import { GridComponentOptions } from '.'; +import { BaseGrid, IBaseGrid, IBaseGridView } from './baseGrid'; -const nextLayoutId = sequentialNumberGenerator() +const nextLayoutId = sequentialNumberGenerator(); export interface AddComponentOptions { - component: string - params?: { [key: string]: any } - id: string + component: string; + params?: { [key: string]: any }; + id: string; position?: { - direction?: 'left' | 'right' | 'above' | 'below' | 'within' - reference: string - } - size?: number - priority?: LayoutPriority - snap?: boolean + 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 +export interface IComponentGridview extends IBaseGridView { + init?: (params: { params: any }) => void; + priority?: LayoutPriority; } -export interface IComponentGridview extends IGridView { - id: string - init: (params: { params: any }) => void - priority?: LayoutPriority -} - -export interface MovementOptions2 { - group?: IComponentGridview -} - -export interface IComponentGridviewLayout { - addComponent(options: AddComponentOptions): void +export interface IComponentGridviewLayout + extends IBaseGrid { + addComponent(options: AddComponentOptions): void; } export class ComponentGridview - extends CompositeDisposable + extends BaseGrid implements IComponentGridviewLayout { - private readonly _id = nextLayoutId.next() - private readonly groups = new Map< - string, - IValueDisposable - >() - private readonly gridview: Gridview = new Gridview(false) // events - private readonly _onDidLayoutChange = new Emitter() + private readonly _onDidLayoutChange = new Emitter(); readonly onDidLayoutChange: Event = this - ._onDidLayoutChange.event + ._onDidLayoutChange.event; // everything else - private _size: number - private _orthogonalSize: number - private _activeGroup: IComponentGridview - private _deserializer: IPanelDeserializer - private resizeTimer: NodeJS.Timer - private debugContainer: DebugWidget + + private _deserializer: IPanelDeserializer; + private debugContainer: DebugWidget; constructor( - private readonly element: HTMLElement, + element: HTMLElement, public readonly options: GridComponentOptions ) { - super() - - this.element.appendChild(this.gridview.element) + super(element, { proportionalLayout: true }); if (!this.options.components) { - this.options.components = {} + this.options.components = {}; } if (!this.options.frameworkComponents) { - this.options.frameworkComponents = {} + this.options.frameworkComponents = {}; } this.addDisposables( this.gridview.onDidChange((e) => { - this._onDidLayoutChange.fire({ kind: GroupChangeKind.LAYOUT }) + 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 + 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 = {} - } - if (!options.group) { - options.group = this.activeGroup - } - - 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 - } - - const location = getGridLocation(options.group.element) - const next = this.gridview.preivous(location) - ?.view as IComponentGridview - this.doSetGroupActive(next) + this._deserializer = value; } /** @@ -168,21 +88,21 @@ export class ComponentGridview * @returns A JSON respresentation of the layout */ public toJSON() { - const data = this.gridview.serialize() + const data = this.gridview.serialize(); - return { grid: data } + return { grid: data }; } public deserialize(data: any) { - this.gridview.clear() - this.groups.clear() + this.gridview.clear(); + this.groups.clear(); - this.fromJSON(data, this.deserializer) - this.gridview.layout(this._size, this._orthogonalSize) + this.fromJSON(data, this.deserializer); + this.gridview.layout(this._size, this._orthogonalSize); } public fromJSON(data: any, deserializer: IPanelDeserializer) { - const { grid, panels } = data + const { grid, panels } = data; // this.gridview.deserialize( // grid, @@ -195,48 +115,26 @@ export class ComponentGridview // }, // }) // ); - 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) + this._onDidLayoutChange.fire({ kind: GroupChangeKind.NEW_LAYOUT }); } public addComponent(options: AddComponentOptions) { - let relativeLocation: number[] = [0] + let relativeLocation: number[] = [0]; if (options.position?.reference) { const referenceGroup = this.groups.get(options.position.reference) - .value + .value; - const target = this.toTarget(options.position.direction) + const target = this.toTarget(options.position.direction); if (target === Position.Center) { - throw new Error(`${target} not supported as an option`) + throw new Error(`${target} not supported as an option`); } else { - const location = getGridLocation(referenceGroup.element) + const location = getGridLocation(referenceGroup.element); relativeLocation = getRelativeLocation( this.gridview.orientation, location, target - ) + ); } } @@ -245,28 +143,24 @@ export class ComponentGridview this.options.components, this.options.frameworkComponents, this.options.frameworkComponentFactory.createComponent - ) - view.init({ params: {} }) - view.priority = options.priority - view.snap = options.snap + ); + 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 + this.doAddGroup(view, relativeLocation, options.size); } public removeGroup(group: IComponentGridview) { if (group === this._activeGroup) { - this._activeGroup = undefined + this._activeGroup = undefined; } - this.doRemoveGroup(group) + this.doRemoveGroup(group); } private doAddGroup( @@ -274,10 +168,10 @@ export class ComponentGridview location: number[], size?: number ) { - this.gridview.addView(group, size ?? { type: 'distribute' }, location) + this.gridview.addView(group, size ?? { type: 'distribute' }, location); - this._onDidLayoutChange.fire({ kind: GroupChangeKind.ADD_GROUP }) - this.doSetGroupActive(group) + this._onDidLayoutChange.fire({ kind: GroupChangeKind.ADD_GROUP }); + this.doSetGroupActive(group); } private doRemoveGroup( @@ -285,94 +179,71 @@ export class ComponentGridview options?: { skipActive?: boolean; skipDispose?: boolean } ) { if (!this.groups.has(group.id)) { - throw new Error('invalid operation') + throw new Error('invalid operation'); } - const { disposable } = this.groups.get(group.id) + const { disposable } = this.groups.get(group.id); if (!options?.skipDispose) { - disposable.dispose() - this.groups.delete(group.id) + disposable.dispose(); + this.groups.delete(group.id); } - const view = this.gridview.remove(group, { type: 'distribute' }) - this._onDidLayoutChange.fire({ kind: GroupChangeKind.REMOVE_GROUP }) + 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) + 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 + return view; } public moveGroup( referenceGroup: IComponentGridview, groupId: string, - itemId: string, target: Position ) { - const sourceGroup = groupId ? this.groups.get(groupId).value : undefined + const sourceGroup = groupId + ? this.groups.get(groupId).value + : undefined; - const referenceLocation = getGridLocation(referenceGroup.element) + 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) + 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) + this.gridview.moveView(sourceParentLocation, from, to); - return + return; } // source group will become empty so delete the group const targetGroup = this.doRemoveGroup(sourceGroup, { skipActive: true, skipDispose: true, - }) as IComponentGridview + }) as IComponentGridview; // after deleting the group we need to re-evaulate the ref location - const updatedReferenceLocation = getGridLocation(referenceGroup.element) + 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) + ); + this.doAddGroup(targetGroup, location); } private toTarget( @@ -380,31 +251,24 @@ export class ComponentGridview ) { switch (direction) { case 'left': - return Position.Left + return Position.Left; case 'right': - return Position.Right + return Position.Right; case 'above': - return Position.Top + return Position.Top; case 'below': - return Position.Bottom + return Position.Bottom; case 'within': default: - return Position.Center + return Position.Center; } } public dispose() { - super.dispose() + super.dispose(); - this.gridview.dispose() + this.debugContainer?.dispose(); - this.debugContainer?.dispose() - - if (this.resizeTimer) { - clearInterval(this.resizeTimer) - this.resizeTimer = undefined - } - - this._onDidLayoutChange.dispose() + this._onDidLayoutChange.dispose(); } } diff --git a/packages/splitview/src/layout/components/debug/debug.ts b/packages/splitview/src/layout/components/debug/debug.ts index 36f1f89fa..e7f876d74 100644 --- a/packages/splitview/src/layout/components/debug/debug.ts +++ b/packages/splitview/src/layout/components/debug/debug.ts @@ -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() + 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) + 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 = document.createElement('div'); this._element.innerHTML = `
` + `
Groups:0
` + `
Panels:0
` + - `
` + ``; - container.appendChild(this._element) + container.appendChild(this._element); - const gc = this._element.querySelector('#group-count') - const pc = this._element.querySelector('#panel-count') + 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() + gc.textContent = this.layout.size.toString(); + pc.textContent = this.layout.totalPanels.toString(); } }) - ) + ); } public dispose() { - super.dispose() + super.dispose(); - this._element.remove() + this._element.remove(); - const container = document.getElementById('layout-debug-container') + const container = document.getElementById('layout-debug-container'); if (container && container.children.length === 0) { - container.remove() + container.remove(); } } } diff --git a/packages/splitview/src/layout/components/tab/defaultTab.ts b/packages/splitview/src/layout/components/tab/defaultTab.ts index 9449ce490..5c007caa9 100644 --- a/packages/splitview/src/layout/components/tab/defaultTab.ts +++ b/packages/splitview/src/layout/components/tab/defaultTab.ts @@ -1,96 +1,96 @@ -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' +} 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 _content: HTMLElement; + private _actionContainer: HTMLElement; + private _list: HTMLElement; + private action: HTMLElement; // - private params: PartInitParameters + private params: PartInitParameters; // - private isDirtyDisposable = new MutableDisposable() + private isDirtyDisposable = new MutableDisposable(); get element() { - return this._element + return this._element; } get id() { - return '__DEFAULT_TAB__' + return '__DEFAULT_TAB__'; } constructor() { - super() + super(); - this._element = document.createElement('div') - this._element.className = 'default-tab' + this._element = document.createElement('div'); + this._element.className = 'default-tab'; // - this._content = document.createElement('div') - this._content.className = 'tab-content' + this._content = document.createElement('div'); + this._content.className = 'tab-content'; // - this._actionContainer = document.createElement('div') - this._actionContainer.className = 'action-container' + this._actionContainer = document.createElement('div'); + this._actionContainer.className = 'action-container'; // - this._list = document.createElement('ul') - this._list.className = 'tab-list' + this._list = document.createElement('ul'); + this._list.className = 'tab-list'; // - this.action = document.createElement('a') - this.action.className = 'tab-action' + 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._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() + ev.preventDefault(); }) - ) + ); - this.render() + this.render(); } public toJSON() { - return { id: this.id } + return { id: this.id }; } public init(params: PartInitParameters) { - this.params = params - this._content.textContent = params.title + this.params = params; + this._content.textContent = params.title; this.isDirtyDisposable.value = this.params.api.onDidDirtyChange( (event) => { - const isDirty = event - toggleClass(this.action, 'dirty', isDirty) + const isDirty = event; + toggleClass(this.action, 'dirty', isDirty); } - ) + ); if (!this.params.suppressClosable) { addDisposableListener(this.action, 'click', (ev) => { - ev.preventDefault() // - this.params.api.close() - }) + ev.preventDefault(); // + this.params.api.close(); + }); } else { - this.action.classList.add('disable-close') + this.action.classList.add('disable-close'); } } public setVisible(isPanelVisible: boolean, isGroupVisible: boolean) { - this._isPanelVisible = isPanelVisible - this._isGroupActive = isGroupVisible + this._isPanelVisible = isPanelVisible; + this._isGroupActive = isGroupVisible; - this.render() + this.render(); } private render() { diff --git a/packages/splitview/src/layout/components/watermark/watermark.ts b/packages/splitview/src/layout/components/watermark/watermark.ts index 43d9872f4..e017bd7d5 100644 --- a/packages/splitview/src/layout/components/watermark/watermark.ts +++ b/packages/splitview/src/layout/components/watermark/watermark.ts @@ -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' +} 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' + 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) - }) + ev.preventDefault(); // + this.accessor.removeGroup(this._group); + }); } public init(params: WatermarkPartInitParameters) { - this.accessor = params.accessor + this.accessor = params.accessor; this.addDisposables( this.accessor.onDidLayoutChange((event) => { - this.render() + this.render(); }) - ) + ); - this.render() + this.render(); } public setVisible(visible: boolean, group: IGroupview): void { - this._visible = visible - this._group = group - this.render() + this._visible = visible; + this._group = group; + this.render(); } get element() { - return this._element + return this._element; } private render() { - const isOneGroup = this.accessor.size <= 1 - toggleClass(this.element, 'has-actions', isOneGroup) + const isOneGroup = this.accessor.size <= 1; + toggleClass(this.element, 'has-actions', isOneGroup); } public dispose() { - super.dispose() + super.dispose(); } } diff --git a/packages/splitview/src/layout/deserializer.ts b/packages/splitview/src/layout/deserializer.ts index 622b2e9fe..0e7a8b6bf 100644 --- a/packages/splitview/src/layout/deserializer.ts +++ b/packages/splitview/src/layout/deserializer.ts @@ -1,9 +1,9 @@ -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 { @@ -13,22 +13,22 @@ export class DefaultDeserializer implements IViewDeserializer { ) {} public fromJSON(data: { [key: string]: any }): IGridView { - const children = data.views - const active = data.activeView + 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) + 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 + return group; } } diff --git a/packages/splitview/src/layout/index.ts b/packages/splitview/src/layout/index.ts index b718923fd..2d1f210a7 100644 --- a/packages/splitview/src/layout/index.ts +++ b/packages/splitview/src/layout/index.ts @@ -1,3 +1,3 @@ -export * from './layout' -export * from './componentGridview' -export * from './options' +export * from './layout'; +export * from './componentGridview'; +export * from './options'; diff --git a/packages/splitview/src/layout/layout.ts b/packages/splitview/src/layout/layout.ts index 93229470c..ad4902b10 100644 --- a/packages/splitview/src/layout/layout.ts +++ b/packages/splitview/src/layout/layout.ts @@ -1,7 +1,7 @@ -import { Gridview, getRelativeLocation } from '../gridview/gridview' -import { Position } from '../groupview/droptarget/droptarget' -import { getGridLocation } from '../gridview/gridview' -import { tail, sequenceEquals } from '../array' +import { getRelativeLocation } from '../gridview/gridview'; +import { Position } from '../groupview/droptarget/droptarget'; +import { getGridLocation } from '../gridview/gridview'; +import { tail, sequenceEquals } from '../array'; import { IGroupview, Groupview, @@ -9,26 +9,26 @@ import { GroupChangeKind, GroupChangeEvent, GroupDropEvent, -} from '../groupview/groupview' -import { IGroupPanel } from '../groupview/panel/types' -import { DefaultPanel } from '../groupview/panel/panel' +} from '../groupview/groupview'; +import { IGroupPanel } from '../groupview/panel/types'; +import { DefaultPanel } from '../groupview/panel/panel'; import { CompositeDisposable, IDisposable, IValueDisposable, -} from '../lifecycle' -import { Event, Emitter, addDisposableListener } from '../events' -import { Watermark } from './components/watermark/watermark' -import { timeoutPromise } from '../async' -import { DebugWidget } from './components/debug/debug' +} from '../lifecycle'; +import { Event, Emitter, addDisposableListener } from '../events'; +import { Watermark } from './components/watermark/watermark'; +import { timeoutPromise } from '../async'; +import { DebugWidget } from './components/debug/debug'; import { PanelContentPartConstructor, PanelHeaderPartConstructor, -} from '../groupview/panel/parts' -import { debounce } from '../functions' -import { sequentialNumberGenerator } from '../math' -import { DefaultDeserializer, IPanelDeserializer } from './deserializer' -import { createContentComponent, createTabComponent } from './componentFactory' +} from '../groupview/panel/parts'; +import { debounce } from '../functions'; +import { sequentialNumberGenerator } from '../math'; +import { DefaultDeserializer, IPanelDeserializer } from './deserializer'; +import { createContentComponent, createTabComponent } from './componentFactory'; import { AddGroupOptions, AddPanelOptions, @@ -36,269 +36,227 @@ import { LayoutOptions, MovementOptions, TabContextMenuEvent, -} from './options' +} from './options'; import { DataTransferSingleton, DATA_KEY, DragType, -} from '../groupview/droptarget/dataTransfer' -import { LayoutMouseEvent, MouseEventKind } from '../groupview/events' +} from '../groupview/droptarget/dataTransfer'; +import { LayoutMouseEvent, MouseEventKind } from '../groupview/events'; +import { BaseGrid, IBaseGrid } from './baseGrid'; -const nextGroupId = sequentialNumberGenerator() -const nextLayoutId = sequentialNumberGenerator() +const nextGroupId = sequentialNumberGenerator(); export interface PanelReference { - update: (event: { params: { [key: string]: any } }) => void - remove: () => void + update: (event: { params: { [key: string]: any } }) => void; + remove: () => void; } export interface Api { - readonly minimumHeight: number - readonly maximumHeight: number - readonly minimumWidth: number - readonly maximumWidth: number - layout(width: number, height: number): void + readonly minimumHeight: number; + readonly maximumHeight: number; + readonly minimumWidth: number; + readonly maximumWidth: number; + layout(width: number, height: number): void; // - setAutoResizeToFit(enabled: boolean): void - resizeToFit(): void - setTabHeight(height: number): void - getTabHeight(): number - size: number - totalPanels: number + setAutoResizeToFit(enabled: boolean): void; + resizeToFit(): void; + setTabHeight(height: number): void; + getTabHeight(): number; + readonly size: number; + totalPanels: number; // lifecycle - addPanelFromComponent(options: AddPanelOptions): PanelReference - addEmptyGroup(options?: AddGroupOptions): void - closeAllGroups: () => Promise - toJSON(): object - deserialize: (data: object) => void - deserializer: IPanelDeserializer + addPanelFromComponent(options: AddPanelOptions): PanelReference; + addEmptyGroup(options?: AddGroupOptions): void; + closeAllGroups: () => Promise; + toJSON(): object; + deserialize: (data: object) => void; + deserializer: IPanelDeserializer; // events - onDidLayoutChange: Event - onTabInteractionEvent: Event - onTabContextMenu: Event - moveToNext(options?: MovementOptions): void - moveToPrevious(options?: MovementOptions): void - activeGroup: IGroupview + onDidLayoutChange: Event; + onTabInteractionEvent: Event; + onTabContextMenu: Event; + moveToNext(options?: MovementOptions): void; + moveToPrevious(options?: MovementOptions): void; createDragTarget( target: { - element: HTMLElement - content: string + element: HTMLElement; + content: string; }, options: (() => PanelOptions) | PanelOptions - ): IDisposable + ): IDisposable; addDndHandle( type: string, cb: (event: LayoutDropEvent) => PanelOptions - ): void + ): void; + readonly activeGroup: IGroupview; } export interface IGroupAccessor { - id: string - getGroup: (id: string) => IGroupview - moveGroup( + readonly id: string; + getGroup: (id: string) => IGroupview; + moveGroupOrPanel( referenceGroup: IGroupview, groupId: string, itemId: string, target: Position, index?: number - ): void - doSetGroupActive: (group: IGroupview) => void - removeGroup: (group: IGroupview) => void - size: number - totalPanels: number - options: LayoutOptions - onDidLayoutChange: Event - activeGroup: IGroupview + ): void; + doSetGroupActive: (group: IGroupview) => void; + removeGroup: (group: IGroupview) => void; + readonly size: number; + totalPanels: number; + options: LayoutOptions; + onDidLayoutChange: Event; // - addPanelFromComponent(options: AddPanelOptions): PanelReference - addPanel(options: AddPanelOptions): IGroupPanel + addPanelFromComponent(options: AddPanelOptions): PanelReference; + addPanel(options: AddPanelOptions): IGroupPanel; // - getPanel: (id: string) => IGroupPanel - fireMouseEvent(event: LayoutMouseEvent): void + getPanel: (id: string) => IGroupPanel; + fireMouseEvent(event: LayoutMouseEvent): void; } -export interface ILayout extends IGroupAccessor, Api {} +export interface ILayout extends IGroupAccessor, Api, IBaseGrid {} export interface LayoutDropEvent { - event: GroupDropEvent + event: GroupDropEvent; } -export class Layout extends CompositeDisposable implements ILayout { - private readonly _element: HTMLElement - private readonly _id = nextLayoutId.next() - private readonly groups = new Map>() - private readonly panels = new Map>() - private readonly gridview: Gridview = new Gridview(true) - private readonly dirtyPanels = new Set() +export class Layout extends BaseGrid implements ILayout { + private readonly panels = new Map>(); + private readonly dirtyPanels = new Set(); private readonly debouncedDeque = debounce( this.syncConfigs.bind(this), 5000 - ) + ); // events - private readonly _onDidLayoutChange = new Emitter() + private readonly _onDidLayoutChange = new Emitter(); readonly onDidLayoutChange: Event = this - ._onDidLayoutChange.event - private readonly _onTabInteractionEvent = new Emitter() + ._onDidLayoutChange.event; + private readonly _onTabInteractionEvent = new Emitter(); readonly onTabInteractionEvent: Event = this - ._onTabInteractionEvent.event - private readonly _onTabContextMenu = new Emitter() + ._onTabInteractionEvent.event; + private readonly _onTabContextMenu = new Emitter(); readonly onTabContextMenu: Event = this - ._onTabContextMenu.event + ._onTabContextMenu.event; // everything else - private _size: number - private _orthogonalSize: number - private _activeGroup: IGroupview - private _deserializer: IPanelDeserializer - private resizeTimer: NodeJS.Timer - private debugContainer: DebugWidget - private panelState = {} + private drag: IDisposable; + private _deserializer: IPanelDeserializer; + private debugContainer: DebugWidget; + private panelState = {}; private registry = new Map< string, (event: LayoutDropEvent) => PanelOptions - >() + >(); addDndHandle( type: string, cb: (event: LayoutDropEvent) => PanelOptions ): void { - this.registry.set(type, cb) + this.registry.set(type, cb); } - constructor(public readonly options: LayoutOptions) { - super() - - this._element = document.createElement('div') - this._element.appendChild(this.gridview.element) + constructor(element: HTMLElement, public readonly options: LayoutOptions) { + super(element, { proportionalLayout: false }); if (!this.options.components) { - this.options.components = {} + this.options.components = {}; } if (!this.options.frameworkComponents) { - this.options.frameworkComponents = {} + this.options.frameworkComponents = {}; } if (!this.options.frameworkTabComponents) { - this.options.frameworkTabComponents = {} + this.options.frameworkTabComponents = {}; } if (!this.options.tabComponents) { - this.options.tabComponents = {} + this.options.tabComponents = {}; } if (!this.options.watermarkComponent) { - this.options.watermarkComponent = Watermark + this.options.watermarkComponent = Watermark; } this.addDisposables( this.gridview.onDidChange((e) => { - this._onDidLayoutChange.fire({ kind: GroupChangeKind.LAYOUT }) + this._onDidLayoutChange.fire({ kind: GroupChangeKind.LAYOUT }); }) - ) + ); - this.updateContainer() - } - - get minimumHeight() { - return this.gridview.minimumHeight - } - get maximumHeight() { - return this.gridview.maximumHeight - } - get minimumWidth() { - return this.gridview.minimumWidth - } - get maximumWidth() { - return this.gridview.maximumWidth - } - - get activeGroup() { - return this._activeGroup + this.updateContainer(); } get totalPanels() { - return this.panels.size + return this.panels.size; } get deserializer() { - return this._deserializer + return this._deserializer; } set deserializer(value: IPanelDeserializer) { - this._deserializer = value - } - - get id() { - return this._id - } - - get size() { - return this.groups.size - } - - get element() { - return this._element + this._deserializer = value; } public getPanel(id: string): IGroupPanel { - return this.panels.get(id)?.value + return this.panels.get(id)?.value; } - private drag: IDisposable - public createDragTarget( target: { - element: HTMLElement - content: string + element: HTMLElement; + content: string; }, options: (() => PanelOptions) | PanelOptions ): IDisposable { const disposables = new CompositeDisposable( addDisposableListener(target.element, 'dragstart', (event) => { const panelOptions = - typeof options === 'function' ? options() : options + typeof options === 'function' ? options() : options; - const panel = this.panels.get(panelOptions.id)?.value + const panel = this.panels.get(panelOptions.id)?.value; if (panel) { - this.drag = panel.group.startActiveDrag(panel) + this.drag = panel.group.startActiveDrag(panel); } const data = JSON.stringify({ type: DragType.EXTERNAL, ...panelOptions, - }) + }); - DataTransferSingleton.setData(this.id, data) + DataTransferSingleton.setData(this.id, data); - event.dataTransfer.effectAllowed = 'move' + event.dataTransfer.effectAllowed = 'move'; - const dragImage = document.createElement('div') - dragImage.textContent = target.content - dragImage.classList.add('custom-dragging') + const dragImage = document.createElement('div'); + dragImage.textContent = target.content; + dragImage.classList.add('custom-dragging'); - document.body.appendChild(dragImage) + document.body.appendChild(dragImage); event.dataTransfer.setDragImage( dragImage, event.offsetX, event.offsetY - ) - setTimeout(() => document.body.removeChild(dragImage), 0) + ); + setTimeout(() => document.body.removeChild(dragImage), 0); - event.dataTransfer.setData(DATA_KEY, data) + event.dataTransfer.setData(DATA_KEY, data); }), - addDisposableListener(this._element, 'dragend', (ev) => { + addDisposableListener(this.element, 'dragend', (ev) => { // drop events fire before dragend so we can remove this safely - DataTransferSingleton.removeData(this.id) - this.drag?.dispose() - this.drag = undefined + DataTransferSingleton.removeData(this.id); + this.drag?.dispose(); + this.drag = undefined; }) - ) + ); - return disposables + return disposables; } public moveToNext(options?: MovementOptions) { if (!options) { - options = {} + options = {}; } if (!options.group) { - options.group = this.activeGroup + options.group = this.activeGroup; } if (options.includePanel) { @@ -306,64 +264,64 @@ export class Layout extends CompositeDisposable implements ILayout { options.group.activePanel !== options.group.panels[options.group.panels.length - 1] ) { - options.group.moveToNext({ suppressRoll: true }) - return + options.group.moveToNext({ suppressRoll: true }); + return; } } - const location = getGridLocation(options.group.element) - const next = this.gridview.next(location)?.view as IGroupview - this.doSetGroupActive(next) + const location = getGridLocation(options.group.element); + const next = this.gridview.next(location)?.view as IGroupview; + this.doSetGroupActive(next); } public moveToPrevious(options?: MovementOptions) { if (!options) { - options = {} + options = {}; } if (!options.group) { - options.group = this.activeGroup + options.group = this.activeGroup; } if (options.includePanel) { if (options.group.activePanel !== options.group.panels[0]) { - options.group.moveToPrevious({ suppressRoll: true }) - return + options.group.moveToPrevious({ suppressRoll: true }); + return; } } - const location = getGridLocation(options.group.element) - const next = this.gridview.preivous(location)?.view as IGroupview - this.doSetGroupActive(next) + const location = getGridLocation(options.group.element); + const next = this.gridview.preivous(location)?.view as IGroupview; + this.doSetGroupActive(next); } public registerPanel(panel: IGroupPanel) { if (this.panels.has(panel.id)) { - throw new Error(`panel ${panel.id} already exists`) + throw new Error(`panel ${panel.id} already exists`); } const disposable = new CompositeDisposable( panel.onDidStateChange((e) => this.addDirtyPanel(panel)) - ) + ); - this.panels.set(panel.id, { value: panel, disposable }) + this.panels.set(panel.id, { value: panel, disposable }); - this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_CREATED }) + this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_CREATED }); } public unregisterPanel(panel: IGroupPanel) { if (!this.panels.has(panel.id)) { - throw new Error(`panel ${panel.id} doesn't exist`) + throw new Error(`panel ${panel.id} doesn't exist`); } const { disposable, value: unregisteredPanel } = this.panels.get( panel.id - ) + ); - disposable.dispose() - unregisteredPanel.dispose() + disposable.dispose(); + unregisteredPanel.dispose(); - this.panels.delete(panel.id) + this.panels.delete(panel.id); - this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_DESTROYED }) + this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_DESTROYED }); } /** @@ -372,138 +330,116 @@ export class Layout extends CompositeDisposable implements ILayout { * @returns A JSON respresentation of the layout */ public toJSON() { - this.syncConfigs() + this.syncConfigs(); - const data = this.gridview.serialize() + const data = this.gridview.serialize(); - const state = { ...this.panelState } + const state = { ...this.panelState }; const panels = Array.from(this.panels.values()).reduce( (collection, panel) => { if (!this.panelState[panel.value.id]) { - collection[panel.value.id] = panel.value.toJSON() + collection[panel.value.id] = panel.value.toJSON(); } - return collection + return collection; }, state - ) + ); - return { grid: data, panels } + return { grid: data, panels }; } /** * Ensure the local copy of the layout state is up-to-date */ private syncConfigs() { - const dirtyPanels = Array.from(this.dirtyPanels) + const dirtyPanels = Array.from(this.dirtyPanels); if (dirtyPanels.length === 0) { - console.debug('[layout#syncConfigs] no dirty panels') + console.debug('[layout#syncConfigs] no dirty panels'); } - this.dirtyPanels.clear() + this.dirtyPanels.clear(); const partialPanelState = dirtyPanels .map((panel) => this.panels.get(panel.id)) .filter((_) => !!_) .reduce((collection, panel) => { - collection[panel.value.id] = panel.value.toJSON() - return collection - }, {}) + collection[panel.value.id] = panel.value.toJSON(); + return collection; + }, {}); this.panelState = { ...this.panelState, ...partialPanelState, - } + }; dirtyPanels .filter((p) => this.panels.has(p.id)) .forEach((panel) => { - panel.setDirty(false) + panel.setDirty(false); this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_CLEAN, - }) - }) + }); + }); this._onDidLayoutChange.fire({ kind: GroupChangeKind.LAYOUT_CONFIG_UPDATED, - }) + }); } public deserialize(data: any) { - this.gridview.clear() + this.gridview.clear(); this.panels.forEach((panel) => { - panel.disposable.dispose() - panel.value.dispose() - }) - this.panels.clear() - this.groups.clear() + panel.disposable.dispose(); + panel.value.dispose(); + }); + this.panels.clear(); + this.groups.clear(); - this.fromJSON(data, this.deserializer) - this.gridview.layout(this._size, this._orthogonalSize) + this.fromJSON(data, this.deserializer); + this.gridview.layout(this._size, this._orthogonalSize); } public fromJSON(data: any, deserializer: IPanelDeserializer) { - const { grid, panels } = data + 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 + const panelData = panels[id]; + const panel = deserializer.fromJSON(panelData); + this.registerPanel(panel); + return panel; }, }) - ) - this._onDidLayoutChange.fire({ kind: GroupChangeKind.NEW_LAYOUT }) + ); + this._onDidLayoutChange.fire({ kind: GroupChangeKind.NEW_LAYOUT }); } public async closeAllGroups() { for (const entry of this.groups.entries()) { - const [key, group] = entry + const [key, group] = entry; - const didCloseAll = await group.value.closeAllPanels() + const didCloseAll = await group.value.closeAllPanels(); if (!didCloseAll) { - return false + return false; } - await timeoutPromise(0) + await timeoutPromise(0); } - return true + return true; } public setTabHeight(height: number) { - this.options.tabHeight = height + this.options.tabHeight = height; this.groups.forEach((value) => { - value.value.tabHeight = height - }) + value.value.tabHeight = height; + }); } public getTabHeight() { - return this.options.tabHeight - } - - 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) + return this.options.tabHeight; } fireMouseEvent(event: LayoutMouseEvent) { @@ -514,63 +450,63 @@ export class Layout extends CompositeDisposable implements ILayout { event: event.event, api: this, panel: event.panel, - }) + }); } - break + break; } } public addPanelFromComponent(options: AddPanelOptions): PanelReference { - const panel = this.addPanel(options) + const panel = this.addPanel(options); if (options.position?.referencePanel) { const referencePanel = this.panels.get( options.position.referencePanel - ).value - const referenceGroup = this.findGroup(referencePanel) + ).value; + const referenceGroup = this.findGroup(referencePanel); - const target = this.toTarget(options.position.direction) + const target = this.toTarget(options.position.direction); if (target === Position.Center) { - referenceGroup.openPanel(panel) + referenceGroup.openPanel(panel); } else { - const location = getGridLocation(referenceGroup.element) + const location = getGridLocation(referenceGroup.element); const relativeLocation = getRelativeLocation( this.gridview.orientation, location, target - ) - this.addPanelToNewGroup(panel, relativeLocation) + ); + this.addPanelToNewGroup(panel, relativeLocation); } } else { - this.addPanelToNewGroup(panel) + this.addPanelToNewGroup(panel); } return { update: (event: { params: { [key: string]: any } }) => { if (panel.update) { - panel.update({ params: event.params }) + panel.update({ params: event.params }); } }, remove: () => { - const group = this.findGroup(panel) - group.removePanel(panel) + const group = this.findGroup(panel); + group.removePanel(panel); }, - } + }; } public addPanel(options: AddPanelOptions): IGroupPanel { - const component = this.createContentComponent(options.componentName) - const tabComponent = this.createTabComponent(options.tabComponentName) + const component = this.createContentComponent(options.componentName); + const tabComponent = this.createTabComponent(options.tabComponentName); - const panel = new DefaultPanel(options.id, tabComponent, component) + const panel = new DefaultPanel(options.id, tabComponent, component); panel.init({ title: options.title || options.id, suppressClosable: options?.suppressClosable, params: options?.params || {}, - }) + }); - this.registerPanel(panel) - return panel + this.registerPanel(panel); + return panel; } private createContentComponent( @@ -581,7 +517,7 @@ export class Layout extends CompositeDisposable implements ILayout { this.options.components, this.options.frameworkComponents, this.options.frameworkComponentFactory.content - ) + ); } private createTabComponent( @@ -592,67 +528,64 @@ export class Layout extends CompositeDisposable implements ILayout { this.options.tabComponents, this.options.frameworkTabComponents, this.options.frameworkComponentFactory.tab - ) + ); } public addEmptyGroup(options: AddGroupOptions) { - const group = this.createGroup() + const group = this.createGroup(); if (options) { - const referencePanel = this.panels.get(options.referencePanel).value - const referenceGroup = this.findGroup(referencePanel) + const referencePanel = this.panels.get(options.referencePanel) + .value; + const referenceGroup = this.findGroup(referencePanel); - const target = this.toTarget(options.direction) + const target = this.toTarget(options.direction); - const location = getGridLocation(referenceGroup.element) + const location = getGridLocation(referenceGroup.element); const relativeLocation = getRelativeLocation( this.gridview.orientation, location, target - ) - this.doAddGroup(group, relativeLocation) + ); + this.doAddGroup(group, relativeLocation); } else { - this.doAddGroup(group) + this.doAddGroup(group); } } - public getGroup(id: string) { - return this.groups.get(id)?.value - } - public removeGroup(group: IGroupview) { if (this.groups.size === 1) { - group.panels.forEach((panel) => group.removePanel(panel)) - this._activeGroup = group - return + group.panels.forEach((panel) => group.removePanel(panel)); + this._activeGroup = group; + return; } if (group === this._activeGroup) { - this._activeGroup = undefined + this._activeGroup = undefined; } - this.doRemoveGroup(group) + this.doRemoveGroup(group); } private addPanelToNewGroup(panel: IGroupPanel, location: number[] = [0]) { - let group: IGroupview + let group: IGroupview; if ( this.groups.size === 1 && Array.from(this.groups.values())[0].value.size === 0 ) { - group = Array.from(this.groups.values())[0].value + group = Array.from(this.groups.values())[0].value; } else { - group = this.createGroup() - this.doAddGroup(group, location) + group = this.createGroup(); + this.doAddGroup(group, location); } - group.openPanel(panel) + group.openPanel(panel); } private doAddGroup(group: IGroupview, location: number[] = [0]) { - this.gridview.addView(group, { type: 'distribute' }, location) - this._onDidLayoutChange.fire({ kind: GroupChangeKind.ADD_GROUP }) - this.doSetGroupActive(group) + this.gridview.addView(group, { type: 'distribute' }, location); + this._onDidLayoutChange.fire({ kind: GroupChangeKind.ADD_GROUP }); + this.doSetGroupActive(group); } private doRemoveGroup( @@ -660,187 +593,169 @@ export class Layout extends CompositeDisposable implements ILayout { options?: { skipActive?: boolean; skipDispose?: boolean } ) { if (!this.groups.has(group.id)) { - throw new Error('invalid operation') + throw new Error('invalid operation'); } - const { disposable } = this.groups.get(group.id) + const { disposable } = this.groups.get(group.id); if (!options?.skipDispose) { - disposable.dispose() - this.groups.delete(group.id) + disposable.dispose(); + this.groups.delete(group.id); } - const view = this.gridview.remove(group, { type: 'distribute' }) - this._onDidLayoutChange.fire({ kind: GroupChangeKind.REMOVE_GROUP }) + 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) + this.doSetGroupActive(Array.from(this.groups.values())[0].value); } - return view + return view; } - public doSetGroupActive(group: IGroupview) { - if (this._activeGroup && this._activeGroup !== group) { - this._activeGroup.setActive(false) - } - group.setActive(true) - this._activeGroup = group - } - - public moveGroup( + public moveGroupOrPanel( referenceGroup: IGroupview, groupId: string, itemId: string, target: Position, index?: number ) { - const sourceGroup = groupId ? this.groups.get(groupId).value : undefined + const sourceGroup = groupId + ? this.groups.get(groupId).value + : undefined; switch (target) { case Position.Center: case undefined: const groupItem = sourceGroup?.removePanel(itemId) || - this.panels.get(itemId).value + this.panels.get(itemId).value; if (sourceGroup?.size === 0) { - this.doRemoveGroup(sourceGroup) + this.doRemoveGroup(sourceGroup); } - referenceGroup.openPanel(groupItem, index) + referenceGroup.openPanel(groupItem, index); - return + return; } - const referenceLocation = getGridLocation(referenceGroup.element) + const referenceLocation = getGridLocation(referenceGroup.element); const targetLocation = getRelativeLocation( this.gridview.orientation, referenceLocation, target - ) + ); if (sourceGroup?.size < 2) { - const [targetParentLocation, to] = tail(targetLocation) - const sourceLocation = getGridLocation(sourceGroup.element) - const [sourceParentLocation, from] = tail(sourceLocation) + 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) + this.gridview.moveView(sourceParentLocation, from, to); - return + return; } // source group will become empty so delete the group const targetGroup = this.doRemoveGroup(sourceGroup, { skipActive: true, skipDispose: true, - }) as IGroupview + }) as IGroupview; // 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) + ); + this.doAddGroup(targetGroup, location); } else { const groupItem = sourceGroup?.removePanel(itemId) || - this.panels.get(itemId).value + this.panels.get(itemId).value; const dropLocation = getRelativeLocation( this.gridview.orientation, referenceLocation, target - ) + ); - this.addPanelToNewGroup(groupItem, dropLocation) + this.addPanelToNewGroup(groupItem, dropLocation); } } createGroup(options?: GroupOptions) { - const group = new Groupview(this, nextGroupId.next(), options) + const group = new Groupview(this, nextGroupId.next(), options); if (typeof this.options.tabHeight === 'number') { - group.tabHeight = this.options.tabHeight + group.tabHeight = this.options.tabHeight; } if (!this.groups.has(group.id)) { const disposable = new CompositeDisposable( group.onMove((event) => { - const { groupId, itemId, target, index } = event - this.moveGroup(group, groupId, itemId, target, index) + const { groupId, itemId, target, index } = event; + this.moveGroupOrPanel( + group, + groupId, + itemId, + target, + index + ); }), group.onDidGroupChange((event) => { - this._onDidLayoutChange.fire(event) + this._onDidLayoutChange.fire(event); }), group.onDrop((event) => { - const dragEvent = event.event - const dataTransfer = dragEvent.dataTransfer + const dragEvent = event.event; + const dataTransfer = dragEvent.dataTransfer; if (dataTransfer.types.length === 0) { - return + return; } if (!this.registry.has(dataTransfer.types[0])) { - return + return; } - const cb = this.registry.get(dataTransfer.types[0]) + const cb = this.registry.get(dataTransfer.types[0]); - const panelOptions = cb({ event }) + const panelOptions = cb({ event }); - let panel = this.getPanel(panelOptions.id) + let panel = this.getPanel(panelOptions.id); if (!panel) { - panel = this.addPanel(panelOptions) + panel = this.addPanel(panelOptions); } - this.moveGroup( + this.moveGroupOrPanel( group, panel?.group?.id, panel.id, event.target, event.index - ) + ); }) - ) + ); - this.groups.set(group.id, { value: group, disposable }) + this.groups.set(group.id, { value: group, disposable }); } - return group - } - - 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) + return group; } private findGroup(panel: IGroupPanel): IGroupview | undefined { return Array.from(this.groups.values()).find((group) => group.value.containsPanel(panel) - ).value + ).value; } private addDirtyPanel(panel: IGroupPanel) { - this.dirtyPanels.add(panel) - panel.setDirty(true) - this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_DIRTY }) - this.debouncedDeque() + this.dirtyPanels.add(panel); + panel.setDirty(true); + this._onDidLayoutChange.fire({ kind: GroupChangeKind.PANEL_DIRTY }); + this.debouncedDeque(); } private toTarget( @@ -848,41 +763,34 @@ export class Layout extends CompositeDisposable implements ILayout { ) { switch (direction) { case 'left': - return Position.Left + return Position.Left; case 'right': - return Position.Right + return Position.Right; case 'above': - return Position.Top + return Position.Top; case 'below': - return Position.Bottom + return Position.Bottom; case 'within': default: - return Position.Center + return Position.Center; } } public dispose() { - super.dispose() + super.dispose(); - this.gridview.dispose() + this.debugContainer?.dispose(); - this.debugContainer?.dispose() - - if (this.resizeTimer) { - clearInterval(this.resizeTimer) - this.resizeTimer = undefined - } - - this._onDidLayoutChange.dispose() + this._onDidLayoutChange.dispose(); } private updateContainer() { if (this.options.debug) { if (!this.debugContainer) { - this.debugContainer = new DebugWidget(this) + this.debugContainer = new DebugWidget(this); } else { - this.debugContainer.dispose() - this.debugContainer = undefined + this.debugContainer.dispose(); + this.debugContainer = undefined; } } } diff --git a/packages/splitview/src/layout/options.ts b/packages/splitview/src/layout/options.ts index b9d868718..73ab944ea 100644 --- a/packages/splitview/src/layout/options.ts +++ b/packages/splitview/src/layout/options.ts @@ -1,72 +1,91 @@ -import { IGroupview } from '../groupview/groupview' +import { IGridView } from '../gridview/gridview'; +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' +} from '../groupview/panel/parts'; +import { IGroupPanel } from '../groupview/panel/types'; +import { Orientation } from '../splitview/splitview'; +import { FrameworkFactory } from '../types'; +import { IComponentGridview } from './componentGridview'; +import { Api } from './layout'; export interface GroupPanelFrameworkComponentFactory { - content: FrameworkFactory - tab: FrameworkFactory + content: FrameworkFactory; + tab: FrameworkFactory; } export interface TabContextMenuEvent { - event: MouseEvent - api: Api - panel: IGroupPanel + event: MouseEvent; + api: Api; + panel: IGroupPanel; +} + +export interface GridComponentOptions { + orientation: Orientation; + components?: { + [componentName: string]: IComponentGridview; + }; + frameworkComponents?: { + [componentName: string]: any; + }; + frameworkComponentFactory: any; + tabHeight?: number; } export interface LayoutOptions { tabComponents?: { - [componentName: string]: PanelHeaderPartConstructor - } + [componentName: string]: PanelHeaderPartConstructor; + }; components?: { - [componentName: string]: PanelContentPartConstructor - } + [componentName: string]: PanelContentPartConstructor; + }; frameworkTabComponents?: { - [componentName: string]: any - } + [componentName: string]: any; + }; frameworkComponents?: { - [componentName: string]: any - } - watermarkComponent?: WatermarkConstructor - watermarkFrameworkComponent?: any - frameworkComponentFactory: GroupPanelFrameworkComponentFactory - tabHeight?: number - debug?: boolean - enableExternalDragEvents?: boolean + [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 { - componentName: string | PanelContentPartConstructor - tabComponentName?: string | PanelHeaderPartConstructor + componentName: string | PanelContentPartConstructor; + tabComponentName?: string | PanelHeaderPartConstructor; position?: { - direction?: 'left' | 'right' | 'above' | 'below' | 'within' - referencePanel: string - } + 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 +export interface MovementOptions2 { + group?: IGridView; +} + +export interface MovementOptions extends MovementOptions2 { + includePanel?: boolean; + group?: IGroupview; } diff --git a/packages/splitview/src/lifecycle.ts b/packages/splitview/src/lifecycle.ts index 75421274a..8989da7df 100644 --- a/packages/splitview/src/lifecycle.ts +++ b/packages/splitview/src/lifecycle.ts @@ -1,56 +1,56 @@ export interface IDisposable { - dispose: () => void + dispose: () => void; } export interface IValueDisposable { - 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) + return new CompositeDisposable(...args); } constructor(...args: IDisposable[]) { - this.disposables = args + this.disposables = args; } public addDisposables(...args: IDisposable[]) { - args?.forEach((arg) => this.disposables.push(arg)) + args?.forEach((arg) => this.disposables.push(arg)); } public dispose() { - this.disposables.forEach((arg) => arg.dispose()) + this.disposables.forEach((arg) => arg.dispose()); } } export class MutableDisposable implements IDisposable { - private _disposable: IDisposable + private _disposable: IDisposable; constructor() {} set value(disposable: IDisposable) { if (this._disposable) { - this._disposable.dispose() + this._disposable.dispose(); } - this._disposable = disposable + this._disposable = disposable; } public dispose() { if (this._disposable) { - this._disposable.dispose() + this._disposable.dispose(); } } } diff --git a/packages/splitview/src/math.ts b/packages/splitview/src/math.ts index 957c23192..10b2be454 100644 --- a/packages/splitview/src/math.ts +++ b/packages/splitview/src/math.ts @@ -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() }; +}; diff --git a/packages/splitview/src/panel/api.ts b/packages/splitview/src/panel/api.ts index d79af4aa1..782c62714 100644 --- a/packages/splitview/src/panel/api.ts +++ b/packages/splitview/src/panel/api.ts @@ -1,7 +1,7 @@ -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 @@ -13,74 +13,74 @@ type StateObject = | null | object | StateObject[] - | { [key: string]: 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 - onDidStateChange: Event - onDidFocusChange: Event + onDidDimensionsChange: Event; + onDidStateChange: Event; + onDidFocusChange: Event; // state - setState(key: string, value: StateObject): void - setState(state: State): void - getState: () => State - getStateKey: (key: string) => T + setState(key: string, value: StateObject): void; + setState(state: State): void; + getState: () => State; + getStateKey: (key: string) => T; // - readonly isFocused: boolean + 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() - readonly onDidStateChange: Event = this._onDidStateChange.event + readonly _onDidStateChange = new Emitter(); + readonly onDidStateChange: Event = this._onDidStateChange.event; // readonly _onDidPanelDimensionChange = new Emitter< PanelDimensionChangeEvent >({ emitLastValue: true, - }) - readonly onDidDimensionsChange = this._onDidPanelDimensionChange.event + }); + readonly onDidDimensionsChange = this._onDidPanelDimensionChange.event; // readonly _onDidChangeFocus = new Emitter({ emitLastValue: true, - }) + }); readonly onDidFocusChange: Event = this._onDidChangeFocus - .event + .event; // get isFocused() { - return this._isFocused + return this._isFocused; } constructor() { - super() + super(); this.addDisposables( this._onDidStateChange, this._onDidChangeFocus, this._onDidPanelDimensionChange, this.onDidFocusChange((event) => { - this._isFocused = event.isFocused + this._isFocused = event.isFocused; }) - ) + ); } public setState( @@ -88,76 +88,76 @@ export class BaseViewApi extends CompositeDisposable implements IBaseViewApi { value?: StateObject ) { if (typeof key === 'object') { - this._state = key + this._state = key; } else { - this._state[key] = value + this._state[key] = value; } - this._onDidStateChange.fire(undefined) + this._onDidStateChange.fire(undefined); } public getState(): State { - return this._state + return this._state; } public getStateKey(key: string): T { - return this._state[key] as T + return this._state[key] as T; } public dispose() { - super.dispose() + super.dispose(); } } interface PanelConstraintChangeEvent { - minimumSize?: FunctionOrValue - maximumSize?: FunctionOrValue + minimumSize?: FunctionOrValue; + maximumSize?: FunctionOrValue; } export interface IPanelApi extends IBaseViewApi { - onDidConstraintsChange: Event - setConstraints(value: PanelConstraintChangeEvent): void + onDidConstraintsChange: Event; + setConstraints(value: PanelConstraintChangeEvent): void; } export class PanelApi extends BaseViewApi implements IBaseViewApi { readonly _onDidConstraintsChange = new Emitter({ emitLastValue: true, - }) + }); readonly onDidConstraintsChange: Event = this - ._onDidConstraintsChange.event + ._onDidConstraintsChange.event; constructor() { - super() + super(); } public setConstraints(value: PanelConstraintChangeEvent) { - this._onDidConstraintsChange.fire(value) + this._onDidConstraintsChange.fire(value); } } interface GridConstraintChangeEvent { - minimumWidth?: FunctionOrValue - minimumHeight?: FunctionOrValue - maximumWidth?: FunctionOrValue - maximumHeight?: FunctionOrValue + minimumWidth?: FunctionOrValue; + minimumHeight?: FunctionOrValue; + maximumWidth?: FunctionOrValue; + maximumHeight?: FunctionOrValue; } export interface IGridApi extends IBaseViewApi { - onDidConstraintsChange: Event - setConstraints(value: GridConstraintChangeEvent): void + onDidConstraintsChange: Event; + setConstraints(value: GridConstraintChangeEvent): void; } export class GridApi extends BaseViewApi implements IBaseViewApi { readonly _onDidConstraintsChange = new Emitter({ emitLastValue: true, - }) + }); readonly onDidConstraintsChange: Event = this - ._onDidConstraintsChange.event + ._onDidConstraintsChange.event; constructor() { - super() + super(); } public setConstraints(value: GridConstraintChangeEvent) { - this._onDidConstraintsChange.fire(value) + this._onDidConstraintsChange.fire(value); } } diff --git a/packages/splitview/src/panel/types.ts b/packages/splitview/src/panel/types.ts index 75924f8b8..bf1817786 100644 --- a/packages/splitview/src/panel/types.ts +++ b/packages/splitview/src/panel/types.ts @@ -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; } diff --git a/packages/splitview/src/paneview/paneview.ts b/packages/splitview/src/paneview/paneview.ts index 9090abab7..09be0f7dc 100644 --- a/packages/splitview/src/paneview/paneview.ts +++ b/packages/splitview/src/paneview/paneview.ts @@ -1,151 +1,151 @@ -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 = new Emitter< boolean - >() - public onDidChangeExpansionState = this._onDidChangeExpansionState.event + >(); + public onDidChangeExpansionState = this._onDidChangeExpansionState.event; private _onDidChange: Emitter = new Emitter< number | undefined - >() - public onDidChange = this._onDidChange.event + >(); + 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' + this.element = document.createElement('div'); + this.element.className = 'pane'; this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize - : 120 + : 120; this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize - : Number.POSITIVE_INFINITY + : Number.POSITIVE_INFINITY; - this._isExpanded = options.isExpanded - this.orientation = options.orientation + this._isExpanded = options.isExpanded; + this.orientation = options.orientation; } public get minimumSize(): number { - const headerSize = this.headerSize - const expanded = this.isExpanded() + const headerSize = this.headerSize; + const expanded = this.isExpanded(); const minimumBodySize = expanded ? this._minimumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 - : 0 + : 0; - return headerSize + minimumBodySize + return headerSize + minimumBodySize; } public get maximumSize(): number { - const headerSize = this.headerSize - const expanded = this.isExpanded() + const headerSize = this.headerSize; + const expanded = this.isExpanded(); const maximumBodySize = expanded ? this._maximumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 - : 0 + : 0; - return headerSize + maximumBodySize + return headerSize + maximumBodySize; } public isExpanded() { - return this._isExpanded + return this._isExpanded; } public get orientation() { - return this._orientation + return this._orientation; } public get orthogonalSize() { - return this._orthogonalSize + return this._orthogonalSize; } public set minimumSize(size: number) { - this._minimumSize = size - this._onDidChange.fire(undefined) + this._minimumSize = size; + this._onDidChange.fire(undefined); } public set maximumSize(size: number) { - this._maximumSize = size - this._onDidChange.fire(undefined) + this._maximumSize = size; + this._onDidChange.fire(undefined); } public setExpanded(expanded: boolean) { - this._isExpanded = expanded + this._isExpanded = expanded; if (expanded) { if (this.animationTimer) { - clearTimeout(this.animationTimer) + clearTimeout(this.animationTimer); } - this.element.appendChild(this.body) + this.element.appendChild(this.body); } else { this.animationTimer = setTimeout(() => { - this.body.remove() - }, 200) + this.body.remove(); + }, 200); } - this._onDidChangeExpansionState.fire(expanded) - this._onDidChange.fire(expanded ? this.expandedSize : undefined) + this._onDidChangeExpansionState.fire(expanded); + this._onDidChange.fire(expanded ? this.expandedSize : undefined); } public set orientation(orientation: Orientation) { - this._orientation = orientation + this._orientation = orientation; } public set orthogonalSize(size: number) { - this._orthogonalSize = size + this._orthogonalSize = size; } public layout(size: number, orthogonalSize: number) { if (this.isExpanded()) { - this.expandedSize = size + 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.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) + 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(); @@ -174,103 +174,103 @@ export abstract class Pane implements IView { // 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 + 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) + container.appendChild(this.element); this.splitview = new SplitView(this.element, { orientation: this._orientation, - }) + }); } public setOrientation(orientation: Orientation) { - this._orientation = orientation + this._orientation = orientation; } public addPane(pane: Pane, size?: number, index = this.splitview.length) { - const disposable = pane.onDidChangeExpansionState(this.setupAnimation) + const disposable = pane.onDidChangeExpansionState(this.setupAnimation); const paneItem: PaneItem = { pane, disposable: { dispose: () => { - disposable.dispose() + disposable.dispose(); }, }, - } + }; - this.paneItems.splice(index, 0, paneItem) - pane.orientation = this._orientation - pane.orthogonalSize = this.orthogonalSize - this.splitview.addView(pane, size, index) + 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[] + 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 + 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) + const view = this.removePane(from); + this.addPane(view.pane, to); } public layout(size: number, orthogonalSize: number): void { - this.orthogonalSize = orthogonalSize - this.size = size + this.orthogonalSize = orthogonalSize; + this.size = size; for (const paneItem of this.paneItems) { - paneItem.pane.orthogonalSize = this.orthogonalSize + paneItem.pane.orthogonalSize = this.orthogonalSize; } - this.splitview.layout(this.size, this.orthogonalSize) + this.splitview.layout(this.size, this.orthogonalSize); } private setupAnimation() { if (this.animationTimer) { - clearTimeout(this.animationTimer) + clearTimeout(this.animationTimer); } - addClasses(this.element, 'animated') + addClasses(this.element, 'animated'); this.animationTimer = setTimeout(() => { - this.animationTimer = undefined - removeClasses(this.element, 'animated') - }, 200) + this.animationTimer = undefined; + removeClasses(this.element, 'animated'); + }, 200); } public dispose() { this.paneItems.forEach((paneItem) => { - paneItem.disposable.dispose() - }) + paneItem.disposable.dispose(); + }); } } diff --git a/packages/splitview/src/react/deserializer.ts b/packages/splitview/src/react/deserializer.ts index 574437bd9..2d083532a 100644 --- a/packages/splitview/src/react/deserializer.ts +++ b/packages/splitview/src/react/deserializer.ts @@ -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' +} from '../layout/componentFactory'; export class ReactPanelDeserialzier implements IPanelDeserializer { 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 + 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 + ) as PanelContentPart; const headerPart = createTabComponent( tab.id, this.layout.options.tabComponents, this.layout.options.frameworkComponentFactory, this.layout.options.frameworkComponentFactory.tab - ) as PanelHeaderPart + ) as PanelHeaderPart; - const panel = new DefaultPanel(panelId, headerPart, contentPart) + const panel = new DefaultPanel(panelId, headerPart, contentPart); panel.init({ title, suppressClosable, params: props || {}, state: state || {}, - }) + }); - return panel + return panel; } } diff --git a/packages/splitview/src/react/gridview.tsx b/packages/splitview/src/react/gridview.tsx index c65debbbc..22936c0b6 100644 --- a/packages/splitview/src/react/gridview.tsx +++ b/packages/splitview/src/react/gridview.tsx @@ -1,43 +1,43 @@ -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' +} 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 + orientation: Orientation; + onReady?: (event: GridviewReadyEvent) => void; components: { - [index: string]: React.FunctionComponent - } + [index: string]: React.FunctionComponent; + }; } export const GridviewComponent = (props: IGridviewComponentProps) => { - const domReference = React.useRef() - const gridview = React.useRef() - const [portals, setPortals] = React.useState([]) + const domReference = React.useRef(); + const gridview = React.useRef(); + const [portals, setPortals] = React.useState([]); const addPortal = React.useCallback((p: React.ReactPortal) => { - setPortals((portals) => [...portals, p]) + setPortals((portals) => [...portals, p]); return { dispose: () => { setPortals((portals) => portals.filter((portal) => portal !== p) - ) + ); }, - } - }, []) + }; + }, []); React.useEffect(() => { gridview.current = new ComponentGridview(domReference.current, { @@ -47,15 +47,15 @@ export const GridviewComponent = (props: IGridviewComponentProps) => { createComponent: (id: string, component: any) => { return new ReactComponentGridView(id, id, component, { addPortal, - }) + }); }, }, - }) + }); if (props.onReady) { - props.onReady({ api: gridview.current }) + props.onReady({ api: gridview.current }); } - }, []) + }, []); return (
{ > {portals}
- ) -} + ); +}; diff --git a/packages/splitview/src/react/layout.tsx b/packages/splitview/src/react/layout.tsx index 95c24f0ae..875564234 100644 --- a/packages/splitview/src/react/layout.tsx +++ b/packages/splitview/src/react/layout.tsx @@ -1,66 +1,66 @@ -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 * 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' +} 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 - } + [componentName: string]: React.FunctionComponent; + }; tabComponents?: { - [componentName: string]: React.FunctionComponent - } - watermarkComponent?: React.FunctionComponent - onReady?: (event: OnReadyEvent) => void - autoSizeToFitContainer?: boolean - serializedLayout?: {} + [componentName: string]: React.FunctionComponent; + }; + watermarkComponent?: React.FunctionComponent; + onReady?: (event: OnReadyEvent) => void; + autoSizeToFitContainer?: boolean; + serializedLayout?: {}; deserializer?: { fromJSON: ( data: any ) => { - component: React.FunctionComponent - tabComponent?: React.FunctionComponent - props?: { [key: string]: any } - } - } - debug?: boolean - tabHeight?: number - enableExternalDragEvents?: boolean - onTabContextMenu?: (event: TabContextMenuEvent) => void + component: React.FunctionComponent; + tabComponent?: React.FunctionComponent; + props?: { [key: string]: any }; + }; + }; + debug?: boolean; + tabHeight?: number; + enableExternalDragEvents?: boolean; + onTabContextMenu?: (event: TabContextMenuEvent) => void; } export const ReactGrid = (props: IReactGridProps) => { - const domReference = React.useRef() - const layoutReference = React.useRef() + const domReference = React.useRef(); + const layoutReference = React.useRef(); - const [portals, setPortals] = React.useState([]) + const [portals, setPortals] = React.useState([]); React.useEffect(() => { const addPortal = (p: React.ReactPortal) => { - setPortals((portals) => [...portals, p]) + setPortals((portals) => [...portals, p]); return { dispose: () => { setPortals((portals) => portals.filter((portal) => portal !== p) - ) + ); }, - } - } + }; + }; const factory: GroupPanelFrameworkComponentFactory = { content: { @@ -70,7 +70,7 @@ export const ReactGrid = (props: IReactGridProps) => { ) => { return new ReactPanelContentPart(id, component, { addPortal, - }) + }); }, }, tab: { @@ -80,53 +80,57 @@ export const ReactGrid = (props: IReactGridProps) => { ) => { return new ReactPanelHeaderPart(id, component, { addPortal, - }) + }); }, }, - } + }; - const layout = new Layout({ + const element = document.createElement('div'); + + const layout = new Layout(element, { 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) + layout.deserialize(props.serializedLayout); } if (props.onReady) { - props.onReady({ api: layout }) + props.onReady({ api: layout }); } return () => { - layout.dispose() - } - }, []) + layout.dispose(); + }; + }, []); React.useEffect(() => { const disposable = layoutReference.current.onTabContextMenu((event) => { - props.onTabContextMenu(event) - }) + props.onTabContextMenu(event); + }); return () => { - disposable.dispose() - } - }, [props.onTabContextMenu]) + disposable.dispose(); + }; + }, [props.onTabContextMenu]); React.useEffect(() => { - layoutReference.current.setAutoResizeToFit(props.autoSizeToFitContainer) - }, [props.autoSizeToFitContainer]) + layoutReference.current.setAutoResizeToFit( + props.autoSizeToFitContainer + ); + }, [props.autoSizeToFitContainer]); return (
{ > {portals}
- ) -} + ); +}; diff --git a/packages/splitview/src/react/react.tsx b/packages/splitview/src/react/react.tsx index a5f546f44..80b561134 100644 --- a/packages/splitview/src/react/react.tsx +++ b/packages/splitview/src/react/react.tsx @@ -1,61 +1,61 @@ -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 - componentProps: any + component: React.FunctionComponent; + 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) => { - const [_, triggerRender] = React.useState() + const [_, triggerRender] = React.useState(); 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()) + _props.current = { ..._props.current, ...props }; + triggerRender(Date.now()); }, }), [] - ) + ); React.useEffect(() => { - console.debug('[reactwrapper] component mounted ') + console.debug('[reactwrapper] component mounted '); return () => { - console.debug('[reactwrapper] component unmounted ') - } - }, []) + console.debug('[reactwrapper] component unmounted '); + }; + }, []); 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, @@ -64,49 +64,49 @@ export class ReactPart implements IDisposable { private readonly component: React.FunctionComponent<{}>, private readonly parameters: { [key: string]: any } ) { - this.createPortal() + this.createPortal(); } public update(props: {}) { if (this.disposed) { - throw new Error('invalid operation') + throw new Error('invalid operation'); } - this.componentInstance?.update(props) + this.componentInstance?.update(props); } private createPortal() { if (this.disposed) { - throw new Error('invalid operation') + throw new Error('invalid operation'); } let props = { api: this.api, ...this.parameters, - } as any + } as any; const wrapper = React.createElement(PanelWrapper, { component: this.component, componentProps: props, ref: (element: any) => { - this.componentInstance = element + this.componentInstance = element; }, - }) + }); const portal = ReactDOM.createPortal( wrapper, this.parent, counter.next() - ) + ); this.ref = { portal, disposable: this.addPortal(portal), - } + }; } public dispose() { - this.ref?.disposable?.dispose() - this.ref = undefined - this.disposed = true + this.ref?.disposable?.dispose(); + this.ref = undefined; + this.disposed = true; } } diff --git a/packages/splitview/src/react/reactComponentGridView.ts b/packages/splitview/src/react/reactComponentGridView.ts index b755d8834..c6123b857 100644 --- a/packages/splitview/src/react/reactComponentGridView.ts +++ b/packages/splitview/src/react/reactComponentGridView.ts @@ -1,55 +1,55 @@ -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 + private _element: HTMLElement; + private part: ReactPart; + private params: { params: any }; + private api: GridApi; private _onDidChange: Emitter = new Emitter< number | undefined - >() - public onDidChange = this._onDidChange.event + >(); + public onDidChange = this._onDidChange.event; get element() { - return this._element + return this._element; } - private _minimumWidth: FunctionOrValue = 200 - private _minimumHeight: FunctionOrValue = 200 - private _maximumWidth: FunctionOrValue = Number.MAX_SAFE_INTEGER - private _maximumHeight: FunctionOrValue = Number.MAX_SAFE_INTEGER + private _minimumWidth: FunctionOrValue = 200; + private _minimumHeight: FunctionOrValue = 200; + private _maximumWidth: FunctionOrValue = Number.MAX_SAFE_INTEGER; + private _maximumHeight: FunctionOrValue = Number.MAX_SAFE_INTEGER; get minimumWidth() { return typeof this._minimumWidth === 'function' ? this._minimumWidth() - : this._minimumWidth + : this._minimumWidth; } get minimumHeight() { return typeof this._minimumHeight === 'function' ? this._minimumHeight() - : this._minimumHeight + : this._minimumHeight; } get maximumHeight() { return typeof this._maximumHeight === 'function' ? this._maximumHeight() - : this._maximumHeight + : this._maximumHeight; } get maximumWidth() { return typeof this._maximumWidth === 'function' ? this._maximumWidth() - : this._maximumWidth + : this._maximumWidth; } constructor( @@ -60,17 +60,17 @@ export class ReactComponentGridView >, private readonly parent: ReactLayout ) { - super() - this.api = new GridApi() + super(); + this.api = new GridApi(); if (!this.component) { - throw new Error('React.FunctionalComponent cannot be undefined') + throw new Error('React.FunctionalComponent cannot be undefined'); } - this._element = document.createElement('div') - this._element.tabIndex = -1 - this._element.style.outline = 'none' + this._element = document.createElement('div'); + this._element.tabIndex = -1; + this._element.style.outline = 'none'; - const { onDidFocus, onDidBlur } = trackFocus(this._element) + const { onDidFocus, onDidBlur } = trackFocus(this._element); this.addDisposables( this.api.onDidConstraintsChange((event) => { @@ -78,54 +78,58 @@ export class ReactComponentGridView typeof event.minimumWidth === 'number' || typeof event.minimumWidth === 'function' ) { - this._minimumWidth = event.minimumWidth + this._minimumWidth = event.minimumWidth; } if ( typeof event.minimumHeight === 'number' || typeof event.minimumHeight === 'function' ) { - this._minimumHeight = event.minimumHeight + this._minimumHeight = event.minimumHeight; } if ( typeof event.maximumWidth === 'number' || typeof event.maximumWidth === 'function' ) { - this._maximumWidth = event.maximumWidth + this._maximumWidth = event.maximumWidth; } if ( typeof event.maximumHeight === 'number' || typeof event.maximumHeight === 'function' ) { - this._maximumHeight = event.maximumHeight + this._maximumHeight = event.maximumHeight; } }), onDidFocus(() => { - this.api._onDidChangeFocus.fire({ isFocused: true }) + this.api._onDidChangeFocus.fire({ isFocused: true }); }), onDidBlur(() => { - this.api._onDidChangeFocus.fire({ isFocused: false }) + this.api._onDidChangeFocus.fire({ isFocused: false }); }) - ) + ); + } + + setActive(isActive: boolean) { + // noop } layout(width: number, height: number) { - this.api._onDidPanelDimensionChange.fire({ width, height }) + this.api._onDidPanelDimensionChange.fire({ width, height }); } init(parameters: InitParameters): void { - this.params = parameters + 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) + this.params = { ...this.params.params, ...params }; + this.part.update(params); } toJSON(): object { @@ -134,11 +138,11 @@ export class ReactComponentGridView component: this.componentName, props: this.params.params, state: this.api.getState(), - } + }; } dispose() { - super.dispose() - this.api.dispose() + super.dispose(); + this.api.dispose(); } } diff --git a/packages/splitview/src/react/reactComponentView.ts b/packages/splitview/src/react/reactComponentView.ts index 100cf98c3..5ba8858ca 100644 --- a/packages/splitview/src/react/reactComponentView.ts +++ b/packages/splitview/src/react/reactComponentView.ts @@ -1,13 +1,13 @@ -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 @@ -15,43 +15,43 @@ import { PanelUpdateEvent, InitParameters, IPanel } from '../panel/types' export class ReactComponentView extends CompositeDisposable implements IView, IPanel { - private _element: HTMLElement - private part: ReactPart - private params: { params: any } - private api: PanelApi + private _element: HTMLElement; + private part: ReactPart; + private params: { params: any }; + private api: PanelApi; private _onDidChange: Emitter = new Emitter< number | undefined - >() - public onDidChange = this._onDidChange.event + >(); + public onDidChange = this._onDidChange.event; get element() { - return this._element + return this._element; } - private _minimumSize: number = 200 - private _maximumSize: number = Number.MAX_SAFE_INTEGER - private _snapSize: number + private _minimumSize: number = 200; + private _maximumSize: number = Number.MAX_SAFE_INTEGER; + private _snapSize: number; get minimumSize() { - return this._minimumSize + return this._minimumSize; } set minimumSize(value: number) { - this._minimumSize = value + this._minimumSize = value; } get snapSize() { - return this._snapSize + return this._snapSize; } set snapSize(value: number) { - this._snapSize = value + this._snapSize = value; } get maximumSize() { - return this._maximumSize + return this._maximumSize; } set maximumSize(value: number) { - this._maximumSize = value + this._maximumSize = value; } constructor( @@ -62,46 +62,46 @@ export class ReactComponentView >, private readonly parent: ReactLayout ) { - super() - this.api = new PanelApi() + super(); + this.api = new PanelApi(); if (!this.component) { - throw new Error('React.FunctionalComponent cannot be undefined') + throw new Error('React.FunctionalComponent cannot be undefined'); } - this._element = document.createElement('div') - this._element.tabIndex = -1 - this._element.style.outline = 'none' + this._element = document.createElement('div'); + this._element.tabIndex = -1; + this._element.style.outline = 'none'; - const { onDidFocus, onDidBlur } = trackFocus(this._element) + const { onDidFocus, onDidBlur } = trackFocus(this._element); this.addDisposables( onDidFocus(() => { - this.api._onDidChangeFocus.fire({ isFocused: true }) + this.api._onDidChangeFocus.fire({ isFocused: true }); }), onDidBlur(() => { - this.api._onDidChangeFocus.fire({ isFocused: false }) + this.api._onDidChangeFocus.fire({ isFocused: false }); }) - ) + ); } layout(width: number, height: number) { - this.api._onDidPanelDimensionChange.fire({ width, height }) + this.api._onDidPanelDimensionChange.fire({ width, height }); } init(parameters: InitParameters): void { - this.params = parameters + 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) + this.params = { ...this.params.params, ...params }; + this.part.update(params); } toJSON(): object { @@ -110,11 +110,11 @@ export class ReactComponentView component: this.componentName, props: this.params.params, state: this.api.getState(), - } + }; } dispose() { - super.dispose() - this.api.dispose() + super.dispose(); + this.api.dispose(); } } diff --git a/packages/splitview/src/react/reactContentPart.ts b/packages/splitview/src/react/reactContentPart.ts index a3bd0af7a..adb1ccaa0 100644 --- a/packages/splitview/src/react/reactContentPart.ts +++ b/packages/splitview/src/react/reactContentPart.ts @@ -1,18 +1,18 @@ -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' +} 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 + return this._element; } constructor( @@ -20,7 +20,7 @@ export class ReactPanelContentPart implements PanelContentPart { private readonly component: React.FunctionComponent, private readonly parent: ReactLayout ) { - this._element = document.createElement('div') + this._element = document.createElement('div'); } public init(parameters: PartInitParameters): void { @@ -30,17 +30,17 @@ export class ReactPanelContentPart implements PanelContentPart { this.parent.addPortal, this.component, parameters.params - ) + ); } public toJSON() { return { id: this.id, - } + }; } public update(params: {}) { - this.part.update(params) + this.part.update(params); } public setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void { @@ -50,13 +50,13 @@ export class ReactPanelContentPart implements PanelContentPart { public layout(width: number, height: number): void {} public close(): Promise { - return Promise.resolve(ClosePanelResult.CLOSE) + return Promise.resolve(ClosePanelResult.CLOSE); } public focus(): void {} public onHide(): void {} public dispose() { - this.part?.dispose() + this.part?.dispose(); } } diff --git a/packages/splitview/src/react/reactHeaderPart.ts b/packages/splitview/src/react/reactHeaderPart.ts index 35194ecbe..756f83af5 100644 --- a/packages/splitview/src/react/reactHeaderPart.ts +++ b/packages/splitview/src/react/reactHeaderPart.ts @@ -1,14 +1,14 @@ -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 + return this._element; } constructor( @@ -16,7 +16,7 @@ export class ReactPanelHeaderPart implements PanelHeaderPart { private readonly component: React.FunctionComponent, private readonly parent: ReactLayout ) { - this._element = document.createElement('div') + this._element = document.createElement('div'); } public init(parameters: PartInitParameters): void { @@ -26,13 +26,13 @@ export class ReactPanelHeaderPart implements PanelHeaderPart { this.parent.addPortal, this.component, parameters.params - ) + ); } public toJSON() { return { id: this.id, - } + }; } public layout(height: string) { @@ -44,6 +44,6 @@ export class ReactPanelHeaderPart implements PanelHeaderPart { } public dispose() { - this.part?.dispose() + this.part?.dispose(); } } diff --git a/packages/splitview/src/react/splitview.tsx b/packages/splitview/src/react/splitview.tsx index 2bfa1bbe3..fd3fb3794 100644 --- a/packages/splitview/src/react/splitview.tsx +++ b/packages/splitview/src/react/splitview.tsx @@ -1,57 +1,57 @@ -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' +} 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 + 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 + orientation: Orientation; + onReady?: (event: SplitviewReadyEvent) => void; components: { - [index: string]: React.FunctionComponent - } + [index: string]: React.FunctionComponent; + }; } export const SplitViewComponent = (props: ISplitviewComponentProps) => { - const domReference = React.useRef() - const splitpanel = React.useRef() - const [portals, setPortals] = React.useState([]) + const domReference = React.useRef(); + const splitpanel = React.useRef(); + const [portals, setPortals] = React.useState([]); const addPortal = React.useCallback((p: React.ReactPortal) => { - setPortals((portals) => [...portals, p]) + setPortals((portals) => [...portals, p]); return { dispose: () => { setPortals((portals) => portals.filter((portal) => portal !== p) - ) + ); }, - } - }, []) + }; + }, []); React.useEffect(() => { splitpanel.current = new ComponentSplitview(domReference.current, { @@ -61,27 +61,27 @@ export const SplitViewComponent = (props: ISplitviewComponentProps) => { createComponent: (id: string, component: any) => { return new ReactComponentView(id, id, component, { addPortal, - }) + }); }, }, proportionalLayout: false, - }) + }); - const { width, height } = domReference.current.getBoundingClientRect() + const { width, height } = domReference.current.getBoundingClientRect(); const [size, orthogonalSize] = props.orientation === Orientation.HORIZONTAL ? [width, height] - : [height, width] - splitpanel.current.layout(size, orthogonalSize) + : [height, width]; + splitpanel.current.layout(size, orthogonalSize); if (props.onReady) { - props.onReady({ api: splitpanel.current }) + props.onReady({ api: splitpanel.current }); } return () => { - splitpanel.current.dispose() - } - }, []) + splitpanel.current.dispose(); + }; + }, []); return (
{ > {portals}
- ) -} + ); +}; diff --git a/packages/splitview/src/splitview/componentSplitview.ts b/packages/splitview/src/splitview/componentSplitview.ts index 25ed3d27d..f6fe2c24e 100644 --- a/packages/splitview/src/splitview/componentSplitview.ts +++ b/packages/splitview/src/splitview/componentSplitview.ts @@ -1,77 +1,77 @@ -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' +} from './options'; export interface IComponentSplitview extends IDisposable { addFromComponent(options: { - id: string - component: string + 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 + [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 = {} + options.components = {}; } if (!options.frameworkComponents) { - options.frameworkComponents = {} + options.frameworkComponents = {}; } - this.splitview = new SplitView(this.element, options) + this.splitview = new SplitView(this.element, options); } get minimumSize() { - return this.splitview.minimumSize + return this.splitview.minimumSize; } addFromComponent(options: { - id: string - component: string + id: string; + component: string; params?: { - [index: string]: any - } - priority?: LayoutPriority + [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.registerView(view); - this.splitview.addView(view, { type: 'distribute' }) - view.init({ params: options.params }) - view.priority = options.priority + this.splitview.addView(view, { type: 'distribute' }); + view.init({ params: options.params }); + view.priority = options.priority; return { dispose: () => { // }, - } + }; } private registerView(view: ISerializableView) { @@ -82,77 +82,77 @@ export class ComponentSplitview implements IComponentSplitview { const [size, orthogonalSize] = this.splitview.orientation === Orientation.HORIZONTAL ? [width, height] - : [height, width] - this.splitview.layout(size, orthogonalSize) + : [height, width]; + this.splitview.layout(size, orthogonalSize); } onChange(cb: (event: { proportions: number[] }) => void): IDisposable { return this.splitview.onDidSashEnd(() => { - cb({ proportions: this.splitview.proportions }) - }) + cb({ proportions: this.splitview.proportions }); + }); } toJSON(): object { const views = this.splitview .getViews() .map((v: ISerializableView, i) => { - const size = this.splitview.getViewSize(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 + const { views, orientation, size } = data; - this.splitview.dispose() + this.splitview.dispose(); this.splitview = new SplitView(this.element, { orientation, proportionalLayout: false, descriptor: { size, views: views.map((v) => { - const data = v.data + 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 + view.minimumSize = v.minimumSize; } if (typeof v.maximumSize === 'number') { - view.maximumSize = v.maximumSize + view.maximumSize = v.maximumSize; } if (typeof v.snapSize === 'number') { - view.snapSize = v.snapSize + view.snapSize = v.snapSize; } - view.init({ params: v.props }) + view.init({ params: v.props }); - view.priority = v.priority + view.priority = v.priority; - return { size: v.size, view } + return { size: v.size, view }; }), }, - }) + }); - this.splitview.orientation = orientation + this.splitview.orientation = orientation; } public dispose() { - this.splitview.dispose() + this.splitview.dispose(); } } diff --git a/packages/splitview/src/splitview/options.ts b/packages/splitview/src/splitview/options.ts index 511116532..8e6d54394 100644 --- a/packages/splitview/src/splitview/options.ts +++ b/packages/splitview/src/splitview/options.ts @@ -1,19 +1,19 @@ -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 - } + [componentName: string]: ISerializableView; + }; frameworkComponents?: { - [componentName: string]: any - } - frameworkWrapper?: FrameworkFactory + [componentName: string]: any; + }; + frameworkWrapper?: FrameworkFactory; } export interface ISerializableViewConstructor @@ -22,37 +22,37 @@ export interface ISerializableViewConstructor export function createComponent( componentName: string | Constructor | any, components: { - [componentName: string]: T + [componentName: string]: T; }, frameworkComponents: { - [componentName: string]: any + [componentName: string]: any; }, createFrameworkComponent: (id: string, component: any) => T ): T { const Component = typeof componentName === 'string' ? components[componentName] - : componentName + : componentName; const FrameworkComponent = typeof componentName === 'string' ? frameworkComponents[componentName] - : componentName + : componentName; if (Component && FrameworkComponent) { throw new Error( `cannot register component ${componentName} as both a component and frameworkComponent` - ) + ); } if (FrameworkComponent) { if (!createFrameworkComponent) { throw new Error( 'you must register a frameworkPanelWrapper to use framework components' - ) + ); } const wrappedComponent = createFrameworkComponent( componentName, FrameworkComponent - ) - return wrappedComponent + ); + return wrappedComponent; } - return new Component() as T + return new Component() as T; } diff --git a/packages/splitview/src/splitview/splitview.ts b/packages/splitview/src/splitview/splitview.ts index 5abf1ead5..5510b5338 100644 --- a/packages/splitview/src/splitview/splitview.ts +++ b/packages/splitview/src/splitview/splitview.ts @@ -1,18 +1,18 @@ -import { removeClasses, addClasses, firstIndex, toggleClass } from '../dom' -import { clamp } from '../math' -import { Event, Emitter } from '../events' -import { pushToStart, pushToEnd, range } from '../array' +import { removeClasses, addClasses, firstIndex, toggleClass } from '../dom'; +import { clamp } from '../math'; +import { Event, Emitter } from '../events'; +import { pushToStart, pushToEnd, range } from '../array'; export const clampView = (view: IView, size: number) => { - const result = clamp(size, view.minimumSize, view.maximumSize) + const result = clamp(size, view.minimumSize, view.maximumSize); if (typeof view.snapSize !== 'number' || size >= view.minimumSize) { - return result + return result; } - const snapSize = Math.min(view.snapSize, view.minimumSize) - return size < snapSize ? 0 : view.minimumSize -} + const snapSize = Math.min(view.snapSize, view.minimumSize); + return size < snapSize ? 0 : view.minimumSize; +}; export enum Orientation { HORIZONTAL = 'HORIZONTAL', @@ -27,9 +27,9 @@ export enum SashState { } export interface ISplitViewOptions { - orientation: Orientation - readonly descriptor?: ISplitViewDescriptor - proportionalLayout?: boolean + orientation: Orientation; + readonly descriptor?: ISplitViewDescriptor; + proportionalLayout?: boolean; } export enum LayoutPriority { Low = 'low', @@ -38,148 +38,148 @@ export enum LayoutPriority { } export interface IBaseView { - minimumSize: number - maximumSize: number - snapSize?: number - priority?: LayoutPriority + minimumSize: number; + maximumSize: number; + snapSize?: number; + priority?: LayoutPriority; } export interface IView extends IBaseView { - readonly element: HTMLElement | DocumentFragment - readonly onDidChange: Event - layout(size: number, orthogonalSize: number): void - setVisible?(visible: boolean): void + readonly element: HTMLElement | DocumentFragment; + readonly onDidChange: Event; + layout(size: number, orthogonalSize: number): void; + setVisible?(visible: boolean): void; } export interface IViewItem { - view: IView - size: number - container: HTMLElement - dispose: () => void + view: IView; + size: number; + container: HTMLElement; + dispose: () => void; } interface ISashItem { - container: HTMLElement - disposable: () => void + container: HTMLElement; + disposable: () => void; } -export type DistributeSizing = { type: 'distribute' } -export type SplitSizing = { type: 'split'; index: number } -export type Sizing = DistributeSizing | SplitSizing +export type DistributeSizing = { type: 'distribute' }; +export type SplitSizing = { type: 'split'; index: number }; +export type Sizing = DistributeSizing | SplitSizing; export interface ISplitViewDescriptor { - size: number + size: number; views: { - visible?: boolean - size: number - view: IView - }[] + visible?: boolean; + size: number; + view: IView; + }[]; } export class SplitView { - private element: HTMLElement - private viewContainer: HTMLElement - private sashContainer: HTMLElement - private views: IViewItem[] = [] - private sashes: ISashItem[] = [] - private _orientation: Orientation - private _size: number - private _orthogonalSize: number - private contentSize: number - private _proportions: number[] - private proportionalLayout: boolean + private element: HTMLElement; + private viewContainer: HTMLElement; + private sashContainer: HTMLElement; + private views: IViewItem[] = []; + private sashes: ISashItem[] = []; + private _orientation: Orientation; + private _size: number; + private _orthogonalSize: number; + private contentSize: number; + private _proportions: number[]; + private proportionalLayout: boolean; - private _onDidSashEnd = new Emitter() - public onDidSashEnd = this._onDidSashEnd.event + private _onDidSashEnd = new Emitter(); + public onDidSashEnd = this._onDidSashEnd.event; get size() { - return this._size + return this._size; } get orthogonalSize() { - return this._orthogonalSize + return this._orthogonalSize; } public get length() { - return this.views.length + return this.views.length; } public get proportions() { - return this._proportions ? [...this._proportions] : undefined + return this._proportions ? [...this._proportions] : undefined; } get orientation() { - return this._orientation + return this._orientation; } get minimumSize(): number { - return this.views.reduce((r, item) => r + item.view.minimumSize, 0) + return this.views.reduce((r, item) => r + item.view.minimumSize, 0); } get maximumSize(): number { return this.length === 0 ? Number.POSITIVE_INFINITY - : this.views.reduce((r, item) => r + item.view.maximumSize, 0) + : this.views.reduce((r, item) => r + item.view.maximumSize, 0); } constructor( private readonly container: HTMLElement, options: ISplitViewOptions ) { - this._orientation = options.orientation - this.element = this.createContainer() + this._orientation = options.orientation; + this.element = this.createContainer(); this.proportionalLayout = options.proportionalLayout === undefined ? true - : !!options.proportionalLayout + : !!options.proportionalLayout; - this.viewContainer = this.createViewContainer() - this.sashContainer = this.createSashContainer() + this.viewContainer = this.createViewContainer(); + this.sashContainer = this.createSashContainer(); - this.element.appendChild(this.sashContainer) - this.element.appendChild(this.viewContainer) + this.element.appendChild(this.sashContainer); + this.element.appendChild(this.viewContainer); - this.container.appendChild(this.element) + this.container.appendChild(this.element); // We have an existing set of view, add them now if (options.descriptor) { - this._size = options.descriptor.size + this._size = options.descriptor.size; options.descriptor.views.forEach((viewDescriptor, index) => { - const sizing = viewDescriptor.size + const sizing = viewDescriptor.size; - const view = viewDescriptor.view + const view = viewDescriptor.view; this.addView( view, sizing, index, true // true skip layout - ) - }) + ); + }); // Initialize content size and proportions for first layout - this.contentSize = this.views.reduce((r, i) => r + i.size, 0) - this.saveProportions() + this.contentSize = this.views.reduce((r, i) => r + i.size, 0); + this.saveProportions(); } } getViewSize(index: number): number { if (index < 0 || index >= this.views.length) { - return -1 + return -1; } - return this.views[index].size + return this.views[index].size; } resizeView(index: number, size: number): void { if (index < 0 || index >= this.views.length) { - return + return; } const indexes = // range(this.views.length) - this.views.map((_, i) => i).filter((i) => i !== index) + this.views.map((_, i) => i).filter((i) => i !== index); // const lowPriorityIndexes = [ // ...indexes.filter((i) => this.views[i].priority === LayoutPriority.Low), // index, @@ -188,48 +188,48 @@ export class SplitView { // (i) => this.views[i].priority === LayoutPriority.High // ); - const item = this.views[index] - size = Math.round(size) + const item = this.views[index]; + size = Math.round(size); size = clamp( size, item.view.minimumSize, Math.min(item.view.maximumSize, this._size) - ) + ); - item.size = size + item.size = size; this .relayout // lowPriorityIndexes, highPriorityIndexes - () + (); } public getViews() { - return this.views.map((x) => x.view) + return this.views.map((x) => x.view); } private onDidChange(item: IViewItem, size: number | undefined): void { - const index = this.views.indexOf(item) + const index = this.views.indexOf(item); if (index < 0 || index >= this.views.length) { - return + return; } - size = typeof size === 'number' ? size : item.size - size = clamp(size, item.view.minimumSize, item.view.maximumSize) + size = typeof size === 'number' ? size : item.size; + size = clamp(size, item.view.minimumSize, item.view.maximumSize); - item.size = size + item.size = size; - const contentSize = this.views.reduce((r, i) => r + i.size, 0) + const contentSize = this.views.reduce((r, i) => r + i.size, 0); this.resize( this.views.length - 1, this._size - contentSize, undefined, [index] - ) - this.distributeEmptySpace() - this.layoutViews() - this.saveProportions() + ); + this.distributeEmptySpace(); + this.layoutViews(); + this.saveProportions(); } public addView( @@ -238,23 +238,23 @@ export class SplitView { index: number = this.views.length, skipLayout?: boolean ) { - const container = document.createElement('div') - container.className = 'view' + const container = document.createElement('div'); + container.className = 'view'; - container.appendChild(view.element) + container.appendChild(view.element); const disposable = view.onDidChange((size) => this.onDidChange(viewItem, size) - ) + ); - let viewSize: number + let viewSize: number; if (typeof size === 'number') { - viewSize = size + viewSize = size; } else if (size.type === 'split') { - viewSize = this.getViewSize(size.index) / 2 + viewSize = this.getViewSize(size.index) / 2; } else { - viewSize = view.minimumSize + viewSize = view.minimumSize; } const viewItem: IViewItem = { @@ -262,86 +262,86 @@ export class SplitView { size: viewSize, container, dispose: () => { - disposable?.dispose() - this.viewContainer.removeChild(container) + disposable?.dispose(); + this.viewContainer.removeChild(container); }, - } + }; if (index === this.views.length) { - this.viewContainer.appendChild(container) + this.viewContainer.appendChild(container); } else { this.viewContainer.insertBefore( container, this.viewContainer.children.item(index) - ) + ); } - this.views.splice(index, 0, viewItem) + this.views.splice(index, 0, viewItem); if (this.views.length > 1) { //add sash - const sash = document.createElement('div') - sash.className = 'sash' + const sash = document.createElement('div'); + sash.className = 'sash'; const cb = (event: MouseEvent) => { let start = this._orientation === Orientation.HORIZONTAL ? event.clientX - : event.clientY - const sizes = this.views.map((x) => x.size) + : event.clientY; + const sizes = this.views.map((x) => x.size); const index = firstIndex( this.sashes, (s) => s.container === sash - ) + ); const mousemove = (event: MouseEvent) => { const current = this._orientation === Orientation.HORIZONTAL ? event.clientX - : event.clientY - const delta = current - start + : event.clientY; + const delta = current - start; this.resize( index, delta, sizes // sizes - ) - this.distributeEmptySpace() - this.layoutViews() - } + ); + this.distributeEmptySpace(); + this.layoutViews(); + }; const end = () => { - this.saveProportions() + this.saveProportions(); - document.removeEventListener('mousemove', mousemove) - document.removeEventListener('mouseup', end) - document.removeEventListener('mouseend', end) + document.removeEventListener('mousemove', mousemove); + document.removeEventListener('mouseup', end); + document.removeEventListener('mouseend', end); - this._onDidSashEnd.fire(undefined) - } + this._onDidSashEnd.fire(undefined); + }; - document.addEventListener('mousemove', mousemove) - document.addEventListener('mouseup', end) - document.addEventListener('mouseend', end) - } + document.addEventListener('mousemove', mousemove); + document.addEventListener('mouseup', end); + document.addEventListener('mouseend', end); + }; - sash.addEventListener('mousedown', cb) + sash.addEventListener('mousedown', cb); const disposable = () => { - sash.removeEventListener('mousedown', cb) - this.sashContainer.removeChild(sash) - } + sash.removeEventListener('mousedown', cb); + this.sashContainer.removeChild(sash); + }; - const sashItem: ISashItem = { container: sash, disposable } + const sashItem: ISashItem = { container: sash, disposable }; - this.sashContainer.appendChild(sash) - this.sashes.push(sashItem) + this.sashContainer.appendChild(sash); + this.sashes.push(sashItem); } if (!skipLayout) { - this.relayout([index]) + this.relayout([index]); } if ( @@ -349,29 +349,29 @@ export class SplitView { typeof size !== 'number' && size.type === 'distribute' ) { - this.distributeViewSizes() + this.distributeViewSizes(); } } distributeViewSizes(): void { - const flexibleViewItems: IViewItem[] = [] - let flexibleSize = 0 + const flexibleViewItems: IViewItem[] = []; + let flexibleSize = 0; for (const item of this.views) { if (item.view.maximumSize - item.view.minimumSize > 0) { - flexibleViewItems.push(item) - flexibleSize += item.size + flexibleViewItems.push(item); + flexibleSize += item.size; } } - const size = Math.floor(flexibleSize / flexibleViewItems.length) + const size = Math.floor(flexibleSize / flexibleViewItems.length); for (const item of flexibleViewItems) { item.size = clamp( size, item.view.minimumSize, item.view.maximumSize - ) + ); } // const indexes = range(this.viewItems.length); @@ -385,65 +385,65 @@ export class SplitView { this .relayout // lowPriorityIndexes, highPriorityIndexes - () + (); } public removeView(index: number, sizing?: Sizing): IView { // Remove view - const viewItem = this.views.splice(index, 1)[0] - viewItem.dispose() + const viewItem = this.views.splice(index, 1)[0]; + viewItem.dispose(); // Remove sash if (this.views.length >= 1) { - const sashIndex = Math.max(index - 1, 0) - const sashItem = this.sashes.splice(sashIndex, 1)[0] - sashItem.disposable() + const sashIndex = Math.max(index - 1, 0); + const sashItem = this.sashes.splice(sashIndex, 1)[0]; + sashItem.disposable(); } - this.relayout() - this.distributeEmptySpace() + this.relayout(); + this.distributeEmptySpace(); if (sizing && sizing.type === 'distribute') { - this.distributeViewSizes() + this.distributeViewSizes(); } - return viewItem.view + return viewItem.view; } public moveView(from: number, to: number) { - const sizing = this.getViewSize(from) - const view = this.removeView(from) - this.addView(view, sizing, to) + const sizing = this.getViewSize(from); + const view = this.removeView(from); + this.addView(view, sizing, to); } set orientation(orientation: Orientation) { if (orientation === this._orientation) { - return + return; } - this._orientation = orientation + this._orientation = orientation; const classname = - orientation === Orientation.HORIZONTAL ? 'horizontal' : 'vertical' + orientation === Orientation.HORIZONTAL ? 'horizontal' : 'vertical'; - removeClasses(this.viewContainer, 'vertical', 'horizontal') - removeClasses(this.sashContainer, 'vertical', 'horizontal') - addClasses(this.viewContainer, classname) - addClasses(this.sashContainer, classname) + removeClasses(this.viewContainer, 'vertical', 'horizontal'); + removeClasses(this.sashContainer, 'vertical', 'horizontal'); + addClasses(this.viewContainer, classname); + addClasses(this.sashContainer, classname); } public layout(size: number, orthogonalSize: number) { - const previousSize = Math.max(this.size, this.contentSize) - this._size = size - this._orthogonalSize = orthogonalSize + const previousSize = Math.max(this.size, this.contentSize); + this._size = size; + this._orthogonalSize = orthogonalSize; if (!this.proportions) { - const indexes = range(this.views.length) + const indexes = range(this.views.length); const lowPriorityIndexes = indexes.filter( (i) => this.views[i].view.priority === LayoutPriority.Low - ) + ); const highPriorityIndexes = indexes.filter( (i) => this.views[i].view.priority === LayoutPriority.High - ) + ); this.resize( this.views.length - 1, @@ -451,27 +451,27 @@ export class SplitView { undefined, lowPriorityIndexes, highPriorityIndexes - ) + ); } else { for (let i = 0; i < this.views.length; i++) { - const item = this.views[i] + const item = this.views[i]; item.size = clampView( item.view, Math.round(this._proportions[i] * size) - ) + ); } } - this.distributeEmptySpace() - this.layoutViews() + this.distributeEmptySpace(); + this.layoutViews(); } private relayout( lowPriorityIndexes?: number[], highPriorityIndexes?: number[] ) { - const contentSize = this.views.reduce((r, i) => r + i.size, 0) + const contentSize = this.views.reduce((r, i) => r + i.size, 0); this.resize( this.views.length - 1, @@ -479,133 +479,135 @@ export class SplitView { undefined, lowPriorityIndexes, highPriorityIndexes - ) - this.layoutViews() - this.saveProportions() + ); + this.layoutViews(); + this.saveProportions(); } private distributeEmptySpace() { - let contentSize = this.views.reduce((r, i) => r + i.size, 0) - let emptyDelta = this._size - contentSize + let contentSize = this.views.reduce((r, i) => r + i.size, 0); + let emptyDelta = this._size - contentSize; for (let i = this.views.length - 1; emptyDelta !== 0 && i >= 0; i--) { - const item = this.views[i] - const size = clampView(item.view, item.size + emptyDelta) - const viewDelta = size - item.size + const item = this.views[i]; + const size = clampView(item.view, item.size + emptyDelta); + const viewDelta = size - item.size; - emptyDelta -= viewDelta - item.size = size + emptyDelta -= viewDelta; + item.size = size; } } private saveProportions(): void { if (this.proportionalLayout && this.contentSize > 0) { - this._proportions = this.views.map((i) => i.size / this.contentSize) + this._proportions = this.views.map( + (i) => i.size / this.contentSize + ); } } private layoutViews() { - this.contentSize = this.views.reduce((r, i) => r + i.size, 0) - let sum = 0 - let x: number[] = [] + this.contentSize = this.views.reduce((r, i) => r + i.size, 0); + let sum = 0; + let x: number[] = []; - this.updateSashEnablement() + this.updateSashEnablement(); for (let i = 0; i < this.views.length - 1; i++) { - sum += this.views[i].size - x.push(sum) + sum += this.views[i].size; + x.push(sum); - const offset = Math.min(Math.max(0, sum - 2), this.size - 4) + const offset = Math.min(Math.max(0, sum - 2), this.size - 4); if (this._orientation === Orientation.HORIZONTAL) { - this.sashes[i].container.style.left = `${offset}px` - this.sashes[i].container.style.top = `0px` + this.sashes[i].container.style.left = `${offset}px`; + this.sashes[i].container.style.top = `0px`; } if (this._orientation === Orientation.VERTICAL) { - this.sashes[i].container.style.left = `0px` - this.sashes[i].container.style.top = `${offset}px` + this.sashes[i].container.style.left = `0px`; + this.sashes[i].container.style.top = `${offset}px`; } } this.views.forEach((view, i) => { if (this._orientation === Orientation.HORIZONTAL) { - view.container.style.width = `${view.size}px` - view.container.style.left = i == 0 ? '0px' : `${x[i - 1]}px` - view.container.style.top = '' - view.container.style.height = '' + view.container.style.width = `${view.size}px`; + view.container.style.left = i == 0 ? '0px' : `${x[i - 1]}px`; + view.container.style.top = ''; + view.container.style.height = ''; } if (this._orientation === Orientation.VERTICAL) { - view.container.style.height = `${view.size}px` - view.container.style.top = i == 0 ? '0px' : `${x[i - 1]}px` - view.container.style.width = '' - view.container.style.left = '' + view.container.style.height = `${view.size}px`; + view.container.style.top = i == 0 ? '0px' : `${x[i - 1]}px`; + view.container.style.width = ''; + view.container.style.left = ''; } - view.view.layout(view.size, this._orthogonalSize) - }) + view.view.layout(view.size, this._orthogonalSize); + }); } private findFirstSnapIndex(indexes: number[]): number | undefined { // visible views first for (const index of indexes) { - const viewItem = this.views[index] + const viewItem = this.views[index]; // if (!viewItem.visible) { // continue; // } if (viewItem.view.snapSize) { - return index + return index; } } - return undefined + return undefined; } private updateSashEnablement(): void { - let previous = false + let previous = false; const collapsesDown = this.views.map( (i) => (previous = i.size - i.view.minimumSize > 0 || previous) - ) + ); - previous = false + previous = false; const expandsDown = this.views.map( (i) => (previous = i.view.maximumSize - i.size > 0 || previous) - ) + ); - const reverseViews = [...this.views].reverse() - previous = false + const reverseViews = [...this.views].reverse(); + previous = false; const collapsesUp = reverseViews .map( (i) => (previous = i.size - i.view.minimumSize > 0 || previous) ) - .reverse() + .reverse(); - previous = false + previous = false; const expandsUp = reverseViews .map( (i) => (previous = i.view.maximumSize - i.size > 0 || previous) ) - .reverse() + .reverse(); - let position = 0 + let position = 0; for (let index = 0; index < this.sashes.length; index++) { - const sash = this.sashes[index] - const viewItem = this.views[index] - position += viewItem.size + const sash = this.sashes[index]; + const viewItem = this.views[index]; + position += viewItem.size; - const min = !(collapsesDown[index] && expandsUp[index + 1]) - const max = !(expandsDown[index] && collapsesUp[index + 1]) + const min = !(collapsesDown[index] && expandsUp[index + 1]); + const max = !(expandsDown[index] && collapsesUp[index + 1]); if (min && max) { - const upIndexes = range(index, -1) - const downIndexes = range(index + 1, this.views.length) - const snapBeforeIndex = this.findFirstSnapIndex(upIndexes) - const snapAfterIndex = this.findFirstSnapIndex(downIndexes) + const upIndexes = range(index, -1); + const downIndexes = range(index + 1, this.views.length); + const snapBeforeIndex = this.findFirstSnapIndex(upIndexes); + const snapAfterIndex = this.findFirstSnapIndex(downIndexes); - const snappedBefore = false + const snappedBefore = false; // typeof snapBeforeIndex === "number" && // !this.views[snapBeforeIndex].visible; - const snappedAfter = false + const snappedAfter = false; // typeof snapAfterIndex === "number" && // !this.views[snapAfterIndex].visible; @@ -616,7 +618,7 @@ export class SplitView { position > 0 // || this.startSnappingEnabled) ) { - this.updateSash(sash, SashState.MINIMUM) + this.updateSash(sash, SashState.MINIMUM); // sash.state = SashState.Minimum; } else if ( snappedAfter && @@ -626,30 +628,30 @@ export class SplitView { // || this.endSnappingEnabled) ) { // sash.state = SashState.Maximum; - this.updateSash(sash, SashState.MAXIMUM) + this.updateSash(sash, SashState.MAXIMUM); } else { // sash.state = SashState.Disabled; - this.updateSash(sash, SashState.DISABLED) + this.updateSash(sash, SashState.DISABLED); } } else if (min && !max) { // sash.state = SashState.Minimum; - this.updateSash(sash, SashState.MINIMUM) + this.updateSash(sash, SashState.MINIMUM); } else if (!min && max) { // sash.state = SashState.Maximum; - this.updateSash(sash, SashState.MAXIMUM) + this.updateSash(sash, SashState.MAXIMUM); } else { // sash.state = SashState.Enabled; - this.updateSash(sash, SashState.ENABLED) + this.updateSash(sash, SashState.ENABLED); } } } private updateSash(sash: ISashItem, state: SashState) { - toggleClass(sash.container, 'disabled', state === SashState.DISABLED) - toggleClass(sash.container, 'enabled', state === SashState.ENABLED) - toggleClass(sash.container, 'maximum', state === SashState.MAXIMUM) - toggleClass(sash.container, 'minimum', state === SashState.MINIMUM) + toggleClass(sash.container, 'disabled', state === SashState.DISABLED); + toggleClass(sash.container, 'enabled', state === SashState.ENABLED); + toggleClass(sash.container, 'maximum', state === SashState.MAXIMUM); + toggleClass(sash.container, 'minimum', state === SashState.MINIMUM); } private resize = ( @@ -660,31 +662,31 @@ export class SplitView { highPriorityIndexes?: number[] ) => { if (index < 0 || index > this.views.length) { - return + return; } - const upIndexes = range(index, -1) - const downIndexes = range(index + 1, this.views.length) + const upIndexes = range(index, -1); + const downIndexes = range(index + 1, this.views.length); // if (highPriorityIndexes) { for (const index of highPriorityIndexes) { - pushToStart(upIndexes, index) - pushToStart(downIndexes, index) + pushToStart(upIndexes, index); + pushToStart(downIndexes, index); } } if (lowPriorityIndexes) { for (const index of lowPriorityIndexes) { - pushToEnd(upIndexes, index) - pushToEnd(downIndexes, index) + pushToEnd(upIndexes, index); + pushToEnd(downIndexes, index); } } // - const upItems = upIndexes.map((i) => this.views[i]) - const upSizes = upIndexes.map((i) => sizes[i]) + const upItems = upIndexes.map((i) => this.views[i]); + const upSizes = upIndexes.map((i) => sizes[i]); // - const downItems = downIndexes.map((i) => this.views[i]) - const downSizes = downIndexes.map((i) => sizes[i]) + const downItems = downIndexes.map((i) => this.views[i]); + const downSizes = downIndexes.map((i) => sizes[i]); // const minDeltaUp = upIndexes.reduce( (_, i) => @@ -694,11 +696,11 @@ export class SplitView { : this.views[i].view.minimumSize) - sizes[i], 0 - ) + ); const maxDeltaUp = upIndexes.reduce( (_, i) => _ + this.views[i].view.maximumSize - sizes[i], 0 - ) + ); // const maxDeltaDown = downIndexes.length === 0 @@ -711,73 +713,73 @@ export class SplitView { ? 0 : this.views[i].view.minimumSize), 0 - ) + ); const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce( (_, i) => _ + sizes[i] - this.views[i].view.maximumSize, 0 - ) + ); // - const minDelta = Math.max(minDeltaUp, minDeltaDown) - const maxDelta = Math.min(maxDeltaDown, maxDeltaUp) + const minDelta = Math.max(minDeltaUp, minDeltaDown); + const maxDelta = Math.min(maxDeltaDown, maxDeltaUp); // - const tentativeDelta = clamp(delta, minDelta, maxDelta) - let actualDelta = 0 + const tentativeDelta = clamp(delta, minDelta, maxDelta); + let actualDelta = 0; // - let deltaUp = tentativeDelta + let deltaUp = tentativeDelta; for (let i = 0; i < upItems.length; i++) { - const item = upItems[i] - const size = clampView(item.view, upSizes[i] + deltaUp) - const viewDelta = size - upSizes[i] + const item = upItems[i]; + const size = clampView(item.view, upSizes[i] + deltaUp); + const viewDelta = size - upSizes[i]; - actualDelta += viewDelta - deltaUp -= viewDelta - item.size = size + actualDelta += viewDelta; + deltaUp -= viewDelta; + item.size = size; } // - let deltaDown = actualDelta + let deltaDown = actualDelta; for (let i = 0; i < downItems.length; i++) { - const item = downItems[i] - const size = clampView(item.view, downSizes[i] - deltaDown) - const viewDelta = size - downSizes[i] + const item = downItems[i]; + const size = clampView(item.view, downSizes[i] - deltaDown); + const viewDelta = size - downSizes[i]; - deltaDown += viewDelta - item.size = size + deltaDown += viewDelta; + item.size = size; } // - } + }; private createViewContainer() { - const element = document.createElement('div') - element.className = 'view-container' - return element + const element = document.createElement('div'); + element.className = 'view-container'; + return element; } private createSashContainer() { - const element = document.createElement('div') - element.className = 'sash-container' - return element + const element = document.createElement('div'); + element.className = 'sash-container'; + return element; } private createContainer() { - const element = document.createElement('div') + const element = document.createElement('div'); const orientationClassname = this._orientation === Orientation.HORIZONTAL ? 'horizontal' - : 'vertical' - element.className = `split-view-container ${orientationClassname}` - return element + : 'vertical'; + element.className = `split-view-container ${orientationClassname}`; + return element; } public dispose() { - this.element.remove() + this.element.remove(); for (let i = 0; i < this.element.children.length; i++) { if (this.element.children.item[i] === this.element) { - this.element.removeChild(this.element) - break + this.element.removeChild(this.element); + break; } } } diff --git a/packages/splitview/src/types.ts b/packages/splitview/src/types.ts index ef1e4822d..24c9b9502 100644 --- a/packages/splitview/src/types.ts +++ b/packages/splitview/src/types.ts @@ -1,9 +1,9 @@ export interface Constructor { - new (): T + new (): T; } export interface FrameworkFactory { - createComponent: (id: string, component: any) => T + createComponent: (id: string, component: any) => T; } -export type FunctionOrValue = (() => T) | T +export type FunctionOrValue = (() => T) | T; diff --git a/scripts/build.js b/scripts/build.js index 6028ae760..2a1d12924 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -1,11 +1,11 @@ -const gulp = require('gulp') -const gulpClean = require('gulp-clean') -const gulpTypescript = require('gulp-typescript') -const merge = require('merge2') -const header = require('gulp-header') -const gulpSass = require('gulp-sass') -const concat = require('gulp-concat') -const sourcemaps = require('gulp-sourcemaps') +const gulp = require('gulp'); +const gulpClean = require('gulp-clean'); +const gulpTypescript = require('gulp-typescript'); +const merge = require('merge2'); +const header = require('gulp-header'); +const gulpSass = require('gulp-sass'); +const concat = require('gulp-concat'); +const sourcemaps = require('gulp-sourcemaps'); const headerTemplate = [ '/**', @@ -14,25 +14,25 @@ const headerTemplate = [ ' * @link <%= pkg.homepage %>', ' * @licence <%= pkg.licence %>', ' */\n', -].join('\n') +].join('\n'); const dtsHeaderTemplate = [ '// Type definitions for <%= pkg.name %> v <%= pkg.version %>', '// Project <%= pkg.homepage %>\n', -].join('\n') +].join('\n'); const build = (options) => { - const { tsconfig, package } = options + const { tsconfig, package } = options; gulp.task('clean', () => gulp.src('dist', { read: false, allowEmpty: true }).pipe(gulpClean()) - ) + ); gulp.task('esm', () => { - const ts = gulpTypescript.createProject(tsconfig) + const ts = gulpTypescript.createProject(tsconfig); const tsResult = gulp .src(['src/**/*.ts', 'src/**/*.tsx']) .pipe(sourcemaps.init()) - .pipe(ts()) + .pipe(ts()); return merge([ tsResult.dts .pipe(header(dtsHeaderTemplate, { pkg: package })) @@ -43,8 +43,8 @@ const build = (options) => { tsResult .pipe(sourcemaps.write('.', { includeContent: false })) .pipe(gulp.dest('./dist/esm')), - ]) - }) + ]); + }); gulp.task('sass', () => { return ( @@ -56,8 +56,8 @@ const build = (options) => { // ) .pipe(concat('styles.css')) .pipe(gulp.dest('./dist')) - ) - }) -} + ); + }); +}; -module.exports = { build } +module.exports = { build };