mirror of
https://github.com/mathuo/dockview
synced 2025-03-15 18:32:04 +00:00
code
This commit is contained in:
parent
4033489e38
commit
47f27633da
@ -2,7 +2,7 @@ import * as React from "react";
|
|||||||
import { LoadFromConfig } from "./loadFromConfig";
|
import { LoadFromConfig } from "./loadFromConfig";
|
||||||
import { FromApi } from "./fromApi";
|
import { FromApi } from "./fromApi";
|
||||||
import { PaneDemo } from "./pane";
|
import { PaneDemo } from "./pane";
|
||||||
import { TestGrid } from "./reactgrid";
|
import { TestGrid } from "./layout-grid/reactgrid";
|
||||||
|
|
||||||
const options = [
|
const options = [
|
||||||
{ id: "config", component: LoadFromConfig },
|
{ id: "config", component: LoadFromConfig },
|
||||||
|
6
packages/splitview-demo/src/layout-grid/customTab.tsx
Normal file
6
packages/splitview-demo/src/layout-grid/customTab.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { IPanelProps } from "splitview";
|
||||||
|
|
||||||
|
export const CustomTab = (props: IPanelProps) => {
|
||||||
|
return <div>hello</div>;
|
||||||
|
};
|
@ -9,6 +9,39 @@ import {
|
|||||||
CompositeDisposable,
|
CompositeDisposable,
|
||||||
GroupChangeKind,
|
GroupChangeKind,
|
||||||
} from "splitview";
|
} from "splitview";
|
||||||
|
import { CustomTab } from "./customTab";
|
||||||
|
import { SplitPanel } from "./splitPanel";
|
||||||
|
|
||||||
|
const Editor = (props: IPanelProps & { layoutApi: Api }) => {
|
||||||
|
const [tabHeight, setTabHeight] = React.useState<number>(0);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (props.layoutApi) {
|
||||||
|
setTabHeight(props.layoutApi.getTabHeight());
|
||||||
|
}
|
||||||
|
}, [props.layoutApi]);
|
||||||
|
|
||||||
|
const onTabHeightChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const value = Number(event.target.value);
|
||||||
|
if (!Number.isNaN(value)) {
|
||||||
|
setTabHeight(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
props.layoutApi.setTabHeight(tabHeight);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: "100%", backgroundColor: "white", color: "black" }}>
|
||||||
|
<label>
|
||||||
|
Tab height
|
||||||
|
<input onChange={onTabHeightChange} value={tabHeight} type="number" />
|
||||||
|
<button onClick={onClick}>Apply</button>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
inner_component: (props: IPanelProps) => {
|
inner_component: (props: IPanelProps) => {
|
||||||
@ -122,6 +155,7 @@ const components = {
|
|||||||
|
|
||||||
const backgroundColor = React.useMemo(
|
const backgroundColor = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
// "#1e1e1e",
|
||||||
`rgb(${Math.floor(Math.random() * 256)},${Math.floor(
|
`rgb(${Math.floor(Math.random() * 256)},${Math.floor(
|
||||||
Math.random() * 256
|
Math.random() * 256
|
||||||
)},${Math.floor(Math.random() * 256)})`,
|
)},${Math.floor(Math.random() * 256)})`,
|
||||||
@ -143,6 +177,12 @@ const components = {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
editor: Editor,
|
||||||
|
split_panel: SplitPanel,
|
||||||
|
};
|
||||||
|
|
||||||
|
const tabComponents = {
|
||||||
|
default: CustomTab,
|
||||||
};
|
};
|
||||||
|
|
||||||
const nextGuid = (() => {
|
const nextGuid = (() => {
|
||||||
@ -177,7 +217,7 @@ export const TestGrid = () => {
|
|||||||
title: "Item 2",
|
title: "Item 2",
|
||||||
});
|
});
|
||||||
api.addPanelFromComponent({
|
api.addPanelFromComponent({
|
||||||
componentName: "test_component",
|
componentName: "split_panel",
|
||||||
id: nextGuid(),
|
id: nextGuid(),
|
||||||
title: "Item 3 with a long title",
|
title: "Item 3 with a long title",
|
||||||
});
|
});
|
||||||
@ -202,6 +242,18 @@ export const TestGrid = () => {
|
|||||||
componentName: "test_component",
|
componentName: "test_component",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
api.addDndHandle("Files", (ev) => {
|
||||||
|
const { event } = ev;
|
||||||
|
|
||||||
|
ev.event.event.preventDefault();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: Date.now().toString(),
|
||||||
|
title: event.event.dataTransfer.files[0].name,
|
||||||
|
componentName: "test_component",
|
||||||
|
};
|
||||||
|
});
|
||||||
}, [api]);
|
}, [api]);
|
||||||
|
|
||||||
const onAdd = () => {
|
const onAdd = () => {
|
||||||
@ -224,6 +276,7 @@ export const TestGrid = () => {
|
|||||||
_api.current?.layout(width, height);
|
_api.current?.layout(width, height);
|
||||||
};
|
};
|
||||||
window.addEventListener("resize", callback);
|
window.addEventListener("resize", callback);
|
||||||
|
callback(undefined);
|
||||||
|
|
||||||
const dis = _api.current.onDidLayoutChange((event) => {
|
const dis = _api.current.onDidLayoutChange((event) => {
|
||||||
console.log(event.kind);
|
console.log(event.kind);
|
||||||
@ -280,7 +333,6 @@ export const TestGrid = () => {
|
|||||||
if (!api) {
|
if (!api) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("create drag refs");
|
|
||||||
api.createDragTarget(
|
api.createDragTarget(
|
||||||
{ element: dragRef.current, content: "drag me" },
|
{ element: dragRef.current, content: "drag me" },
|
||||||
() => ({
|
() => ({
|
||||||
@ -294,10 +346,25 @@ export const TestGrid = () => {
|
|||||||
event.dataTransfer.setData("text/plain", "Panel2");
|
event.dataTransfer.setData("text/plain", "Panel2");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onAddEditor = () => {
|
||||||
|
api.addPanelFromComponent({
|
||||||
|
id: "editor",
|
||||||
|
componentName: "editor",
|
||||||
|
tabComponentName: "default",
|
||||||
|
params: { layoutApi: api },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTabContextMenu = (event: MouseEvent) => {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: "100%" }}>
|
<div
|
||||||
|
// className="visual-studio-theme"
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
<div style={{ height: "20px", display: "flex" }}>
|
<div style={{ height: "20px", display: "flex" }}>
|
||||||
<button onClick={onAdd}>Add</button>
|
<button onClick={onAdd}>Add</button>
|
||||||
|
<button onClick={onAddEditor}>Expr</button>
|
||||||
<button onClick={onAddEmpty}>Add empty</button>
|
<button onClick={onAddEmpty}>Add empty</button>
|
||||||
<button onClick={onConfig}>Save</button>
|
<button onClick={onConfig}>Save</button>
|
||||||
<button onClick={onLoad}>Load</button>
|
<button onClick={onLoad}>Load</button>
|
||||||
@ -335,9 +402,12 @@ export const TestGrid = () => {
|
|||||||
// autoSizeToFitContainer={true}
|
// autoSizeToFitContainer={true}
|
||||||
onReady={onReady}
|
onReady={onReady}
|
||||||
components={components}
|
components={components}
|
||||||
|
tabComponents={tabComponents}
|
||||||
debug={true}
|
debug={true}
|
||||||
|
// tabHeight={30}
|
||||||
enableExternalDragEvents={true}
|
enableExternalDragEvents={true}
|
||||||
// serializedLayout={data}
|
// serializedLayout={data}
|
||||||
|
// onTabContextMenu={onTabContextMenu}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
43
packages/splitview-demo/src/layout-grid/splitPanel.tsx
Normal file
43
packages/splitview-demo/src/layout-grid/splitPanel.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
IPanelProps,
|
||||||
|
Orientation,
|
||||||
|
SplitviewFacade,
|
||||||
|
SplitviewReadyEvent,
|
||||||
|
} from "splitview";
|
||||||
|
import { SplitViewComponent } from "splitview";
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
default1: (props) => {
|
||||||
|
return <div style={{ height: "100%", width: "100%" }}>hiya</div>;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SplitPanel = (props: IPanelProps) => {
|
||||||
|
const api = React.useRef<SplitviewFacade>();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
props.api.onDidPanelDimensionChange((event) => {
|
||||||
|
// const [height,width] = [event.height, event.width]
|
||||||
|
// const [size, orthogonalSize] =
|
||||||
|
// props.orientation === Orientation.HORIZONTAL
|
||||||
|
// ? [width, height]
|
||||||
|
// : [height, width];
|
||||||
|
api.current?.layout(event.width, event.height);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onReady = (event: SplitviewReadyEvent) => {
|
||||||
|
event.api.addFromComponent({ id: "1", component: "default1" });
|
||||||
|
event.api.addFromComponent({ id: "2", component: "default1" });
|
||||||
|
api.current = event.api;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SplitViewComponent
|
||||||
|
components={components}
|
||||||
|
onReady={onReady}
|
||||||
|
orientation={Orientation.VERTICAL}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
@ -16,7 +16,7 @@ export interface IPaneComponentRef {
|
|||||||
layout: (size: number, orthogonalSize: number) => void;
|
layout: (size: number, orthogonalSize: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PaneComponent = React.RefForwardingComponent<
|
export type PaneComponent = React.ForwardRefRenderFunction<
|
||||||
IPaneComponentRef,
|
IPaneComponentRef,
|
||||||
IPaneComponentProps
|
IPaneComponentProps
|
||||||
>;
|
>;
|
||||||
@ -27,7 +27,7 @@ export interface IPaneHeaderComponentProps extends IViewWithReactComponent {
|
|||||||
userprops?: { [index: string]: any };
|
userprops?: { [index: string]: any };
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PaneHeaderComponent = React.RefForwardingComponent<
|
export type PaneHeaderComponent = React.ForwardRefRenderFunction<
|
||||||
{},
|
{},
|
||||||
IPaneHeaderComponentProps
|
IPaneHeaderComponentProps
|
||||||
>;
|
>;
|
||||||
|
@ -12,7 +12,7 @@ export interface IViewComponentRef {
|
|||||||
layout: (size: number, orthogonalSize: number) => void;
|
layout: (size: number, orthogonalSize: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ViewComponent = React.RefForwardingComponent<
|
export type ViewComponent = React.ForwardRefRenderFunction<
|
||||||
IViewComponentRef,
|
IViewComponentRef,
|
||||||
IViewComponentProps
|
IViewComponentProps
|
||||||
>;
|
>;
|
||||||
|
@ -24,19 +24,19 @@ export interface IPaneViewReactProps {
|
|||||||
initialLayout?: PaneViewSerializedConfig;
|
initialLayout?: PaneViewSerializedConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PaneViewReadyEvent = {
|
export interface PaneViewReadyEvent {
|
||||||
api: PaneviewApi;
|
api: PaneviewApi;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type PaneViewSerializedConfig = {
|
export interface PaneViewSerializedConfig {
|
||||||
views: Array<
|
views: Array<
|
||||||
Omit<IPaneWithReactComponent, "component" | "headerComponent"> & {
|
Omit<IPaneWithReactComponent, "component" | "headerComponent"> & {
|
||||||
size?: number;
|
size?: number;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type PaneviewApi = {
|
export interface PaneviewApi {
|
||||||
add: (
|
add: (
|
||||||
options: Omit<IPaneWithReactComponent, "component" | "headerComponent"> & {
|
options: Omit<IPaneWithReactComponent, "component" | "headerComponent"> & {
|
||||||
size?: number;
|
size?: number;
|
||||||
@ -45,7 +45,7 @@ export type PaneviewApi = {
|
|||||||
) => void;
|
) => void;
|
||||||
moveView: (from: number, to: number) => void;
|
moveView: (from: number, to: number) => void;
|
||||||
toJSON: () => {};
|
toJSON: () => {};
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface IPaneViewComponentRef {
|
export interface IPaneViewComponentRef {
|
||||||
layout: (size: number, orthogonalSize: number) => void;
|
layout: (size: number, orthogonalSize: number) => void;
|
||||||
|
@ -9,15 +9,15 @@ export interface IViewWithReactComponent extends IBaseView {
|
|||||||
component: ViewComponent;
|
component: ViewComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OnReadyEvent = {
|
export interface OnReadyEvent {
|
||||||
api: SplitviewApi;
|
api: SplitviewApi;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type SerializedConfig = {
|
export interface SerializedConfig {
|
||||||
views: Array<Omit<IViewWithReactComponent, "component"> & { size?: number }>;
|
views: Array<Omit<IViewWithReactComponent, "component"> & { size?: number }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type SplitviewApi = {
|
export interface SplitviewApi {
|
||||||
add: (
|
add: (
|
||||||
options: Omit<IViewWithReactComponent, "component"> & {
|
options: Omit<IViewWithReactComponent, "component"> & {
|
||||||
size?: number;
|
size?: number;
|
||||||
@ -26,7 +26,7 @@ export type SplitviewApi = {
|
|||||||
) => void;
|
) => void;
|
||||||
moveView: (from: number, to: number) => void;
|
moveView: (from: number, to: number) => void;
|
||||||
toJSON: () => {};
|
toJSON: () => {};
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface ISplitViewReactProps {
|
export interface ISplitViewReactProps {
|
||||||
orientation: Orientation;
|
orientation: Orientation;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"types": "dist/esm/index.d.ts",
|
"types": "dist/esm/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "gulp run",
|
"build": "gulp run",
|
||||||
"docs": "typedoc --excludeNotExported --excludePrivate true --mode file --out typedocs/ src/"
|
"docs": "typedoc"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Orientation, Sizing } from "../splitview/splitview";
|
import { Orientation, Sizing } from "../splitview/splitview";
|
||||||
import { Target } from "../groupview/droptarget/droptarget";
|
import { Position } from "../groupview/droptarget/droptarget";
|
||||||
import { tail } from "../array";
|
import { tail } from "../array";
|
||||||
import { LeafNode } from "./leafNode";
|
import { LeafNode } from "./leafNode";
|
||||||
import { BranchNode } from "./branchNode";
|
import { BranchNode } from "./branchNode";
|
||||||
@ -91,7 +91,7 @@ export function getGridLocation(element: HTMLElement): number[] {
|
|||||||
export function getRelativeLocation(
|
export function getRelativeLocation(
|
||||||
rootOrientation: Orientation,
|
rootOrientation: Orientation,
|
||||||
location: number[],
|
location: number[],
|
||||||
direction: Target
|
direction: Position
|
||||||
): number[] {
|
): number[] {
|
||||||
const orientation = getLocationOrientation(rootOrientation, location);
|
const orientation = getLocationOrientation(rootOrientation, location);
|
||||||
const directionOrientation = getDirectionOrientation(direction);
|
const directionOrientation = getDirectionOrientation(direction);
|
||||||
@ -99,20 +99,20 @@ export function getRelativeLocation(
|
|||||||
if (orientation === directionOrientation) {
|
if (orientation === directionOrientation) {
|
||||||
let [rest, index] = tail(location);
|
let [rest, index] = tail(location);
|
||||||
|
|
||||||
if (direction === Target.Right || direction === Target.Bottom) {
|
if (direction === Position.Right || direction === Position.Bottom) {
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [...rest, index];
|
return [...rest, index];
|
||||||
} else {
|
} else {
|
||||||
const index =
|
const index =
|
||||||
direction === Target.Right || direction === Target.Bottom ? 1 : 0;
|
direction === Position.Right || direction === Position.Bottom ? 1 : 0;
|
||||||
return [...location, index];
|
return [...location, index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDirectionOrientation(direction: Target): Orientation {
|
export function getDirectionOrientation(direction: Position): Orientation {
|
||||||
return direction === Target.Top || direction === Target.Bottom
|
return direction === Position.Top || direction === Position.Bottom
|
||||||
? Orientation.VERTICAL
|
? Orientation.VERTICAL
|
||||||
: Orientation.HORIZONTAL;
|
: Orientation.HORIZONTAL;
|
||||||
}
|
}
|
||||||
@ -193,9 +193,9 @@ export interface INodeDescriptor {
|
|||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IViewDeserializer = {
|
export interface IViewDeserializer {
|
||||||
fromJSON: (data: {}) => IGridView;
|
fromJSON: (data: {}) => IGridView;
|
||||||
};
|
}
|
||||||
|
|
||||||
export class Gridview {
|
export class Gridview {
|
||||||
private _root: BranchNode;
|
private _root: BranchNode;
|
||||||
|
@ -11,12 +11,12 @@ export enum DragType {
|
|||||||
EXTERNAL = "external_group_drag",
|
EXTERNAL = "external_group_drag",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DragItem = {
|
export interface DragItem {
|
||||||
itemId: string;
|
itemId: string;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type ExternalDragItem = PanelOptions;
|
export interface ExternalDragItem extends PanelOptions {}
|
||||||
|
|
||||||
export type DataObject = DragItem | ExternalDragItem;
|
export type DataObject = DragItem | ExternalDragItem;
|
||||||
|
|
||||||
|
@ -20,18 +20,18 @@
|
|||||||
transition-duration: 0.15s;
|
transition-duration: 0.15s;
|
||||||
transition-timing-function: ease-out;
|
transition-timing-function: ease-out;
|
||||||
|
|
||||||
&.left {
|
&.left,
|
||||||
|
&.right {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
&.right {
|
&.right {
|
||||||
left: 50%;
|
transform: translate(100%, 0%);
|
||||||
width: 50%;
|
|
||||||
}
|
}
|
||||||
&.bottom {
|
&.bottom {
|
||||||
top: 50%;
|
transform: translate(0%, 100%);
|
||||||
height: 50%;
|
|
||||||
}
|
}
|
||||||
&.top {
|
&.top,
|
||||||
|
&.bottom {
|
||||||
height: 50%;
|
height: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Emitter, Event } from "../../events";
|
import { Emitter, Event } from "../../events";
|
||||||
import { DataTransferSingleton } from "./dataTransfer";
|
import { DataTransferSingleton } from "./dataTransfer";
|
||||||
|
|
||||||
export enum Target {
|
export enum Position {
|
||||||
Top = "Top",
|
Top = "Top",
|
||||||
Left = "Left",
|
Left = "Left",
|
||||||
Bottom = "Bottom",
|
Bottom = "Bottom",
|
||||||
@ -9,10 +9,10 @@ export enum Target {
|
|||||||
Center = "Center",
|
Center = "Center",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DroptargetEvent = {
|
export interface DroptargetEvent {
|
||||||
target: Target;
|
position: Position;
|
||||||
event: DragEvent;
|
event: DragEvent;
|
||||||
};
|
}
|
||||||
|
|
||||||
const HAS_PROCESSED_KEY = "__drop_target_processed__";
|
const HAS_PROCESSED_KEY = "__drop_target_processed__";
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ const toggleClassName = (
|
|||||||
export class Droptarget {
|
export class Droptarget {
|
||||||
private target: HTMLElement;
|
private target: HTMLElement;
|
||||||
private overlay: HTMLElement;
|
private overlay: HTMLElement;
|
||||||
private state: Target;
|
private state: Position | undefined;
|
||||||
|
|
||||||
private readonly _onDidChange = new Emitter<DroptargetEvent>();
|
private readonly _onDidChange = new Emitter<DroptargetEvent>();
|
||||||
readonly onDidChange: Event<DroptargetEvent> = this._onDidChange.event;
|
readonly onDidChange: Event<DroptargetEvent> = this._onDidChange.event;
|
||||||
@ -106,7 +106,7 @@ export class Droptarget {
|
|||||||
this.removeDropTarget();
|
this.removeDropTarget();
|
||||||
|
|
||||||
if (!hasProcessed(event)) {
|
if (!hasProcessed(event)) {
|
||||||
this._onDidChange.fire({ target: this.state, event });
|
this._onDidChange.fire({ position: this.state, event });
|
||||||
} else {
|
} else {
|
||||||
console.debug("[dragtarget] already processed");
|
console.debug("[dragtarget] already processed");
|
||||||
}
|
}
|
||||||
@ -140,15 +140,15 @@ export class Droptarget {
|
|||||||
toggleClassName(this.overlay, "bottom", isBottom);
|
toggleClassName(this.overlay, "bottom", isBottom);
|
||||||
|
|
||||||
if (isRight) {
|
if (isRight) {
|
||||||
this.state = Target.Right;
|
this.state = Position.Right;
|
||||||
} else if (isLeft) {
|
} else if (isLeft) {
|
||||||
this.state = Target.Left;
|
this.state = Position.Left;
|
||||||
} else if (isTop) {
|
} else if (isTop) {
|
||||||
this.state = Target.Top;
|
this.state = Position.Top;
|
||||||
} else if (isBottom) {
|
} else if (isBottom) {
|
||||||
this.state = Target.Bottom;
|
this.state = Position.Bottom;
|
||||||
} else {
|
} else {
|
||||||
this.state = Target.Center;
|
this.state = Position.Center;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
import { DroptargetEvent } from "./droptarget/droptarget";
|
import { DroptargetEvent } from "./droptarget/droptarget";
|
||||||
|
|
||||||
export enum TabChangedEventType {
|
export interface TabDropEvent {
|
||||||
CLICK,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TabChangedEvent = { type: TabChangedEventType };
|
|
||||||
export type TabDropEvent = {
|
|
||||||
event: DroptargetEvent;
|
event: DroptargetEvent;
|
||||||
index?: number;
|
index?: number;
|
||||||
};
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { IDisposable, CompositeDisposable, Disposable } from "../lifecycle";
|
import { IDisposable, CompositeDisposable, Disposable } from "../lifecycle";
|
||||||
import { ITabContainer, TabContainer } from "./tabs/tabContainer";
|
import { ITabContainer, TabContainer } from "./titlebar/tabContainer";
|
||||||
import { IContentContainer, ContentContainer } from "./content";
|
import { IContentContainer, ContentContainer } from "./panel/content/content";
|
||||||
import { IGridView } from "../gridview/gridview";
|
import { IGridView } from "../gridview/gridview";
|
||||||
import { Target, Droptarget, DroptargetEvent } from "./droptarget/droptarget";
|
import { Position, Droptarget, DroptargetEvent } from "./droptarget/droptarget";
|
||||||
import { Event, Emitter, addDisposableListener } from "../events";
|
import { Event, Emitter, addDisposableListener } from "../events";
|
||||||
import { IGroupAccessor, Layout } from "../layout";
|
import { IGroupAccessor, Layout } from "../layout";
|
||||||
import { toggleClass } from "../dom";
|
import { toggleClass } from "../dom";
|
||||||
@ -44,12 +44,12 @@ export interface IGroupItem {
|
|||||||
body: { element: HTMLElement };
|
body: { element: HTMLElement };
|
||||||
}
|
}
|
||||||
|
|
||||||
type GroupMoveEvent = {
|
interface GroupMoveEvent {
|
||||||
groupId: string;
|
groupId: string;
|
||||||
itemId: string;
|
itemId: string;
|
||||||
target: Target;
|
target: Position;
|
||||||
index?: number;
|
index?: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface GroupOptions {
|
export interface GroupOptions {
|
||||||
panels: IPanel[];
|
panels: IPanel[];
|
||||||
@ -88,11 +88,11 @@ export interface IGroupview extends IDisposable, IGridView {
|
|||||||
moveToPrevious(options?: { panel?: IPanel; suppressRoll?: boolean }): void;
|
moveToPrevious(options?: { panel?: IPanel; suppressRoll?: boolean }): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GroupDropEvent = {
|
export interface GroupDropEvent {
|
||||||
event: DragEvent;
|
event: DragEvent;
|
||||||
target: Target;
|
target: Position;
|
||||||
index?: number;
|
index?: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export class Groupview extends CompositeDisposable implements IGroupview {
|
export class Groupview extends CompositeDisposable implements IGroupview {
|
||||||
private _element: HTMLElement;
|
private _element: HTMLElement;
|
||||||
@ -129,6 +129,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
|
|
||||||
set tabHeight(height: number) {
|
set tabHeight(height: number) {
|
||||||
this.tabContainer.height = height;
|
this.tabContainer.height = height;
|
||||||
|
this.layout(this._width, this._height);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isActive() {
|
get isActive() {
|
||||||
@ -290,7 +291,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
this.dropTarget.onDidChange((event) => {
|
this.dropTarget.onDidChange((event) => {
|
||||||
// if we've center dropped on ourself then ignore
|
// if we've center dropped on ourself then ignore
|
||||||
if (
|
if (
|
||||||
event.target === Target.Center &&
|
event.position === Position.Center &&
|
||||||
this.tabContainer.hasActiveDragEvent
|
this.tabContainer.hasActiveDragEvent
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@ -531,18 +532,18 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
|
|
||||||
private handleDropEvent(event: DroptargetEvent, index?: number) {
|
private handleDropEvent(event: DroptargetEvent, index?: number) {
|
||||||
if (isPanelTransferEvent(event.event)) {
|
if (isPanelTransferEvent(event.event)) {
|
||||||
this.handlePanelDropEvent(event.event, event.target, index);
|
this.handlePanelDropEvent(event.event, event.position, index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._onDrop.fire({ event: event.event, target: event.target, index });
|
this._onDrop.fire({ event: event.event, target: event.position, index });
|
||||||
|
|
||||||
console.debug("[customDropEvent]");
|
console.debug("[customDropEvent]");
|
||||||
}
|
}
|
||||||
|
|
||||||
private handlePanelDropEvent(
|
private handlePanelDropEvent(
|
||||||
event: DragEvent,
|
event: DragEvent,
|
||||||
target: Target,
|
target: Position,
|
||||||
index?: number
|
index?: number
|
||||||
) {
|
) {
|
||||||
const dataObject = extractData(event);
|
const dataObject = extractData(event);
|
||||||
@ -550,10 +551,10 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
if (isTabDragEvent(dataObject)) {
|
if (isTabDragEvent(dataObject)) {
|
||||||
const { groupId, itemId } = dataObject;
|
const { groupId, itemId } = dataObject;
|
||||||
const isSameGroup = this.id === groupId;
|
const isSameGroup = this.id === groupId;
|
||||||
if (isSameGroup) {
|
if (isSameGroup && !target) {
|
||||||
const index = this.tabContainer.indexOf(itemId);
|
const oldIndex = this.tabContainer.indexOf(itemId);
|
||||||
if (index > -1 && index === this.panels.length - 1) {
|
if (oldIndex === index) {
|
||||||
console.debug("[tabs] dropped in empty space");
|
console.debug("[tabs] drop indicates no change in position");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,15 @@ import { ClosePanelResult } from "./parts";
|
|||||||
import { IPanel } from "./types";
|
import { IPanel } from "./types";
|
||||||
import { CompositeDisposable, IDisposable } from "../../lifecycle";
|
import { CompositeDisposable, IDisposable } from "../../lifecycle";
|
||||||
|
|
||||||
export type PanelStateChangeEvent = {
|
export interface PanelStateChangeEvent {
|
||||||
isPanelVisible: boolean;
|
isPanelVisible: boolean;
|
||||||
isGroupActive: boolean;
|
isGroupActive: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type PanelDimensionChangeEvent = {
|
export interface PanelDimensionChangeEvent {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface PanelApi extends IDisposable {
|
export interface PanelApi extends IDisposable {
|
||||||
onDidPanelStateChange: Event<PanelStateChangeEvent>;
|
onDidPanelStateChange: Event<PanelStateChangeEvent>;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { CompositeDisposable, IDisposable } from "../lifecycle";
|
import { CompositeDisposable, IDisposable } from "../../../lifecycle";
|
||||||
import { Emitter, Event } from "../events";
|
import { Emitter, Event } from "../../../events";
|
||||||
import { trackFocus } from "../dom";
|
import { trackFocus } from "../../../dom";
|
||||||
|
|
||||||
export interface IContentContainer extends IDisposable {
|
export interface IContentContainer extends IDisposable {
|
||||||
onDidFocus: Event<void>;
|
onDidFocus: Event<void>;
|
@ -149,7 +149,11 @@ export class DefaultPanel extends CompositeDisposable implements IPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public layout(width: number, height: number) {
|
public layout(width: number, height: number) {
|
||||||
this._onDidPanelDimensionsChange.fire({ width, height });
|
// thw height of the panel excluded the height of the title/tab
|
||||||
|
this._onDidPanelDimensionsChange.fire({
|
||||||
|
width,
|
||||||
|
height: height - (this.group?.tabHeight || 0),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose() {
|
public dispose() {
|
||||||
|
@ -14,13 +14,13 @@ interface Methods extends IDisposable {
|
|||||||
setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void;
|
setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WatermarkPartInitParameters = {
|
export interface WatermarkPartInitParameters {
|
||||||
accessor: IGroupAccessor;
|
accessor: IGroupAccessor;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type PartInitParameters = {
|
export interface PartInitParameters extends PanelInitParameters {
|
||||||
api: PanelApi;
|
api: PanelApi;
|
||||||
} & PanelInitParameters;
|
}
|
||||||
|
|
||||||
export interface PanelHeaderPart extends Methods {
|
export interface PanelHeaderPart extends Methods {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -1,23 +1,31 @@
|
|||||||
import { addDisposableListener, Emitter, Event } from "../../events";
|
import { addDisposableListener, Emitter, Event } from "../../../events";
|
||||||
import { Droptarget, DroptargetEvent } from "../droptarget/droptarget";
|
import { Droptarget, DroptargetEvent } from "../../droptarget/droptarget";
|
||||||
import { CompositeDisposable } from "../../lifecycle";
|
import { CompositeDisposable } from "../../../lifecycle";
|
||||||
import { TabChangedEvent, TabDropEvent, TabChangedEventType } from "../events";
|
import { IGroupview } from "../../groupview";
|
||||||
import { IGroupview } from "../groupview";
|
|
||||||
import {
|
import {
|
||||||
DataTransferSingleton,
|
DataTransferSingleton,
|
||||||
DATA_KEY,
|
DATA_KEY,
|
||||||
DragType,
|
DragType,
|
||||||
extractData,
|
} from "../../droptarget/dataTransfer";
|
||||||
} from "../droptarget/dataTransfer";
|
// import { IGroupAccessor } from "../../layout";
|
||||||
import { IGroupAccessor } from "../../layout";
|
import { toggleClass } from "../../../dom";
|
||||||
import { toggleClass } from "../../dom";
|
import { IGroupAccessor } from "../../../layout";
|
||||||
|
|
||||||
|
export enum TabInteractionKind {
|
||||||
|
CLICK = "CLICK",
|
||||||
|
CONTEXT_MENU = "CONTEXT_MEU",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TabInteractionEvent {
|
||||||
|
kind: TabInteractionKind;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ITab {
|
export interface ITab {
|
||||||
id: string;
|
id: string;
|
||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
hasActiveDragEvent: boolean;
|
hasActiveDragEvent: boolean;
|
||||||
setContent: (element: HTMLElement) => void;
|
setContent: (element: HTMLElement) => void;
|
||||||
onChanged: Event<TabChangedEvent>;
|
onChanged: Event<TabInteractionEvent>;
|
||||||
onDropped: Event<DroptargetEvent>;
|
onDropped: Event<DroptargetEvent>;
|
||||||
setActive(isActive: boolean): void;
|
setActive(isActive: boolean): void;
|
||||||
startDragEvent(): void;
|
startDragEvent(): void;
|
||||||
@ -32,8 +40,8 @@ export class Tab extends CompositeDisposable implements ITab {
|
|||||||
private droptarget: Droptarget;
|
private droptarget: Droptarget;
|
||||||
private content: HTMLElement;
|
private content: HTMLElement;
|
||||||
|
|
||||||
private readonly _onChanged = new Emitter<TabChangedEvent>();
|
private readonly _onChanged = new Emitter<TabInteractionEvent>();
|
||||||
readonly onChanged: Event<TabChangedEvent> = this._onChanged.event;
|
readonly onChanged: Event<TabInteractionEvent> = this._onChanged.event;
|
||||||
|
|
||||||
private readonly _onDropped = new Emitter<DroptargetEvent>();
|
private readonly _onDropped = new Emitter<DroptargetEvent>();
|
||||||
readonly onDropped: Event<DroptargetEvent> = this._onDropped.event;
|
readonly onDropped: Event<DroptargetEvent> = this._onDropped.event;
|
||||||
@ -72,7 +80,10 @@ export class Tab extends CompositeDisposable implements ITab {
|
|||||||
if (ev.defaultPrevented) {
|
if (ev.defaultPrevented) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._onChanged.fire({ type: TabChangedEventType.CLICK });
|
this._onChanged.fire({ kind: TabInteractionKind.CLICK });
|
||||||
|
}),
|
||||||
|
addDisposableListener(this._element, "contextmenu", (ev) => {
|
||||||
|
this._onChanged.fire({ kind: TabInteractionKind.CONTEXT_MENU });
|
||||||
}),
|
}),
|
||||||
addDisposableListener(this._element, "dragstart", (event) => {
|
addDisposableListener(this._element, "dragstart", (event) => {
|
||||||
this.dragInPlayDetails = { isDragging: true, id: this.accessor.id };
|
this.dragInPlayDetails = { isDragging: true, id: this.accessor.id };
|
@ -5,18 +5,18 @@ import { PanelHeaderPart, PanelContentPart, ClosePanelResult } from "./parts";
|
|||||||
|
|
||||||
// objects
|
// objects
|
||||||
|
|
||||||
export type PanelUpdateEvent = {
|
export interface PanelUpdateEvent {
|
||||||
params: { [key: string]: any };
|
params: { [key: string]: any };
|
||||||
};
|
}
|
||||||
|
|
||||||
// init parameters
|
// init parameters
|
||||||
|
|
||||||
export type PanelInitParameters = {
|
export interface PanelInitParameters {
|
||||||
title: string;
|
title: string;
|
||||||
suppressClosable?: boolean;
|
suppressClosable?: boolean;
|
||||||
params: { [index: string]: any };
|
params: { [index: string]: any };
|
||||||
state?: { [index: string]: any };
|
state?: { [index: string]: any };
|
||||||
};
|
}
|
||||||
|
|
||||||
// constructors
|
// constructors
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { IDisposable, CompositeDisposable } from "../../lifecycle";
|
import { IDisposable, CompositeDisposable } from "../../lifecycle";
|
||||||
import { addDisposableListener, Emitter, Event } from "../../events";
|
import { addDisposableListener, Emitter, Event } from "../../events";
|
||||||
import { ITab, Tab } from "./tab";
|
import { ITab, Tab, TabInteractionKind } from "../panel/tab/tab";
|
||||||
import { removeClasses, addClasses, toggleClass } from "../../dom";
|
import { removeClasses, addClasses, toggleClass } from "../../dom";
|
||||||
import { hasProcessed } from "../droptarget/droptarget";
|
import { hasProcessed, Position } from "../droptarget/droptarget";
|
||||||
import { TabDropEvent } from "../events";
|
import { TabDropEvent } from "../events";
|
||||||
|
|
||||||
import { IGroupview } from "../groupview";
|
import { IGroupview } from "../groupview";
|
||||||
@ -144,7 +144,8 @@ export class TabContainer extends CompositeDisposable implements ITabContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._onDropped.fire({
|
this._onDropped.fire({
|
||||||
event: { event, target: undefined },
|
event: { event, position: Position.Center },
|
||||||
|
index: this.tabs.length - 1,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -195,7 +196,13 @@ export class TabContainer extends CompositeDisposable implements ITabContainer {
|
|||||||
// TODO - dispose of resources
|
// TODO - dispose of resources
|
||||||
const disposables = CompositeDisposable.from(
|
const disposables = CompositeDisposable.from(
|
||||||
tab.onChanged((event) => {
|
tab.onChanged((event) => {
|
||||||
|
switch (event.kind) {
|
||||||
|
case TabInteractionKind.CLICK:
|
||||||
this.group.openPanel(panel);
|
this.group.openPanel(panel);
|
||||||
|
break;
|
||||||
|
case TabInteractionKind.CONTEXT_MENU:
|
||||||
|
// TODO finish
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
tab.onDropped((event) => {
|
tab.onDropped((event) => {
|
||||||
this._onDropped.fire({ event, index: this.indexOf(tab) });
|
this._onDropped.fire({ event, index: this.indexOf(tab) });
|
@ -2,8 +2,8 @@ export * from "./splitview/splitview";
|
|||||||
export * from "./splitview/paneview";
|
export * from "./splitview/paneview";
|
||||||
export * from "./gridview/gridview";
|
export * from "./gridview/gridview";
|
||||||
export * from "./groupview/groupview";
|
export * from "./groupview/groupview";
|
||||||
export * from "./groupview/content";
|
export * from "./groupview/panel/content/content";
|
||||||
export * from "./groupview/tabs/tab";
|
export * from "./groupview/panel/tab/tab";
|
||||||
export * from "./events";
|
export * from "./events";
|
||||||
export * from "./lifecycle";
|
export * from "./lifecycle";
|
||||||
export * from "./groupview/panel/panel";
|
export * from "./groupview/panel/panel";
|
||||||
@ -12,6 +12,7 @@ export * from "./react/react";
|
|||||||
export * from "./groupview/panel/types";
|
export * from "./groupview/panel/types";
|
||||||
export * from "./groupview/panel/parts";
|
export * from "./groupview/panel/parts";
|
||||||
export * from "./react/layout";
|
export * from "./react/layout";
|
||||||
|
export * from "./react/splitview";
|
||||||
export * from "./react/reactContentPart";
|
export * from "./react/reactContentPart";
|
||||||
export * from "./react/reactHeaderPart";
|
export * from "./react/reactHeaderPart";
|
||||||
|
|
||||||
|
@ -1,67 +1,17 @@
|
|||||||
.groupview {
|
|
||||||
&.active-group {
|
|
||||||
> .title-container > .tab-container > .tab {
|
|
||||||
&.active-tab {
|
|
||||||
.default-tab {
|
|
||||||
background-color: var(--tab-background-visible);
|
|
||||||
color: var(--active-group-visible-panel-color);
|
|
||||||
}
|
|
||||||
.tab-action {
|
|
||||||
background-color: var(--active-group-visible-panel-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.inactive-tab {
|
|
||||||
.default-tab {
|
|
||||||
background-color: var(--tab-background-hidden);
|
|
||||||
color: var(--active-group-hidden-panel-color);
|
|
||||||
}
|
|
||||||
.tab-action {
|
|
||||||
background-color: var(--active-group-hidden-panel-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.inactive-group {
|
|
||||||
> .title-container > .tab-container > .tab {
|
|
||||||
&.active-tab {
|
|
||||||
.default-tab {
|
|
||||||
background-color: var(--tab-background-visible);
|
|
||||||
color: var(--inactive-group-visible-panel-color);
|
|
||||||
}
|
|
||||||
.tab-action {
|
|
||||||
background-color: var(--inactive-group-visible-panel-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.inactive-tab {
|
|
||||||
.default-tab {
|
|
||||||
background-color: var(--tab-background-hidden);
|
|
||||||
color: var(--inactive-group-hidden-panel-color);
|
|
||||||
}
|
|
||||||
.tab-action {
|
|
||||||
background-color: var(--inactive-group-hidden-panel-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
&.dragging {
|
&.dragging {
|
||||||
color: var(--active-group-visible-panel-color);
|
|
||||||
.tab-action {
|
.tab-action {
|
||||||
background-color: var(--active-group-visible-panel-color);
|
background-color: var(--active-group-visible-panel-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active-tab > .default-tab {
|
&.active-tab > .default-tab {
|
||||||
background-color: var(--tab-background-visible);
|
|
||||||
.tab-action {
|
.tab-action {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.inactive-tab > .default-tab {
|
&.inactive-tab > .default-tab {
|
||||||
background-color: var(--tab-background-hidden);
|
|
||||||
.tab-action:not(.dirty) {
|
.tab-action:not(.dirty) {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
@ -9,3 +9,55 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.groupview {
|
||||||
|
&.active-group {
|
||||||
|
> .title-container > .tab-container > .tab {
|
||||||
|
&.active-tab {
|
||||||
|
background-color: var(--active-tab-background-visible);
|
||||||
|
color: var(--active-group-visible-panel-color);
|
||||||
|
|
||||||
|
.tab-action {
|
||||||
|
background-color: var(--active-group-visible-panel-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.inactive-tab {
|
||||||
|
background-color: var(--active-tab-background-hidden);
|
||||||
|
color: var(--active-group-hidden-panel-color);
|
||||||
|
|
||||||
|
.tab-action {
|
||||||
|
background-color: var(--active-group-hidden-panel-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.inactive-group {
|
||||||
|
> .title-container > .tab-container > .tab {
|
||||||
|
&.active-tab {
|
||||||
|
background-color: var(--inactive-tab-background-visible);
|
||||||
|
color: var(--inactive-group-visible-panel-color);
|
||||||
|
|
||||||
|
.tab-action {
|
||||||
|
background-color: var(--inactive-group-visible-panel-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.inactive-tab {
|
||||||
|
background-color: var(--inactive-tab-background-hidden);
|
||||||
|
color: var(--inactive-group-hidden-panel-color);
|
||||||
|
|
||||||
|
.tab-action {
|
||||||
|
background-color: var(--inactive-group-hidden-panel-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// when a tab is dragged we loss the above stylings because they are conditional on parent elements
|
||||||
|
// there we also set some stylings for the dragging event
|
||||||
|
.tab {
|
||||||
|
&.dragging {
|
||||||
|
background-color: var(--active-tab-background-visible);
|
||||||
|
color: var(--active-group-visible-panel-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Gridview, getRelativeLocation } from "../gridview/gridview";
|
import { Gridview, getRelativeLocation } from "../gridview/gridview";
|
||||||
import { Target } from "../groupview/droptarget/droptarget";
|
import { Position } from "../groupview/droptarget/droptarget";
|
||||||
import { getGridLocation } from "../gridview/gridview";
|
import { getGridLocation } from "../gridview/gridview";
|
||||||
import { tail, sequenceEquals } from "../array";
|
import { tail, sequenceEquals } from "../array";
|
||||||
import {
|
import {
|
||||||
@ -45,10 +45,10 @@ import {
|
|||||||
const nextGroupId = sequentialNumberGenerator();
|
const nextGroupId = sequentialNumberGenerator();
|
||||||
const nextLayoutId = sequentialNumberGenerator();
|
const nextLayoutId = sequentialNumberGenerator();
|
||||||
|
|
||||||
export type PanelReference = {
|
export interface PanelReference {
|
||||||
update: (event: { params: { [key: string]: any } }) => void;
|
update: (event: { params: { [key: string]: any } }) => void;
|
||||||
remove: () => void;
|
remove: () => void;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface Api {
|
export interface Api {
|
||||||
layout(width: number, height: number): void;
|
layout(width: number, height: number): void;
|
||||||
@ -56,6 +56,7 @@ export interface Api {
|
|||||||
setAutoResizeToFit(enabled: boolean): void;
|
setAutoResizeToFit(enabled: boolean): void;
|
||||||
resizeToFit(): void;
|
resizeToFit(): void;
|
||||||
setTabHeight(height: number): void;
|
setTabHeight(height: number): void;
|
||||||
|
getTabHeight(): number;
|
||||||
size: number;
|
size: number;
|
||||||
totalPanels: number;
|
totalPanels: number;
|
||||||
// lifecycle
|
// lifecycle
|
||||||
@ -90,7 +91,7 @@ export interface IGroupAccessor {
|
|||||||
referenceGroup: IGroupview,
|
referenceGroup: IGroupview,
|
||||||
groupId: string,
|
groupId: string,
|
||||||
itemId: string,
|
itemId: string,
|
||||||
target: Target,
|
target: Position,
|
||||||
index?: number
|
index?: number
|
||||||
): void;
|
): void;
|
||||||
doSetGroupActive: (group: IGroupview) => void;
|
doSetGroupActive: (group: IGroupview) => void;
|
||||||
@ -399,11 +400,16 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setTabHeight(height: number) {
|
public setTabHeight(height: number) {
|
||||||
|
this.options.tabHeight = height;
|
||||||
this.groups.forEach((value) => {
|
this.groups.forEach((value) => {
|
||||||
value.value.tabHeight = height;
|
value.value.tabHeight = height;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTabHeight() {
|
||||||
|
return this.options.tabHeight;
|
||||||
|
}
|
||||||
|
|
||||||
public setAutoResizeToFit(enabled: boolean) {
|
public setAutoResizeToFit(enabled: boolean) {
|
||||||
if (this.resizeTimer) {
|
if (this.resizeTimer) {
|
||||||
clearInterval(this.resizeTimer);
|
clearInterval(this.resizeTimer);
|
||||||
@ -435,7 +441,7 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
const referenceGroup = this.findGroup(referencePanel);
|
const referenceGroup = this.findGroup(referencePanel);
|
||||||
|
|
||||||
const target = this.toTarget(options.position.direction);
|
const target = this.toTarget(options.position.direction);
|
||||||
if (target === Target.Center) {
|
if (target === Position.Center) {
|
||||||
referenceGroup.openPanel(panel);
|
referenceGroup.openPanel(panel);
|
||||||
} else {
|
} else {
|
||||||
const location = getGridLocation(referenceGroup.element);
|
const location = getGridLocation(referenceGroup.element);
|
||||||
@ -597,13 +603,13 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
referenceGroup: IGroupview,
|
referenceGroup: IGroupview,
|
||||||
groupId: string,
|
groupId: string,
|
||||||
itemId: string,
|
itemId: string,
|
||||||
target: Target,
|
target: Position,
|
||||||
index?: number
|
index?: number
|
||||||
) {
|
) {
|
||||||
const sourceGroup = groupId ? this.groups.get(groupId).value : undefined;
|
const sourceGroup = groupId ? this.groups.get(groupId).value : undefined;
|
||||||
|
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case Target.Center:
|
case Position.Center:
|
||||||
case undefined:
|
case undefined:
|
||||||
const groupItem =
|
const groupItem =
|
||||||
sourceGroup?.removePanel(itemId) || this.panels.get(itemId).value;
|
sourceGroup?.removePanel(itemId) || this.panels.get(itemId).value;
|
||||||
@ -699,7 +705,7 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
}
|
}
|
||||||
this.moveGroup(
|
this.moveGroup(
|
||||||
group,
|
group,
|
||||||
panel?.group.id,
|
panel?.group?.id,
|
||||||
panel.id,
|
panel.id,
|
||||||
event.target,
|
event.target,
|
||||||
event.index
|
event.index
|
||||||
@ -774,16 +780,16 @@ export class Layout extends CompositeDisposable implements ILayout {
|
|||||||
private toTarget(direction: "left" | "right" | "above" | "below" | "within") {
|
private toTarget(direction: "left" | "right" | "above" | "below" | "within") {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case "left":
|
case "left":
|
||||||
return Target.Left;
|
return Position.Left;
|
||||||
case "right":
|
case "right":
|
||||||
return Target.Right;
|
return Position.Right;
|
||||||
case "above":
|
case "above":
|
||||||
return Target.Top;
|
return Position.Top;
|
||||||
case "below":
|
case "below":
|
||||||
return Target.Bottom;
|
return Position.Bottom;
|
||||||
case "within":
|
case "within":
|
||||||
default:
|
default:
|
||||||
return Target.Center;
|
return Position.Center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { IGroupview } from "../groupview/groupview";
|
import { IGroupview } from "../groupview/groupview";
|
||||||
|
import { PanelApi } from "../groupview/panel/api";
|
||||||
import {
|
import {
|
||||||
PanelContentPart,
|
PanelContentPart,
|
||||||
PanelContentPartConstructor,
|
PanelContentPartConstructor,
|
||||||
@ -6,11 +7,20 @@ import {
|
|||||||
PanelHeaderPartConstructor,
|
PanelHeaderPartConstructor,
|
||||||
WatermarkConstructor,
|
WatermarkConstructor,
|
||||||
} from "../groupview/panel/parts";
|
} from "../groupview/panel/parts";
|
||||||
|
import { IPanel } from "../groupview/panel/types";
|
||||||
|
import { Api } from "./layout";
|
||||||
|
|
||||||
export type FrameworkPanelWrapper = {
|
export interface FrameworkPanelWrapper {
|
||||||
createContentWrapper: (id: string, component: any) => PanelContentPart;
|
createContentWrapper: (id: string, component: any) => PanelContentPart;
|
||||||
createTabWrapper: (id: string, component: any) => PanelHeaderPart;
|
createTabWrapper: (id: string, component: any) => PanelHeaderPart;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export interface TabContextMenuEvent {
|
||||||
|
event: MouseEvent;
|
||||||
|
api: Api;
|
||||||
|
panelApi: PanelApi;
|
||||||
|
panel: IPanel;
|
||||||
|
}
|
||||||
|
|
||||||
export interface LayoutOptions {
|
export interface LayoutOptions {
|
||||||
tabComponents?: {
|
tabComponents?: {
|
||||||
@ -31,6 +41,7 @@ export interface LayoutOptions {
|
|||||||
tabHeight?: number;
|
tabHeight?: number;
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
enableExternalDragEvents?: boolean;
|
enableExternalDragEvents?: boolean;
|
||||||
|
onTabContextMenu?: (event: TabContextMenuEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelOptions {
|
export interface PanelOptions {
|
||||||
|
@ -6,9 +6,9 @@ import { ReactPanelHeaderPart } from "./reactHeaderPart";
|
|||||||
import { IPanelProps } from "./react";
|
import { IPanelProps } from "./react";
|
||||||
import { ReactPanelDeserialzier } from "./deserializer";
|
import { ReactPanelDeserialzier } from "./deserializer";
|
||||||
|
|
||||||
export type OnReadyEvent = {
|
export interface OnReadyEvent {
|
||||||
api: Api;
|
api: Api;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface ReactLayout {
|
export interface ReactLayout {
|
||||||
addPortal: (portal: React.ReactPortal) => IDisposable;
|
addPortal: (portal: React.ReactPortal) => IDisposable;
|
||||||
|
57
packages/splitview/src/react/reactView.ts
Normal file
57
packages/splitview/src/react/reactView.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { Emitter } from "../events";
|
||||||
|
import { IView } from "../splitview/splitview";
|
||||||
|
import { ReactLayout } from "./layout";
|
||||||
|
import { ReactPart } from "./react";
|
||||||
|
|
||||||
|
export class ReactView implements IView {
|
||||||
|
private _element: HTMLElement;
|
||||||
|
private part: ReactPart;
|
||||||
|
|
||||||
|
private _onDidChange: Emitter<number | undefined> = new Emitter<
|
||||||
|
number | undefined
|
||||||
|
>();
|
||||||
|
public onDidChange = this._onDidChange.event;
|
||||||
|
|
||||||
|
get element() {
|
||||||
|
return this._element;
|
||||||
|
}
|
||||||
|
|
||||||
|
get minimumSize() {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
// get snapSize() {
|
||||||
|
// return 100;
|
||||||
|
// }
|
||||||
|
|
||||||
|
get maximumSize() {
|
||||||
|
return Number.MAX_SAFE_INTEGER;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly id: string,
|
||||||
|
private readonly component: React.FunctionComponent<{}>,
|
||||||
|
private readonly parent: ReactLayout
|
||||||
|
) {
|
||||||
|
if (!this.component) {
|
||||||
|
throw new Error("React.FunctionalComponent cannot be undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
this._element = document.createElement("div");
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(size: number, orthogonalSize: number) {}
|
||||||
|
|
||||||
|
init(parameters: { params: any }): void {
|
||||||
|
this.part = new ReactPart(
|
||||||
|
this.element,
|
||||||
|
{} as any,
|
||||||
|
this.parent.addPortal,
|
||||||
|
this.component,
|
||||||
|
parameters.params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(params: {}) {
|
||||||
|
this.part.update(params);
|
||||||
|
}
|
||||||
|
}
|
95
packages/splitview/src/react/splitview.tsx
Normal file
95
packages/splitview/src/react/splitview.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { Orientation, SplitView } from "../splitview/splitview";
|
||||||
|
import { ReactView } from "./reactView";
|
||||||
|
|
||||||
|
export interface SplitviewFacade {
|
||||||
|
addFromComponent(options: { id: string; component: string }): void;
|
||||||
|
layout(size: number, orthogonalSize: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SplitviewReadyEvent {
|
||||||
|
api: SplitviewFacade;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISplitviewComponentProps {
|
||||||
|
orientation: Orientation;
|
||||||
|
onReady?: (event: SplitviewReadyEvent) => void;
|
||||||
|
components: { [index: string]: React.FunctionComponent<{}> };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SplitViewComponent = (props: ISplitviewComponentProps) => {
|
||||||
|
const domReference = React.useRef<HTMLDivElement>();
|
||||||
|
const splitview = React.useRef<SplitView>();
|
||||||
|
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));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
splitview.current = new SplitView(domReference.current, {
|
||||||
|
orientation: props.orientation,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createViewWrapper = (
|
||||||
|
id: string,
|
||||||
|
component: React.FunctionComponent<{}>
|
||||||
|
) => {
|
||||||
|
return new ReactView(id, component, { addPortal });
|
||||||
|
};
|
||||||
|
|
||||||
|
const facade: SplitviewFacade = {
|
||||||
|
addFromComponent: (options) => {
|
||||||
|
const component = props.components[options.component];
|
||||||
|
const view = createViewWrapper(options.id, component);
|
||||||
|
|
||||||
|
splitview.current.addView(view, { type: "distribute" });
|
||||||
|
view.init({ params: {} });
|
||||||
|
return {
|
||||||
|
dispose: () => {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
layout: (width, height) => {
|
||||||
|
const [size, orthogonalSize] =
|
||||||
|
props.orientation === Orientation.HORIZONTAL
|
||||||
|
? [width, height]
|
||||||
|
: [height, width];
|
||||||
|
splitview.current.layout(size, orthogonalSize);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { width, height } = domReference.current.getBoundingClientRect();
|
||||||
|
const [size, orthogonalSize] =
|
||||||
|
props.orientation === Orientation.HORIZONTAL
|
||||||
|
? [width, height]
|
||||||
|
: [height, width];
|
||||||
|
splitview.current.layout(size, orthogonalSize);
|
||||||
|
|
||||||
|
if (props.onReady) {
|
||||||
|
props.onReady({ api: facade });
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
splitview.current.dispose();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: "100%",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
ref={domReference}
|
||||||
|
>
|
||||||
|
{portals}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -4,11 +4,13 @@
|
|||||||
--title-bar-background-color: #252526;
|
--title-bar-background-color: #252526;
|
||||||
--title-bar-scroll-bar-color: #888;
|
--title-bar-scroll-bar-color: #888;
|
||||||
//
|
//
|
||||||
--tab-background-visible: #1e1e1e;
|
--active-tab-background-visible: #1e1e1e;
|
||||||
--tab-background-hidden: #2d2d2d;
|
--active-tab-background-hidden: #2d2d2d;
|
||||||
|
--inactive-tab-background-visible: #1e1e1e;
|
||||||
|
--inactive-tab-background-hidden: #2d2d2d;
|
||||||
--tab-divider-color: #1e1e1e;
|
--tab-divider-color: #1e1e1e;
|
||||||
//
|
//
|
||||||
--drag-over-background-color: rgba(255, 0, 0, 0.5);
|
--drag-over-background-color: rgba(83, 89, 93, 0.5);
|
||||||
//
|
//
|
||||||
--active-group-visible-panel-color: white;
|
--active-group-visible-panel-color: white;
|
||||||
--active-group-hidden-panel-color: #969696;
|
--active-group-hidden-panel-color: #969696;
|
||||||
@ -20,3 +22,20 @@
|
|||||||
//
|
//
|
||||||
--splitview-divider-color: rgb(68, 68, 68);
|
--splitview-divider-color: rgb(68, 68, 68);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.visual-studio-theme {
|
||||||
|
--active-tab-background-visible: dodgerblue;
|
||||||
|
|
||||||
|
.groupview {
|
||||||
|
&.active-group {
|
||||||
|
> .title-container {
|
||||||
|
border-bottom: 2px solid var(--active-tab-background-visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.inactive-group {
|
||||||
|
> .title-container {
|
||||||
|
border-bottom: 2px solid var(--inactive-tab-background-visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
11
packages/splitview/typedoc.json
Normal file
11
packages/splitview/typedoc.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"out": "typedocs",
|
||||||
|
"mode": "file",
|
||||||
|
"inputFiles": ["./src"],
|
||||||
|
"exclude": ["**/_test/**/*.*", "**/index.ts"],
|
||||||
|
"ignoreCompilerErrors": true,
|
||||||
|
"disableOutputCheck": true,
|
||||||
|
"excludeExternals": true,
|
||||||
|
"excludePrivate": true,
|
||||||
|
"excludeNotExported": true
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user