mirror of
https://github.com/mathuo/dockview
synced 2025-05-02 17:48:25 +00:00
feat: expose dnd functions
This commit is contained in:
parent
7ec824ea01
commit
c51597fde3
@ -49,18 +49,27 @@ export const Activitybar = (props: IGridviewPanelProps) => {
|
||||
|
||||
return (
|
||||
<div className="activity-bar" onClick={onOpenSidebar}>
|
||||
<DockviewDropTarget
|
||||
validOverlays={'vertical'}
|
||||
canDisplayOverlay={true}
|
||||
>
|
||||
<div className="activity-bar-item">
|
||||
<ActivitybarImage
|
||||
url={
|
||||
'https://fonts.gstatic.com/s/i/materialicons/search/v7/24px.svg'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</DockviewDropTarget>
|
||||
<div className="activity-bar-item">
|
||||
<ActivitybarImage
|
||||
url={
|
||||
'https://fonts.gstatic.com/s/i/materialicons/search/v7/24px.svg'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="activity-bar-item">
|
||||
<ActivitybarImage
|
||||
url={
|
||||
'https://fonts.gstatic.com/s/i/materialicons/search/v7/24px.svg'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="activity-bar-item">
|
||||
<ActivitybarImage
|
||||
url={
|
||||
'https://fonts.gstatic.com/s/i/materialicons/search/v7/24px.svg'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -8,12 +8,12 @@ import {
|
||||
SerializedGridview,
|
||||
GridviewApi,
|
||||
} from 'dockview';
|
||||
import { Activitybar } from './activitybar';
|
||||
import { Activitybar } from '../services/widgets';
|
||||
import { Footer } from './footer';
|
||||
import { Panel } from './panel';
|
||||
import { TestGrid } from './layoutGrid';
|
||||
import { useLayoutRegistry } from './registry';
|
||||
import { Sidebar } from './sidebar';
|
||||
import { Sidebar } from '../services/widgets';
|
||||
|
||||
const rootcomponents: {
|
||||
[index: string]: React.FunctionComponent<IGridviewPanelProps>;
|
||||
|
@ -5,7 +5,7 @@
|
||||
"data": {
|
||||
"id": "1",
|
||||
"component": "controlCenter",
|
||||
"headerComponent": "default",
|
||||
|
||||
"props": {},
|
||||
"title": "Control Center",
|
||||
"state": {}
|
||||
@ -17,7 +17,7 @@
|
||||
"data": {
|
||||
"id": "2",
|
||||
"component": "default",
|
||||
"headerComponent": "default",
|
||||
|
||||
"props": {},
|
||||
"title": "Panel 1",
|
||||
"state": {}
|
||||
@ -30,7 +30,7 @@
|
||||
"data": {
|
||||
"id": "3",
|
||||
"component": "default",
|
||||
"headerComponent": "default",
|
||||
|
||||
"props": {},
|
||||
"title": "Panel 2",
|
||||
"state": {}
|
||||
@ -43,7 +43,7 @@
|
||||
"data": {
|
||||
"id": "4",
|
||||
"component": "default",
|
||||
"headerComponent": "default",
|
||||
|
||||
"props": {},
|
||||
"title": "Panel 3",
|
||||
"state": {}
|
||||
|
@ -12,107 +12,6 @@ import { ControlCenter } from './controlCenter';
|
||||
import { toggleClass } from '../dom';
|
||||
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 = {
|
||||
default: (props: IPaneviewPanelProps) => {
|
||||
return <div style={{ height: '100%' }}>This is an example panel</div>;
|
||||
@ -120,10 +19,6 @@ const components = {
|
||||
controlCenter: ControlCenter,
|
||||
};
|
||||
|
||||
const headerComponents = {
|
||||
default: DefaultHeader,
|
||||
};
|
||||
|
||||
export const Sidebar = (props: IGridviewPanelProps) => {
|
||||
const api = React.useRef<PaneviewApi>();
|
||||
|
||||
@ -139,25 +34,21 @@ export const Sidebar = (props: IGridviewPanelProps) => {
|
||||
event.api.addPanel({
|
||||
id: '1',
|
||||
component: 'default',
|
||||
headerComponent: 'default',
|
||||
title: 'Control Center',
|
||||
});
|
||||
event.api.addPanel({
|
||||
id: '2',
|
||||
component: 'default',
|
||||
headerComponent: 'default',
|
||||
title: 'Panel 1',
|
||||
});
|
||||
event.api.addPanel({
|
||||
id: '3',
|
||||
component: 'default',
|
||||
headerComponent: 'default',
|
||||
title: 'Panel 2',
|
||||
});
|
||||
event.api.addPanel({
|
||||
id: '4',
|
||||
component: 'default',
|
||||
headerComponent: 'default',
|
||||
title: 'Panel 3',
|
||||
});
|
||||
|
||||
@ -196,7 +87,6 @@ export const Sidebar = (props: IGridviewPanelProps) => {
|
||||
}}
|
||||
>
|
||||
<PaneviewReact
|
||||
headerComponents={headerComponents}
|
||||
components={components}
|
||||
onReady={onReady}
|
||||
onDidDrop={onDidDrop}
|
||||
|
34
packages/dockview-demo/src/services/view.ts
Normal file
34
packages/dockview-demo/src/services/view.ts
Normal file
@ -0,0 +1,34 @@
|
||||
export interface View {
|
||||
readonly id: string;
|
||||
readonly isExpanded: boolean;
|
||||
readonly title: string;
|
||||
toJSON(): object;
|
||||
}
|
||||
|
||||
export class DefaultView implements View {
|
||||
private readonly _id: string;
|
||||
private readonly _title: string;
|
||||
private readonly _isExpanded: boolean;
|
||||
|
||||
get id() {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
get isExpanded() {
|
||||
return this._isExpanded;
|
||||
}
|
||||
|
||||
constructor(id: string, title: string, isExpanded: boolean) {
|
||||
this._id = id;
|
||||
this._title = title;
|
||||
this._isExpanded = isExpanded;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return { id: this.id, title: this.title, isExpanded: this.isExpanded };
|
||||
}
|
||||
}
|
112
packages/dockview-demo/src/services/viewContainer.ts
Normal file
112
packages/dockview-demo/src/services/viewContainer.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import {
|
||||
CompositeDisposable,
|
||||
Emitter,
|
||||
Event,
|
||||
SerializedPaneview,
|
||||
} from 'dockview';
|
||||
import { DefaultView, View } from './view';
|
||||
|
||||
export interface ViewContainer<T = any> {
|
||||
readonly id: string;
|
||||
readonly canDelete: boolean;
|
||||
readonly views: View[];
|
||||
readonly schema: T | undefined;
|
||||
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(): object;
|
||||
}
|
||||
|
||||
export class PaneviewContainer implements ViewContainer<SerializedPaneview> {
|
||||
private readonly _id: string;
|
||||
private readonly _canDelete: boolean;
|
||||
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 canDelete() {
|
||||
return this._canDelete;
|
||||
}
|
||||
|
||||
get schema(): SerializedPaneview | undefined {
|
||||
if (!this._schema) {
|
||||
this._schema = JSON.parse(
|
||||
localStorage.getItem(`viewcontainer_${this.id}`)
|
||||
);
|
||||
}
|
||||
return this._schema;
|
||||
}
|
||||
|
||||
constructor(id: string, canDelete: boolean) {
|
||||
this._id = id;
|
||||
this._canDelete = canDelete;
|
||||
|
||||
const schema = this.schema;
|
||||
|
||||
if (this.schema) {
|
||||
this._views = this.schema.views.map((v) => {
|
||||
return new DefaultView(v.data.id, v.data.title, v.expanded);
|
||||
});
|
||||
} else {
|
||||
this._views = [];
|
||||
}
|
||||
// super();
|
||||
|
||||
// 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() {
|
||||
return { id: this.id, views: this.views.map((v) => ({ id: v.id })) };
|
||||
}
|
||||
}
|
175
packages/dockview-demo/src/services/viewService.ts
Normal file
175
packages/dockview-demo/src/services/viewService.ts
Normal file
@ -0,0 +1,175 @@
|
||||
import { Emitter, Event } from 'dockview';
|
||||
import { DefaultView, View } from './view';
|
||||
import { PaneviewContainer, ViewContainer } from './viewContainer';
|
||||
|
||||
export interface IViewService {
|
||||
readonly containers: ViewContainer[];
|
||||
readonly onDidActiveContainerChange: Event<void>;
|
||||
readonly onDidRemoveContainer: Event<void>;
|
||||
readonly onDidAddContainer: 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;
|
||||
addViews(view: View, viewContainer: ViewContainer, location?: number): void;
|
||||
removeViews(removeViews: View[], viewContainer: ViewContainer): void;
|
||||
getViewContainer(id: string): ViewContainer | undefined;
|
||||
toJSON(): object;
|
||||
load(layout: any): 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() {
|
||||
//
|
||||
}
|
||||
|
||||
load(layout: any): void {
|
||||
const { containers, activeContainer } = layout;
|
||||
|
||||
for (const container of containers) {
|
||||
const { id, views } = container;
|
||||
const viewContainer = new PaneviewContainer(id, true);
|
||||
for (const view of views) {
|
||||
viewContainer.addView(
|
||||
new DefaultView(view.id, view.title, view.isExpanded)
|
||||
);
|
||||
}
|
||||
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() {
|
||||
return {
|
||||
containers: this.containers.map((c) => c.toJSON()),
|
||||
activeContainer: this.activeContainer.id,
|
||||
};
|
||||
}
|
||||
}
|
291
packages/dockview-demo/src/services/widgets.tsx
Normal file
291
packages/dockview-demo/src/services/widgets.tsx
Normal file
@ -0,0 +1,291 @@
|
||||
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 { ViewService } from './viewService';
|
||||
import { DefaultView } from './view';
|
||||
|
||||
const viewService = new ViewService();
|
||||
|
||||
const layout = localStorage.getItem('viewservice');
|
||||
if (layout) {
|
||||
viewService.load(JSON.parse(layout));
|
||||
} else {
|
||||
const container1 = new PaneviewContainer('c1', true);
|
||||
if (!container1.schema) {
|
||||
container1.addView(new DefaultView('panel1', 'Panel 1', true));
|
||||
container1.addView(new DefaultView('panel2', 'Panel 2', true));
|
||||
}
|
||||
const container2 = new PaneviewContainer('c2', true);
|
||||
if (!container2.schema) {
|
||||
container2.addView(new DefaultView('panel3', 'Panel 3', true));
|
||||
container2.addView(new DefaultView('panel4', 'Panel 4', true));
|
||||
}
|
||||
viewService.addContainer(container1);
|
||||
viewService.addContainer(container2);
|
||||
}
|
||||
|
||||
const save = () => {
|
||||
localStorage.setItem('viewservice', JSON.stringify(viewService.toJSON()));
|
||||
};
|
||||
|
||||
viewService.onDidActiveContainerChange(save);
|
||||
viewService.onDidRemoveContainer(save);
|
||||
viewService.onDidAddContainer(save);
|
||||
|
||||
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.activeContainer.id
|
||||
);
|
||||
const [containers, setContainers] = React.useState<ViewContainer[]>(
|
||||
viewService.containers
|
||||
);
|
||||
|
||||
const registry = useLayoutRegistry();
|
||||
|
||||
React.useEffect(() => {
|
||||
const disposable = new CompositeDisposable(
|
||||
viewService.onDidActiveContainerChange(() => {
|
||||
setActiveContainerId(viewService.activeContainer.id);
|
||||
}),
|
||||
viewService.onDidAddContainer(() => {
|
||||
setContainers(viewService.containers);
|
||||
}),
|
||||
viewService.onDidRemoveContainer(() => {
|
||||
setContainers(viewService.containers);
|
||||
}),
|
||||
viewService.onDidContainersChange(() => {
|
||||
setContainers(viewService.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.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.getViewContainer(container);
|
||||
viewService.insertContainerAfter(sourceContainer, targetContainer);
|
||||
}
|
||||
};
|
||||
|
||||
const onNewContainer = (event: React.DragEvent) => {
|
||||
const data = getPaneData();
|
||||
if (data) {
|
||||
const { paneId } = data;
|
||||
const view = viewService.getView(paneId);
|
||||
const viewContainer = viewService.getViewContainer2(view);
|
||||
viewService.removeViews([view], viewContainer);
|
||||
// viewContainer.removeView(view);
|
||||
const newContainer = new PaneviewContainer(
|
||||
`t_${Date.now().toString().substr(5)}`,
|
||||
true
|
||||
);
|
||||
newContainer.addView(view);
|
||||
viewService.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={{
|
||||
height: '48px',
|
||||
boxSizing: 'border-box',
|
||||
borderLeft: isActive
|
||||
? '1px solid white'
|
||||
: '1px solid transparent',
|
||||
}}
|
||||
key={i}
|
||||
>
|
||||
{container.id}
|
||||
</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.activeContainer.id
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const disposable = viewService.onDidActiveContainerChange(() => {
|
||||
setSidebarId(viewService.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.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.getViewContainer(props.id);
|
||||
const view = viewService.getView(viewId);
|
||||
|
||||
viewService.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/options';
|
||||
export * from './gridview/gridviewComponent';
|
||||
export * from './dnd/dataTransfer';
|
||||
|
||||
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.splitview.onDidSashEnd(() => {
|
||||
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 minimumSize: number;
|
||||
readonly maximumSize: number;
|
||||
readonly onDidDrop: Event<DroptargetEvent>;
|
||||
readonly onDidLayoutChange: Event<void>;
|
||||
addPanel(options: AddPaneviewCompponentOptions): IDisposable;
|
||||
layout(width: number, height: number): void;
|
||||
onDidLayoutChange: Event<void>;
|
||||
toJSON(): SerializedPaneview;
|
||||
fromJSON(
|
||||
serializedPaneview: SerializedPaneview,
|
||||
@ -142,6 +143,7 @@ export interface IPaneviewComponent extends IDisposable {
|
||||
removePanel(panel: IPaneviewPanel): void;
|
||||
getPanel(id: string): IPaneviewPanel | undefined;
|
||||
movePanel(from: number, to: number): void;
|
||||
updateOptions(options: Partial<PaneviewComponentOptions>): void;
|
||||
}
|
||||
|
||||
export class PaneviewComponent
|
||||
@ -199,12 +201,20 @@ export class PaneviewComponent
|
||||
: this.paneview.orthogonalSize;
|
||||
}
|
||||
|
||||
private _options: PaneviewComponentOptions;
|
||||
|
||||
get options() {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private element: HTMLElement,
|
||||
private readonly options: PaneviewComponentOptions
|
||||
options: PaneviewComponentOptions
|
||||
) {
|
||||
super();
|
||||
|
||||
this._options = options;
|
||||
|
||||
if (!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 {
|
||||
const body = createComponent(
|
||||
options.id,
|
||||
@ -269,17 +283,17 @@ export class PaneviewComponent
|
||||
disableDnd: !!this.options.disableDnd,
|
||||
});
|
||||
|
||||
view.onDidDrop((event) => {
|
||||
this._onDidDrop.fire(event);
|
||||
});
|
||||
const disposable = new CompositeDisposable(
|
||||
view.onDidDrop((event) => {
|
||||
this._onDidDrop.fire(event);
|
||||
})
|
||||
);
|
||||
|
||||
const size: Sizing | number =
|
||||
typeof options.size === 'number' ? options.size : Sizing.Distribute;
|
||||
const index =
|
||||
typeof options.index === 'number' ? options.index : undefined;
|
||||
|
||||
this.paneview.addPane(view, size, index);
|
||||
|
||||
view.init({
|
||||
params: options.params || {},
|
||||
minimumBodySize: options.minimumBodySize,
|
||||
@ -289,13 +303,11 @@ export class PaneviewComponent
|
||||
containerApi: new PaneviewApi(this),
|
||||
});
|
||||
|
||||
this.paneview.addPane(view, size, index);
|
||||
|
||||
view.orientation = this.paneview.orientation;
|
||||
|
||||
return {
|
||||
dispose: () => {
|
||||
//
|
||||
},
|
||||
};
|
||||
return disposable;
|
||||
}
|
||||
|
||||
getPanels(): PaneviewPanel[] {
|
||||
|
@ -62,6 +62,18 @@ export const PaneviewReact = React.forwardRef(
|
||||
};
|
||||
}, [props.disableAutoResizing]);
|
||||
|
||||
React.useEffect(() => {
|
||||
paneviewRef.current?.updateOptions({
|
||||
frameworkComponents: props.components,
|
||||
});
|
||||
}, [props.components]);
|
||||
|
||||
React.useEffect(() => {
|
||||
paneviewRef.current?.updateOptions({
|
||||
headerframeworkComponents: props.headerComponents,
|
||||
});
|
||||
}, [props.headerComponents]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const createComponent = (
|
||||
id: string,
|
||||
@ -90,12 +102,6 @@ export const PaneviewReact = React.forwardRef(
|
||||
|
||||
const api = new PaneviewApi(paneview);
|
||||
|
||||
const disposable = paneview.onDidDrop((event) => {
|
||||
if (props.onDidDrop) {
|
||||
props.onDidDrop({ event, api });
|
||||
}
|
||||
});
|
||||
|
||||
const { clientWidth, clientHeight } = domRef.current!;
|
||||
paneview.layout(clientWidth, clientHeight);
|
||||
|
||||
@ -106,11 +112,28 @@ export const PaneviewReact = React.forwardRef(
|
||||
paneviewRef.current = paneview;
|
||||
|
||||
return () => {
|
||||
disposable.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 (
|
||||
<div
|
||||
className={props.className}
|
||||
|
Loading…
Reference in New Issue
Block a user