diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index a6a13434a..856df6dc8 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -23,10 +23,10 @@ "/packages/docs/sandboxes/tabheight-dockview", "/packages/docs/sandboxes/updatetitle-dockview", "/packages/docs/sandboxes/watermark-dockview", - "/packages/docs/sandboxes/typescript/fullwidthtab-dockview", - "/packages/docs/sandboxes/typescript/simple-dockview", - "/packages/docs/sandboxes/typescript/tabheight-dockview", - "/packages/docs/sandboxes/typescript/vanilla-dockview" + "/packages/docs/sandboxes/javascript/fullwidthtab-dockview", + "/packages/docs/sandboxes/javascript/simple-dockview", + "/packages/docs/sandboxes/javascript/tabheight-dockview", + "/packages/docs/sandboxes/javascript/vanilla-dockview" ], "node": "16" -} \ No newline at end of file +} diff --git a/lerna.json b/lerna.json index b2d59ced6..5d0c99866 100644 --- a/lerna.json +++ b/lerna.json @@ -3,7 +3,7 @@ "packages/*" ], "useWorkspaces": true, - "version": "1.7.2", + "version": "1.7.3", "npmClient": "yarn", "command": { "publish": { diff --git a/package.json b/package.json index cf33e48ae..3148b38c4 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "gulp": "^4.0.2", "gulp-concat": "^2.6.1", "gulp-dart-sass": "^1.0.2", + "jest": "^29.5.0", "jest-environment-jsdom": "^29.4.3", "jest-sonar-reporter": "^2.0.0", "jsdom": "^21.1.0", @@ -58,14 +59,12 @@ "ts-jest": "^29.0.5", "ts-loader": "^9.4.2", "tslib": "^2.5.0", + "ts-node": "^10.9.1", "typedoc": "^0.24.7", "typescript": "^4.9.5", "webpack": "^5.75.0", "webpack-cli": "^5.0.1", "webpack-dev-server": "^4.11.1" }, - "dependencies": { - "jest": "^29.5.0", - "ts-node": "^10.9.1" - } -} + "dependencies": {} +} \ No newline at end of file diff --git a/packages/dockview-core/package.json b/packages/dockview-core/package.json index cdceee251..e8bbfe657 100644 --- a/packages/dockview-core/package.json +++ b/packages/dockview-core/package.json @@ -1,6 +1,6 @@ { "name": "dockview-core", - "version": "1.7.2", + "version": "1.7.3", "description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support", "main": "./dist/cjs/index.js", "types": "./dist/cjs/index.d.ts", diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index 253cd4f1e..2142045d5 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -46,6 +46,7 @@ class PanelContentPartTest implements IContentRenderer { dispose(): void { this.isDisposed = true; this._onDidDispose.fire(); + this._onDidDispose.dispose(); } } @@ -80,6 +81,7 @@ class PanelTabPartTest implements ITabRenderer { dispose(): void { this.isDisposed = true; this._onDidDispose.fire(); + this._onDidDispose.dispose(); } } @@ -98,6 +100,68 @@ describe('dockviewComponent', () => { }); }); + test('event leakage', () => { + Emitter.setLeakageMonitorEnabled(true); + + dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + }); + + dockview.layout(500, 1000); + + dockview.addPanel({ + id: 'panel1', + component: 'default', + }); + + const panel2 = dockview.addPanel({ + id: 'panel2', + component: 'default', + }); + + dockview.removePanel(panel2); + + const panel3 = dockview.addPanel({ + id: 'panel3', + component: 'default', + position: { + direction: 'right', + referencePanel: 'panel1', + }, + }); + + const panel4 = dockview.addPanel({ + id: 'panel4', + component: 'default', + position: { + direction: 'above', + }, + }); + + dockview.moveGroupOrPanel( + panel4.group, + panel3.group.id, + panel3.id, + 'center' + ); + + dockview.dispose(); + + if (Emitter.MEMORY_LEAK_WATCHER.size > 0) { + for (const entry of Array.from( + Emitter.MEMORY_LEAK_WATCHER.events + )) { + console.log('disposal', entry[1]); + } + throw new Error('not all listeners disposed'); + } + + Emitter.setLeakageMonitorEnabled(false); + }); + test('duplicate panel', () => { dockview.layout(500, 1000); @@ -112,6 +176,8 @@ describe('dockviewComponent', () => { component: 'default', }); }).toThrowError('panel with id panel1 already exists'); + + dockview.dispose(); }); test('set active panel', () => { @@ -1285,21 +1351,21 @@ describe('dockviewComponent', () => { tabComponent: 'default', }); - const panel2 = dockview.addPanel({ - id: 'panel2', - component: 'default', - tabComponent: 'default', - }); + // const panel2 = dockview.addPanel({ + // id: 'panel2', + // component: 'default', + // tabComponent: 'default', + // }); - expect(panel1.group).toEqual(panel2.group); + // expect(panel1.group).toEqual(panel2.group); const panel1Spy = jest.spyOn(panel1, 'dispose'); - const panel2Spy = jest.spyOn(panel2, 'dispose'); + // const panel2Spy = jest.spyOn(panel2, 'dispose'); dockview.dispose(); expect(panel1Spy).toBeCalledTimes(1); - expect(panel2Spy).toBeCalledTimes(1); + // expect(panel2Spy).toBeCalledTimes(1); }); test('panel is disposed of when from JSON is called', () => { @@ -2295,4 +2361,91 @@ describe('dockviewComponent', () => { panels: {}, }); }); + + test('that title and params.title do not conflict', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); + + dockview.layout(100, 100); + + dockview.addPanel({ + id: 'panel1', + component: 'default', + title: 'Panel 1', + params: { + title: 'Panel 1', + }, + }); + + dockview.addPanel({ + id: 'panel2', + component: 'default', + title: 'Panel 2', + }); + + dockview.addPanel({ + id: 'panel3', + component: 'default', + params: { + title: 'Panel 3', + }, + }); + + expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({ + grid: { + root: { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panel1', 'panel2', 'panel3'], + activeView: 'panel3', + id: '1', + }, + size: 100, + }, + ], + size: 100, + }, + width: 100, + height: 100, + orientation: 'HORIZONTAL', + }, + panels: { + panel1: { + id: 'panel1', + contentComponent: 'default', + params: { + title: 'Panel 1', + }, + title: 'Panel 1', + }, + panel2: { + id: 'panel2', + contentComponent: 'default', + title: 'Panel 2', + }, + panel3: { + id: 'panel3', + contentComponent: 'default', + params: { + title: 'Panel 3', + }, + title: 'panel3', + }, + }, + activeGroup: '1', + }); + }); }); diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewPanel.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewPanel.spec.ts index e6d7ab10e..d87ac438b 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewPanel.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewPanel.spec.ts @@ -37,7 +37,7 @@ describe('dockviewPanel', () => { latestTitle = event.title; }); - expect(cut.title).toBe(''); + expect(cut.title).toBeUndefined(); cut.init({ title: 'new title', params: {} }); expect(latestTitle).toBe('new title'); diff --git a/packages/dockview-core/src/__tests__/splitview/splitview.spec.ts b/packages/dockview-core/src/__tests__/splitview/splitview.spec.ts index 60d94d513..23c9b1df8 100644 --- a/packages/dockview-core/src/__tests__/splitview/splitview.spec.ts +++ b/packages/dockview-core/src/__tests__/splitview/splitview.spec.ts @@ -585,8 +585,15 @@ describe('splitview', () => { expect(container.childNodes.length).toBeGreaterThan(0); - splitview.dispose(); + let anyEvents = false; + const listener = splitview.onDidRemoveView((e) => { + anyEvents = true; // disposing of the splitview shouldn't fire onDidRemoveView events + }); + splitview.dispose(); + listener.dispose(); + + expect(anyEvents).toBeFalsy(); expect(container.childNodes.length).toBe(0); }); }); diff --git a/packages/dockview-core/src/__tests__/splitview/splitviewComponent.spec.ts b/packages/dockview-core/src/__tests__/splitview/splitviewComponent.spec.ts index e3c16e751..e43c94239 100644 --- a/packages/dockview-core/src/__tests__/splitview/splitviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/splitview/splitviewComponent.spec.ts @@ -1,4 +1,5 @@ import { PanelDimensionChangeEvent } from '../../api/panelApi'; +import { Emitter } from '../../events'; import { CompositeDisposable } from '../../lifecycle'; import { Orientation } from '../../splitview/splitview'; import { SplitviewComponent } from '../../splitview/splitviewComponent'; @@ -25,6 +26,45 @@ describe('componentSplitview', () => { container.className = 'container'; }); + test('event leakage', () => { + Emitter.setLeakageMonitorEnabled(true); + + const splitview = new SplitviewComponent({ + parentElement: container, + orientation: Orientation.VERTICAL, + components: { + testPanel: TestPanel, + }, + }); + splitview.layout(600, 400); + + const panel1 = splitview.addPanel({ + id: 'panel1', + component: 'testPanel', + }); + const panel2 = splitview.addPanel({ + id: 'panel2', + component: 'testPanel', + }); + + splitview.movePanel(0, 1); + + splitview.removePanel(panel1); + + splitview.dispose(); + + if (Emitter.MEMORY_LEAK_WATCHER.size > 0) { + for (const entry of Array.from( + Emitter.MEMORY_LEAK_WATCHER.events + )) { + console.log(entry[1]); + } + throw new Error('not all listeners disposed'); + } + + Emitter.setLeakageMonitorEnabled(false); + }); + test('remove panel', () => { const splitview = new SplitviewComponent({ parentElement: container, diff --git a/packages/dockview-core/src/api/dockviewPanelApi.ts b/packages/dockview-core/src/api/dockviewPanelApi.ts index 48978c688..d42ac65a4 100644 --- a/packages/dockview-core/src/api/dockviewPanelApi.ts +++ b/packages/dockview-core/src/api/dockviewPanelApi.ts @@ -19,7 +19,7 @@ export interface DockviewPanelApi > { readonly group: DockviewGroupPanel; readonly isGroupActive: boolean; - readonly title: string; + readonly title: string | undefined; readonly onDidActiveGroupChange: Event; readonly onDidGroupChange: Event; close(): void; @@ -43,7 +43,7 @@ export class DockviewPanelApiImpl private readonly disposable = new MutableDisposable(); - get title(): string { + get title(): string | undefined { return this.panel.title; } diff --git a/packages/dockview-core/src/api/panelApi.ts b/packages/dockview-core/src/api/panelApi.ts index 37648cd84..795ac1589 100644 --- a/packages/dockview-core/src/api/panelApi.ts +++ b/packages/dockview-core/src/api/panelApi.ts @@ -126,15 +126,6 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi { super(); this.addDisposables( - this.panelUpdatesDisposable, - this._onDidDimensionChange, - this._onDidChangeFocus, - this._onDidVisibilityChange, - this._onDidActiveChange, - this._onFocusEvent, - this._onActiveChange, - this._onVisibilityChange, - this._onUpdateParameters, this.onDidFocusChange((event) => { this._isFocused = event.isFocused; }), @@ -147,7 +138,16 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi { this.onDidDimensionsChange((event) => { this._width = event.width; this._height = event.height; - }) + }), + this.panelUpdatesDisposable, + this._onDidDimensionChange, + this._onDidChangeFocus, + this._onDidVisibilityChange, + this._onDidActiveChange, + this._onFocusEvent, + this._onActiveChange, + this._onVisibilityChange, + this._onUpdateParameters ); } diff --git a/packages/dockview-core/src/dnd/abstractDragHandler.ts b/packages/dockview-core/src/dnd/abstractDragHandler.ts index b1483f347..d7569f14f 100644 --- a/packages/dockview-core/src/dnd/abstractDragHandler.ts +++ b/packages/dockview-core/src/dnd/abstractDragHandler.ts @@ -16,6 +16,9 @@ export abstract class DragHandler extends CompositeDisposable { constructor(protected readonly el: HTMLElement) { super(); + + this.addDisposables(this._onDragStart); + this.configure(); } diff --git a/packages/dockview-core/src/dnd/droptarget.ts b/packages/dockview-core/src/dnd/droptarget.ts index ba3f21c82..a45cac380 100644 --- a/packages/dockview-core/src/dnd/droptarget.ts +++ b/packages/dockview-core/src/dnd/droptarget.ts @@ -182,6 +182,7 @@ export class Droptarget extends CompositeDisposable { public dispose(): void { this.removeDropTarget(); + super.dispose(); } private toggleClasses( diff --git a/packages/dockview-core/src/dnd/groupDragHandler.ts b/packages/dockview-core/src/dnd/groupDragHandler.ts index 2acb296f0..b47ea4a03 100644 --- a/packages/dockview-core/src/dnd/groupDragHandler.ts +++ b/packages/dockview-core/src/dnd/groupDragHandler.ts @@ -60,8 +60,4 @@ export class GroupDragHandler extends DragHandler { }, }; } - - public dispose(): void { - // - } } diff --git a/packages/dockview-core/src/dockview/components/panel/content.ts b/packages/dockview-core/src/dockview/components/panel/content.ts index 75859e4d2..9934c5ec4 100644 --- a/packages/dockview-core/src/dockview/components/panel/content.ts +++ b/packages/dockview-core/src/dockview/components/panel/content.ts @@ -77,11 +77,12 @@ export class ContentContainer const _onDidFocus = this.panel.view.content.onDidFocus; const _onDidBlur = this.panel.view.content.onDidBlur; - const { onDidFocus, onDidBlur } = trackFocus(this._element); + const focusTracker = trackFocus(this._element); disposable.addDisposables( - onDidFocus(() => this._onDidFocus.fire()), - onDidBlur(() => this._onDidBlur.fire()) + focusTracker, + focusTracker.onDidFocus(() => this._onDidFocus.fire()), + focusTracker.onDidBlur(() => this._onDidBlur.fire()) ); if (_onDidFocus) { diff --git a/packages/dockview-core/src/dockview/components/tab/tab.ts b/packages/dockview-core/src/dockview/components/tab/tab.ts index d05eef298..c33455172 100644 --- a/packages/dockview-core/src/dockview/components/tab/tab.ts +++ b/packages/dockview-core/src/dockview/components/tab/tab.ts @@ -13,7 +13,7 @@ import { DroptargetEvent, Droptarget } from '../../../dnd/droptarget'; import { DragHandler } from '../../../dnd/abstractDragHandler'; import { DockviewPanel } from '../../dockviewPanel'; -export interface ITab { +export interface ITab extends IDisposable { readonly panelId: string; readonly element: HTMLElement; setContent: (element: ITabRenderer) => void; @@ -44,8 +44,6 @@ export class Tab extends CompositeDisposable implements ITab { ) { super(); - this.addDisposables(this._onChanged, this._onDropped); - this._element = document.createElement('div'); this._element.className = 'tab'; this._element.tabIndex = 0; @@ -54,6 +52,8 @@ export class Tab extends CompositeDisposable implements ITab { toggleClass(this.element, 'inactive-tab', true); this.addDisposables( + this._onChanged, + this._onDropped, new (class Handler extends DragHandler { private readonly panelTransfer = LocalSelectionTransfer.getInstance(); @@ -72,10 +72,6 @@ export class Tab extends CompositeDisposable implements ITab { }, }; } - - public dispose(): void { - // - } })(this._element) ); @@ -141,7 +137,8 @@ export class Tab extends CompositeDisposable implements ITab { this.addDisposables( this.droptarget.onDrop((event) => { this._onDropped.fire(event); - }) + }), + this.droptarget ); } @@ -160,6 +157,5 @@ export class Tab extends CompositeDisposable implements ITab { public dispose(): void { super.dispose(); - this.droptarget.dispose(); } } diff --git a/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts b/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts index debdf42e0..68d8cfe1b 100644 --- a/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts +++ b/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts @@ -216,6 +216,7 @@ export class TabsContainer const { value, disposable } = tabToRemove; disposable.dispose(); + value.dispose(); value.element.remove(); } @@ -275,9 +276,11 @@ export class TabsContainer public dispose(): void { super.dispose(); - this.tabs.forEach((tab) => { - tab.disposable.dispose(); - }); + for (const { value, disposable } of this.tabs) { + disposable.dispose(); + value.dispose(); + } + this.tabs = []; } } diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index 218cb7207..39f334bc7 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -262,7 +262,6 @@ export class DockviewComponent }); this.addDisposables( - dropTarget, dropTarget.onDrop((event) => { const data = getPanelData(); @@ -281,7 +280,8 @@ export class DockviewComponent getData: getPanelData, }); } - }) + }), + dropTarget ); this._api = new DockviewApi(this); @@ -827,43 +827,49 @@ export class DockviewComponent } moveGroupOrPanel( - referenceGroup: DockviewGroupPanel, - groupId: string, - itemId: string | undefined, - target: Position, - index?: number + destinationGroup: DockviewGroupPanel, + sourceGroupId: string, + sourceItemId: string | undefined, + destinationTarget: Position, + destinationIndex?: number ): void { - const sourceGroup = groupId - ? this._groups.get(groupId)?.value + const sourceGroup = sourceGroupId + ? this._groups.get(sourceGroupId)?.value : undefined; - if (itemId === undefined) { + if (sourceItemId === undefined) { if (sourceGroup) { - this.moveGroup(sourceGroup, referenceGroup, target); + this.moveGroup( + sourceGroup, + destinationGroup, + destinationTarget + ); } return; } - if (!target || target === 'center') { + if (!destinationTarget || destinationTarget === 'center') { const groupItem: IDockviewPanel | undefined = - sourceGroup?.model.removePanel(itemId) || - this.panels.find((panel) => panel.id === itemId); + sourceGroup?.model.removePanel(sourceItemId) || + this.panels.find((panel) => panel.id === sourceItemId); if (!groupItem) { - throw new Error(`No panel with id ${itemId}`); + throw new Error(`No panel with id ${sourceItemId}`); } if (sourceGroup?.model.size === 0) { this.doRemoveGroup(sourceGroup); } - referenceGroup.model.openPanel(groupItem, { index }); + destinationGroup.model.openPanel(groupItem, { + index: destinationIndex, + }); } else { - const referenceLocation = getGridLocation(referenceGroup.element); + const referenceLocation = getGridLocation(destinationGroup.element); const targetLocation = getRelativeLocation( this.gridview.orientation, referenceLocation, - target + destinationTarget ); if (sourceGroup && sourceGroup.size < 2) { @@ -898,27 +904,27 @@ export class DockviewComponent // after deleting the group we need to re-evaulate the ref location const updatedReferenceLocation = getGridLocation( - referenceGroup.element + destinationGroup.element ); const location = getRelativeLocation( this.gridview.orientation, updatedReferenceLocation, - target + destinationTarget ); this.doAddGroup(targetGroup, location); } else { const groupItem: IDockviewPanel | undefined = - sourceGroup?.model.removePanel(itemId) || - this.panels.find((panel) => panel.id === itemId); + sourceGroup?.model.removePanel(sourceItemId) || + this.panels.find((panel) => panel.id === sourceItemId); if (!groupItem) { - throw new Error(`No panel with id ${itemId}`); + throw new Error(`No panel with id ${sourceItemId}`); } const dropLocation = getRelativeLocation( this.gridview.orientation, referenceLocation, - target + destinationTarget ); const group = this.createGroupAtLocation(dropLocation); @@ -1094,11 +1100,11 @@ export class DockviewComponent } public dispose(): void { - super.dispose(); - this._onDidActivePanelChange.dispose(); this._onDidAddPanel.dispose(); this._onDidRemovePanel.dispose(); this._onDidLayoutFromJSON.dispose(); + + super.dispose(); } } diff --git a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts index 365fc9613..4c05cefef 100644 --- a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts +++ b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts @@ -297,12 +297,6 @@ export class DockviewGroupPanelModel this.locked = !!options.locked; this.addDisposables( - this._onMove, - this._onDidChange, - this._onDidDrop, - this._onDidAddPanel, - this._onDidRemovePanel, - this._onDidActivePanelChange, this.tabsContainer.onDrop((event) => { this.handleDropEvent(event.event, 'center', event.index); }), @@ -314,7 +308,13 @@ export class DockviewGroupPanelModel }), this.dropTarget.onDrop((event) => { this.handleDropEvent(event.nativeEvent, event.position); - }) + }), + this._onMove, + this._onDidChange, + this._onDidDrop, + this._onDidAddPanel, + this._onDidRemovePanel, + this._onDidActivePanelChange ); } diff --git a/packages/dockview-core/src/dockview/dockviewPanel.ts b/packages/dockview-core/src/dockview/dockviewPanel.ts index 66ac5be5f..cc34b1545 100644 --- a/packages/dockview-core/src/dockview/dockviewPanel.ts +++ b/packages/dockview-core/src/dockview/dockviewPanel.ts @@ -18,7 +18,7 @@ export interface IDockviewPanel extends IDisposable, IPanel { readonly view: IDockviewPanelModel; readonly group: DockviewGroupPanel; readonly api: DockviewPanelApi; - readonly title: string; + readonly title: string | undefined; readonly params: Record | undefined; updateParentGroup(group: DockviewGroupPanel, isGroupActive: boolean): void; init(params: IGroupPanelInitParameters): void; @@ -34,13 +34,13 @@ export class DockviewPanel private _group: DockviewGroupPanel; private _params?: Parameters; - private _title: string; + private _title: string | undefined; get params(): Parameters | undefined { return this._params; } - get title(): string { + get title(): string | undefined { return this._title; } @@ -56,7 +56,6 @@ export class DockviewPanel readonly view: IDockviewPanelModel ) { super(); - this._title = ''; this._group = group; this.api = new DockviewPanelApiImpl(this, this._group); @@ -76,13 +75,13 @@ export class DockviewPanel public init(params: IGroupPanelInitParameters): void { this._params = params.params; - this.setTitle(params.title); - this.view.init({ ...params, api: this.api, containerApi: this.containerApi, }); + + this.setTitle(params.title); } focus(): void { @@ -103,12 +102,12 @@ export class DockviewPanel } setTitle(title: string): void { - const didTitleChange = title !== this._params?.title; + const didTitleChange = title !== this.title; if (didTitleChange) { this._title = title; - this.view?.update({ + this.view.update({ params: { params: this._params, title: this.title, @@ -128,10 +127,10 @@ export class DockviewPanel if (params.title !== this.title) { this._title = params.title; - this.api._onDidTitleChange.fire({ title: this.title }); + this.api._onDidTitleChange.fire({ title: params.title }); } - this.view?.update({ + this.view.update({ params: { params: this._params, title: this.title, diff --git a/packages/dockview-core/src/dom.ts b/packages/dockview-core/src/dom.ts index eb1a37c05..4a36f4bde 100644 --- a/packages/dockview-core/src/dom.ts +++ b/packages/dockview-core/src/dom.ts @@ -111,6 +111,8 @@ class FocusTracker extends CompositeDisposable implements IFocusTracker { constructor(element: HTMLElement | Window) { super(); + this.addDisposables(this._onDidFocus, this._onDidBlur); + let hasFocus = isAncestor(document.activeElement, element); let loosingFocus = false; @@ -169,11 +171,4 @@ class FocusTracker extends CompositeDisposable implements IFocusTracker { refreshState(): void { this._refreshStateHandler(); } - - public dispose(): void { - super.dispose(); - - this._onDidBlur.dispose(); - this._onDidFocus.dispose(); - } } diff --git a/packages/dockview-core/src/events.ts b/packages/dockview-core/src/events.ts index 7f6b07ebf..13b8b3382 100644 --- a/packages/dockview-core/src/events.ts +++ b/packages/dockview-core/src/events.ts @@ -24,24 +24,76 @@ export namespace Event { }; } -// dumb event emitter with better typings than nodes event emitter -// https://github.com/microsoft/vscode/blob/master/src/vs/base/common/event.ts +class LeakageMonitor { + readonly events = new Map, Stacktrace>(); + + get size(): number { + return this.events.size; + } + + add(event: Event, stacktrace: Stacktrace): void { + this.events.set(event, stacktrace); + } + + delete(event: Event): void { + this.events.delete(event); + } + + clear(): void { + this.events.clear(); + } +} + +class Stacktrace { + static create(): Stacktrace { + return new Stacktrace(new Error().stack ?? ''); + } + + private constructor(readonly value: string) {} + + print(): void { + console.warn(this.value); + } +} + +class Listener { + constructor( + readonly callback: (t: T) => void, + readonly stacktrace: Stacktrace | undefined + ) {} +} + +// relatively simple event emitter taken from https://github.com/microsoft/vscode/blob/master/src/vs/base/common/event.ts export class Emitter implements IDisposable { private _event?: Event; private _last?: T; - private _listeners: Array<(e: T) => any> = []; + private _listeners: Listener[] = []; private _disposed = false; + static ENABLE_TRACKING = false; + static readonly MEMORY_LEAK_WATCHER = new LeakageMonitor(); + + static setLeakageMonitorEnabled(isEnabled: boolean) { + if (isEnabled !== Emitter.ENABLE_TRACKING) { + Emitter.MEMORY_LEAK_WATCHER.clear(); + } + Emitter.ENABLE_TRACKING = isEnabled; + } + constructor(private readonly options?: EmitterOptions) {} get event(): Event { if (!this._event) { - this._event = (listener: (e: T) => void): IDisposable => { + this._event = (callback: (e: T) => void): IDisposable => { if (this.options?.replay && this._last !== undefined) { - listener(this._last); + callback(this._last); } + const listener = new Listener( + callback, + Emitter.ENABLE_TRACKING ? Stacktrace.create() : undefined + ); this._listeners.push(listener); return { @@ -49,10 +101,22 @@ export class Emitter implements IDisposable { const index = this._listeners.indexOf(listener); if (index > -1) { this._listeners.splice(index, 1); + } else if (Emitter.ENABLE_TRACKING) { + console.warn( + `Listener already disposed`, + Stacktrace.create().print() + ); } }, }; }; + + if (Emitter.ENABLE_TRACKING) { + Emitter.MEMORY_LEAK_WATCHER.add( + this._event, + Stacktrace.create() + ); + } } return this._event; } @@ -60,13 +124,31 @@ export class Emitter implements IDisposable { public fire(e: T): void { this._last = e; for (const listener of this._listeners) { - listener(e); + listener.callback(e); } } public dispose(): void { - this._listeners = []; - this._disposed = true; + if (!this._disposed) { + this._disposed = true; + + if (this._listeners.length > 0) { + if (Emitter.ENABLE_TRACKING) { + queueMicrotask(() => { + // don't check until stack of execution is completed to allow for out-of-order disposals within the same execution block + for (const listener of this._listeners) { + console.warn(listener.stacktrace?.print()); + } + }); + } + + this._listeners = []; + } + + if (Emitter.ENABLE_TRACKING && this._event) { + Emitter.MEMORY_LEAK_WATCHER.delete(this._event); + } + } } } diff --git a/packages/dockview-core/src/gridview/baseComponentGridview.ts b/packages/dockview-core/src/gridview/baseComponentGridview.ts index 7f2c281df..93c6861eb 100644 --- a/packages/dockview-core/src/gridview/baseComponentGridview.ts +++ b/packages/dockview-core/src/gridview/baseComponentGridview.ts @@ -143,10 +143,7 @@ export abstract class BaseGrid this.addDisposables( this.gridview.onDidChange(() => { this._bufferOnDidLayoutChange.fire(); - }) - ); - - this.addDisposables( + }), Event.any( this.onDidAddGroup, this.onDidRemoveGroup, @@ -297,8 +294,6 @@ export abstract class BaseGrid } public dispose(): void { - super.dispose(); - this._onDidActiveGroupChange.dispose(); this._onDidAddGroup.dispose(); this._onDidRemoveGroup.dispose(); @@ -309,5 +304,7 @@ export abstract class BaseGrid } this.gridview.dispose(); + + super.dispose(); } } diff --git a/packages/dockview-core/src/gridview/basePanelView.ts b/packages/dockview-core/src/gridview/basePanelView.ts index 375b37a47..f61473864 100644 --- a/packages/dockview-core/src/gridview/basePanelView.ts +++ b/packages/dockview-core/src/gridview/basePanelView.ts @@ -68,16 +68,17 @@ export abstract class BasePanelView this._element.style.width = '100%'; this._element.style.overflow = 'hidden'; - const { onDidFocus, onDidBlur } = trackFocus(this._element); + const focusTracker = trackFocus(this._element); this.addDisposables( this.api, - onDidFocus(() => { + focusTracker.onDidFocus(() => { this.api._onDidChangeFocus.fire({ isFocused: true }); }), - onDidBlur(() => { + focusTracker.onDidBlur(() => { this.api._onDidChangeFocus.fire({ isFocused: false }); - }) + }), + focusTracker ); } @@ -124,9 +125,9 @@ export abstract class BasePanelView } dispose(): void { - super.dispose(); - this.api.dispose(); this.part?.dispose(); + + super.dispose(); } } diff --git a/packages/dockview-core/src/gridview/branchNode.ts b/packages/dockview-core/src/gridview/branchNode.ts index c97e33243..6295b3f88 100644 --- a/packages/dockview-core/src/gridview/branchNode.ts +++ b/packages/dockview-core/src/gridview/branchNode.ts @@ -260,13 +260,13 @@ export class BranchNode extends CompositeDisposable implements IView { return this.splitview.getViewCachedVisibleSize(index); } - public removeChild(index: number, sizing?: Sizing): void { + public removeChild(index: number, sizing?: Sizing): Node { if (index < 0 || index >= this.children.length) { throw new Error('Invalid index'); } this.splitview.removeView(index, sizing); - this._removeChild(index); + return this._removeChild(index); } private _addChild(node: Node, index: number): void { @@ -296,9 +296,10 @@ export class BranchNode extends CompositeDisposable implements IView { } public dispose(): void { - super.dispose(); this._childrenDisposable.dispose(); - this.children.forEach((child) => child.dispose()); this.splitview.dispose(); + this.children.forEach((child) => child.dispose()); + + super.dispose(); } } diff --git a/packages/dockview-core/src/gridview/gridview.ts b/packages/dockview-core/src/gridview/gridview.ts index 37242cc33..583b63c6f 100644 --- a/packages/dockview-core/src/gridview/gridview.ts +++ b/packages/dockview-core/src/gridview/gridview.ts @@ -462,7 +462,8 @@ export class Gridview implements IDisposable { if (oldRoot.children.length === 1) { // can remove one level of redundant branching if there is only a single child const childReference = oldRoot.children[0]; - oldRoot.removeChild(0); // remove to prevent disposal when disposing of unwanted root + const child = oldRoot.removeChild(0); // remove to prevent disposal when disposing of unwanted root + child.dispose(); oldRoot.dispose(); this._root.addChild( @@ -632,7 +633,8 @@ export class Gridview implements IDisposable { newSiblingSize = Sizing.Invisible(newSiblingCachedVisibleSize); } - grandParent.removeChild(parentIndex); + const child = grandParent.removeChild(parentIndex); + child.dispose(); const newParent = new BranchNode( parent.orientation, @@ -682,14 +684,18 @@ export class Gridview implements IDisposable { throw new Error('Invalid location'); } - parent.removeChild(index, sizing); + const view = node.view; + node.dispose(); // dispose of node + + const child = parent.removeChild(index, sizing); + child.dispose(); if (parent.children.length === 0) { - return node.view; + return view; } if (parent.children.length > 1) { - return node.view; + return view; } const sibling = parent.children[0]; @@ -698,25 +704,28 @@ export class Gridview implements IDisposable { // parent is root if (sibling instanceof LeafNode) { - return node.view; + return view; } // we must promote sibling to be the new root - parent.removeChild(0, sizing); + const child = parent.removeChild(0, sizing); + child.dispose(); this.root = sibling; - return node.view; + return view; } const [grandParent, ..._] = [...pathToParent].reverse(); const [parentIndex, ...__] = [...rest].reverse(); const isSiblingVisible = parent.isChildVisible(0); - parent.removeChild(0, sizing); + const childNode = parent.removeChild(0, sizing); + childNode.dispose(); const sizes = grandParent.children.map((_size, i) => grandParent.getChildSize(i) ); - grandParent.removeChild(parentIndex, sizing); + const parentNode = grandParent.removeChild(parentIndex, sizing); + parentNode.dispose(); if (sibling instanceof BranchNode) { sizes.splice( @@ -745,7 +754,7 @@ export class Gridview implements IDisposable { grandParent.resizeChild(i, sizes[i]); } - return node.view; + return view; } public layout(width: number, height: number): void { diff --git a/packages/dockview-core/src/gridview/gridviewPanel.ts b/packages/dockview-core/src/gridview/gridviewPanel.ts index 530a3cc70..9562bb92b 100644 --- a/packages/dockview-core/src/gridview/gridviewPanel.ts +++ b/packages/dockview-core/src/gridview/gridviewPanel.ts @@ -154,7 +154,6 @@ export abstract class GridviewPanel this.api.initialize(this); // TODO: required to by-pass 'super before this' requirement this.addDisposables( - this._onDidChange, this.api.onVisibilityChange((event) => { const { isVisible } = event; const { accessor } = this._params as GridviewInitParameters; @@ -195,7 +194,8 @@ export abstract class GridviewPanel height: event.height, width: event.width, }); - }) + }), + this._onDidChange ); } diff --git a/packages/dockview-core/src/lifecycle.ts b/packages/dockview-core/src/lifecycle.ts index e756bc803..8262b28d1 100644 --- a/packages/dockview-core/src/lifecycle.ts +++ b/packages/dockview-core/src/lifecycle.ts @@ -16,7 +16,7 @@ export namespace Disposable { } export class CompositeDisposable { - private readonly disposables: IDisposable[]; + private readonly _disposables: IDisposable[]; private _isDisposed = false; protected get isDisposed(): boolean { @@ -28,15 +28,15 @@ export class CompositeDisposable { } constructor(...args: IDisposable[]) { - this.disposables = args; + this._disposables = args; } public addDisposables(...args: IDisposable[]): void { - args.forEach((arg) => this.disposables.push(arg)); + args.forEach((arg) => this._disposables.push(arg)); } public dispose(): void { - this.disposables.forEach((arg) => arg.dispose()); + this._disposables.forEach((arg) => arg.dispose()); this._isDisposed = true; } diff --git a/packages/dockview-core/src/splitview/splitview.ts b/packages/dockview-core/src/splitview/splitview.ts index 447529cfe..48295a8c7 100644 --- a/packages/dockview-core/src/splitview/splitview.ts +++ b/packages/dockview-core/src/splitview/splitview.ts @@ -13,6 +13,7 @@ import { Event, Emitter } from '../events'; import { pushToStart, pushToEnd, firstIndex } from '../array'; import { range, clamp } from '../math'; import { ViewItem } from './viewItem'; +import { IDisposable } from '../lifecycle'; export enum Orientation { HORIZONTAL = 'HORIZONTAL', @@ -42,7 +43,7 @@ export enum LayoutPriority { Normal = 'normal', } -export interface IBaseView { +export interface IBaseView extends IDisposable { minimumSize: number; maximumSize: number; snap?: boolean; @@ -97,7 +98,7 @@ export class Splitview { private element: HTMLElement; private viewContainer: HTMLElement; private sashContainer: HTMLElement; - private views: ViewItem[] = []; + private viewItems: ViewItem[] = []; private sashes: ISashItem[] = []; private _orientation: Orientation; private _size = 0; @@ -132,7 +133,7 @@ export class Splitview { } public get length(): number { - return this.views.length; + return this.viewItems.length; } public get proportions(): number[] | undefined { @@ -159,13 +160,13 @@ export class Splitview { } get minimumSize(): number { - return this.views.reduce((r, item) => r + item.minimumSize, 0); + return this.viewItems.reduce((r, item) => r + item.minimumSize, 0); } get maximumSize(): number { return this.length === 0 ? Number.POSITIVE_INFINITY - : this.views.reduce((r, item) => r + item.maximumSize, 0); + : this.viewItems.reduce((r, item) => r + item.maximumSize, 0); } get startSnappingEnabled(): boolean { @@ -240,7 +241,7 @@ export class Splitview { }); // Initialize content size and proportions for first layout - this.contentSize = this.views.reduce((r, i) => r + i.size, 0); + this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); this.saveProportions(); } } @@ -261,22 +262,22 @@ export class Splitview { } isViewVisible(index: number): boolean { - if (index < 0 || index >= this.views.length) { + if (index < 0 || index >= this.viewItems.length) { throw new Error('Index out of bounds'); } - const viewItem = this.views[index]; + const viewItem = this.viewItems[index]; return viewItem.visible; } setViewVisible(index: number, visible: boolean): void { - if (index < 0 || index >= this.views.length) { + if (index < 0 || index >= this.viewItems.length) { throw new Error('Index out of bounds'); } toggleClass(this.container, 'visible', visible); - const viewItem = this.views[index]; + const viewItem = this.viewItems[index]; toggleClass(this.container, 'visible', visible); @@ -288,30 +289,30 @@ export class Splitview { } getViewSize(index: number): number { - if (index < 0 || index >= this.views.length) { + if (index < 0 || index >= this.viewItems.length) { return -1; } - return this.views[index].size; + return this.viewItems[index].size; } resizeView(index: number, size: number): void { - if (index < 0 || index >= this.views.length) { + if (index < 0 || index >= this.viewItems.length) { return; } - const indexes = range(this.views.length).filter((i) => i !== index); + const indexes = range(this.viewItems.length).filter((i) => i !== index); const lowPriorityIndexes = [ ...indexes.filter( - (i) => this.views[i].priority === LayoutPriority.Low + (i) => this.viewItems[i].priority === LayoutPriority.Low ), index, ]; const highPriorityIndexes = indexes.filter( - (i) => this.views[i].priority === LayoutPriority.High + (i) => this.viewItems[i].priority === LayoutPriority.High ); - const item = this.views[index]; + const item = this.viewItems[index]; size = Math.round(size); size = clamp( size, @@ -324,13 +325,13 @@ export class Splitview { } public getViews(): T[] { - return this.views.map((x) => x.view as T); + return this.viewItems.map((x) => x.view as T); } private onDidChange(item: ViewItem, size: number | undefined): void { - const index = this.views.indexOf(item); + const index = this.viewItems.indexOf(item); - if (index < 0 || index >= this.views.length) { + if (index < 0 || index >= this.viewItems.length) { return; } @@ -345,7 +346,7 @@ export class Splitview { public addView( view: IView, size: number | Sizing = { type: 'distribute' }, - index: number = this.views.length, + index: number = this.viewItems.length, skipLayout?: boolean ): void { const container = document.createElement('div'); @@ -369,14 +370,14 @@ export class Splitview { this.onDidChange(viewItem, newSize.size) ); - const dispose = () => { - disposable?.dispose(); - this.viewContainer.removeChild(container); - }; + const viewItem = new ViewItem(container, view, viewSize, { + dispose: () => { + disposable.dispose(); + this.viewContainer.removeChild(container); + }, + }); - const viewItem = new ViewItem(container, view, viewSize, { dispose }); - - if (index === this.views.length) { + if (index === this.viewItems.length) { this.viewContainer.appendChild(container); } else { this.viewContainer.insertBefore( @@ -385,15 +386,15 @@ export class Splitview { ); } - this.views.splice(index, 0, viewItem); + this.viewItems.splice(index, 0, viewItem); - if (this.views.length > 1) { + if (this.viewItems.length > 1) { //add sash const sash = document.createElement('div'); sash.className = 'sash'; const onStart = (event: MouseEvent) => { - for (const item of this.views) { + for (const item of this.viewItems) { item.enabled = false; } @@ -417,19 +418,20 @@ export class Splitview { ); // - const sizes = this.views.map((x) => x.size); + const sizes = this.viewItems.map((x) => x.size); // let snapBefore: ISashDragSnapState | undefined; let snapAfter: ISashDragSnapState | undefined; const upIndexes = range(sashIndex, -1); - const downIndexes = range(sashIndex + 1, this.views.length); + const downIndexes = range(sashIndex + 1, this.viewItems.length); const minDeltaUp = upIndexes.reduce( - (r, i) => r + (this.views[i].minimumSize - sizes[i]), + (r, i) => r + (this.viewItems[i].minimumSize - sizes[i]), 0 ); const maxDeltaUp = upIndexes.reduce( - (r, i) => r + (this.views[i].viewMaximumSize - sizes[i]), + (r, i) => + r + (this.viewItems[i].viewMaximumSize - sizes[i]), 0 ); const maxDeltaDown = @@ -437,7 +439,8 @@ export class Splitview { ? Number.POSITIVE_INFINITY : downIndexes.reduce( (r, i) => - r + (sizes[i] - this.views[i].minimumSize), + r + + (sizes[i] - this.viewItems[i].minimumSize), 0 ); const minDeltaDown = @@ -446,7 +449,8 @@ export class Splitview { : downIndexes.reduce( (r, i) => r + - (sizes[i] - this.views[i].viewMaximumSize), + (sizes[i] - + this.viewItems[i].viewMaximumSize), 0 ); const minDelta = Math.max(minDeltaUp, minDeltaDown); @@ -454,7 +458,7 @@ export class Splitview { const snapBeforeIndex = this.findFirstSnapIndex(upIndexes); const snapAfterIndex = this.findFirstSnapIndex(downIndexes); if (typeof snapBeforeIndex === 'number') { - const snappedViewItem = this.views[snapBeforeIndex]; + const snappedViewItem = this.viewItems[snapBeforeIndex]; const halfSize = Math.floor( snappedViewItem.viewMinimumSize / 2 ); @@ -469,7 +473,7 @@ export class Splitview { } if (typeof snapAfterIndex === 'number') { - const snappedViewItem = this.views[snapAfterIndex]; + const snappedViewItem = this.viewItems[snapAfterIndex]; const halfSize = Math.floor( snappedViewItem.viewMinimumSize / 2 ); @@ -507,7 +511,7 @@ export class Splitview { }; const end = () => { - for (const item of this.views) { + for (const item of this.viewItems) { item.enabled = true; } @@ -562,7 +566,7 @@ export class Splitview { const flexibleViewItems: ViewItem[] = []; let flexibleSize = 0; - for (const item of this.views) { + for (const item of this.viewItems) { if (item.maximumSize - item.minimumSize > 0) { flexibleViewItems.push(item); flexibleSize += item.size; @@ -575,12 +579,12 @@ export class Splitview { item.size = clamp(size, item.minimumSize, item.maximumSize); } - const indexes = range(this.views.length); + const indexes = range(this.viewItems.length); const lowPriorityIndexes = indexes.filter( - (i) => this.views[i].priority === LayoutPriority.Low + (i) => this.viewItems[i].priority === LayoutPriority.Low ); const highPriorityIndexes = indexes.filter( - (i) => this.views[i].priority === LayoutPriority.High + (i) => this.viewItems[i].priority === LayoutPriority.High ); this.relayout(lowPriorityIndexes, highPriorityIndexes); @@ -592,11 +596,11 @@ export class Splitview { skipLayout = false ): IView { // Remove view - const viewItem = this.views.splice(index, 1)[0]; + const viewItem = this.viewItems.splice(index, 1)[0]; viewItem.dispose(); // Remove sash - if (this.views.length >= 1) { + if (this.viewItems.length >= 1) { const sashIndex = Math.max(index - 1, 0); const sashItem = this.sashes.splice(sashIndex, 1)[0]; sashItem.disposable(); @@ -616,11 +620,11 @@ export class Splitview { } getViewCachedVisibleSize(index: number): number | undefined { - if (index < 0 || index >= this.views.length) { + if (index < 0 || index >= this.viewItems.length) { throw new Error('Index out of bounds'); } - const viewItem = this.views[index]; + const viewItem = this.viewItems[index]; return viewItem.cachedVisibleSize; } @@ -640,24 +644,24 @@ export class Splitview { this.orthogonalSize = orthogonalSize; if (!this.proportions) { - const indexes = range(this.views.length); + const indexes = range(this.viewItems.length); const lowPriorityIndexes = indexes.filter( - (i) => this.views[i].priority === LayoutPriority.Low + (i) => this.viewItems[i].priority === LayoutPriority.Low ); const highPriorityIndexes = indexes.filter( - (i) => this.views[i].priority === LayoutPriority.High + (i) => this.viewItems[i].priority === LayoutPriority.High ); this.resize( - this.views.length - 1, + this.viewItems.length - 1, size - previousSize, undefined, lowPriorityIndexes, highPriorityIndexes ); } else { - for (let i = 0; i < this.views.length; i++) { - const item = this.views[i]; + for (let i = 0; i < this.viewItems.length; i++) { + const item = this.viewItems[i]; item.size = clamp( Math.round(this.proportions[i] * size), @@ -675,10 +679,10 @@ export class Splitview { lowPriorityIndexes?: number[], highPriorityIndexes?: number[] ): void { - const contentSize = this.views.reduce((r, i) => r + i.size, 0); + const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); this.resize( - this.views.length - 1, + this.viewItems.length - 1, this._size - contentSize, undefined, lowPriorityIndexes, @@ -690,15 +694,15 @@ export class Splitview { } private distributeEmptySpace(lowPriorityIndex?: number): void { - const contentSize = this.views.reduce((r, i) => r + i.size, 0); + const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); let emptyDelta = this.size - contentSize; - const indexes = range(this.views.length - 1, -1); + const indexes = range(this.viewItems.length - 1, -1); const lowPriorityIndexes = indexes.filter( - (i) => this.views[i].priority === LayoutPriority.Low + (i) => this.viewItems[i].priority === LayoutPriority.Low ); const highPriorityIndexes = indexes.filter( - (i) => this.views[i].priority === LayoutPriority.High + (i) => this.viewItems[i].priority === LayoutPriority.High ); for (const index of highPriorityIndexes) { @@ -714,7 +718,7 @@ export class Splitview { } for (let i = 0; emptyDelta !== 0 && i < indexes.length; i++) { - const item = this.views[indexes[i]]; + const item = this.viewItems[indexes[i]]; const size = clamp( item.size + emptyDelta, item.minimumSize, @@ -729,21 +733,21 @@ export class Splitview { private saveProportions(): void { if (this.proportionalLayout && this.contentSize > 0) { - this._proportions = this.views.map( + this._proportions = this.viewItems.map( (i) => i.size / this.contentSize ); } } private layoutViews(): void { - this.contentSize = this.views.reduce((r, i) => r + i.size, 0); + this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); let sum = 0; const x: number[] = []; this.updateSashEnablement(); - for (let i = 0; i < this.views.length - 1; i++) { - sum += this.views[i].size; + for (let i = 0; i < this.viewItems.length - 1; i++) { + sum += this.viewItems[i].size; x.push(sum); const offset = Math.min(Math.max(0, sum - 2), this.size - 4); @@ -757,7 +761,7 @@ export class Splitview { this.sashes[i].container.style.top = `${offset}px`; } } - this.views.forEach((view, i) => { + this.viewItems.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`; @@ -778,7 +782,7 @@ export class Splitview { private findFirstSnapIndex(indexes: number[]): number | undefined { // visible views first for (const index of indexes) { - const viewItem = this.views[index]; + const viewItem = this.viewItems[index]; if (!viewItem.visible) { continue; @@ -791,7 +795,7 @@ export class Splitview { // then, hidden views for (const index of indexes) { - const viewItem = this.views[index]; + const viewItem = this.viewItems[index]; if ( viewItem.visible && @@ -810,16 +814,16 @@ export class Splitview { private updateSashEnablement(): void { let previous = false; - const collapsesDown = this.views.map( + const collapsesDown = this.viewItems.map( (i) => (previous = i.size - i.minimumSize > 0 || previous) ); previous = false; - const expandsDown = this.views.map( + const expandsDown = this.viewItems.map( (i) => (previous = i.maximumSize - i.size > 0 || previous) ); - const reverseViews = [...this.views].reverse(); + const reverseViews = [...this.viewItems].reverse(); previous = false; const collapsesUp = reverseViews .map((i) => (previous = i.size - i.minimumSize > 0 || previous)) @@ -833,7 +837,7 @@ export class Splitview { let position = 0; for (let index = 0; index < this.sashes.length; index++) { const sash = this.sashes[index]; - const viewItem = this.views[index]; + const viewItem = this.viewItems[index]; position += viewItem.size; const min = !(collapsesDown[index] && expandsUp[index + 1]); @@ -841,16 +845,16 @@ export class Splitview { if (min && max) { const upIndexes = range(index, -1); - const downIndexes = range(index + 1, this.views.length); + const downIndexes = range(index + 1, this.viewItems.length); const snapBeforeIndex = this.findFirstSnapIndex(upIndexes); const snapAfterIndex = this.findFirstSnapIndex(downIndexes); const snappedBefore = typeof snapBeforeIndex === 'number' && - !this.views[snapBeforeIndex].visible; + !this.viewItems[snapBeforeIndex].visible; const snappedAfter = typeof snapAfterIndex === 'number' && - !this.views[snapAfterIndex].visible; + !this.viewItems[snapAfterIndex].visible; if ( snappedBefore && @@ -887,7 +891,7 @@ export class Splitview { private resize = ( index: number, delta: number, - sizes: number[] = this.views.map((x) => x.size), + sizes: number[] = this.viewItems.map((x) => x.size), lowPriorityIndexes?: number[], highPriorityIndexes?: number[], overloadMinDelta: number = Number.NEGATIVE_INFINITY, @@ -895,12 +899,12 @@ export class Splitview { snapBefore?: ISashDragSnapState, snapAfter?: ISashDragSnapState ): number => { - if (index < 0 || index > this.views.length) { + if (index < 0 || index > this.viewItems.length) { return 0; } const upIndexes = range(index, -1); - const downIndexes = range(index + 1, this.views.length); + const downIndexes = range(index + 1, this.viewItems.length); // if (highPriorityIndexes) { for (const i of highPriorityIndexes) { @@ -916,18 +920,18 @@ export class Splitview { } } // - const upItems = upIndexes.map((i) => this.views[i]); + const upItems = upIndexes.map((i) => this.viewItems[i]); const upSizes = upIndexes.map((i) => sizes[i]); // - const downItems = downIndexes.map((i) => this.views[i]); + const downItems = downIndexes.map((i) => this.viewItems[i]); const downSizes = downIndexes.map((i) => sizes[i]); // const minDeltaUp = upIndexes.reduce( - (_, i) => _ + this.views[i].minimumSize - sizes[i], + (_, i) => _ + this.viewItems[i].minimumSize - sizes[i], 0 ); const maxDeltaUp = upIndexes.reduce( - (_, i) => _ + this.views[i].maximumSize - sizes[i], + (_, i) => _ + this.viewItems[i].maximumSize - sizes[i], 0 ); // @@ -935,7 +939,7 @@ export class Splitview { downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce( - (_, i) => _ + sizes[i] - this.views[i].minimumSize, + (_, i) => _ + sizes[i] - this.viewItems[i].minimumSize, 0 ); @@ -943,7 +947,7 @@ export class Splitview { downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce( - (_, i) => _ + sizes[i] - this.views[i].maximumSize, + (_, i) => _ + sizes[i] - this.viewItems[i].maximumSize, 0 ); // @@ -952,14 +956,14 @@ export class Splitview { // let snapped = false; if (snapBefore) { - const snapView = this.views[snapBefore.index]; + const snapView = this.viewItems[snapBefore.index]; const visible = delta >= snapBefore.limitDelta; snapped = visible !== snapView.visible; snapView.setVisible(visible, snapBefore.size); } if (!snapped && snapAfter) { - const snapView = this.views[snapAfter.index]; + const snapView = this.viewItems[snapAfter.index]; const visible = delta < snapAfter.limitDelta; snapped = visible !== snapView.visible; snapView.setVisible(visible, snapAfter.size); @@ -1047,6 +1051,10 @@ export class Splitview { } } + for (const viewItem of this.viewItems) { + viewItem.dispose(); + } + this.element.remove(); } } diff --git a/packages/dockview-core/src/splitview/splitviewComponent.ts b/packages/dockview-core/src/splitview/splitviewComponent.ts index f4f0bee7c..37573c40e 100644 --- a/packages/dockview-core/src/splitview/splitviewComponent.ts +++ b/packages/dockview-core/src/splitview/splitviewComponent.ts @@ -1,7 +1,6 @@ import { CompositeDisposable, IDisposable, - IValueDisposable, MutableDisposable, } from '../lifecycle'; import { @@ -83,10 +82,10 @@ export class SplitviewComponent extends Resizable implements ISplitviewComponent { - private _disposable = new MutableDisposable(); + private _splitviewChangeDisposable = new MutableDisposable(); private _splitview!: Splitview; private _activePanel: SplitviewPanel | undefined; - private _panels = new Map>(); + private _panels = new Map(); private _options: SplitviewComponentOptions; private readonly _onDidLayoutfromJSON = new Emitter(); @@ -124,7 +123,7 @@ export class SplitviewComponent set splitview(value: Splitview) { this._splitview = value; - this._disposable.value = new CompositeDisposable( + this._splitviewChangeDisposable.value = new CompositeDisposable( this._splitview.onDidSashEnd(() => { this._onDidLayoutChange.fire(undefined); }), @@ -170,7 +169,6 @@ export class SplitviewComponent this.splitview = new Splitview(this.element, options); this.addDisposables( - this._disposable, this._onDidAddView, this._onDidLayoutfromJSON, this._onDidRemoveView, @@ -226,19 +224,19 @@ export class SplitviewComponent } removePanel(panel: SplitviewPanel, sizing?: Sizing): void { - const disposable = this._panels.get(panel.id); + const item = this._panels.get(panel.id); - if (!disposable) { + if (!item) { throw new Error(`unknown splitview panel ${panel.id}`); } - disposable.disposable.dispose(); - disposable.value.dispose(); + item.dispose(); this._panels.delete(panel.id); const index = this.panels.findIndex((_) => _ === panel); - this.splitview.removeView(index, sizing); + const removedView = this.splitview.removeView(index, sizing); + removedView.dispose(); const panels = this.panels; if (panels.length > 0) { @@ -250,7 +248,7 @@ export class SplitviewComponent return this.panels.find((view) => view.id === id); } - addPanel(options: AddSplitviewComponentOptions): ISplitviewPanel { + addPanel(options: AddSplitviewComponentOptions): SplitviewPanel { if (this._panels.has(options.id)) { throw new Error(`panel ${options.id} already exists`); } @@ -308,7 +306,7 @@ export class SplitviewComponent this.setActive(view, true); }); - this._panels.set(view.id, { disposable, value: view }); + this._panels.set(view.id, disposable); } toJSON(): SerializedSplitview { @@ -404,23 +402,34 @@ export class SplitviewComponent } clear(): void { - for (const [_, value] of this._panels.entries()) { - value.disposable.dispose(); - value.value.dispose(); + for (const disposable of this._panels.values()) { + disposable.dispose(); } + this._panels.clear(); - this.splitview.dispose(); + + while (this.splitview.length > 0) { + const view = this.splitview.removeView(0, Sizing.Distribute, true); + view.dispose(); + } } dispose(): void { - for (const [_, value] of this._panels.entries()) { - value.disposable.dispose(); - value.value.dispose(); + for (const disposable of this._panels.values()) { + disposable.dispose(); } + this._panels.clear(); + const views = this.splitview.getViews(); + + this._splitviewChangeDisposable.dispose(); this.splitview.dispose(); + for (const view of views) { + view.dispose(); + } + super.dispose(); } } diff --git a/packages/dockview-core/src/splitview/splitviewPanel.ts b/packages/dockview-core/src/splitview/splitviewPanel.ts index 4782e8c30..d0ac1c41c 100644 --- a/packages/dockview-core/src/splitview/splitviewPanel.ts +++ b/packages/dockview-core/src/splitview/splitviewPanel.ts @@ -7,6 +7,7 @@ import { SplitviewPanelApiImpl } from '../api/splitviewPanelApi'; import { LayoutPriority, Orientation } from './splitview'; import { FunctionOrValue } from '../types'; import { Emitter, Event } from '../events'; +import { CompositeDisposable } from '../lifecycle'; export interface ISplitviewPanel extends BasePanelViewExported { diff --git a/packages/dockview/package.json b/packages/dockview/package.json index 19ec29c99..975e2e48c 100644 --- a/packages/dockview/package.json +++ b/packages/dockview/package.json @@ -1,6 +1,6 @@ { "name": "dockview", - "version": "1.7.2", + "version": "1.7.3", "description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support", "main": "./dist/cjs/index.js", "types": "./dist/cjs/index.d.ts", @@ -56,7 +56,7 @@ "author": "https://github.com/mathuo", "license": "MIT", "dependencies": { - "dockview-core": "^1.7.2" + "dockview-core": "^1.7.3" }, "devDependencies": { "@rollup/plugin-node-resolve": "^15.0.1", diff --git a/packages/docs/blog/2023-06-03-dockview-1.7.3.md b/packages/docs/blog/2023-06-03-dockview-1.7.3.md new file mode 100644 index 000000000..7a4e6cba8 --- /dev/null +++ b/packages/docs/blog/2023-06-03-dockview-1.7.3.md @@ -0,0 +1,17 @@ +--- +slug: dockview-1.7.3-release +title: Dockview 1.7.3 +tags: [release] +--- + +# Release Notes + +Please reference to docs @ [dockview.dev](https://dockview.dev). + +## 🚀 Features + +## 🛠 Miscs + +- Fix bug custom params named 'title' conflicting with built-in tab 'title' object [#258](https://github.com/mathuo/dockview/issues/258) + +## 🔥 Breaking changes diff --git a/packages/docs/package.json b/packages/docs/package.json index a5a509892..a1a77a4bb 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "dockview-docs", - "version": "1.7.2", + "version": "1.7.3", "private": true, "scripts": { "docusaurus": "docusaurus", @@ -22,7 +22,7 @@ "@minoru/react-dnd-treeview": "^3.4.3", "axios": "^1.3.3", "clsx": "^1.2.1", - "dockview": "^1.7.2", + "dockview": "^1.7.3", "prism-react-renderer": "^1.3.5", "react": "^18.2.0", "react-dnd": "^16.0.1", diff --git a/packages/docs/sandboxes/constraints-dockview/package.json b/packages/docs/sandboxes/constraints-dockview/package.json index e1497330a..3a34098c8 100644 --- a/packages/docs/sandboxes/constraints-dockview/package.json +++ b/packages/docs/sandboxes/constraints-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/constraints-dockview/tsconfig.json b/packages/docs/sandboxes/constraints-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/constraints-dockview/tsconfig.json +++ b/packages/docs/sandboxes/constraints-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/customheader-dockview/package.json b/packages/docs/sandboxes/customheader-dockview/package.json index 8bf9bd86d..d3ede7462 100644 --- a/packages/docs/sandboxes/customheader-dockview/package.json +++ b/packages/docs/sandboxes/customheader-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/customheader-dockview/tsconfig.json b/packages/docs/sandboxes/customheader-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/customheader-dockview/tsconfig.json +++ b/packages/docs/sandboxes/customheader-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/demo-dockview/package.json b/packages/docs/sandboxes/demo-dockview/package.json index 99ba4505f..fe47d8659 100644 --- a/packages/docs/sandboxes/demo-dockview/package.json +++ b/packages/docs/sandboxes/demo-dockview/package.json @@ -16,7 +16,8 @@ "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "@types/uuid": "^9.0.0", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/demo-dockview/tsconfig.json b/packages/docs/sandboxes/demo-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/demo-dockview/tsconfig.json +++ b/packages/docs/sandboxes/demo-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/dnd-dockview/package.json b/packages/docs/sandboxes/dnd-dockview/package.json index f9ef48454..fec39fd83 100644 --- a/packages/docs/sandboxes/dnd-dockview/package.json +++ b/packages/docs/sandboxes/dnd-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/dnd-dockview/tsconfig.json b/packages/docs/sandboxes/dnd-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/dnd-dockview/tsconfig.json +++ b/packages/docs/sandboxes/dnd-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/dockview-app/package.json b/packages/docs/sandboxes/dockview-app/package.json index b4c4e1d29..edc8773be 100644 --- a/packages/docs/sandboxes/dockview-app/package.json +++ b/packages/docs/sandboxes/dockview-app/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/dockview-app/tsconfig.json b/packages/docs/sandboxes/dockview-app/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/dockview-app/tsconfig.json +++ b/packages/docs/sandboxes/dockview-app/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/events-dockview/package.json b/packages/docs/sandboxes/events-dockview/package.json index d32c8451d..176fc41c3 100644 --- a/packages/docs/sandboxes/events-dockview/package.json +++ b/packages/docs/sandboxes/events-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/events-dockview/tsconfig.json b/packages/docs/sandboxes/events-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/events-dockview/tsconfig.json +++ b/packages/docs/sandboxes/events-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/externaldnd-dockview/package.json b/packages/docs/sandboxes/externaldnd-dockview/package.json index 10ab655c5..fba6c4e59 100644 --- a/packages/docs/sandboxes/externaldnd-dockview/package.json +++ b/packages/docs/sandboxes/externaldnd-dockview/package.json @@ -16,7 +16,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/externaldnd-dockview/tsconfig.json b/packages/docs/sandboxes/externaldnd-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/externaldnd-dockview/tsconfig.json +++ b/packages/docs/sandboxes/externaldnd-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/fullwidthtab-dockview/package.json b/packages/docs/sandboxes/fullwidthtab-dockview/package.json index 5fddc50b3..ade7f643e 100644 --- a/packages/docs/sandboxes/fullwidthtab-dockview/package.json +++ b/packages/docs/sandboxes/fullwidthtab-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/fullwidthtab-dockview/tsconfig.json b/packages/docs/sandboxes/fullwidthtab-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/fullwidthtab-dockview/tsconfig.json +++ b/packages/docs/sandboxes/fullwidthtab-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/groupcontrol-dockview/package.json b/packages/docs/sandboxes/groupcontrol-dockview/package.json index 21e91b3a4..7c88c11f1 100644 --- a/packages/docs/sandboxes/groupcontrol-dockview/package.json +++ b/packages/docs/sandboxes/groupcontrol-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/groupcontrol-dockview/tsconfig.json b/packages/docs/sandboxes/groupcontrol-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/groupcontrol-dockview/tsconfig.json +++ b/packages/docs/sandboxes/groupcontrol-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/javascript/fullwidthtab-dockview/package.json b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/package.json index c4d975c1a..14c42f556 100644 --- a/packages/docs/sandboxes/javascript/fullwidthtab-dockview/package.json +++ b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/package.json @@ -10,7 +10,8 @@ "dockview-core": "*" }, "devDependencies": { - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/javascript/fullwidthtab-dockview/tsconfig.json b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/javascript/fullwidthtab-dockview/tsconfig.json +++ b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/javascript/simple-dockview/package.json b/packages/docs/sandboxes/javascript/simple-dockview/package.json index bc36e6b5d..d06484078 100644 --- a/packages/docs/sandboxes/javascript/simple-dockview/package.json +++ b/packages/docs/sandboxes/javascript/simple-dockview/package.json @@ -10,7 +10,8 @@ "dockview-core": "*" }, "devDependencies": { - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/javascript/simple-dockview/tsconfig.json b/packages/docs/sandboxes/javascript/simple-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/javascript/simple-dockview/tsconfig.json +++ b/packages/docs/sandboxes/javascript/simple-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/javascript/tabheight-dockview/package.json b/packages/docs/sandboxes/javascript/tabheight-dockview/package.json index 5aeba5d4d..754b48268 100644 --- a/packages/docs/sandboxes/javascript/tabheight-dockview/package.json +++ b/packages/docs/sandboxes/javascript/tabheight-dockview/package.json @@ -10,7 +10,8 @@ "dockview-core": "*" }, "devDependencies": { - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/javascript/tabheight-dockview/tsconfig.json b/packages/docs/sandboxes/javascript/tabheight-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/javascript/tabheight-dockview/tsconfig.json +++ b/packages/docs/sandboxes/javascript/tabheight-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/javascript/vanilla-dockview/package.json b/packages/docs/sandboxes/javascript/vanilla-dockview/package.json index b6bf01665..73bc3df0a 100644 --- a/packages/docs/sandboxes/javascript/vanilla-dockview/package.json +++ b/packages/docs/sandboxes/javascript/vanilla-dockview/package.json @@ -1,5 +1,5 @@ { - "name": "vanilla-dockview", + "name": "javascript-vanilla-dockview", "description": "", "keywords": [ "dockview" @@ -10,9 +10,15 @@ "dockview-core": "*" }, "devDependencies": { - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" }, - "scripts": {}, "browserslist": [ ">0.2%", "not dead", diff --git a/packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json b/packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json index 728865f76..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json +++ b/packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json @@ -1,19 +1,18 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true + } } diff --git a/packages/docs/sandboxes/layout-dockview/package.json b/packages/docs/sandboxes/layout-dockview/package.json index 309473289..f8aafc4e2 100644 --- a/packages/docs/sandboxes/layout-dockview/package.json +++ b/packages/docs/sandboxes/layout-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/layout-dockview/tsconfig.json b/packages/docs/sandboxes/layout-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/layout-dockview/tsconfig.json +++ b/packages/docs/sandboxes/layout-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/nativeapp-dockview/package.json b/packages/docs/sandboxes/nativeapp-dockview/package.json index ad8d43fcc..9ebe7692a 100644 --- a/packages/docs/sandboxes/nativeapp-dockview/package.json +++ b/packages/docs/sandboxes/nativeapp-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/nativeapp-dockview/tsconfig.json b/packages/docs/sandboxes/nativeapp-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/nativeapp-dockview/tsconfig.json +++ b/packages/docs/sandboxes/nativeapp-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/nested-dockview/package.json b/packages/docs/sandboxes/nested-dockview/package.json index 4914f99c2..8732f49ac 100644 --- a/packages/docs/sandboxes/nested-dockview/package.json +++ b/packages/docs/sandboxes/nested-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/nested-dockview/tsconfig.json b/packages/docs/sandboxes/nested-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/nested-dockview/tsconfig.json +++ b/packages/docs/sandboxes/nested-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/rendering-dockview/package.json b/packages/docs/sandboxes/rendering-dockview/package.json index c9c6b849a..79f8f007d 100644 --- a/packages/docs/sandboxes/rendering-dockview/package.json +++ b/packages/docs/sandboxes/rendering-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/rendering-dockview/tsconfig.json b/packages/docs/sandboxes/rendering-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/rendering-dockview/tsconfig.json +++ b/packages/docs/sandboxes/rendering-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/resize-dockview/package.json b/packages/docs/sandboxes/resize-dockview/package.json index a8f59f461..451e6f516 100644 --- a/packages/docs/sandboxes/resize-dockview/package.json +++ b/packages/docs/sandboxes/resize-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/resize-dockview/tsconfig.json b/packages/docs/sandboxes/resize-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/resize-dockview/tsconfig.json +++ b/packages/docs/sandboxes/resize-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/resizecontainer-dockview/package.json b/packages/docs/sandboxes/resizecontainer-dockview/package.json index 9763e5795..fb2418926 100644 --- a/packages/docs/sandboxes/resizecontainer-dockview/package.json +++ b/packages/docs/sandboxes/resizecontainer-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/resizecontainer-dockview/tsconfig.json b/packages/docs/sandboxes/resizecontainer-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/resizecontainer-dockview/tsconfig.json +++ b/packages/docs/sandboxes/resizecontainer-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/simple-dockview/package.json b/packages/docs/sandboxes/simple-dockview/package.json index 217115b06..ab2f1757f 100644 --- a/packages/docs/sandboxes/simple-dockview/package.json +++ b/packages/docs/sandboxes/simple-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/simple-dockview/tsconfig.json b/packages/docs/sandboxes/simple-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/simple-dockview/tsconfig.json +++ b/packages/docs/sandboxes/simple-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/tabheight-dockview/package.json b/packages/docs/sandboxes/tabheight-dockview/package.json index af6d19d63..9b315f76e 100644 --- a/packages/docs/sandboxes/tabheight-dockview/package.json +++ b/packages/docs/sandboxes/tabheight-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", @@ -28,4 +29,4 @@ "not ie <= 11", "not op_mini all" ] -} +} \ No newline at end of file diff --git a/packages/docs/sandboxes/tabheight-dockview/tsconfig.json b/packages/docs/sandboxes/tabheight-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/tabheight-dockview/tsconfig.json +++ b/packages/docs/sandboxes/tabheight-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/updatetitle-dockview/package.json b/packages/docs/sandboxes/updatetitle-dockview/package.json index 1c7c2ac45..1fcbb4f55 100644 --- a/packages/docs/sandboxes/updatetitle-dockview/package.json +++ b/packages/docs/sandboxes/updatetitle-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/updatetitle-dockview/tsconfig.json b/packages/docs/sandboxes/updatetitle-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/updatetitle-dockview/tsconfig.json +++ b/packages/docs/sandboxes/updatetitle-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/sandboxes/watermark-dockview/package.json b/packages/docs/sandboxes/watermark-dockview/package.json index c04cc2521..faa017e11 100644 --- a/packages/docs/sandboxes/watermark-dockview/package.json +++ b/packages/docs/sandboxes/watermark-dockview/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "react-scripts": "*" }, "scripts": { "start": "react-scripts start", diff --git a/packages/docs/sandboxes/watermark-dockview/tsconfig.json b/packages/docs/sandboxes/watermark-dockview/tsconfig.json index 8aebe3efe..cdc4fb5f5 100644 --- a/packages/docs/sandboxes/watermark-dockview/tsconfig.json +++ b/packages/docs/sandboxes/watermark-dockview/tsconfig.json @@ -13,7 +13,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": true, - "strictNullChecks": true, - "noUnusedLocals": true + "strictNullChecks": true } } diff --git a/packages/docs/versioned_docs/version-1.7.2/basics.mdx b/packages/docs/versioned_docs/version-1.7.3/basics.mdx similarity index 99% rename from packages/docs/versioned_docs/version-1.7.2/basics.mdx rename to packages/docs/versioned_docs/version-1.7.3/basics.mdx index 33d0270c0..b2c8ebcdb 100644 --- a/packages/docs/versioned_docs/version-1.7.2/basics.mdx +++ b/packages/docs/versioned_docs/version-1.7.3/basics.mdx @@ -8,7 +8,6 @@ import { SimpleSplitview2 } from '@site/src/components/simpleSplitview2'; # Basics -asd This section will take you through a number of concepts that can be applied to all dockview components. ## Panels diff --git a/packages/docs/versioned_docs/version-1.7.2/components/_category_.json b/packages/docs/versioned_docs/version-1.7.3/components/_category_.json similarity index 100% rename from packages/docs/versioned_docs/version-1.7.2/components/_category_.json rename to packages/docs/versioned_docs/version-1.7.3/components/_category_.json diff --git a/packages/docs/versioned_docs/version-1.7.2/components/dockview.mdx b/packages/docs/versioned_docs/version-1.7.3/components/dockview.mdx similarity index 95% rename from packages/docs/versioned_docs/version-1.7.2/components/dockview.mdx rename to packages/docs/versioned_docs/version-1.7.3/components/dockview.mdx index 8097910eb..835dee189 100644 --- a/packages/docs/versioned_docs/version-1.7.2/components/dockview.mdx +++ b/packages/docs/versioned_docs/version-1.7.3/components/dockview.mdx @@ -2,7 +2,10 @@ description: Dockview Documentation --- -import { Container } from '@site/src/components/ui/container'; +import { + Container, + MultiFrameworkContainer, +} from '@site/src/components/ui/container'; import Link from '@docusaurus/Link'; import useBaseUrl from '@docusaurus/useBaseUrl'; @@ -24,7 +27,11 @@ import RenderingDockview from '@site/sandboxes/rendering-dockview/src/app'; import DockviewExternalDnd from '@site/sandboxes/externaldnd-dockview/src/app'; import DockviewResizeContainer from '@site/sandboxes/resizecontainer-dockview/src/app'; import DockviewTabheight from '@site/sandboxes/tabheight-dockview/src/app'; + import { attach as attachDockviewVanilla } from '@site/sandboxes/javascript/vanilla-dockview/src/app'; +import { attach as attachSimpleDockview } from '@site/sandboxes/javascript/simple-dockview/src/app'; +import { attach as attachTabHeightDockview } from '@site/sandboxes/javascript/tabheight-dockview/src/app'; +import { attach as attachNativeDockview } from '@site/sandboxes/javascript/fullwidthtab-dockview/src/app'; # Dockview @@ -32,12 +39,16 @@ import { attach as attachDockviewVanilla } from '@site/sandboxes/javascript/vani Dockview is an abstraction built on top of [Gridviews](./gridview) where each view is a container of many tabbed panels. - - - + -You can access the panels associated group through the `panel.group` variable. -The group will always be defined and will change if a panel is moved into another group. +
+ +> You can access the panels associated group through the `panel.group` variable. +> The group will always be defined and will change if a panel is moved into another group. ## DockviewReact Component @@ -340,7 +351,9 @@ return ( ### Third Party Dnd Libraries -To be completed... +This shows a simple example of a third-party library used inside a panel that relies on drag +and drop functionalities. This examples serves to show that `dockview` doesn't interfer with +any drag and drop logic for other controls. @@ -606,15 +619,21 @@ to the entire width of the group. For example: ``` - - - + ### Tab Height - - - +Tab height can be controlled through CSS. + + ## Groups @@ -705,19 +724,11 @@ If you wish to interact with the drop event from one dockview instance in anothe -### Example - -hello +### Window-like mananger with tabs -hello 2 - -
- -
- -## VanillaJS +## Vanilla JS > Note: This section is experimental and support for Vanilla JS is a work in progress. @@ -728,6 +739,6 @@ The core library is published as an independant package under the name `dockview > `dockview-core` is a dependency of `dockview` and automatically installed during the installation process of `dockview` via `npm install dockview`. diff --git a/packages/docs/versioned_docs/version-1.7.2/components/gridview.mdx b/packages/docs/versioned_docs/version-1.7.3/components/gridview.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.2/components/gridview.mdx rename to packages/docs/versioned_docs/version-1.7.3/components/gridview.mdx diff --git a/packages/docs/versioned_docs/version-1.7.2/components/paneview.mdx b/packages/docs/versioned_docs/version-1.7.3/components/paneview.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.2/components/paneview.mdx rename to packages/docs/versioned_docs/version-1.7.3/components/paneview.mdx diff --git a/packages/docs/versioned_docs/version-1.7.2/components/splitview.mdx b/packages/docs/versioned_docs/version-1.7.3/components/splitview.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.2/components/splitview.mdx rename to packages/docs/versioned_docs/version-1.7.3/components/splitview.mdx diff --git a/packages/docs/versioned_docs/version-1.7.2/index.mdx b/packages/docs/versioned_docs/version-1.7.3/index.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.2/index.mdx rename to packages/docs/versioned_docs/version-1.7.3/index.mdx diff --git a/packages/docs/versioned_docs/version-1.7.2/theme.mdx b/packages/docs/versioned_docs/version-1.7.3/theme.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.2/theme.mdx rename to packages/docs/versioned_docs/version-1.7.3/theme.mdx diff --git a/packages/docs/versioned_sidebars/version-1.7.2-sidebars.json b/packages/docs/versioned_sidebars/version-1.7.3-sidebars.json similarity index 100% rename from packages/docs/versioned_sidebars/version-1.7.2-sidebars.json rename to packages/docs/versioned_sidebars/version-1.7.3-sidebars.json diff --git a/packages/docs/versions.json b/packages/docs/versions.json index 519af9b47..900dc7f77 100644 --- a/packages/docs/versions.json +++ b/packages/docs/versions.json @@ -1,3 +1,3 @@ [ - "1.7.2" -] + "1.7.3" +] \ No newline at end of file