From ecbf0f7fc77893d690dc897b7a6e2860546a067f Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Mon, 25 Oct 2021 21:21:48 +0100 Subject: [PATCH] feat: improve layout events --- packages/dockview-demo/package-lock.json | 57 +++- packages/dockview-demo/package.json | 3 +- packages/dockview-demo/src/index.tsx | 10 +- .../src/layout-grid/application.tsx | 15 +- .../src/layout-grid/controlCenter.tsx | 15 -- .../dockview-demo/src/layout-grid/footer.tsx | 16 +- .../src/layout-grid/layoutGrid.tsx | 132 +++------- .../dockview/dockviewComponent.spec.ts | 113 +++++++- .../src/__tests__/gridview/gridview.spec.ts | 5 +- .../src/__tests__/groupview/groupview.spec.ts | 37 ++- packages/dockview/src/api/component.api.ts | 248 +++++++++--------- .../src/dockview/dockviewComponent.ts | 69 ++++- .../src/dockview/dockviewGroupPanel.ts | 4 +- .../src/gridview/baseComponentGridview.ts | 23 +- .../src/gridview/gridviewComponent.ts | 2 +- packages/dockview/src/groupview/groupview.ts | 72 ++--- packages/dockview/src/index.ts | 1 + 17 files changed, 479 insertions(+), 343 deletions(-) diff --git a/packages/dockview-demo/package-lock.json b/packages/dockview-demo/package-lock.json index a166babca..56bf55c0d 100644 --- a/packages/dockview-demo/package-lock.json +++ b/packages/dockview-demo/package-lock.json @@ -5,13 +5,13 @@ "requires": true, "packages": { "": { - "name": "dockview-demo", - "version": "0.0.23", + "version": "0.0.25", "license": "MIT", "dependencies": { - "dockview": "^0.0.23", + "dockview": "^0.0.25", "react": "^17.0.1", - "react-dom": "^17.0.1" + "react-dom": "^17.0.1", + "recoil": "^0.4.1" }, "devDependencies": { "@babel/core": "^7.13.10", @@ -9340,9 +9340,9 @@ } }, "node_modules/dockview": { - "version": "0.0.23", - "resolved": "https://registry.npmjs.org/dockview/-/dockview-0.0.23.tgz", - "integrity": "sha512-thuhpBS/loWdlDcF1B3I/+rg49I6phbEldyozx7H09/59++/r/caWIojxjkBh6vQA09dm9+tzUk4jXAcQmduFQ==", + "version": "0.0.25", + "resolved": "https://registry.npmjs.org/dockview/-/dockview-0.0.25.tgz", + "integrity": "sha512-XRUIh4OT33sTKVdAzZsh6JTyha6OeI/6Ew2eOrrp+I3UzSfruj3yf2nwrJA2iGlVmndBCc7amAV74zhco17H+g==", "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" @@ -11374,6 +11374,11 @@ "node": ">=6" } }, + "node_modules/hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE=" + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -15895,6 +15900,25 @@ "node": ">=8.10.0" } }, + "node_modules/recoil": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.4.1.tgz", + "integrity": "sha512-vp6KPwlHOjJ4bJofmdDchmgI9ilMTCoUisK8/WYLl8dThH7e7KmtZttiLgvDb2Em99dUfTEsk8vT8L1nUMgqXQ==", + "dependencies": { + "hamt_plus": "1.0.2" + }, + "peerDependencies": { + "react": ">=16.13.1" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/recursive-readdir": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", @@ -27250,9 +27274,9 @@ } }, "dockview": { - "version": "0.0.23", - "resolved": "https://registry.npmjs.org/dockview/-/dockview-0.0.23.tgz", - "integrity": "sha512-thuhpBS/loWdlDcF1B3I/+rg49I6phbEldyozx7H09/59++/r/caWIojxjkBh6vQA09dm9+tzUk4jXAcQmduFQ==", + "version": "0.0.25", + "resolved": "https://registry.npmjs.org/dockview/-/dockview-0.0.25.tgz", + "integrity": "sha512-XRUIh4OT33sTKVdAzZsh6JTyha6OeI/6Ew2eOrrp+I3UzSfruj3yf2nwrJA2iGlVmndBCc7amAV74zhco17H+g==", "requires": {} }, "doctrine": { @@ -28898,6 +28922,11 @@ "pify": "^4.0.1" } }, + "hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE=" + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -32357,6 +32386,14 @@ "picomatch": "^2.2.1" } }, + "recoil": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.4.1.tgz", + "integrity": "sha512-vp6KPwlHOjJ4bJofmdDchmgI9ilMTCoUisK8/WYLl8dThH7e7KmtZttiLgvDb2Em99dUfTEsk8vT8L1nUMgqXQ==", + "requires": { + "hamt_plus": "1.0.2" + } + }, "recursive-readdir": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", diff --git a/packages/dockview-demo/package.json b/packages/dockview-demo/package.json index ecf2653bb..1b15510a7 100644 --- a/packages/dockview-demo/package.json +++ b/packages/dockview-demo/package.json @@ -16,7 +16,8 @@ "dependencies": { "dockview": "^0.0.25", "react": "^17.0.1", - "react-dom": "^17.0.1" + "react-dom": "^17.0.1", + "recoil": "^0.4.1" }, "devDependencies": { "@babel/core": "^7.13.10", diff --git a/packages/dockview-demo/src/index.tsx b/packages/dockview-demo/src/index.tsx index 3ea75ca4d..c3db9de69 100644 --- a/packages/dockview-demo/src/index.tsx +++ b/packages/dockview-demo/src/index.tsx @@ -1,8 +1,14 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import './index.scss'; import { Application } from './layout-grid/application'; +import { RecoilRoot } from 'recoil'; +import './index.scss'; document.getElementById('app').classList.add('dockview-theme-dark'); -ReactDOM.render(, document.getElementById('app')); +ReactDOM.render( + + + , + document.getElementById('app') +); diff --git a/packages/dockview-demo/src/layout-grid/application.tsx b/packages/dockview-demo/src/layout-grid/application.tsx index 748396699..0c275ab32 100644 --- a/packages/dockview-demo/src/layout-grid/application.tsx +++ b/packages/dockview-demo/src/layout-grid/application.tsx @@ -50,13 +50,19 @@ export const Application = () => { // event.api.fromJSON(require('./application.layout.json')); + let success = false; const state = localStorage.getItem('dockview-layout'); if (state) { - console.log('loading from save'); - const jsonstate = JSON.parse(state) as SerializedGridview; - event.api.fromJSON(jsonstate); - } else { + try { + event.api.fromJSON(JSON.parse(state)); + success = true; + } catch (err) { + console.error('failed to load layout', err); + } + } + + if (!success) { event.api.addPanel({ id: 'i', component: 'activitybar', @@ -110,7 +116,6 @@ export const Application = () => { return ( { const dragRef = React.useRef(); - // React.useEffect(() => { - // const api = registry.get('dockview'); - // const target = api.createDragTarget( - // { element: dragRef.current, content: 'drag me' }, - // () => ({ - // id: 'yellow', - // component: 'test_component', - // }) - // ); - - // return () => { - // target.dispose(); - // }; - // }, []); - const onDragStart = (event: React.DragEvent) => { event.dataTransfer.setData('text/plain', 'Panel2'); }; diff --git a/packages/dockview-demo/src/layout-grid/footer.tsx b/packages/dockview-demo/src/layout-grid/footer.tsx index bc0c5926e..1266373a5 100644 --- a/packages/dockview-demo/src/layout-grid/footer.tsx +++ b/packages/dockview-demo/src/layout-grid/footer.tsx @@ -1,13 +1,27 @@ import * as React from 'react'; import { IGridviewPanelProps } from 'dockview'; +import { atom, useRecoilValue } from 'recoil'; + +export const selectedPanelAtom = atom({ + key: 'selectedPanelAtom', + default: '', +}); export const Footer = (props: IGridviewPanelProps) => { + const selectedPanel = useRecoilValue(selectedPanelAtom); + return (
+ > + + {selectedPanel} + ); }; diff --git a/packages/dockview-demo/src/layout-grid/layoutGrid.tsx b/packages/dockview-demo/src/layout-grid/layoutGrid.tsx index e26deaaf0..c010fbddd 100644 --- a/packages/dockview-demo/src/layout-grid/layoutGrid.tsx +++ b/packages/dockview-demo/src/layout-grid/layoutGrid.tsx @@ -22,6 +22,8 @@ import './layoutGrid.scss'; import { WelcomePanel } from '../panels/welcome/welcome'; import { SplitviewPanel } from '../panels/splitview/splitview'; import { GridviewDemoPanel } from '../panels/gridview/gridview'; +import { useRecoilCallback } from 'recoil'; +import { selectedPanelAtom } from './footer'; const Test = (props: IDockviewPanelProps) => { const [counter, setCounter] = React.useState(0); @@ -102,23 +104,6 @@ const components: PanelCollection = { } }; - React.useEffect(() => { - const compDis = new CompositeDisposable( - props.api.onDidDimensionsChange((event) => { - // _api.current?.layout(event.width, event.height); - }), - _api.current.onGridEvent((event) => { - if (event.kind === GroupChangeKind.LAYOUT_CONFIG_UPDATED) { - props.api.setState('layout', _api.current.toJSON()); - } - }) - ); - - return () => { - compDis.dispose(); - }; - }, []); - return (
{ })(); export const TestGrid = (props: IGridviewPanelProps) => { - const _api = React.useRef(); + const [api, setApi] = React.useState(); const registry = useLayoutRegistry(); const onReady = (event: DockviewReadyEvent) => { const api = event.api; - _api.current = event.api; + setApi(api); registry.register('dockview', api); + }; - // api.addDndHandle('text/plain', (ev) => { - // const { event } = ev; - - // return { - // id: 'yellow', - // component: 'test_component', - // }; - // }); - - // api.addDndHandle('Files', (ev) => { - // const { event } = ev; + const setSelectedPanel = useRecoilCallback( + ({ set }) => (value: string) => set(selectedPanelAtom, value), + [] + ); - // ev.event.event.preventDefault(); + React.useEffect(() => { + if (!api) { + return () => { + // + }; + } + props.api.setConstraints({ + minimumWidth: () => api.minimumWidth, + minimumHeight: () => api.minimumHeight, + }); - // return { - // id: Date.now().toString(), - // title: event.event.dataTransfer.files[0].name, - // component: 'test_component', - // }; - // }); + const disposable = new CompositeDisposable( + api.onDidLayoutChange(() => { + const state = api.toJSON(); + localStorage.setItem('dockview', JSON.stringify(state)); + }), + api.onGridEvent((e) => { + console.log(e); + if (e.kind === GroupChangeKind.PANEL_ACTIVE) { + setSelectedPanel(e.panel?.id || ''); + } + }) + ); const state = localStorage.getItem('dockview'); + let success = false; if (state) { - api.fromJSON(JSON.parse(state)); - } else { + try { + api.fromJSON(JSON.parse(state)); + success = true; + } catch (err) { + console.error('failed to load layout', err); + } + } + if (!success) { api.addPanel({ component: 'welcome', id: 'welcome', title: 'Welcome', }); - - // event.api.deserialize(require('./layoutGrid.layout.json')); - return; - - api.addPanel({ - component: 'test_component', - id: nextGuid(), - title: 'Item 1', - params: { text: 'how low?' }, - }); - api.addPanel({ - component: 'test_component', - id: 'item2', - title: 'Item 2', - }); - api.addPanel({ - component: 'split_panel', - id: nextGuid(), - title: 'Item 3 with a long title', - }); - api.addPanel({ - component: 'test_component', - id: nextGuid(), - title: 'Item 3', - position: { direction: 'below', referencePanel: 'item2' }, - suppressClosable: true, - }); } - // api.addPanel({ - // component: 'test', - // id: 'test', - // title: 'Test', - // }); - }; - - React.useEffect(() => { - props.api.setConstraints({ - minimumWidth: () => _api.current.minimumWidth, - minimumHeight: () => _api.current.minimumHeight, - }); - - const disposable = new CompositeDisposable( - _api.current.onDidLayoutChange(() => { - const state = _api.current.toJSON(); - localStorage.setItem('dockview', JSON.stringify(state)); - }), - props.api.onDidDimensionsChange((event) => { - const { width, height } = event; - // _api.current.layout(width, height); - }) - ); - return () => { disposable.dispose(); }; - }, []); + }, [api]); const [coord, setCoord] = React.useState<{ x: number; diff --git a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts index a7558c5a5..44418beeb 100644 --- a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts @@ -1,5 +1,4 @@ import { DockviewComponent } from '../../dockview/dockviewComponent'; - import { GroupPanelPartInitParameters, IContentRenderer, @@ -9,7 +8,10 @@ import { Orientation } from '../../splitview/core/splitview'; import { ReactPanelDeserialzier } from '../../react/deserializer'; import { Position } from '../../dnd/droptarget'; import { GroupviewPanel } from '../../groupview/groupviewPanel'; -import { IGroupPanel } from '../../groupview/groupPanel'; +import { + GroupChangeEvent, + GroupChangeKind, +} from '../../gridview/baseComponentGridview'; class PanelContentPartTest implements IContentRenderer { element: HTMLElement = document.createElement('div'); @@ -618,4 +620,111 @@ describe('dockviewComponent', () => { disposable.dispose(); }); + + test('events flow', () => { + dockview.layout(1000, 1000); + + let events: GroupChangeEvent[] = []; + const disposable = dockview.onGridEvent((e) => events.push(e)); + + const panel1 = dockview.addPanel({ + id: 'panel1', + component: 'default', + }); + expect(events).toEqual([ + { kind: GroupChangeKind.ADD_GROUP }, + { kind: GroupChangeKind.GROUP_ACTIVE }, + { kind: GroupChangeKind.ADD_PANEL, panel: panel1 }, + { kind: GroupChangeKind.PANEL_ACTIVE, panel: panel1 }, + ]); + + events = []; + const panel2 = dockview.addPanel({ + id: 'panel2', + component: 'default', + }); + expect(events).toEqual([ + { kind: GroupChangeKind.ADD_PANEL, panel: panel2 }, + { kind: GroupChangeKind.PANEL_ACTIVE, panel: panel2 }, + ]); + + events = []; + const panel3 = dockview.addPanel({ + id: 'panel3', + component: 'default', + }); + expect(events).toEqual([ + { kind: GroupChangeKind.ADD_PANEL, panel: panel3 }, + { kind: GroupChangeKind.PANEL_ACTIVE, panel: panel3 }, + ]); + + events = []; + dockview.removePanel(panel1); + expect(events).toEqual([ + { kind: GroupChangeKind.REMOVE_PANEL, panel: panel1 }, + ]); + + events = []; + dockview.removePanel(panel3); + expect(events).toEqual([ + { kind: GroupChangeKind.REMOVE_PANEL, panel: panel3 }, + { kind: GroupChangeKind.PANEL_ACTIVE, panel: panel2 }, + ]); + + events = []; + const panel4 = dockview.addPanel({ + id: 'panel4', + component: 'default', + position: { referencePanel: panel2.id, direction: 'right' }, + }); + expect(events).toEqual([ + { kind: GroupChangeKind.ADD_GROUP }, + { kind: GroupChangeKind.GROUP_ACTIVE }, + { kind: GroupChangeKind.ADD_PANEL, panel: panel4 }, + { kind: GroupChangeKind.PANEL_ACTIVE, panel: panel4 }, + ]); + + events = []; + const panel5 = dockview.addPanel({ + id: 'panel5', + component: 'default', + position: { referencePanel: panel4.id, direction: 'within' }, + }); + expect(events).toEqual([ + { kind: GroupChangeKind.ADD_PANEL, panel: panel5 }, + { kind: GroupChangeKind.PANEL_ACTIVE, panel: panel5 }, + ]); + + events = []; + dockview.moveGroupOrPanel( + panel2.group!, + panel5.group!.id, + panel5.id, + Position.Center + ); + expect(events).toEqual([ + { kind: GroupChangeKind.REMOVE_PANEL, panel: panel5 }, + { kind: GroupChangeKind.PANEL_ACTIVE, panel: panel4 }, + { kind: GroupChangeKind.ADD_PANEL, panel: panel5 }, + { kind: GroupChangeKind.PANEL_ACTIVE, panel: panel5 }, + { kind: GroupChangeKind.GROUP_ACTIVE }, + { kind: GroupChangeKind.PANEL_ACTIVE, panel: panel5 }, + ]); + + events = []; + dockview.moveGroupOrPanel( + panel2.group!, + panel4.group!.id, + panel4.id, + Position.Center + ); + expect(events).toEqual([ + { kind: GroupChangeKind.REMOVE_PANEL, panel: panel4 }, + { kind: GroupChangeKind.REMOVE_GROUP }, + { kind: GroupChangeKind.ADD_PANEL, panel: panel4 }, + { kind: GroupChangeKind.PANEL_ACTIVE, panel: panel4 }, + ]); + + disposable.dispose(); + }); }); diff --git a/packages/dockview/src/__tests__/gridview/gridview.spec.ts b/packages/dockview/src/__tests__/gridview/gridview.spec.ts index bcc5e7231..01f3ba99d 100644 --- a/packages/dockview/src/__tests__/gridview/gridview.spec.ts +++ b/packages/dockview/src/__tests__/gridview/gridview.spec.ts @@ -1,6 +1,9 @@ +import { + GroupChangeEvent, + GroupChangeKind, +} from '../../gridview/baseComponentGridview'; import { GridviewComponent } from '../../gridview/gridviewComponent'; import { GridviewPanel } from '../../gridview/gridviewPanel'; -import { GroupChangeEvent, GroupChangeKind } from '../../groupview/groupview'; import { IFrameworkPart } from '../../panel/types'; import { Orientation } from '../../splitview/core/splitview'; diff --git a/packages/dockview/src/__tests__/groupview/groupview.spec.ts b/packages/dockview/src/__tests__/groupview/groupview.spec.ts index ebe94b389..4285154b4 100644 --- a/packages/dockview/src/__tests__/groupview/groupview.spec.ts +++ b/packages/dockview/src/__tests__/groupview/groupview.spec.ts @@ -13,7 +13,7 @@ import { } from '../../groupview/types'; import { PanelUpdateEvent } from '../../panel/types'; import { GroupviewPanel } from '../../groupview/groupviewPanel'; -import { GroupChangeKind, GroupOptions } from '../../groupview/groupview'; +import { GroupChangeKind2, GroupOptions } from '../../groupview/groupview'; import { DockviewPanelApi } from '../../api/groupPanelApi'; import { DefaultGroupPanelView, @@ -259,7 +259,7 @@ describe('groupview', () => { }); const events: Array<{ - kind: GroupChangeKind; + kind: GroupChangeKind2; }> = []; const disposable = groupview2.model.onDidGroupChange((e) => { events.push(e); @@ -269,19 +269,19 @@ describe('groupview', () => { expect(events).toEqual([ { - kind: GroupChangeKind.ADD_PANEL, + kind: GroupChangeKind2.ADD_PANEL, panel: panel1, }, { - kind: GroupChangeKind.ADD_PANEL, + kind: GroupChangeKind2.ADD_PANEL, panel: panel2, }, { - kind: GroupChangeKind.ADD_PANEL, + kind: GroupChangeKind2.ADD_PANEL, panel: panel3, }, { - kind: GroupChangeKind.PANEL_ACTIVE, + kind: GroupChangeKind2.PANEL_ACTIVE, panel: panel2, }, ]); @@ -291,7 +291,7 @@ describe('groupview', () => { test('panel events flow', () => { let events: Array<{ - kind: GroupChangeKind; + kind: GroupChangeKind2; }> = []; const disposable = groupview.model.onDidGroupChange((e) => { events.push(e); @@ -306,11 +306,11 @@ describe('groupview', () => { groupview.model.openPanel(panel1); expect(events).toEqual([ { - kind: GroupChangeKind.ADD_PANEL, + kind: GroupChangeKind2.ADD_PANEL, panel: panel1, }, { - kind: GroupChangeKind.PANEL_ACTIVE, + kind: GroupChangeKind2.PANEL_ACTIVE, panel: panel1, }, ]); @@ -319,11 +319,11 @@ describe('groupview', () => { groupview.model.openPanel(panel2); expect(events).toEqual([ { - kind: GroupChangeKind.ADD_PANEL, + kind: GroupChangeKind2.ADD_PANEL, panel: panel2, }, { - kind: GroupChangeKind.PANEL_ACTIVE, + kind: GroupChangeKind2.PANEL_ACTIVE, panel: panel2, }, ]); @@ -332,11 +332,11 @@ describe('groupview', () => { groupview.model.openPanel(panel3); expect(events).toEqual([ { - kind: GroupChangeKind.ADD_PANEL, + kind: GroupChangeKind2.ADD_PANEL, panel: panel3, }, { - kind: GroupChangeKind.PANEL_ACTIVE, + kind: GroupChangeKind2.PANEL_ACTIVE, panel: panel3, }, ]); @@ -345,11 +345,11 @@ describe('groupview', () => { groupview.model.removePanel(panel3); expect(events).toEqual([ { - kind: GroupChangeKind.REMOVE_PANEL, + kind: GroupChangeKind2.REMOVE_PANEL, panel: panel3, }, { - kind: GroupChangeKind.PANEL_ACTIVE, + kind: GroupChangeKind2.PANEL_ACTIVE, panel: panel2, }, ]); @@ -358,7 +358,7 @@ describe('groupview', () => { groupview.model.removePanel(panel1); expect(events).toEqual([ { - kind: GroupChangeKind.REMOVE_PANEL, + kind: GroupChangeKind2.REMOVE_PANEL, panel: panel1, }, ]); @@ -367,12 +367,9 @@ describe('groupview', () => { groupview.model.removePanel(panel2); expect(events).toEqual([ { - kind: GroupChangeKind.REMOVE_PANEL, + kind: GroupChangeKind2.REMOVE_PANEL, panel: panel2, }, - { - kind: GroupChangeKind.PANEL_ACTIVE, - }, ]); events = []; diff --git a/packages/dockview/src/api/component.api.ts b/packages/dockview/src/api/component.api.ts index 4bc61fce8..851823418 100644 --- a/packages/dockview/src/api/component.api.ts +++ b/packages/dockview/src/api/component.api.ts @@ -7,7 +7,7 @@ import { AddPanelOptions, MovementOptions, } from '../dockview/options'; -import { Direction } from '../gridview/baseComponentGridview'; +import { Direction, GroupChangeEvent } from '../gridview/baseComponentGridview'; import { AddComponentOptions, IGridviewComponent, @@ -31,6 +31,8 @@ import { Orientation, Sizing } from '../splitview/core/splitview'; import { ISplitviewPanel } from '../splitview/splitviewPanel'; import { GroupviewPanel } from '../groupview/groupviewPanel'; import { Event } from '../events'; +import { GridviewPanel } from '../gridview/gridviewPanel'; +import { IDisposable } from '../lifecycle'; export interface CommonApi { readonly height: number; @@ -42,27 +44,27 @@ export interface CommonApi { } export class SplitviewApi implements CommonApi { - get minimumSize() { + get minimumSize(): number { return this.component.minimumSize; } - get maximumSize() { + get maximumSize(): number { return this.component.maximumSize; } - get height() { + get height(): number { return this.component.height; } - get width() { + get width(): number { return this.component.width; } - get length() { + get length(): number { return this.component.length; } - get onDidLayoutChange() { + get onDidLayoutChange(): Event { return this.component.onDidLayoutChange; } @@ -76,73 +78,73 @@ export class SplitviewApi implements CommonApi { this.component.updateOptions(options); } - removePanel(panel: ISplitviewPanel, sizing?: Sizing) { + removePanel(panel: ISplitviewPanel, sizing?: Sizing): void { this.component.removePanel(panel, sizing); } - setVisible(panel: ISplitviewPanel, isVisible: boolean) { - return this.component.setVisible(panel, isVisible); + setVisible(panel: ISplitviewPanel, isVisible: boolean): void { + this.component.setVisible(panel, isVisible); } getPanels(): ISplitviewPanel[] { return this.component.getPanels(); } - focus() { - return this.component.focus(); + focus(): void { + this.component.focus(); } - getPanel(id: string) { + getPanel(id: string): ISplitviewPanel | undefined { return this.component.getPanel(id); } - setActive(panel: ISplitviewPanel) { - return this.component.setActive(panel); + setActive(panel: ISplitviewPanel): void { + this.component.setActive(panel); } - layout(width: number, height: number) { + layout(width: number, height: number): void { return this.component.layout(width, height); } - addPanel(options: AddSplitviewComponentOptions) { - return this.component.addPanel(options); + addPanel(options: AddSplitviewComponentOptions): void { + this.component.addPanel(options); } - resizeToFit() { - return this.component.resizeToFit(); + resizeToFit(): void { + this.component.resizeToFit(); } - movePanel(from: number, to: number) { + movePanel(from: number, to: number): void { this.component.movePanel(from, to); } - fromJSON(data: SerializedSplitview, deferComponentLayout?: boolean) { - return this.component.fromJSON(data, deferComponentLayout); + fromJSON(data: SerializedSplitview, deferComponentLayout?: boolean): void { + this.component.fromJSON(data, deferComponentLayout); } - toJSON() { + toJSON(): SerializedSplitview { return this.component.toJSON(); } } export class PaneviewApi implements CommonApi { - get width() { + get width(): number { return this.component.width; } - get height() { + get height(): number { return this.component.height; } - get minimumSize() { + get minimumSize(): number { return this.component.minimumSize; } - get maximumSize() { + get maximumSize(): number { return this.component.maximumSize; } - get onDidLayoutChange() { + get onDidLayoutChange(): Event { return this.component.onDidLayoutChange; } @@ -160,75 +162,73 @@ export class PaneviewApi implements CommonApi { return this.component.getPanel(id); } - movePanel(from: number, to: number) { + movePanel(from: number, to: number): void { this.component.movePanel(from, to); } - focus() { - return this.component.focus(); + focus(): void { + this.component.focus(); } - layout(width: number, height: number) { - return this.component.layout(width, height); + layout(width: number, height: number): void { + this.component.layout(width, height); } - addPanel(options: AddPaneviewCompponentOptions) { + addPanel(options: AddPaneviewCompponentOptions): IDisposable { return this.component.addPanel(options); } - resizeToFit() { - return this.component.resizeToFit(); + resizeToFit(): void { + this.component.resizeToFit(); } - fromJSON(data: SerializedPaneview, deferComponentLayout?: boolean) { - return this.component.fromJSON(data, deferComponentLayout); + fromJSON(data: SerializedPaneview, deferComponentLayout?: boolean): void { + this.component.fromJSON(data, deferComponentLayout); } - toJSON() { + toJSON(): SerializedPaneview { return this.component.toJSON(); } } export class GridviewApi implements CommonApi { - get width() { + get width(): number { return this.component.width; } - get height() { + get height(): number { return this.component.height; } - get minimumHeight() { + get minimumHeight(): number { return this.component.minimumHeight; } - get maximumHeight() { + get maximumHeight(): number { return this.component.maximumHeight; } - get minimumWidth() { + get minimumWidth(): number { return this.component.minimumWidth; } - get maximumWidth() { + get maximumWidth(): number { return this.component.maximumWidth; } - get onGridEvent() { + get onGridEvent(): Event { return this.component.onGridEvent; } - get onDidLayoutChange() { + get onDidLayoutChange(): Event { return this.component.onDidLayoutChange; } - get panels() { + get panels(): GridviewPanel[] { return this.component.groups; } - constructor(private readonly component: IGridviewComponent) {} - - get orientation() { + get orientation(): Orientation { return this.component.orientation; } @@ -236,16 +236,18 @@ export class GridviewApi implements CommonApi { this.component.updateOptions({ orientation: value }); } - focus() { - return this.component.focus(); + constructor(private readonly component: IGridviewComponent) {} + + focus(): void { + this.component.focus(); } - layout(width: number, height: number, force = false) { - return this.component.layout(width, height, force); + layout(width: number, height: number, force = false): void { + this.component.layout(width, height, force); } - addPanel(options: AddComponentOptions) { - return this.component.addPanel(options); + addPanel(options: AddComponentOptions): void { + this.component.addPanel(options); } removePanel(panel: IGridviewPanel, sizing?: Sizing): void { @@ -255,169 +257,163 @@ export class GridviewApi implements CommonApi { movePanel( panel: IGridviewPanel, options: { direction: Direction; reference: string; size?: number } - ) { + ): void { this.component.movePanel(panel, options); } - resizeToFit() { - return this.component.resizeToFit(); + resizeToFit(): void { + this.component.resizeToFit(); } - getPanel(id: string) { + getPanel(id: string): GridviewPanel | undefined { return this.component.getPanel(id); } - toggleVisibility(panel: IGridviewPanel) { - return this.component.toggleVisibility(panel); + toggleVisibility(panel: IGridviewPanel): void { + this.component.toggleVisibility(panel); } - // isVisible(panel: IGridviewPanel) { - // return this.component.isVisible(panel); - // } - - setVisible(panel: IGridviewPanel, visible: boolean) { - return this.component.setVisible(panel, visible); + setVisible(panel: IGridviewPanel, visible: boolean): void { + this.component.setVisible(panel, visible); } setActive(panel: IGridviewPanel): void { this.component.setActive(panel); } - fromJSON(data: SerializedGridview, deferComponentLayout?: boolean) { + fromJSON(data: SerializedGridview, deferComponentLayout?: boolean): void { return this.component.fromJSON(data, deferComponentLayout); } - toJSON() { + toJSON(): SerializedGridview { return this.component.toJSON(); } } export class DockviewApi implements CommonApi { - get width() { + get width(): number { return this.component.width; } - get height() { + get height(): number { return this.component.height; } - get minimumHeight() { + get minimumHeight(): number { return this.component.minimumHeight; } - get maximumHeight() { + get maximumHeight(): number { return this.component.maximumHeight; } - get minimumWidth() { + get minimumWidth(): number { return this.component.minimumWidth; } - get maximumWidth() { + get maximumWidth(): number { return this.component.maximumWidth; } - get size() { + get size(): number { return this.component.size; } - get totalPanels() { + get totalPanels(): number { return this.component.totalPanels; } - get onGridEvent() { + get onGridEvent(): Event { return this.component.onGridEvent; } - get onDidLayoutChange() { + get onDidLayoutChange(): Event { return this.component.onDidLayoutChange; } - get panels() { + get panels(): IGroupPanel[] { return this.component.panels; } - get groups() { + get groups(): GroupviewPanel[] { return this.component.groups; } + get activePanel(): IGroupPanel | undefined { + return this.component.activePanel; + } + + get activeGroup(): GroupviewPanel | undefined { + return this.component.activeGroup; + } + constructor(private readonly component: IDockviewComponent) {} - focus() { - return this.component.focus(); + getTabHeight(): number | undefined { + return this.component.tabHeight; + } + + setTabHeight(height: number | undefined): void { + this.component.tabHeight = height; + } + + focus(): void { + this.component.focus(); } getPanel(id: string): IGroupPanel | undefined { return this.component.getGroupPanel(id); } - setActivePanel(panel: IGroupPanel) { - return this.component.setActivePanel(panel); + setActivePanel(panel: IGroupPanel): void { + this.component.setActivePanel(panel); } - layout(width: number, height: number, force = false) { - return this.component.layout(width, height, force); + layout(width: number, height: number, force = false): void { + this.component.layout(width, height, force); } - addPanel(options: AddPanelOptions) { + addPanel(options: AddPanelOptions): IGroupPanel { return this.component.addPanel(options); } - // addDndHandle(type: string, cb: (event: LayoutDropEvent) => PanelOptions) { - // return this.component.addDndHandle(type, cb); - // } - - // createDragTarget( - // target: { - // element: HTMLElement; - // content: string; - // }, - // options: (() => PanelOptions) | PanelOptions - // ) { - // return this.component.createDragTarget(target, options); - // } + removePanel(panel: IGroupPanel): void { + this.component.removePanel(panel); + } - addEmptyGroup(options?: AddGroupOptions) { - return this.component.addEmptyGroup(options); + addEmptyGroup(options?: AddGroupOptions): void { + this.component.addEmptyGroup(options); } - moveToNext(options?: MovementOptions) { - return this.component.moveToNext(options); + moveToNext(options?: MovementOptions): void { + this.component.moveToNext(options); } - moveToPrevious(options?: MovementOptions) { - return this.component.moveToPrevious(options); + moveToPrevious(options?: MovementOptions): void { + this.component.moveToPrevious(options); } - closeAllGroups() { + closeAllGroups(): Promise { return this.component.closeAllGroups(); } - removeGroup(group: GroupviewPanel) { - return this.component.removeGroup(group); + removeGroup(group: GroupviewPanel): void { + this.component.removeGroup(group); } - resizeToFit() { + resizeToFit(): void { return this.component.resizeToFit(); } - getTabHeight() { - return this.component.tabHeight; - } - - setTabHeight(height: number | undefined) { - this.component.tabHeight = height; - } - - getGroup(id: string) { + getGroup(id: string): GroupviewPanel | undefined { return this.component.getPanel(id); } - fromJSON(data: SerializedDockview) { - return this.component.fromJSON(data); + fromJSON(data: SerializedDockview): void { + this.component.fromJSON(data); } - toJSON() { + toJSON(): SerializedDockview { return this.component.toJSON(); } } diff --git a/packages/dockview/src/dockview/dockviewComponent.ts b/packages/dockview/src/dockview/dockviewComponent.ts index 35d26da3c..45f365dab 100644 --- a/packages/dockview/src/dockview/dockviewComponent.ts +++ b/packages/dockview/src/dockview/dockviewComponent.ts @@ -29,6 +29,7 @@ import { } from './options'; import { BaseGrid, + GroupChangeKind, IBaseGrid, toTarget, } from '../gridview/baseComponentGridview'; @@ -38,7 +39,7 @@ import { LayoutMouseEvent, MouseEventKind } from '../groupview/tab'; import { Orientation } from '../splitview/core/splitview'; import { DefaultTab } from './components/tab/defaultTab'; import { - GroupChangeKind, + GroupChangeKind2, GroupOptions, GroupPanelViewState, } from '../groupview/groupview'; @@ -74,7 +75,7 @@ export type DockviewComponentUpdateOptions = Pick< >; export interface IDockviewComponent extends IBaseGrid { - readonly activeGroup: GroupviewPanel | undefined; + readonly activePanel: IGroupPanel | undefined; readonly totalPanels: number; readonly panels: IGroupPanel[]; tabHeight: number | undefined; @@ -91,6 +92,7 @@ export interface IDockviewComponent extends IBaseGrid { removeGroup: (group: GroupviewPanel) => void; options: DockviewComponentOptions; addPanel(options: AddPanelOptions): IGroupPanel; + removePanel(panel: IGroupPanel): void; getGroupPanel: (id: string) => IGroupPanel | undefined; fireMouseEvent(event: LayoutMouseEvent): void; createWatermarkComponent(): IWatermarkRenderer; @@ -148,10 +150,20 @@ export class DockviewComponent this._deserializer = value; } - get options() { + get options(): DockviewComponentOptions { return this._options; } + get activePanel(): IGroupPanel | undefined { + const activeGroup = this.activeGroup; + + if (!activeGroup) { + return undefined; + } + + return activeGroup.model.activePanel; + } + set tabHeight(height: number | undefined) { this.options.tabHeight = height; this._groups.forEach((value) => { @@ -289,8 +301,6 @@ export class DockviewComponent ); this._panels.set(panel.id, { value: panel, disposable }); - - this._onGridEvent.fire({ kind: GroupChangeKind.PANEL_CREATED }); } unregisterPanel(panel: IGroupPanel): void { @@ -305,8 +315,6 @@ export class DockviewComponent } this._panels.delete(panel.id); - - this._onGridEvent.fire({ kind: GroupChangeKind.PANEL_DESTROYED }); } /** @@ -451,6 +459,10 @@ export class DockviewComponent return panel; } + removePanel(panel: IGroupPanel): void { + panel.group?.model.removePanel(panel); + } + createWatermarkComponent(): IWatermarkRenderer { return createComponent( 'watermark-id', @@ -599,6 +611,21 @@ export class DockviewComponent } } + override doSetGroupActive( + group: GroupviewPanel | undefined, + skipFocus?: boolean + ) { + const isGroupAlreadyFocused = this._activeGroup === group; + super.doSetGroupActive(group, skipFocus); + + if (!isGroupAlreadyFocused && this._activeGroup?.model.activePanel) { + this._onGridEvent.fire({ + kind: GroupChangeKind.PANEL_ACTIVE, + panel: this._activeGroup?.model.activePanel, + }); + } + } + createGroup(options?: GroupOptions): GroupviewPanel { if (!options) { options = { tabHeight: this.tabHeight }; @@ -632,7 +659,32 @@ export class DockviewComponent this.moveGroupOrPanel(view, groupId, itemId, target, index); }), view.model.onDidGroupChange((event) => { - this._onGridEvent.fire(event); + switch (event.kind) { + case GroupChangeKind2.ADD_PANEL: + this._onGridEvent.fire({ + kind: GroupChangeKind.ADD_PANEL, + panel: event.panel, + }); + break; + case GroupChangeKind2.GROUP_ACTIVE: + this._onGridEvent.fire({ + kind: GroupChangeKind.GROUP_ACTIVE, + panel: event.panel, + }); + break; + case GroupChangeKind2.REMOVE_PANEL: + this._onGridEvent.fire({ + kind: GroupChangeKind.REMOVE_PANEL, + panel: event.panel, + }); + break; + case GroupChangeKind2.PANEL_ACTIVE: + this._onGridEvent.fire({ + kind: GroupChangeKind.PANEL_ACTIVE, + panel: event.panel, + }); + break; + } }) ); @@ -755,7 +807,6 @@ export class DockviewComponent private addDirtyPanel(panel: IGroupPanel): void { this.dirtyPanels.add(panel); panel.setDirty(true); - this._onGridEvent.fire({ kind: GroupChangeKind.PANEL_DIRTY }); this.debouncedDeque(); } } diff --git a/packages/dockview/src/dockview/dockviewGroupPanel.ts b/packages/dockview/src/dockview/dockviewGroupPanel.ts index e71554aa2..a00afc646 100644 --- a/packages/dockview/src/dockview/dockviewGroupPanel.ts +++ b/packages/dockview/src/dockview/dockviewGroupPanel.ts @@ -1,3 +1,4 @@ +import { GroupChangeKind2 } from '..'; import { DockviewApi } from '../api/component.api'; import { DockviewPanelApiImpl } from '../api/groupPanelApi'; import { Event } from '../events'; @@ -7,7 +8,6 @@ import { IGroupPanel, IGroupPanelInitParameters, } from '../groupview/groupPanel'; -import { GroupChangeKind } from '../groupview/groupview'; import { GroupviewPanel } from '../groupview/groupviewPanel'; import { CompositeDisposable, MutableDisposable } from '../lifecycle'; import { Parameters } from '../panel/types'; @@ -170,7 +170,7 @@ export class DockviewGroupPanel this.mutableDisposable.value = this._group.model.onDidGroupChange( (ev) => { - if (ev.kind === GroupChangeKind.GROUP_ACTIVE) { + if (ev.kind === GroupChangeKind2.GROUP_ACTIVE) { const isVisible = !!this._group?.model.isPanelActive(this); this.api._onDidActiveChange.fire({ isActive: isGroupActive && isVisible, diff --git a/packages/dockview/src/gridview/baseComponentGridview.ts b/packages/dockview/src/gridview/baseComponentGridview.ts index 91d3cac3d..2d2d14022 100644 --- a/packages/dockview/src/gridview/baseComponentGridview.ts +++ b/packages/dockview/src/gridview/baseComponentGridview.ts @@ -10,7 +10,24 @@ import { } from '../splitview/core/splitview'; import { IPanel } from '../panel/types'; import { MovementOptions2 } from '../dockview/options'; -import { GroupChangeEvent, GroupChangeKind } from '../groupview/groupview'; +import { IGroupPanel } from '../groupview/groupPanel'; + +export enum GroupChangeKind { + ADD_PANEL = 'ADD_PANEL', + REMOVE_PANEL = 'REMOVE_PANEL', + PANEL_ACTIVE = 'PANEL_ACTIVE', + // + GROUP_ACTIVE = 'GROUP_ACTIVE', + ADD_GROUP = 'ADD_GROUP', + REMOVE_GROUP = 'REMOVE_GROUP', + // + LAYOUT_FROM_JSON = 'LAYOUT_FROM_JSON', + LAYOUT = 'LAYOUT', +} +export interface GroupChangeEvent { + readonly kind: GroupChangeKind; + readonly panel?: IGroupPanel; +} const nextLayoutId = sequentialNumberGenerator(); @@ -252,7 +269,9 @@ export abstract class BaseGrid this._activeGroup = group; - this._onGridEvent.fire({ kind: GroupChangeKind.GROUP_ACTIVE }); + this._onGridEvent.fire({ + kind: GroupChangeKind.GROUP_ACTIVE, + }); } public removeGroup(group: T) { diff --git a/packages/dockview/src/gridview/gridviewComponent.ts b/packages/dockview/src/gridview/gridviewComponent.ts index 933a1b86a..8bec7ab28 100644 --- a/packages/dockview/src/gridview/gridviewComponent.ts +++ b/packages/dockview/src/gridview/gridviewComponent.ts @@ -11,6 +11,7 @@ import { GridviewComponentOptions } from './options'; import { BaseGrid, Direction, + GroupChangeKind, IBaseGrid, IGridPanelView, toTarget, @@ -26,7 +27,6 @@ import { GridviewPanelApiImpl } from '../api/gridviewPanelApi'; import { GridviewApi } from '../api/component.api'; import { Orientation, Sizing } from '../splitview/core/splitview'; import { createComponent } from '../panel/componentFactory'; -import { GroupChangeKind } from '../groupview/groupview'; interface PanelReference { api: GridviewPanelApiImpl; diff --git a/packages/dockview/src/groupview/groupview.ts b/packages/dockview/src/groupview/groupview.ts index c3429d8a9..fb1bcfd6c 100644 --- a/packages/dockview/src/groupview/groupview.ts +++ b/packages/dockview/src/groupview/groupview.ts @@ -20,24 +20,11 @@ import { GroupviewPanel } from './groupviewPanel'; import { focusedElement } from '../focusedElement'; import { DockviewDropTargets } from './dnd'; -export enum GroupChangeKind { - GROUP_ACTIVE = 'GROUP_ACTIVE', - ADD_GROUP = 'ADD_GROUP', - REMOVE_GROUP = 'REMOVE_GROUP', - // +export enum GroupChangeKind2 { ADD_PANEL = 'ADD_PANEL', REMOVE_PANEL = 'REMOVE_PANEL', PANEL_ACTIVE = 'PANEL_ACTIVE', - // - LAYOUT_FROM_JSON = 'LAYOUT_FROM_JSON', - LAYOUT = 'LAYOUT', - // - PANEL_CREATED = 'PANEL_CREATED', - PANEL_DESTROYED = 'PANEL_DESTROYED', - PANEL_DIRTY = 'PANEL_DIRTY', - PANEL_CLEAN = 'PANEL_CLEAN', - // - LAYOUT_CONFIG_UPDATED = 'LAYOUT_CONFIG_UPDATED', + GROUP_ACTIVE = 'GROUP_ACTIVE', } export interface DndService { @@ -74,8 +61,8 @@ export interface GroupOptions { tabHeight?: number; } -export interface GroupChangeEvent { - readonly kind: GroupChangeKind; +export interface GroupviewChangeEvent { + readonly kind: GroupChangeKind2; readonly panel?: IGroupPanel; } @@ -104,11 +91,8 @@ export interface IGroupview extends IDisposable, IGridPanelView { containsPanel(panel: IGroupPanel): boolean; removePanel: (panelOrId: IGroupPanel | string) => IGroupPanel; // events - onDidGroupChange: Event<{ kind: GroupChangeKind }>; + onDidGroupChange: Event; onMove: Event; - // - // startActiveDrag(panel: IGroupPanel): IDisposable; - // moveToNext(options?: { panel?: IGroupPanel; suppressRoll?: boolean }): void; moveToPrevious(options?: { panel?: IGroupPanel; @@ -141,8 +125,8 @@ export class Groupview extends CompositeDisposable implements IGroupview { private readonly _onMove = new Emitter(); readonly onMove: Event = this._onMove.event; - private readonly _onDidGroupChange = new Emitter(); - readonly onDidGroupChange: Event<{ kind: GroupChangeKind }> = + private readonly _onDidGroupChange = new Emitter(); + readonly onDidGroupChange: Event = this._onDidGroupChange.event; get element(): HTMLElement { @@ -289,20 +273,6 @@ export class Groupview extends CompositeDisposable implements IGroupview { }; } - // public startActiveDrag(panel: IGroupPanel): IDisposable { - // const index = this.tabsContainer.indexOf(panel.id); - // if (index > -1) { - // const tab = this.tabsContainer.at(index); - // tab.startDragEvent(); - // return { - // dispose: () => { - // tab.stopDragEvent(); - // }, - // }; - // } - // return Disposable.NONE; - // } - public moveToNext(options?: { panel?: IGroupPanel; suppressRoll?: boolean; @@ -504,27 +474,16 @@ export class Groupview extends CompositeDisposable implements IGroupview { this.tabsContainer.setActive(this.isActive); - // this.updateActions(); - if (!this._activePanel && this.panels.length > 0) { this.doSetActivePanel(this.panels[0]); } this.updateContainer(); - // this.panels.forEach((panel) => - // panel.updateParentGroup(this, this.isActive) - // ); - - // if (this.watermark?.updateParentGroup) { - // this.watermark.updateParentGroup(this, this.isActive); - // } - if (isGroupActive) { if (!skipFocus) { this._activePanel?.focus(); } - this._onDidGroupChange.fire({ kind: GroupChangeKind.GROUP_ACTIVE }); } } @@ -575,7 +534,7 @@ export class Groupview extends CompositeDisposable implements IGroupview { } this._onDidGroupChange.fire({ - kind: GroupChangeKind.REMOVE_PANEL, + kind: GroupChangeKind2.REMOVE_PANEL, panel, }); } @@ -599,7 +558,10 @@ export class Groupview extends CompositeDisposable implements IGroupview { this.updateMru(panel); this.panels.splice(index, 0, panel); - this._onDidGroupChange.fire({ kind: GroupChangeKind.ADD_PANEL, panel }); + this._onDidGroupChange.fire({ + kind: GroupChangeKind2.ADD_PANEL, + panel, + }); } private doSetActivePanel(panel: IGroupPanel | undefined) { @@ -611,12 +573,12 @@ export class Groupview extends CompositeDisposable implements IGroupview { panel.layout(this._width, this._height); this.updateMru(panel); - } - this._onDidGroupChange.fire({ - kind: GroupChangeKind.PANEL_ACTIVE, - panel, - }); + this._onDidGroupChange.fire({ + kind: GroupChangeKind2.PANEL_ACTIVE, + panel, + }); + } } private updateMru(panel: IGroupPanel) { diff --git a/packages/dockview/src/index.ts b/packages/dockview/src/index.ts index 7ac35a065..0ba2b62ec 100644 --- a/packages/dockview/src/index.ts +++ b/packages/dockview/src/index.ts @@ -5,6 +5,7 @@ export * from './splitview/splitviewComponent'; export * from './paneview/paneview'; export * from './paneview/paneviewComponent'; export * from './gridview/gridview'; +export * from './gridview/baseComponentGridview'; export * from './groupview/groupview'; export * from './groupview/panel/content'; export * from './groupview/tab';