This commit is contained in:
mathuo 2020-09-24 23:01:48 +01:00
parent 9e26f3d47c
commit 9e34faafea
13 changed files with 148 additions and 102 deletions

View File

@ -0,0 +1,22 @@
import { Emitter, Event } from '../events';
import { IPanelApi, PanelApi } from './panelApi';
interface ExpansionEvent {
isExpanded: boolean;
}
export interface IPanePanelApi extends IPanelApi {
onDidExpansionChange: Event<ExpansionEvent>;
}
export class PanePanelApi extends PanelApi implements IPanePanelApi {
readonly _onDidExpansionChange = new Emitter<ExpansionEvent>({
emitLastValue: true,
});
readonly onDidExpansionChange: Event<ExpansionEvent> = this
._onDidExpansionChange.event;
constructor() {
super();
}
}

View File

@ -76,6 +76,17 @@ export class ComponentPaneView implements IComponentPaneView {
this.paneview.layout(size, orthogonalSize);
}
/**
* Resize the layout to fit the parent container
*/
public resizeToFit(): void {
const {
width,
height,
} = this.element.parentElement.getBoundingClientRect();
this.layout(width, height);
}
public dispose() {
this.paneview.dispose();
}

View File

@ -1,5 +1,5 @@
import { SplitView, IView, Orientation } from '../splitview/splitview';
import { IDisposable } from '../lifecycle';
import { CompositeDisposable, IDisposable } from '../lifecycle';
import { Emitter, Event } from '../events';
import { addClasses, removeClasses } from '../dom';
@ -13,7 +13,7 @@ export interface IPaneview extends IView {
onDidChangeExpansionState: Event<boolean>;
}
export abstract class Pane implements IPaneview {
export abstract class Pane extends CompositeDisposable implements IPaneview {
private _element: HTMLElement;
private _minimumBodySize: number;
private _maximumBodySize: number;
@ -81,6 +81,10 @@ export abstract class Pane implements IPaneview {
}
constructor(options: IPaneOptions) {
super();
this.addDisposables(this._onDidChange, this._onDidChangeExpansionState);
this._element = document.createElement('div');
this._element.className = 'pane';

View File

@ -9,6 +9,7 @@ import {
TabContextMenuEvent,
} from '../../dockview/options';
import { IGroupPanelApi } from '../../api/groupPanelApi';
import { usePortalsLifecycle } from '../react';
export interface IGroupPanelProps {
api: IGroupPanelApi;
@ -51,23 +52,12 @@ export interface IDockviewComponentProps {
export const DockviewComponent: React.FunctionComponent<IDockviewComponentProps> = (
props: IDockviewComponentProps
) => {
const domReference = React.useRef<HTMLDivElement>();
const layoutReference = React.useRef<ComponentDockview>();
const domRef = React.useRef<HTMLDivElement>();
const dockviewRef = React.useRef<ComponentDockview>();
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
const [portals, addPortal] = usePortalsLifecycle();
React.useEffect(() => {
const addPortal = (p: React.ReactPortal) => {
setPortals((portals) => [...portals, p]);
return {
dispose: () => {
setPortals((portals) =>
portals.filter((portal) => portal !== p)
);
},
};
};
const factory: GroupPanelFrameworkComponentFactory = {
content: {
createComponent: (
@ -95,7 +85,7 @@ export const DockviewComponent: React.FunctionComponent<IDockviewComponentProps>
const element = document.createElement('div');
const layout = new ComponentDockview(element, {
const dockview = new ComponentDockview(element, {
frameworkComponentFactory: factory,
frameworkComponents: props.components,
frameworkTabComponents: props.tabComponents,
@ -105,28 +95,28 @@ export const DockviewComponent: React.FunctionComponent<IDockviewComponentProps>
// orientation: props.orientation,
});
layoutReference.current = layout;
domReference.current.appendChild(layoutReference.current.element);
layout.deserializer = new ReactPanelDeserialzier(layout);
layout.resizeToFit();
domRef.current.appendChild(dockview.element);
dockview.deserializer = new ReactPanelDeserialzier(dockview);
if (props.serializedLayout) {
layout.deserialize(props.serializedLayout);
dockview.deserialize(props.serializedLayout);
}
dockview.resizeToFit();
if (props.onReady) {
props.onReady({ api: layout });
props.onReady({ api: dockview });
}
dockviewRef.current = dockview;
return () => {
layout.dispose();
dockview.dispose();
};
}, []);
React.useEffect(() => {
const disposable = layoutReference.current.onTabContextMenu((event) => {
const disposable = dockviewRef.current.onTabContextMenu((event) => {
props.onTabContextMenu(event);
});
@ -136,9 +126,7 @@ export const DockviewComponent: React.FunctionComponent<IDockviewComponentProps>
}, [props.onTabContextMenu]);
React.useEffect(() => {
layoutReference.current.setAutoResizeToFit(
props.autoSizeToFitContainer
);
dockviewRef.current.setAutoResizeToFit(props.autoSizeToFitContainer);
}, [props.autoSizeToFitContainer]);
return (
@ -147,7 +135,7 @@ export const DockviewComponent: React.FunctionComponent<IDockviewComponentProps>
// height: '100%',
width: '100%',
}}
ref={domReference}
ref={domRef}
>
{portals}
</div>

View File

@ -54,9 +54,6 @@ export class ReactPanelContentPart implements PanelContentPart {
return Promise.resolve(ClosePanelResult.CLOSE);
}
public focus(): void {}
public onHide(): void {}
public dispose() {
this.part?.dispose();
}

View File

@ -1,5 +1,4 @@
import * as React from 'react';
import { IGroupPanelApi } from '../../api/groupPanelApi';
import {
PanelHeaderPart,
GroupPanelPartInitParameters,

View File

@ -6,6 +6,7 @@ import {
import { IGridPanelApi } from '../../api/gridPanelApi';
import { Orientation } from '../../splitview/splitview';
import { ReactComponentGridView } from './reactComponentGridView';
import { usePortalsLifecycle } from '../react';
export interface GridviewReadyEvent {
api: IComponentGridview;
@ -26,23 +27,12 @@ export interface IGridviewComponentProps {
export const GridviewComponent: React.FunctionComponent<IGridviewComponentProps> = (
props: IGridviewComponentProps
) => {
const domReference = React.useRef<HTMLDivElement>();
const gridview = React.useRef<IComponentGridview>();
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
const addPortal = React.useCallback((p: React.ReactPortal) => {
setPortals((portals) => [...portals, p]);
return {
dispose: () => {
setPortals((portals) =>
portals.filter((portal) => portal !== p)
);
},
};
}, []);
const domRef = React.useRef<HTMLDivElement>();
const gridviewRef = React.useRef<IComponentGridview>();
const [portals, addPortal] = usePortalsLifecycle();
React.useEffect(() => {
gridview.current = new ComponentGridview(domReference.current, {
const gridview = new ComponentGridview(domRef.current, {
orientation: props.orientation,
frameworkComponents: props.components,
frameworkComponentFactory: {
@ -59,9 +49,17 @@ export const GridviewComponent: React.FunctionComponent<IGridviewComponentProps>
},
});
// gridview.resizeToFit();
if (props.onReady) {
props.onReady({ api: gridview.current });
props.onReady({ api: gridview });
}
gridviewRef.current = gridview;
return () => {
gridview.dispose();
};
}, []);
return (
@ -70,7 +68,7 @@ export const GridviewComponent: React.FunctionComponent<IGridviewComponentProps>
height: '100%',
width: '100%',
}}
ref={domReference}
ref={domRef}
>
{portals}
</div>

View File

@ -1,17 +1,18 @@
import * as React from 'react';
import { IPanelApi } from '../../api/panelApi';
import { IPanePanelApi } from '../../api/panePanelApi';
import {
ComponentPaneView,
IComponentPaneView,
} from '../../paneview/componentPaneView';
import { PaneReact } from './reactPane';
import { usePortalsLifecycle } from '../react';
export interface PaneviewReadyEvent {
api: IComponentPaneView;
}
export interface IPaneviewPanelProps {
api: IPanelApi;
api: IPanePanelApi;
}
export interface IPaneviewComponentProps {
@ -24,23 +25,12 @@ export interface IPaneviewComponentProps {
export const PaneViewComponent: React.FunctionComponent<IPaneviewComponentProps> = (
props: IPaneviewComponentProps
) => {
const domReference = React.useRef<HTMLDivElement>();
const splitpanel = React.useRef<IComponentPaneView>();
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
const addPortal = React.useCallback((p: React.ReactPortal) => {
setPortals((portals) => [...portals, p]);
return {
dispose: () => {
setPortals((portals) =>
portals.filter((portal) => portal !== p)
);
},
};
}, []);
const domRef = React.useRef<HTMLDivElement>();
const paneviewRef = React.useRef<IComponentPaneView>();
const [portals, addPortal] = usePortalsLifecycle();
React.useEffect(() => {
splitpanel.current = new ComponentPaneView(domReference.current, {
const paneview = new ComponentPaneView(domRef.current, {
frameworkComponents: props.components,
components: {},
frameworkWrapper: {
@ -56,16 +46,20 @@ export const PaneViewComponent: React.FunctionComponent<IPaneviewComponentProps>
},
});
const { width, height } = domReference.current.getBoundingClientRect();
const { width, height } = domRef.current.getBoundingClientRect();
const [size, orthogonalSize] = [height, width];
splitpanel.current.layout(size, orthogonalSize);
paneview.layout(size, orthogonalSize);
if (props.onReady) {
props.onReady({ api: splitpanel.current });
props.onReady({ api: paneview });
}
paneview.resizeToFit();
paneviewRef.current = paneview;
return () => {
splitpanel.current.dispose();
paneview.dispose();
};
}, []);
@ -75,7 +69,7 @@ export const PaneViewComponent: React.FunctionComponent<IPaneviewComponentProps>
height: '100%',
width: '100%',
}}
ref={domReference}
ref={domRef}
>
{portals}
</div>

View File

@ -1,12 +1,12 @@
import * as React from 'react';
import { BaseViewApi, IBaseViewApi } from '../../api/api';
import { IPanePanelApi, PanePanelApi } from '../../api/panePanelApi';
import { Pane } from '../../paneview/paneview';
import { ReactLayout } from '../dockview/dockview';
import { ReactPart } from '../react';
export class PaneReact extends Pane {
private params: {};
private api: IBaseViewApi;
private api: PanePanelApi;
private contentPart: ReactPart;
private headerPart: ReactPart;
@ -27,7 +27,14 @@ export class PaneReact extends Pane {
// options.isExpanded
});
this.api = new BaseViewApi();
this.api = new PanePanelApi();
this.addDisposables(
this.onDidChangeExpansionState((isExpanded) => {
this.api._onDidExpansionChange.fire({ isExpanded });
})
);
this.render();
}
@ -62,6 +69,7 @@ export class PaneReact extends Pane {
}
public dispose() {
super.dispose();
this.headerPart.dispose();
this.contentPart.dispose();
this.api.dispose();

View File

@ -118,3 +118,28 @@ export class ReactPart implements IDisposable {
this.disposed = true;
}
}
/**
* A React Hook that returns an array of portals to be rendered by the user of this hook
* and a disposable function to add a portal. Calling dispose remove this portal from the
* portal array
*/
export const usePortalsLifecycle = () => {
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
const addPortal = React.useCallback((p: React.ReactPortal) => {
setPortals((portals) => [...portals, p]);
return {
dispose: () => {
setPortals((portals) =>
portals.filter((portal) => portal !== p)
);
},
};
}, []);
return [portals, addPortal] as [
React.ReactPortal[],
(portal: React.ReactPortal) => IDisposable
];
};

View File

@ -5,6 +5,7 @@ import {
ComponentSplitview,
} from '../../splitview/componentSplitview';
import { Orientation } from '../../splitview/splitview';
import { usePortalsLifecycle } from '../react';
import { ReactComponentView } from './reactComponentView';
export interface SplitviewReadyEvent {
@ -26,23 +27,12 @@ export interface ISplitviewComponentProps {
export const SplitviewComponent: React.FunctionComponent<ISplitviewComponentProps> = (
props: ISplitviewComponentProps
) => {
const domReference = React.useRef<HTMLDivElement>();
const splitpanel = React.useRef<IComponentSplitview>();
const [portals, setPortals] = React.useState<React.ReactPortal[]>([]);
const addPortal = React.useCallback((p: React.ReactPortal) => {
setPortals((portals) => [...portals, p]);
return {
dispose: () => {
setPortals((portals) =>
portals.filter((portal) => portal !== p)
);
},
};
}, []);
const domRef = React.useRef<HTMLDivElement>();
const splitviewRef = React.useRef<IComponentSplitview>();
const [portals, addPortal] = usePortalsLifecycle();
React.useEffect(() => {
splitpanel.current = new ComponentSplitview(domReference.current, {
const splitview = new ComponentSplitview(domRef.current, {
orientation: props.orientation,
frameworkComponents: props.components,
frameworkWrapper: {
@ -55,19 +45,16 @@ export const SplitviewComponent: React.FunctionComponent<ISplitviewComponentProp
proportionalLayout: false,
});
const { width, height } = domReference.current.getBoundingClientRect();
const [size, orthogonalSize] =
props.orientation === Orientation.HORIZONTAL
? [width, height]
: [height, width];
splitpanel.current.layout(size, orthogonalSize);
splitview.resizeToFit();
if (props.onReady) {
props.onReady({ api: splitpanel.current });
props.onReady({ api: splitview });
}
splitviewRef.current = splitview;
return () => {
splitpanel.current.dispose();
splitview.dispose();
};
}, []);
@ -77,7 +64,7 @@ export const SplitviewComponent: React.FunctionComponent<ISplitviewComponentProp
height: '100%',
width: '100%',
}}
ref={domReference}
ref={domRef}
>
{portals}
</div>

View File

@ -79,6 +79,17 @@ export class ComponentSplitview implements IComponentSplitview {
};
}
/**
* Resize the layout to fit the parent container
*/
public resizeToFit(): void {
const {
width,
height,
} = this.element.parentElement.getBoundingClientRect();
this.layout(width, height);
}
private registerView(view: ISerializableView) {
//
}

View File

@ -4,5 +4,7 @@
"path": "."
}
],
"settings": {}
"settings": {
"editor.formatOnSave": true
}
}