mirror of
https://github.com/mathuo/dockview
synced 2025-05-11 22:18:26 +00:00
feat: expose dnd functions
This commit is contained in:
parent
7ec824ea01
commit
c140b560fe
@ -1,7 +1,7 @@
|
|||||||
<!-- <!DOCTYPE html> -->
|
<!-- <!DOCTYPE html> -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
@ -49,18 +49,27 @@ export const Activitybar = (props: IGridviewPanelProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="activity-bar" onClick={onOpenSidebar}>
|
<div className="activity-bar" onClick={onOpenSidebar}>
|
||||||
<DockviewDropTarget
|
<div className="activity-bar-item">
|
||||||
validOverlays={'vertical'}
|
<ActivitybarImage
|
||||||
canDisplayOverlay={true}
|
url={
|
||||||
>
|
'https://fonts.gstatic.com/s/i/materialicons/search/v7/24px.svg'
|
||||||
<div className="activity-bar-item">
|
}
|
||||||
<ActivitybarImage
|
/>
|
||||||
url={
|
</div>
|
||||||
'https://fonts.gstatic.com/s/i/materialicons/search/v7/24px.svg'
|
<div className="activity-bar-item">
|
||||||
}
|
<ActivitybarImage
|
||||||
/>
|
url={
|
||||||
</div>
|
'https://fonts.gstatic.com/s/i/materialicons/search/v7/24px.svg'
|
||||||
</DockviewDropTarget>
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="activity-bar-item">
|
||||||
|
<ActivitybarImage
|
||||||
|
url={
|
||||||
|
'https://fonts.gstatic.com/s/i/materialicons/search/v7/24px.svg'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,12 +8,12 @@ import {
|
|||||||
SerializedGridview,
|
SerializedGridview,
|
||||||
GridviewApi,
|
GridviewApi,
|
||||||
} from 'dockview';
|
} from 'dockview';
|
||||||
import { Activitybar } from './activitybar';
|
import { Activitybar } from '../services/widgets';
|
||||||
import { Footer } from './footer';
|
import { Footer } from './footer';
|
||||||
import { Panel } from './panel';
|
import { Panel } from './panel';
|
||||||
import { TestGrid } from './layoutGrid';
|
import { TestGrid } from './layoutGrid';
|
||||||
import { useLayoutRegistry } from './registry';
|
import { useLayoutRegistry } from './registry';
|
||||||
import { Sidebar } from './sidebar';
|
import { Sidebar } from '../services/widgets';
|
||||||
|
|
||||||
const rootcomponents: {
|
const rootcomponents: {
|
||||||
[index: string]: React.FunctionComponent<IGridviewPanelProps>;
|
[index: string]: React.FunctionComponent<IGridviewPanelProps>;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"component": "controlCenter",
|
"component": "controlCenter",
|
||||||
"headerComponent": "default",
|
|
||||||
"props": {},
|
"props": {},
|
||||||
"title": "Control Center",
|
"title": "Control Center",
|
||||||
"state": {}
|
"state": {}
|
||||||
@ -17,7 +17,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"id": "2",
|
"id": "2",
|
||||||
"component": "default",
|
"component": "default",
|
||||||
"headerComponent": "default",
|
|
||||||
"props": {},
|
"props": {},
|
||||||
"title": "Panel 1",
|
"title": "Panel 1",
|
||||||
"state": {}
|
"state": {}
|
||||||
@ -30,7 +30,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"id": "3",
|
"id": "3",
|
||||||
"component": "default",
|
"component": "default",
|
||||||
"headerComponent": "default",
|
|
||||||
"props": {},
|
"props": {},
|
||||||
"title": "Panel 2",
|
"title": "Panel 2",
|
||||||
"state": {}
|
"state": {}
|
||||||
@ -43,7 +43,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"id": "4",
|
"id": "4",
|
||||||
"component": "default",
|
"component": "default",
|
||||||
"headerComponent": "default",
|
|
||||||
"props": {},
|
"props": {},
|
||||||
"title": "Panel 3",
|
"title": "Panel 3",
|
||||||
"state": {}
|
"state": {}
|
||||||
|
@ -12,107 +12,6 @@ import { ControlCenter } from './controlCenter';
|
|||||||
import { toggleClass } from '../dom';
|
import { toggleClass } from '../dom';
|
||||||
import './sidebar.scss';
|
import './sidebar.scss';
|
||||||
|
|
||||||
const DefaultHeader = (props: IPaneviewPanelProps) => {
|
|
||||||
const ref = React.useRef<HTMLDivElement>();
|
|
||||||
const mouseover = React.useRef<boolean>();
|
|
||||||
|
|
||||||
const [url, setUrl] = React.useState<string>(
|
|
||||||
props.api.isExpanded
|
|
||||||
? 'https://fonts.gstatic.com/s/i/materialicons/expand_more/v6/24px.svg'
|
|
||||||
: 'https://fonts.gstatic.com/s/i/materialicons/chevron_right/v7/24px.svg'
|
|
||||||
);
|
|
||||||
|
|
||||||
const toggle = () => {
|
|
||||||
toggleClass(
|
|
||||||
ref.current,
|
|
||||||
'within',
|
|
||||||
props.api.isExpanded && mouseover.current
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const disposable = new CompositeDisposable(
|
|
||||||
props.api.onDidExpansionChange((event) => {
|
|
||||||
setUrl(
|
|
||||||
event.isExpanded
|
|
||||||
? 'https://fonts.gstatic.com/s/i/materialicons/expand_more/v6/24px.svg'
|
|
||||||
: 'https://fonts.gstatic.com/s/i/materialicons/chevron_right/v7/24px.svg'
|
|
||||||
);
|
|
||||||
toggle();
|
|
||||||
}),
|
|
||||||
props.api.onMouseEnter((ev) => {
|
|
||||||
mouseover.current = true;
|
|
||||||
toggle();
|
|
||||||
}),
|
|
||||||
props.api.onMouseLeave((ev) => {
|
|
||||||
mouseover.current = false;
|
|
||||||
toggle();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
disposable.dispose();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const onClick = (event: React.MouseEvent) => {
|
|
||||||
if (event.defaultPrevented) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
props.api.setExpanded(!props.api.isExpanded);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onClickAction = (event: React.MouseEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="my-header"
|
|
||||||
ref={ref}
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
fontSize: '11px',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
cursor: 'pointer',
|
|
||||||
}}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
<div style={{ width: '20px' }}>
|
|
||||||
<a
|
|
||||||
style={{
|
|
||||||
WebkitMask: `url(${url}) 50% 50% / 100% 100% no-repeat`,
|
|
||||||
height: '100%',
|
|
||||||
display: 'block',
|
|
||||||
backgroundColor: 'lightgray',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span>{props.title}</span>
|
|
||||||
<span style={{ flexGrow: 1 }} />
|
|
||||||
<div className="actions">
|
|
||||||
<div
|
|
||||||
onClick={onClickAction}
|
|
||||||
style={{
|
|
||||||
height: '100%',
|
|
||||||
width: '20px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
title="Example action"
|
|
||||||
style={{
|
|
||||||
WebkitMask: `url(https://fonts.gstatic.com/s/i/materialicons/help_outline/v6/24px.svg) 50% 50% / 80% 80% no-repeat`,
|
|
||||||
height: '100%',
|
|
||||||
display: 'block',
|
|
||||||
backgroundColor: 'lightgray',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
default: (props: IPaneviewPanelProps) => {
|
default: (props: IPaneviewPanelProps) => {
|
||||||
return <div style={{ height: '100%' }}>This is an example panel</div>;
|
return <div style={{ height: '100%' }}>This is an example panel</div>;
|
||||||
@ -120,10 +19,6 @@ const components = {
|
|||||||
controlCenter: ControlCenter,
|
controlCenter: ControlCenter,
|
||||||
};
|
};
|
||||||
|
|
||||||
const headerComponents = {
|
|
||||||
default: DefaultHeader,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Sidebar = (props: IGridviewPanelProps) => {
|
export const Sidebar = (props: IGridviewPanelProps) => {
|
||||||
const api = React.useRef<PaneviewApi>();
|
const api = React.useRef<PaneviewApi>();
|
||||||
|
|
||||||
@ -139,25 +34,21 @@ export const Sidebar = (props: IGridviewPanelProps) => {
|
|||||||
event.api.addPanel({
|
event.api.addPanel({
|
||||||
id: '1',
|
id: '1',
|
||||||
component: 'default',
|
component: 'default',
|
||||||
headerComponent: 'default',
|
|
||||||
title: 'Control Center',
|
title: 'Control Center',
|
||||||
});
|
});
|
||||||
event.api.addPanel({
|
event.api.addPanel({
|
||||||
id: '2',
|
id: '2',
|
||||||
component: 'default',
|
component: 'default',
|
||||||
headerComponent: 'default',
|
|
||||||
title: 'Panel 1',
|
title: 'Panel 1',
|
||||||
});
|
});
|
||||||
event.api.addPanel({
|
event.api.addPanel({
|
||||||
id: '3',
|
id: '3',
|
||||||
component: 'default',
|
component: 'default',
|
||||||
headerComponent: 'default',
|
|
||||||
title: 'Panel 2',
|
title: 'Panel 2',
|
||||||
});
|
});
|
||||||
event.api.addPanel({
|
event.api.addPanel({
|
||||||
id: '4',
|
id: '4',
|
||||||
component: 'default',
|
component: 'default',
|
||||||
headerComponent: 'default',
|
|
||||||
title: 'Panel 3',
|
title: 'Panel 3',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -196,7 +87,6 @@ export const Sidebar = (props: IGridviewPanelProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PaneviewReact
|
<PaneviewReact
|
||||||
headerComponents={headerComponents}
|
|
||||||
components={components}
|
components={components}
|
||||||
onReady={onReady}
|
onReady={onReady}
|
||||||
onDidDrop={onDidDrop}
|
onDidDrop={onDidDrop}
|
||||||
|
64
packages/dockview-demo/src/services/view.ts
Normal file
64
packages/dockview-demo/src/services/view.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
export interface SerializedView {
|
||||||
|
id: string;
|
||||||
|
isExpanded: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ViewOptions = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
isExpanded: boolean;
|
||||||
|
isLocationEditable: boolean;
|
||||||
|
icon: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface View {
|
||||||
|
readonly id: string;
|
||||||
|
readonly isExpanded: boolean;
|
||||||
|
readonly title: string;
|
||||||
|
readonly isLocationEditable: boolean;
|
||||||
|
readonly icon: string;
|
||||||
|
toJSON(): SerializedView;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DefaultView implements View {
|
||||||
|
private readonly _id: string;
|
||||||
|
private readonly _title: string;
|
||||||
|
private readonly _isExpanded: boolean;
|
||||||
|
private readonly _isLocationEditable: boolean;
|
||||||
|
private readonly _icon: string;
|
||||||
|
|
||||||
|
get id(): string {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get title(): string {
|
||||||
|
return this._title;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isExpanded(): boolean {
|
||||||
|
return this._isExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isLocationEditable(): boolean {
|
||||||
|
return this._isLocationEditable;
|
||||||
|
}
|
||||||
|
|
||||||
|
get icon(): string {
|
||||||
|
return this._icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(options: ViewOptions) {
|
||||||
|
this._id = options.id;
|
||||||
|
this._title = options.title;
|
||||||
|
this._isExpanded = options.isExpanded;
|
||||||
|
this._isLocationEditable = options.isLocationEditable;
|
||||||
|
this._icon = options.icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): SerializedView {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
isExpanded: this.isExpanded,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
127
packages/dockview-demo/src/services/viewContainer.ts
Normal file
127
packages/dockview-demo/src/services/viewContainer.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import {
|
||||||
|
CompositeDisposable,
|
||||||
|
Emitter,
|
||||||
|
Event,
|
||||||
|
SerializedPaneview,
|
||||||
|
} from 'dockview';
|
||||||
|
import { DefaultView, View, SerializedView } from './view';
|
||||||
|
import { IViewRegistry } from './viewRegistry';
|
||||||
|
|
||||||
|
export interface SerializedViewContainer {
|
||||||
|
readonly id: string;
|
||||||
|
readonly views: SerializedView[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ViewContainer<T = any> {
|
||||||
|
readonly id: string;
|
||||||
|
readonly views: View[];
|
||||||
|
readonly schema: T | any;
|
||||||
|
readonly icon: string;
|
||||||
|
readonly onDidAddView: Event<View>;
|
||||||
|
readonly onDidRemoveView: Event<View>;
|
||||||
|
addView(view: View, location?: number): void;
|
||||||
|
layout(schema: T): void;
|
||||||
|
removeView(view: View): void;
|
||||||
|
clear(): void;
|
||||||
|
toJSON(): SerializedViewContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PaneviewContainer implements ViewContainer<SerializedPaneview> {
|
||||||
|
private readonly _id: string;
|
||||||
|
private readonly _views: View[] = [];
|
||||||
|
|
||||||
|
private readonly _onDidAddView = new Emitter<View>();
|
||||||
|
readonly onDidAddView = this._onDidAddView.event;
|
||||||
|
private readonly _onDidRemoveView = new Emitter<View>();
|
||||||
|
readonly onDidRemoveView = this._onDidRemoveView.event;
|
||||||
|
|
||||||
|
private _schema: SerializedPaneview | undefined;
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get views() {
|
||||||
|
return this._views;
|
||||||
|
}
|
||||||
|
|
||||||
|
get schema(): SerializedPaneview | undefined {
|
||||||
|
if (!this._schema) {
|
||||||
|
this._schema = JSON.parse(
|
||||||
|
localStorage.getItem(`viewcontainer_${this.id}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this._schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
get icon(): string {
|
||||||
|
const defaultIcon = 'search';
|
||||||
|
if (this.views.length > 0) {
|
||||||
|
return this.views.find((v) => !!v.icon)?.icon || defaultIcon;
|
||||||
|
}
|
||||||
|
return defaultIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
id: string,
|
||||||
|
viewRegistry: IViewRegistry,
|
||||||
|
views?: SerializedView[]
|
||||||
|
) {
|
||||||
|
this._id = id;
|
||||||
|
|
||||||
|
if (views) {
|
||||||
|
for (const view of views) {
|
||||||
|
const registeredView = viewRegistry.getRegisteredView(view.id);
|
||||||
|
this.addView(
|
||||||
|
new DefaultView({
|
||||||
|
id: view.id,
|
||||||
|
title: registeredView.title,
|
||||||
|
isExpanded: view.isExpanded,
|
||||||
|
isLocationEditable: registeredView.isLocationEditable,
|
||||||
|
icon: registeredView.icon,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// this.addDisposables(this._onDidAddView, this._onDidRemoveView);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(schema: SerializedPaneview): void {
|
||||||
|
this._schema = schema;
|
||||||
|
localStorage.setItem(
|
||||||
|
`viewcontainer_${this.id}`,
|
||||||
|
JSON.stringify(schema)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
addView(view: View, location = 0): void {
|
||||||
|
this._views.splice(location, 0, view);
|
||||||
|
this._onDidAddView.fire(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeView(view: View): void {
|
||||||
|
const index = this._views.indexOf(view);
|
||||||
|
if (index < 0) {
|
||||||
|
throw new Error('invalid');
|
||||||
|
}
|
||||||
|
this._views.splice(index, 1);
|
||||||
|
|
||||||
|
if (this._schema) {
|
||||||
|
this._schema = { ...this._schema };
|
||||||
|
this._schema.views = this._schema.views.filter(
|
||||||
|
(v) => v.data.id !== view.id
|
||||||
|
);
|
||||||
|
this.layout(this._schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._onDidRemoveView.fire(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
localStorage.removeItem(`viewcontainer_${this.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): SerializedViewContainer {
|
||||||
|
return { id: this.id, views: this.views.map((v) => v.toJSON()) };
|
||||||
|
}
|
||||||
|
}
|
49
packages/dockview-demo/src/services/viewRegistry.ts
Normal file
49
packages/dockview-demo/src/services/viewRegistry.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
export interface RegisteredView {
|
||||||
|
id: string;
|
||||||
|
icon: string;
|
||||||
|
title: string;
|
||||||
|
isLocationEditable: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IViewRegistry {
|
||||||
|
getRegisteredView(id: string): RegisteredView | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ViewRegistry {
|
||||||
|
private readonly _registry = new Map<string, RegisteredView>();
|
||||||
|
|
||||||
|
register(registeredView: RegisteredView): void {
|
||||||
|
this._registry.set(registeredView.id, registeredView);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRegisteredView(id: string): RegisteredView | undefined {
|
||||||
|
return this._registry.get(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const VIEW_REGISTRY = new ViewRegistry();
|
||||||
|
|
||||||
|
VIEW_REGISTRY.register({
|
||||||
|
id: 'search_widget',
|
||||||
|
title: 'search',
|
||||||
|
icon: 'search',
|
||||||
|
isLocationEditable: false,
|
||||||
|
});
|
||||||
|
VIEW_REGISTRY.register({
|
||||||
|
id: 'home_widget',
|
||||||
|
title: 'Home',
|
||||||
|
icon: 'home',
|
||||||
|
isLocationEditable: true,
|
||||||
|
});
|
||||||
|
VIEW_REGISTRY.register({
|
||||||
|
id: 'account_widget',
|
||||||
|
title: 'Account',
|
||||||
|
icon: 'account_circle',
|
||||||
|
isLocationEditable: true,
|
||||||
|
});
|
||||||
|
VIEW_REGISTRY.register({
|
||||||
|
id: 'settings_widget',
|
||||||
|
title: 'Settings',
|
||||||
|
icon: 'settings',
|
||||||
|
isLocationEditable: true,
|
||||||
|
});
|
200
packages/dockview-demo/src/services/viewService.ts
Normal file
200
packages/dockview-demo/src/services/viewService.ts
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
import {
|
||||||
|
CompositeDisposable,
|
||||||
|
Emitter,
|
||||||
|
Event,
|
||||||
|
IDisposable,
|
||||||
|
IView,
|
||||||
|
} from 'dockview';
|
||||||
|
import { DefaultView, View } from './view';
|
||||||
|
import {
|
||||||
|
PaneviewContainer,
|
||||||
|
ViewContainer,
|
||||||
|
SerializedViewContainer,
|
||||||
|
} from './viewContainer';
|
||||||
|
import { IViewRegistry } from './viewRegistry';
|
||||||
|
|
||||||
|
export interface SerializedViewService {
|
||||||
|
containers: SerializedViewContainer[];
|
||||||
|
activeContainer?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IViewService extends IDisposable {
|
||||||
|
readonly containers: ViewContainer[];
|
||||||
|
readonly onDidActiveContainerChange: Event<void>;
|
||||||
|
readonly onDidRemoveContainer: Event<void>;
|
||||||
|
readonly onDidAddContainer: Event<void>;
|
||||||
|
readonly onDidContainersChange: Event<void>;
|
||||||
|
readonly activeContainer: ViewContainer | undefined;
|
||||||
|
addContainer(container: ViewContainer): void;
|
||||||
|
setActiveViewContainer(id: string): void;
|
||||||
|
getView(id: string): View | undefined;
|
||||||
|
moveViewToLocation(
|
||||||
|
view: View,
|
||||||
|
targetViewContainer: ViewContainer,
|
||||||
|
targetLocation: number
|
||||||
|
): void;
|
||||||
|
insertContainerAfter(source: ViewContainer, target: ViewContainer): void;
|
||||||
|
addViews(view: View, viewContainer: ViewContainer, location?: number): void;
|
||||||
|
removeViews(removeViews: View[], viewContainer: ViewContainer): void;
|
||||||
|
getViewContainer(id: string): ViewContainer | undefined;
|
||||||
|
getViewContainer2(view: View): ViewContainer | undefined;
|
||||||
|
toJSON(): SerializedViewService;
|
||||||
|
load(layout: SerializedViewService): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ViewService implements IViewService {
|
||||||
|
private _viewContainers: ViewContainer[] = [];
|
||||||
|
private readonly _onDidActiveContainerChange = new Emitter<void>();
|
||||||
|
readonly onDidActiveContainerChange = this._onDidActiveContainerChange
|
||||||
|
.event;
|
||||||
|
private readonly _onDidRemoveContainer = new Emitter<void>();
|
||||||
|
readonly onDidRemoveContainer = this._onDidRemoveContainer.event;
|
||||||
|
private readonly _onDidAddContainer = new Emitter<void>();
|
||||||
|
readonly onDidAddContainer = this._onDidAddContainer.event;
|
||||||
|
private readonly _onDidContainersChange = new Emitter<void>();
|
||||||
|
readonly onDidContainersChange = this._onDidContainersChange.event;
|
||||||
|
private _activeViewContainerId: string;
|
||||||
|
|
||||||
|
get containers(): ViewContainer[] {
|
||||||
|
return this._viewContainers;
|
||||||
|
}
|
||||||
|
|
||||||
|
get activeContainer(): ViewContainer | undefined {
|
||||||
|
return this._viewContainers.find(
|
||||||
|
(c) => c.id === this._activeViewContainerId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private readonly viewRegistry: IViewRegistry) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
load(layout: SerializedViewService): void {
|
||||||
|
const { containers, activeContainer } = layout;
|
||||||
|
|
||||||
|
for (const container of containers) {
|
||||||
|
const { id, views } = container;
|
||||||
|
const viewContainer = new PaneviewContainer(
|
||||||
|
id,
|
||||||
|
this.viewRegistry,
|
||||||
|
views
|
||||||
|
);
|
||||||
|
|
||||||
|
this.addContainer(viewContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setActiveViewContainer(activeContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertContainerAfter(source: ViewContainer, target: ViewContainer): void {
|
||||||
|
const sourceIndex = this._viewContainers.findIndex(
|
||||||
|
(c) => c.id === source.id
|
||||||
|
);
|
||||||
|
|
||||||
|
const view = this._viewContainers.splice(sourceIndex, 1)[0];
|
||||||
|
|
||||||
|
const targetIndex = this._viewContainers.findIndex(
|
||||||
|
(c) => c.id === target.id
|
||||||
|
);
|
||||||
|
|
||||||
|
this._viewContainers.splice(targetIndex + 1, 0, view);
|
||||||
|
this._viewContainers = [...this._viewContainers];
|
||||||
|
|
||||||
|
this._onDidContainersChange.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
addContainer(container: ViewContainer): void {
|
||||||
|
this._viewContainers = [...this._viewContainers, container];
|
||||||
|
this._activeViewContainerId = container.id;
|
||||||
|
this._onDidAddContainer.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeContainer(container: ViewContainer): void {
|
||||||
|
this._viewContainers = this._viewContainers.filter(
|
||||||
|
(c) => c.id !== container.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this._activeViewContainerId === container.id) {
|
||||||
|
this._activeViewContainerId =
|
||||||
|
this._viewContainers.length > 0
|
||||||
|
? this._viewContainers[0].id
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._onDidRemoveContainer.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveViewContainer(id: string): void {
|
||||||
|
if (!this._viewContainers.find((c) => c.id === id)) {
|
||||||
|
throw new Error(`invalid container ${id}`);
|
||||||
|
}
|
||||||
|
this._activeViewContainerId = id;
|
||||||
|
this._onDidActiveContainerChange.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
getView(id: string): View | undefined {
|
||||||
|
for (const container of Array.from(this._viewContainers.values())) {
|
||||||
|
const view = container.views.find((v) => v.id === id);
|
||||||
|
if (view) {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
moveViewToLocation(
|
||||||
|
view: View,
|
||||||
|
targetViewContainer: ViewContainer,
|
||||||
|
targetLocation: number
|
||||||
|
): void {
|
||||||
|
const sourceViewContainer = this.getViewContainer2(view);
|
||||||
|
this.removeViews([view], sourceViewContainer);
|
||||||
|
this.addViews(view, targetViewContainer, targetLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
addViews(
|
||||||
|
view: View,
|
||||||
|
viewContainer: ViewContainer,
|
||||||
|
location?: number
|
||||||
|
): void {
|
||||||
|
viewContainer.addView(view, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeViews(removeViews: View[], viewContainer: ViewContainer): void {
|
||||||
|
for (const view of removeViews) {
|
||||||
|
viewContainer.removeView(view);
|
||||||
|
|
||||||
|
if (viewContainer.views.length === 0) {
|
||||||
|
viewContainer.clear();
|
||||||
|
this.removeContainer(viewContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getViewContainer(id: string): ViewContainer | undefined {
|
||||||
|
return this._viewContainers.find((c) => c.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getViewContainer2(view: View): ViewContainer | undefined {
|
||||||
|
for (const container of Array.from(this._viewContainers.values())) {
|
||||||
|
const v = container.views.find((v) => v.id === view.id);
|
||||||
|
if (v) {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): SerializedViewService {
|
||||||
|
return {
|
||||||
|
containers: this.containers.map((c) => c.toJSON()),
|
||||||
|
activeContainer: this.activeContainer.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
this._onDidActiveContainerChange.dispose();
|
||||||
|
this._onDidAddContainer.dispose();
|
||||||
|
this._onDidRemoveContainer.dispose();
|
||||||
|
}
|
||||||
|
}
|
357
packages/dockview-demo/src/services/widgets.tsx
Normal file
357
packages/dockview-demo/src/services/widgets.tsx
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
import {
|
||||||
|
CompositeDisposable,
|
||||||
|
GridviewApi,
|
||||||
|
IGridviewPanelProps,
|
||||||
|
IPaneviewPanelProps,
|
||||||
|
PanelCollection,
|
||||||
|
PaneviewApi,
|
||||||
|
PaneviewDropEvent,
|
||||||
|
PaneviewReact,
|
||||||
|
PaneviewReadyEvent,
|
||||||
|
getPaneData,
|
||||||
|
} from 'dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { useLayoutRegistry } from '../layout-grid/registry';
|
||||||
|
import { PaneviewContainer, ViewContainer } from './viewContainer';
|
||||||
|
import { IViewService, ViewService } from './viewService';
|
||||||
|
import { DefaultView } from './view';
|
||||||
|
import { RegisteredView, VIEW_REGISTRY } from './viewRegistry';
|
||||||
|
|
||||||
|
class ViewServiceModel {
|
||||||
|
private readonly viewService: IViewService;
|
||||||
|
|
||||||
|
get model() {
|
||||||
|
return this.viewService;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.viewService = new ViewService(VIEW_REGISTRY);
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init(): void {
|
||||||
|
const layout = localStorage.getItem('viewservice');
|
||||||
|
if (layout) {
|
||||||
|
this.viewService.load(JSON.parse(layout));
|
||||||
|
} else {
|
||||||
|
const container1 = new PaneviewContainer(
|
||||||
|
'default_container_1',
|
||||||
|
VIEW_REGISTRY
|
||||||
|
);
|
||||||
|
if (!container1.schema) {
|
||||||
|
this.addView(
|
||||||
|
container1,
|
||||||
|
VIEW_REGISTRY.getRegisteredView('search_widget')
|
||||||
|
);
|
||||||
|
this.addView(
|
||||||
|
container1,
|
||||||
|
VIEW_REGISTRY.getRegisteredView('home_widget')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const container2 = new PaneviewContainer(
|
||||||
|
'default_container_2',
|
||||||
|
VIEW_REGISTRY
|
||||||
|
);
|
||||||
|
if (!container2.schema) {
|
||||||
|
this.addView(
|
||||||
|
container2,
|
||||||
|
VIEW_REGISTRY.getRegisteredView('account_widget')
|
||||||
|
);
|
||||||
|
this.addView(
|
||||||
|
container2,
|
||||||
|
VIEW_REGISTRY.getRegisteredView('settings_widget')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.viewService.addContainer(container1);
|
||||||
|
this.viewService.addContainer(container2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'viewservice',
|
||||||
|
JSON.stringify(this.viewService.toJSON())
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.viewService.onDidActiveContainerChange(save);
|
||||||
|
this.viewService.onDidRemoveContainer(save);
|
||||||
|
this.viewService.onDidAddContainer(save);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addView(
|
||||||
|
container: ViewContainer,
|
||||||
|
registedView: RegisteredView
|
||||||
|
): void {
|
||||||
|
container.addView(
|
||||||
|
new DefaultView({
|
||||||
|
id: registedView.id,
|
||||||
|
title: registedView.title,
|
||||||
|
isExpanded: true,
|
||||||
|
isLocationEditable: registedView.isLocationEditable,
|
||||||
|
icon: registedView.icon,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewService = new ViewServiceModel();
|
||||||
|
|
||||||
|
const components: PanelCollection<IPaneviewPanelProps<any>> = {
|
||||||
|
default: (props: IPaneviewPanelProps<{ viewId: string }>) => {
|
||||||
|
return (
|
||||||
|
<div style={{ backgroundColor: 'black', height: '100%' }}>
|
||||||
|
{props.params.viewId}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Activitybar = (props: IGridviewPanelProps) => {
|
||||||
|
const [activeContainerid, setActiveContainerId] = React.useState<string>(
|
||||||
|
viewService.model.activeContainer.id
|
||||||
|
);
|
||||||
|
const [containers, setContainers] = React.useState<ViewContainer[]>(
|
||||||
|
viewService.model.containers
|
||||||
|
);
|
||||||
|
|
||||||
|
const registry = useLayoutRegistry();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const disposable = new CompositeDisposable(
|
||||||
|
viewService.model.onDidActiveContainerChange(() => {
|
||||||
|
setActiveContainerId(viewService.model.activeContainer.id);
|
||||||
|
}),
|
||||||
|
viewService.model.onDidAddContainer(() => {
|
||||||
|
setContainers(viewService.model.containers);
|
||||||
|
}),
|
||||||
|
viewService.model.onDidRemoveContainer(() => {
|
||||||
|
setContainers(viewService.model.containers);
|
||||||
|
}),
|
||||||
|
viewService.model.onDidContainersChange(() => {
|
||||||
|
setContainers(viewService.model.containers);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onClick = (container: ViewContainer, alwaysOpen = false) => (
|
||||||
|
event: React.MouseEvent
|
||||||
|
) => {
|
||||||
|
const api = registry.get<GridviewApi>('gridview');
|
||||||
|
|
||||||
|
const selectedActive = container.id === activeContainerid;
|
||||||
|
|
||||||
|
const sidebarPanel = api.getPanel('sidebar');
|
||||||
|
if (sidebarPanel.api.isVisible) {
|
||||||
|
if (!alwaysOpen && selectedActive) {
|
||||||
|
api.setVisible(sidebarPanel, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.preventDefault(); // prevent focus
|
||||||
|
api.setVisible(sidebarPanel, true);
|
||||||
|
sidebarPanel.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
viewService.model.setActiveViewContainer(container.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDrop = (targetContainer: ViewContainer) => (
|
||||||
|
event: React.DragEvent
|
||||||
|
) => {
|
||||||
|
const data = event.dataTransfer.getData('application/json');
|
||||||
|
if (data) {
|
||||||
|
const { container } = JSON.parse(data);
|
||||||
|
const sourceContainer = viewService.model.getViewContainer(
|
||||||
|
container
|
||||||
|
);
|
||||||
|
viewService.model.insertContainerAfter(
|
||||||
|
sourceContainer,
|
||||||
|
targetContainer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onNewContainer = (event: React.DragEvent) => {
|
||||||
|
const data = getPaneData();
|
||||||
|
if (data) {
|
||||||
|
const { paneId } = data;
|
||||||
|
const view = viewService.model.getView(paneId);
|
||||||
|
const viewContainer = viewService.model.getViewContainer2(view);
|
||||||
|
viewService.model.removeViews([view], viewContainer);
|
||||||
|
// viewContainer.removeView(view);
|
||||||
|
const newContainer = new PaneviewContainer(
|
||||||
|
`t_${Date.now().toString().substr(5)}`,
|
||||||
|
VIEW_REGISTRY
|
||||||
|
);
|
||||||
|
newContainer.addView(view);
|
||||||
|
viewService.model.addContainer(newContainer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ background: 'rgb(51,51,51)' }}>
|
||||||
|
{containers.map((container, i) => {
|
||||||
|
const isActive = activeContainerid === container.id;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onClick={onClick(container)}
|
||||||
|
onDragOver={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onClick(container, true)(e);
|
||||||
|
}}
|
||||||
|
onDragEnter={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
draggable={true}
|
||||||
|
onDragStart={(e) => {
|
||||||
|
e.dataTransfer.setData(
|
||||||
|
'application/json',
|
||||||
|
JSON.stringify({ container: container.id })
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
onDrop={onDrop(container)}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '48px',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
borderLeft: isActive
|
||||||
|
? '1px solid white'
|
||||||
|
: '1px solid transparent',
|
||||||
|
}}
|
||||||
|
key={i}
|
||||||
|
>
|
||||||
|
{/* {container.id} */}
|
||||||
|
<span
|
||||||
|
style={{ fontSize: '30px' }}
|
||||||
|
className="material-icons-outlined"
|
||||||
|
>
|
||||||
|
{container.icon}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<div
|
||||||
|
onDragOver={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
onDragEnter={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
onDrop={onNewContainer}
|
||||||
|
style={{ height: '100%', backgroundColor: 'red' }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Sidebar = () => {
|
||||||
|
const [sidebarId, setSidebarId] = React.useState<string>(
|
||||||
|
viewService.model.activeContainer.id
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const disposable = viewService.model.onDidActiveContainerChange(() => {
|
||||||
|
setSidebarId(viewService.model.activeContainer.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <SidebarPart id={sidebarId} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SidebarPart = (props: { id: string }) => {
|
||||||
|
const [api, setApi] = React.useState<PaneviewApi>();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!api) {
|
||||||
|
return () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewContainer = viewService.model.getViewContainer(props.id);
|
||||||
|
|
||||||
|
const disposables = new CompositeDisposable(
|
||||||
|
api.onDidLayoutChange(() => {
|
||||||
|
viewContainer.layout(api.toJSON());
|
||||||
|
}),
|
||||||
|
viewContainer.onDidAddView((view) => {
|
||||||
|
api.addPanel({
|
||||||
|
id: view.id,
|
||||||
|
isExpanded: view.isExpanded,
|
||||||
|
title: view.title,
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
viewId: view.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
viewContainer.onDidRemoveView((view) => {
|
||||||
|
const panel = api.getPanel(view.id);
|
||||||
|
api.removePanel(panel);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const schema = viewContainer.schema;
|
||||||
|
if (schema) {
|
||||||
|
api.fromJSON(schema);
|
||||||
|
} else {
|
||||||
|
api.getPanels().forEach((p) => {
|
||||||
|
api.removePanel(p);
|
||||||
|
});
|
||||||
|
viewContainer.views.forEach((view) => {
|
||||||
|
api.addPanel({
|
||||||
|
id: view.id,
|
||||||
|
isExpanded: view.isExpanded,
|
||||||
|
title: view.title,
|
||||||
|
component: 'default',
|
||||||
|
params: {
|
||||||
|
viewId: view.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposables.dispose();
|
||||||
|
};
|
||||||
|
}, [api, props.id]);
|
||||||
|
|
||||||
|
const onReady = (event: PaneviewReadyEvent) => {
|
||||||
|
setApi(event.api);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDidDrop = (event: PaneviewDropEvent) => {
|
||||||
|
const data = getPaneData();
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewId = data.paneId;
|
||||||
|
const viewContainer = viewService.model.getViewContainer(props.id);
|
||||||
|
const view = viewService.model.getView(viewId);
|
||||||
|
|
||||||
|
viewService.model.moveViewToLocation(view, viewContainer, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!props.id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PaneviewReact
|
||||||
|
onDidDrop={onDidDrop}
|
||||||
|
components={components}
|
||||||
|
onReady={onReady}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
@ -16,6 +16,7 @@ export * from './groupview/types';
|
|||||||
export * from './dockview/dockviewComponent';
|
export * from './dockview/dockviewComponent';
|
||||||
export * from './dockview/options';
|
export * from './dockview/options';
|
||||||
export * from './gridview/gridviewComponent';
|
export * from './gridview/gridviewComponent';
|
||||||
|
export * from './dnd/dataTransfer';
|
||||||
|
|
||||||
export * from './react'; // TODO: should be conditional on whether user wants the React wrappers
|
export * from './react'; // TODO: should be conditional on whether user wants the React wrappers
|
||||||
|
|
||||||
|
@ -96,6 +96,12 @@ export class Paneview extends CompositeDisposable implements IDisposable {
|
|||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this.splitview.onDidSashEnd(() => {
|
this.splitview.onDidSashEnd(() => {
|
||||||
this._onDidChange.fire(undefined);
|
this._onDidChange.fire(undefined);
|
||||||
|
}),
|
||||||
|
this.splitview.onDidAddView(() => {
|
||||||
|
this._onDidChange.fire();
|
||||||
|
}),
|
||||||
|
this.splitview.onDidRemoveView(() => {
|
||||||
|
this._onDidChange.fire();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -128,9 +128,10 @@ export interface IPaneviewComponent extends IDisposable {
|
|||||||
readonly height: number;
|
readonly height: number;
|
||||||
readonly minimumSize: number;
|
readonly minimumSize: number;
|
||||||
readonly maximumSize: number;
|
readonly maximumSize: number;
|
||||||
|
readonly onDidDrop: Event<DroptargetEvent>;
|
||||||
|
readonly onDidLayoutChange: Event<void>;
|
||||||
addPanel(options: AddPaneviewCompponentOptions): IDisposable;
|
addPanel(options: AddPaneviewCompponentOptions): IDisposable;
|
||||||
layout(width: number, height: number): void;
|
layout(width: number, height: number): void;
|
||||||
onDidLayoutChange: Event<void>;
|
|
||||||
toJSON(): SerializedPaneview;
|
toJSON(): SerializedPaneview;
|
||||||
fromJSON(
|
fromJSON(
|
||||||
serializedPaneview: SerializedPaneview,
|
serializedPaneview: SerializedPaneview,
|
||||||
@ -142,6 +143,7 @@ export interface IPaneviewComponent extends IDisposable {
|
|||||||
removePanel(panel: IPaneviewPanel): void;
|
removePanel(panel: IPaneviewPanel): void;
|
||||||
getPanel(id: string): IPaneviewPanel | undefined;
|
getPanel(id: string): IPaneviewPanel | undefined;
|
||||||
movePanel(from: number, to: number): void;
|
movePanel(from: number, to: number): void;
|
||||||
|
updateOptions(options: Partial<PaneviewComponentOptions>): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PaneviewComponent
|
export class PaneviewComponent
|
||||||
@ -199,12 +201,20 @@ export class PaneviewComponent
|
|||||||
: this.paneview.orthogonalSize;
|
: this.paneview.orthogonalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _options: PaneviewComponentOptions;
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return this._options;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private element: HTMLElement,
|
private element: HTMLElement,
|
||||||
private readonly options: PaneviewComponentOptions
|
options: PaneviewComponentOptions
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this._options = options;
|
||||||
|
|
||||||
if (!options.components) {
|
if (!options.components) {
|
||||||
options.components = {};
|
options.components = {};
|
||||||
}
|
}
|
||||||
@ -224,6 +234,10 @@ export class PaneviewComponent
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateOptions(options: Partial<PaneviewComponentOptions>): void {
|
||||||
|
this._options = { ...this.options, ...options };
|
||||||
|
}
|
||||||
|
|
||||||
addPanel(options: AddPaneviewCompponentOptions): IDisposable {
|
addPanel(options: AddPaneviewCompponentOptions): IDisposable {
|
||||||
const body = createComponent(
|
const body = createComponent(
|
||||||
options.id,
|
options.id,
|
||||||
@ -269,17 +283,17 @@ export class PaneviewComponent
|
|||||||
disableDnd: !!this.options.disableDnd,
|
disableDnd: !!this.options.disableDnd,
|
||||||
});
|
});
|
||||||
|
|
||||||
view.onDidDrop((event) => {
|
const disposable = new CompositeDisposable(
|
||||||
this._onDidDrop.fire(event);
|
view.onDidDrop((event) => {
|
||||||
});
|
this._onDidDrop.fire(event);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const size: Sizing | number =
|
const size: Sizing | number =
|
||||||
typeof options.size === 'number' ? options.size : Sizing.Distribute;
|
typeof options.size === 'number' ? options.size : Sizing.Distribute;
|
||||||
const index =
|
const index =
|
||||||
typeof options.index === 'number' ? options.index : undefined;
|
typeof options.index === 'number' ? options.index : undefined;
|
||||||
|
|
||||||
this.paneview.addPane(view, size, index);
|
|
||||||
|
|
||||||
view.init({
|
view.init({
|
||||||
params: options.params || {},
|
params: options.params || {},
|
||||||
minimumBodySize: options.minimumBodySize,
|
minimumBodySize: options.minimumBodySize,
|
||||||
@ -289,13 +303,11 @@ export class PaneviewComponent
|
|||||||
containerApi: new PaneviewApi(this),
|
containerApi: new PaneviewApi(this),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.paneview.addPane(view, size, index);
|
||||||
|
|
||||||
view.orientation = this.paneview.orientation;
|
view.orientation = this.paneview.orientation;
|
||||||
|
|
||||||
return {
|
return disposable;
|
||||||
dispose: () => {
|
|
||||||
//
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPanels(): PaneviewPanel[] {
|
getPanels(): PaneviewPanel[] {
|
||||||
|
@ -62,6 +62,18 @@ export const PaneviewReact = React.forwardRef(
|
|||||||
};
|
};
|
||||||
}, [props.disableAutoResizing]);
|
}, [props.disableAutoResizing]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
paneviewRef.current?.updateOptions({
|
||||||
|
frameworkComponents: props.components,
|
||||||
|
});
|
||||||
|
}, [props.components]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
paneviewRef.current?.updateOptions({
|
||||||
|
headerframeworkComponents: props.headerComponents,
|
||||||
|
});
|
||||||
|
}, [props.headerComponents]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const createComponent = (
|
const createComponent = (
|
||||||
id: string,
|
id: string,
|
||||||
@ -90,12 +102,6 @@ export const PaneviewReact = React.forwardRef(
|
|||||||
|
|
||||||
const api = new PaneviewApi(paneview);
|
const api = new PaneviewApi(paneview);
|
||||||
|
|
||||||
const disposable = paneview.onDidDrop((event) => {
|
|
||||||
if (props.onDidDrop) {
|
|
||||||
props.onDidDrop({ event, api });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const { clientWidth, clientHeight } = domRef.current!;
|
const { clientWidth, clientHeight } = domRef.current!;
|
||||||
paneview.layout(clientWidth, clientHeight);
|
paneview.layout(clientWidth, clientHeight);
|
||||||
|
|
||||||
@ -106,11 +112,30 @@ export const PaneviewReact = React.forwardRef(
|
|||||||
paneviewRef.current = paneview;
|
paneviewRef.current = paneview;
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
disposable.dispose();
|
|
||||||
paneview.dispose();
|
paneview.dispose();
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!paneviewRef.current) {
|
||||||
|
return () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const paneview = paneviewRef.current;
|
||||||
|
|
||||||
|
const disposable = paneview.onDidDrop((event) => {
|
||||||
|
if (props.onDidDrop) {
|
||||||
|
props.onDidDrop({ event, api: new PaneviewApi(paneview) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}, [props.onDidDrop]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={props.className}
|
className={props.className}
|
||||||
|
Loading…
Reference in New Issue
Block a user