+
@@ -335,9 +402,12 @@ export const TestGrid = () => {
// autoSizeToFitContainer={true}
onReady={onReady}
components={components}
+ tabComponents={tabComponents}
debug={true}
+ // tabHeight={30}
enableExternalDragEvents={true}
// serializedLayout={data}
+ // onTabContextMenu={onTabContextMenu}
/>
);
diff --git a/packages/splitview-demo/src/layout-grid/splitPanel.tsx b/packages/splitview-demo/src/layout-grid/splitPanel.tsx
new file mode 100644
index 000000000..55948381c
--- /dev/null
+++ b/packages/splitview-demo/src/layout-grid/splitPanel.tsx
@@ -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
hiya
;
+ },
+};
+
+export const SplitPanel = (props: IPanelProps) => {
+ const api = React.useRef
();
+
+ 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 (
+
+ );
+};
diff --git a/packages/splitview-react/src/bridge/pane.tsx b/packages/splitview-react/src/bridge/pane.tsx
index 427509f03..e045fbd46 100644
--- a/packages/splitview-react/src/bridge/pane.tsx
+++ b/packages/splitview-react/src/bridge/pane.tsx
@@ -16,7 +16,7 @@ export interface IPaneComponentRef {
layout: (size: number, orthogonalSize: number) => void;
}
-export type PaneComponent = React.RefForwardingComponent<
+export type PaneComponent = React.ForwardRefRenderFunction<
IPaneComponentRef,
IPaneComponentProps
>;
@@ -27,7 +27,7 @@ export interface IPaneHeaderComponentProps extends IViewWithReactComponent {
userprops?: { [index: string]: any };
}
-export type PaneHeaderComponent = React.RefForwardingComponent<
+export type PaneHeaderComponent = React.ForwardRefRenderFunction<
{},
IPaneHeaderComponentProps
>;
diff --git a/packages/splitview-react/src/bridge/view.tsx b/packages/splitview-react/src/bridge/view.tsx
index c6fbd242c..62bc25e5c 100644
--- a/packages/splitview-react/src/bridge/view.tsx
+++ b/packages/splitview-react/src/bridge/view.tsx
@@ -12,7 +12,7 @@ export interface IViewComponentRef {
layout: (size: number, orthogonalSize: number) => void;
}
-export type ViewComponent = React.RefForwardingComponent<
+export type ViewComponent = React.ForwardRefRenderFunction<
IViewComponentRef,
IViewComponentProps
>;
diff --git a/packages/splitview-react/src/paneview.tsx b/packages/splitview-react/src/paneview.tsx
index a43730427..f260e0662 100644
--- a/packages/splitview-react/src/paneview.tsx
+++ b/packages/splitview-react/src/paneview.tsx
@@ -24,19 +24,19 @@ export interface IPaneViewReactProps {
initialLayout?: PaneViewSerializedConfig;
}
-export type PaneViewReadyEvent = {
+export interface PaneViewReadyEvent {
api: PaneviewApi;
-};
+}
-export type PaneViewSerializedConfig = {
+export interface PaneViewSerializedConfig {
views: Array<
Omit & {
size?: number;
}
>;
-};
+}
-export type PaneviewApi = {
+export interface PaneviewApi {
add: (
options: Omit & {
size?: number;
@@ -45,7 +45,7 @@ export type PaneviewApi = {
) => void;
moveView: (from: number, to: number) => void;
toJSON: () => {};
-};
+}
export interface IPaneViewComponentRef {
layout: (size: number, orthogonalSize: number) => void;
diff --git a/packages/splitview-react/src/splitview.tsx b/packages/splitview-react/src/splitview.tsx
index 404235ba5..935007dd9 100644
--- a/packages/splitview-react/src/splitview.tsx
+++ b/packages/splitview-react/src/splitview.tsx
@@ -9,15 +9,15 @@ export interface IViewWithReactComponent extends IBaseView {
component: ViewComponent;
}
-export type OnReadyEvent = {
+export interface OnReadyEvent {
api: SplitviewApi;
-};
+}
-export type SerializedConfig = {
+export interface SerializedConfig {
views: Array & { size?: number }>;
-};
+}
-export type SplitviewApi = {
+export interface SplitviewApi {
add: (
options: Omit & {
size?: number;
@@ -26,7 +26,7 @@ export type SplitviewApi = {
) => void;
moveView: (from: number, to: number) => void;
toJSON: () => {};
-};
+}
export interface ISplitViewReactProps {
orientation: Orientation;
diff --git a/packages/splitview/package.json b/packages/splitview/package.json
index a40b3b281..f58f468c5 100644
--- a/packages/splitview/package.json
+++ b/packages/splitview/package.json
@@ -6,7 +6,7 @@
"types": "dist/esm/index.d.ts",
"scripts": {
"build": "gulp run",
- "docs": "typedoc --excludeNotExported --excludePrivate true --mode file --out typedocs/ src/"
+ "docs": "typedoc"
},
"author": "",
"license": "ISC",
diff --git a/packages/splitview/src/gridview/gridview.ts b/packages/splitview/src/gridview/gridview.ts
index ec423c0cd..7e8fc1921 100644
--- a/packages/splitview/src/gridview/gridview.ts
+++ b/packages/splitview/src/gridview/gridview.ts
@@ -1,5 +1,5 @@
import { Orientation, Sizing } from "../splitview/splitview";
-import { Target } from "../groupview/droptarget/droptarget";
+import { Position } from "../groupview/droptarget/droptarget";
import { tail } from "../array";
import { LeafNode } from "./leafNode";
import { BranchNode } from "./branchNode";
@@ -91,7 +91,7 @@ export function getGridLocation(element: HTMLElement): number[] {
export function getRelativeLocation(
rootOrientation: Orientation,
location: number[],
- direction: Target
+ direction: Position
): number[] {
const orientation = getLocationOrientation(rootOrientation, location);
const directionOrientation = getDirectionOrientation(direction);
@@ -99,20 +99,20 @@ export function getRelativeLocation(
if (orientation === directionOrientation) {
let [rest, index] = tail(location);
- if (direction === Target.Right || direction === Target.Bottom) {
+ if (direction === Position.Right || direction === Position.Bottom) {
index += 1;
}
return [...rest, index];
} else {
const index =
- direction === Target.Right || direction === Target.Bottom ? 1 : 0;
+ direction === Position.Right || direction === Position.Bottom ? 1 : 0;
return [...location, index];
}
}
-export function getDirectionOrientation(direction: Target): Orientation {
- return direction === Target.Top || direction === Target.Bottom
+export function getDirectionOrientation(direction: Position): Orientation {
+ return direction === Position.Top || direction === Position.Bottom
? Orientation.VERTICAL
: Orientation.HORIZONTAL;
}
@@ -193,9 +193,9 @@ export interface INodeDescriptor {
visible?: boolean;
}
-export type IViewDeserializer = {
+export interface IViewDeserializer {
fromJSON: (data: {}) => IGridView;
-};
+}
export class Gridview {
private _root: BranchNode;
diff --git a/packages/splitview/src/groupview/droptarget/dataTransfer.ts b/packages/splitview/src/groupview/droptarget/dataTransfer.ts
index bd44330fb..50360e46f 100644
--- a/packages/splitview/src/groupview/droptarget/dataTransfer.ts
+++ b/packages/splitview/src/groupview/droptarget/dataTransfer.ts
@@ -11,12 +11,12 @@ export enum DragType {
EXTERNAL = "external_group_drag",
}
-export type DragItem = {
+export interface DragItem {
itemId: string;
groupId: string;
-};
+}
-export type ExternalDragItem = PanelOptions;
+export interface ExternalDragItem extends PanelOptions {}
export type DataObject = DragItem | ExternalDragItem;
diff --git a/packages/splitview/src/groupview/droptarget/droptarget.scss b/packages/splitview/src/groupview/droptarget/droptarget.scss
index ef35e94a3..45028835a 100644
--- a/packages/splitview/src/groupview/droptarget/droptarget.scss
+++ b/packages/splitview/src/groupview/droptarget/droptarget.scss
@@ -20,18 +20,18 @@
transition-duration: 0.15s;
transition-timing-function: ease-out;
- &.left {
+ &.left,
+ &.right {
width: 50%;
}
&.right {
- left: 50%;
- width: 50%;
+ transform: translate(100%, 0%);
}
&.bottom {
- top: 50%;
- height: 50%;
+ transform: translate(0%, 100%);
}
- &.top {
+ &.top,
+ &.bottom {
height: 50%;
}
}
diff --git a/packages/splitview/src/groupview/droptarget/droptarget.ts b/packages/splitview/src/groupview/droptarget/droptarget.ts
index 8f279e666..d100a546e 100644
--- a/packages/splitview/src/groupview/droptarget/droptarget.ts
+++ b/packages/splitview/src/groupview/droptarget/droptarget.ts
@@ -1,7 +1,7 @@
import { Emitter, Event } from "../../events";
import { DataTransferSingleton } from "./dataTransfer";
-export enum Target {
+export enum Position {
Top = "Top",
Left = "Left",
Bottom = "Bottom",
@@ -9,10 +9,10 @@ export enum Target {
Center = "Center",
}
-export type DroptargetEvent = {
- target: Target;
+export interface DroptargetEvent {
+ position: Position;
event: DragEvent;
-};
+}
const HAS_PROCESSED_KEY = "__drop_target_processed__";
@@ -39,7 +39,7 @@ const toggleClassName = (
export class Droptarget {
private target: HTMLElement;
private overlay: HTMLElement;
- private state: Target;
+ private state: Position | undefined;
private readonly _onDidChange = new Emitter();
readonly onDidChange: Event = this._onDidChange.event;
@@ -106,7 +106,7 @@ export class Droptarget {
this.removeDropTarget();
if (!hasProcessed(event)) {
- this._onDidChange.fire({ target: this.state, event });
+ this._onDidChange.fire({ position: this.state, event });
} else {
console.debug("[dragtarget] already processed");
}
@@ -140,15 +140,15 @@ export class Droptarget {
toggleClassName(this.overlay, "bottom", isBottom);
if (isRight) {
- this.state = Target.Right;
+ this.state = Position.Right;
} else if (isLeft) {
- this.state = Target.Left;
+ this.state = Position.Left;
} else if (isTop) {
- this.state = Target.Top;
+ this.state = Position.Top;
} else if (isBottom) {
- this.state = Target.Bottom;
+ this.state = Position.Bottom;
} else {
- this.state = Target.Center;
+ this.state = Position.Center;
}
};
diff --git a/packages/splitview/src/groupview/events.ts b/packages/splitview/src/groupview/events.ts
index eb5931222..0c0e0522c 100644
--- a/packages/splitview/src/groupview/events.ts
+++ b/packages/splitview/src/groupview/events.ts
@@ -1,11 +1,6 @@
import { DroptargetEvent } from "./droptarget/droptarget";
-export enum TabChangedEventType {
- CLICK,
-}
-
-export type TabChangedEvent = { type: TabChangedEventType };
-export type TabDropEvent = {
+export interface TabDropEvent {
event: DroptargetEvent;
index?: number;
-};
+}
diff --git a/packages/splitview/src/groupview/groupview.ts b/packages/splitview/src/groupview/groupview.ts
index b40a937e4..dae75bd01 100644
--- a/packages/splitview/src/groupview/groupview.ts
+++ b/packages/splitview/src/groupview/groupview.ts
@@ -1,8 +1,8 @@
import { IDisposable, CompositeDisposable, Disposable } from "../lifecycle";
-import { ITabContainer, TabContainer } from "./tabs/tabContainer";
-import { IContentContainer, ContentContainer } from "./content";
+import { ITabContainer, TabContainer } from "./titlebar/tabContainer";
+import { IContentContainer, ContentContainer } from "./panel/content/content";
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 { IGroupAccessor, Layout } from "../layout";
import { toggleClass } from "../dom";
@@ -44,12 +44,12 @@ export interface IGroupItem {
body: { element: HTMLElement };
}
-type GroupMoveEvent = {
+interface GroupMoveEvent {
groupId: string;
itemId: string;
- target: Target;
+ target: Position;
index?: number;
-};
+}
export interface GroupOptions {
panels: IPanel[];
@@ -88,11 +88,11 @@ export interface IGroupview extends IDisposable, IGridView {
moveToPrevious(options?: { panel?: IPanel; suppressRoll?: boolean }): void;
}
-export type GroupDropEvent = {
+export interface GroupDropEvent {
event: DragEvent;
- target: Target;
+ target: Position;
index?: number;
-};
+}
export class Groupview extends CompositeDisposable implements IGroupview {
private _element: HTMLElement;
@@ -129,6 +129,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
set tabHeight(height: number) {
this.tabContainer.height = height;
+ this.layout(this._width, this._height);
}
get isActive() {
@@ -290,7 +291,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
this.dropTarget.onDidChange((event) => {
// if we've center dropped on ourself then ignore
if (
- event.target === Target.Center &&
+ event.position === Position.Center &&
this.tabContainer.hasActiveDragEvent
) {
return;
@@ -531,18 +532,18 @@ export class Groupview extends CompositeDisposable implements IGroupview {
private handleDropEvent(event: DroptargetEvent, index?: number) {
if (isPanelTransferEvent(event.event)) {
- this.handlePanelDropEvent(event.event, event.target, index);
+ this.handlePanelDropEvent(event.event, event.position, index);
return;
}
- this._onDrop.fire({ event: event.event, target: event.target, index });
+ this._onDrop.fire({ event: event.event, target: event.position, index });
console.debug("[customDropEvent]");
}
private handlePanelDropEvent(
event: DragEvent,
- target: Target,
+ target: Position,
index?: number
) {
const dataObject = extractData(event);
@@ -550,10 +551,10 @@ export class Groupview extends CompositeDisposable implements IGroupview {
if (isTabDragEvent(dataObject)) {
const { groupId, itemId } = dataObject;
const isSameGroup = this.id === groupId;
- if (isSameGroup) {
- const index = this.tabContainer.indexOf(itemId);
- if (index > -1 && index === this.panels.length - 1) {
- console.debug("[tabs] dropped in empty space");
+ if (isSameGroup && !target) {
+ const oldIndex = this.tabContainer.indexOf(itemId);
+ if (oldIndex === index) {
+ console.debug("[tabs] drop indicates no change in position");
return;
}
}
diff --git a/packages/splitview/src/groupview/panel/api.ts b/packages/splitview/src/groupview/panel/api.ts
index 62b261f32..65563508e 100644
--- a/packages/splitview/src/groupview/panel/api.ts
+++ b/packages/splitview/src/groupview/panel/api.ts
@@ -4,15 +4,15 @@ import { ClosePanelResult } from "./parts";
import { IPanel } from "./types";
import { CompositeDisposable, IDisposable } from "../../lifecycle";
-export type PanelStateChangeEvent = {
+export interface PanelStateChangeEvent {
isPanelVisible: boolean;
isGroupActive: boolean;
-};
+}
-export type PanelDimensionChangeEvent = {
+export interface PanelDimensionChangeEvent {
width: number;
height: number;
-};
+}
export interface PanelApi extends IDisposable {
onDidPanelStateChange: Event;
diff --git a/packages/splitview/src/groupview/content.ts b/packages/splitview/src/groupview/panel/content/content.ts
similarity index 88%
rename from packages/splitview/src/groupview/content.ts
rename to packages/splitview/src/groupview/panel/content/content.ts
index 88f0637d4..3405585f4 100644
--- a/packages/splitview/src/groupview/content.ts
+++ b/packages/splitview/src/groupview/panel/content/content.ts
@@ -1,6 +1,6 @@
-import { CompositeDisposable, IDisposable } from "../lifecycle";
-import { Emitter, Event } from "../events";
-import { trackFocus } from "../dom";
+import { CompositeDisposable, IDisposable } from "../../../lifecycle";
+import { Emitter, Event } from "../../../events";
+import { trackFocus } from "../../../dom";
export interface IContentContainer extends IDisposable {
onDidFocus: Event;
diff --git a/packages/splitview/src/groupview/panel/panel.ts b/packages/splitview/src/groupview/panel/panel.ts
index 2dd7cdaf2..a0214376b 100644
--- a/packages/splitview/src/groupview/panel/panel.ts
+++ b/packages/splitview/src/groupview/panel/panel.ts
@@ -149,7 +149,11 @@ export class DefaultPanel extends CompositeDisposable implements IPanel {
}
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() {
diff --git a/packages/splitview/src/groupview/panel/parts.ts b/packages/splitview/src/groupview/panel/parts.ts
index b68d271cc..f52bcf778 100644
--- a/packages/splitview/src/groupview/panel/parts.ts
+++ b/packages/splitview/src/groupview/panel/parts.ts
@@ -14,13 +14,13 @@ interface Methods extends IDisposable {
setVisible(isPanelVisible: boolean, isGroupVisible: boolean): void;
}
-export type WatermarkPartInitParameters = {
+export interface WatermarkPartInitParameters {
accessor: IGroupAccessor;
-};
+}
-export type PartInitParameters = {
+export interface PartInitParameters extends PanelInitParameters {
api: PanelApi;
-} & PanelInitParameters;
+}
export interface PanelHeaderPart extends Methods {
id: string;
diff --git a/packages/splitview/src/groupview/tabs/tab.ts b/packages/splitview/src/groupview/panel/tab/tab.ts
similarity index 80%
rename from packages/splitview/src/groupview/tabs/tab.ts
rename to packages/splitview/src/groupview/panel/tab/tab.ts
index 2971e1d19..cbbf51535 100644
--- a/packages/splitview/src/groupview/tabs/tab.ts
+++ b/packages/splitview/src/groupview/panel/tab/tab.ts
@@ -1,23 +1,31 @@
-import { addDisposableListener, Emitter, Event } from "../../events";
-import { Droptarget, DroptargetEvent } from "../droptarget/droptarget";
-import { CompositeDisposable } from "../../lifecycle";
-import { TabChangedEvent, TabDropEvent, TabChangedEventType } from "../events";
-import { IGroupview } from "../groupview";
+import { addDisposableListener, Emitter, Event } from "../../../events";
+import { Droptarget, DroptargetEvent } from "../../droptarget/droptarget";
+import { CompositeDisposable } from "../../../lifecycle";
+import { IGroupview } from "../../groupview";
import {
DataTransferSingleton,
DATA_KEY,
DragType,
- extractData,
-} from "../droptarget/dataTransfer";
-import { IGroupAccessor } from "../../layout";
-import { toggleClass } from "../../dom";
+} from "../../droptarget/dataTransfer";
+// import { IGroupAccessor } from "../../layout";
+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 {
id: string;
element: HTMLElement;
hasActiveDragEvent: boolean;
setContent: (element: HTMLElement) => void;
- onChanged: Event;
+ onChanged: Event;
onDropped: Event;
setActive(isActive: boolean): void;
startDragEvent(): void;
@@ -32,8 +40,8 @@ export class Tab extends CompositeDisposable implements ITab {
private droptarget: Droptarget;
private content: HTMLElement;
- private readonly _onChanged = new Emitter();
- readonly onChanged: Event = this._onChanged.event;
+ private readonly _onChanged = new Emitter();
+ readonly onChanged: Event = this._onChanged.event;
private readonly _onDropped = new Emitter();
readonly onDropped: Event = this._onDropped.event;
@@ -72,7 +80,10 @@ export class Tab extends CompositeDisposable implements ITab {
if (ev.defaultPrevented) {
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) => {
this.dragInPlayDetails = { isDragging: true, id: this.accessor.id };
diff --git a/packages/splitview/src/groupview/panel/types.ts b/packages/splitview/src/groupview/panel/types.ts
index 3b1100199..00574d5bd 100644
--- a/packages/splitview/src/groupview/panel/types.ts
+++ b/packages/splitview/src/groupview/panel/types.ts
@@ -5,18 +5,18 @@ import { PanelHeaderPart, PanelContentPart, ClosePanelResult } from "./parts";
// objects
-export type PanelUpdateEvent = {
+export interface PanelUpdateEvent {
params: { [key: string]: any };
-};
+}
// init parameters
-export type PanelInitParameters = {
+export interface PanelInitParameters {
title: string;
suppressClosable?: boolean;
params: { [index: string]: any };
state?: { [index: string]: any };
-};
+}
// constructors
diff --git a/packages/splitview/src/groupview/tabs/tabContainer.scss b/packages/splitview/src/groupview/titlebar/tabContainer.scss
similarity index 100%
rename from packages/splitview/src/groupview/tabs/tabContainer.scss
rename to packages/splitview/src/groupview/titlebar/tabContainer.scss
diff --git a/packages/splitview/src/groupview/tabs/tabContainer.ts b/packages/splitview/src/groupview/titlebar/tabContainer.ts
similarity index 93%
rename from packages/splitview/src/groupview/tabs/tabContainer.ts
rename to packages/splitview/src/groupview/titlebar/tabContainer.ts
index e9b638465..380ebf5b3 100644
--- a/packages/splitview/src/groupview/tabs/tabContainer.ts
+++ b/packages/splitview/src/groupview/titlebar/tabContainer.ts
@@ -1,8 +1,8 @@
import { IDisposable, CompositeDisposable } from "../../lifecycle";
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 { hasProcessed } from "../droptarget/droptarget";
+import { hasProcessed, Position } from "../droptarget/droptarget";
import { TabDropEvent } from "../events";
import { IGroupview } from "../groupview";
@@ -144,7 +144,8 @@ export class TabContainer extends CompositeDisposable implements ITabContainer {
}
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
const disposables = CompositeDisposable.from(
tab.onChanged((event) => {
- this.group.openPanel(panel);
+ switch (event.kind) {
+ case TabInteractionKind.CLICK:
+ this.group.openPanel(panel);
+ break;
+ case TabInteractionKind.CONTEXT_MENU:
+ // TODO finish
+ }
}),
tab.onDropped((event) => {
this._onDropped.fire({ event, index: this.indexOf(tab) });
diff --git a/packages/splitview/src/index.ts b/packages/splitview/src/index.ts
index c042303c3..929e0ecde 100644
--- a/packages/splitview/src/index.ts
+++ b/packages/splitview/src/index.ts
@@ -2,8 +2,8 @@ export * from "./splitview/splitview";
export * from "./splitview/paneview";
export * from "./gridview/gridview";
export * from "./groupview/groupview";
-export * from "./groupview/content";
-export * from "./groupview/tabs/tab";
+export * from "./groupview/panel/content/content";
+export * from "./groupview/panel/tab/tab";
export * from "./events";
export * from "./lifecycle";
export * from "./groupview/panel/panel";
@@ -12,6 +12,7 @@ export * from "./react/react";
export * from "./groupview/panel/types";
export * from "./groupview/panel/parts";
export * from "./react/layout";
+export * from "./react/splitview";
export * from "./react/reactContentPart";
export * from "./react/reactHeaderPart";
diff --git a/packages/splitview/src/layout/components/tab/defaultTab.scss b/packages/splitview/src/layout/components/tab/defaultTab.scss
index 86e11f341..b21b8831d 100644
--- a/packages/splitview/src/layout/components/tab/defaultTab.scss
+++ b/packages/splitview/src/layout/components/tab/defaultTab.scss
@@ -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 {
&.dragging {
- color: var(--active-group-visible-panel-color);
.tab-action {
background-color: var(--active-group-visible-panel-color);
}
}
&.active-tab > .default-tab {
- background-color: var(--tab-background-visible);
.tab-action {
visibility: visible;
}
}
&.inactive-tab > .default-tab {
- background-color: var(--tab-background-hidden);
.tab-action:not(.dirty) {
visibility: hidden;
}
diff --git a/packages/splitview/src/layout/layout.scss b/packages/splitview/src/layout/layout.scss
index 2406c0279..29f09a4f5 100644
--- a/packages/splitview/src/layout/layout.scss
+++ b/packages/splitview/src/layout/layout.scss
@@ -9,3 +9,55 @@
position: absolute;
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);
+ }
+}
diff --git a/packages/splitview/src/layout/layout.ts b/packages/splitview/src/layout/layout.ts
index 0510aab0c..50bfdf5cc 100644
--- a/packages/splitview/src/layout/layout.ts
+++ b/packages/splitview/src/layout/layout.ts
@@ -1,5 +1,5 @@
import { Gridview, getRelativeLocation } from "../gridview/gridview";
-import { Target } from "../groupview/droptarget/droptarget";
+import { Position } from "../groupview/droptarget/droptarget";
import { getGridLocation } from "../gridview/gridview";
import { tail, sequenceEquals } from "../array";
import {
@@ -45,10 +45,10 @@ import {
const nextGroupId = sequentialNumberGenerator();
const nextLayoutId = sequentialNumberGenerator();
-export type PanelReference = {
+export interface PanelReference {
update: (event: { params: { [key: string]: any } }) => void;
remove: () => void;
-};
+}
export interface Api {
layout(width: number, height: number): void;
@@ -56,6 +56,7 @@ export interface Api {
setAutoResizeToFit(enabled: boolean): void;
resizeToFit(): void;
setTabHeight(height: number): void;
+ getTabHeight(): number;
size: number;
totalPanels: number;
// lifecycle
@@ -90,7 +91,7 @@ export interface IGroupAccessor {
referenceGroup: IGroupview,
groupId: string,
itemId: string,
- target: Target,
+ target: Position,
index?: number
): void;
doSetGroupActive: (group: IGroupview) => void;
@@ -399,11 +400,16 @@ export class Layout extends CompositeDisposable implements ILayout {
}
public setTabHeight(height: number) {
+ this.options.tabHeight = height;
this.groups.forEach((value) => {
value.value.tabHeight = height;
});
}
+ public getTabHeight() {
+ return this.options.tabHeight;
+ }
+
public setAutoResizeToFit(enabled: boolean) {
if (this.resizeTimer) {
clearInterval(this.resizeTimer);
@@ -435,7 +441,7 @@ export class Layout extends CompositeDisposable implements ILayout {
const referenceGroup = this.findGroup(referencePanel);
const target = this.toTarget(options.position.direction);
- if (target === Target.Center) {
+ if (target === Position.Center) {
referenceGroup.openPanel(panel);
} else {
const location = getGridLocation(referenceGroup.element);
@@ -597,13 +603,13 @@ export class Layout extends CompositeDisposable implements ILayout {
referenceGroup: IGroupview,
groupId: string,
itemId: string,
- target: Target,
+ target: Position,
index?: number
) {
const sourceGroup = groupId ? this.groups.get(groupId).value : undefined;
switch (target) {
- case Target.Center:
+ case Position.Center:
case undefined:
const groupItem =
sourceGroup?.removePanel(itemId) || this.panels.get(itemId).value;
@@ -699,7 +705,7 @@ export class Layout extends CompositeDisposable implements ILayout {
}
this.moveGroup(
group,
- panel?.group.id,
+ panel?.group?.id,
panel.id,
event.target,
event.index
@@ -774,16 +780,16 @@ export class Layout extends CompositeDisposable implements ILayout {
private toTarget(direction: "left" | "right" | "above" | "below" | "within") {
switch (direction) {
case "left":
- return Target.Left;
+ return Position.Left;
case "right":
- return Target.Right;
+ return Position.Right;
case "above":
- return Target.Top;
+ return Position.Top;
case "below":
- return Target.Bottom;
+ return Position.Bottom;
case "within":
default:
- return Target.Center;
+ return Position.Center;
}
}
diff --git a/packages/splitview/src/layout/options.ts b/packages/splitview/src/layout/options.ts
index 93abab529..60d19b7f3 100644
--- a/packages/splitview/src/layout/options.ts
+++ b/packages/splitview/src/layout/options.ts
@@ -1,4 +1,5 @@
import { IGroupview } from "../groupview/groupview";
+import { PanelApi } from "../groupview/panel/api";
import {
PanelContentPart,
PanelContentPartConstructor,
@@ -6,11 +7,20 @@ import {
PanelHeaderPartConstructor,
WatermarkConstructor,
} 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;
createTabWrapper: (id: string, component: any) => PanelHeaderPart;
-};
+}
+
+export interface TabContextMenuEvent {
+ event: MouseEvent;
+ api: Api;
+ panelApi: PanelApi;
+ panel: IPanel;
+}
export interface LayoutOptions {
tabComponents?: {
@@ -31,6 +41,7 @@ export interface LayoutOptions {
tabHeight?: number;
debug?: boolean;
enableExternalDragEvents?: boolean;
+ onTabContextMenu?: (event: TabContextMenuEvent) => void;
}
export interface PanelOptions {
diff --git a/packages/splitview/src/react/layout.tsx b/packages/splitview/src/react/layout.tsx
index 54f176812..eb83b572d 100644
--- a/packages/splitview/src/react/layout.tsx
+++ b/packages/splitview/src/react/layout.tsx
@@ -6,9 +6,9 @@ import { ReactPanelHeaderPart } from "./reactHeaderPart";
import { IPanelProps } from "./react";
import { ReactPanelDeserialzier } from "./deserializer";
-export type OnReadyEvent = {
+export interface OnReadyEvent {
api: Api;
-};
+}
export interface ReactLayout {
addPortal: (portal: React.ReactPortal) => IDisposable;
diff --git a/packages/splitview/src/react/reactView.ts b/packages/splitview/src/react/reactView.ts
new file mode 100644
index 000000000..db91aa68d
--- /dev/null
+++ b/packages/splitview/src/react/reactView.ts
@@ -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 = 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);
+ }
+}
diff --git a/packages/splitview/src/react/splitview.tsx b/packages/splitview/src/react/splitview.tsx
new file mode 100644
index 000000000..54e2ff18c
--- /dev/null
+++ b/packages/splitview/src/react/splitview.tsx
@@ -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();
+ const splitview = React.useRef();
+ const [portals, setPortals] = React.useState([]);
+
+ 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 (
+
+ {portals}
+
+ );
+};
diff --git a/packages/splitview/src/theme.scss b/packages/splitview/src/theme.scss
index e30a6b092..7646fef0f 100644
--- a/packages/splitview/src/theme.scss
+++ b/packages/splitview/src/theme.scss
@@ -4,11 +4,13 @@
--title-bar-background-color: #252526;
--title-bar-scroll-bar-color: #888;
//
- --tab-background-visible: #1e1e1e;
- --tab-background-hidden: #2d2d2d;
+ --active-tab-background-visible: #1e1e1e;
+ --active-tab-background-hidden: #2d2d2d;
+ --inactive-tab-background-visible: #1e1e1e;
+ --inactive-tab-background-hidden: #2d2d2d;
--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-hidden-panel-color: #969696;
@@ -20,3 +22,20 @@
//
--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);
+ }
+ }
+ }
+}
diff --git a/packages/splitview/typedoc.json b/packages/splitview/typedoc.json
new file mode 100644
index 000000000..a8ab0cde3
--- /dev/null
+++ b/packages/splitview/typedoc.json
@@ -0,0 +1,11 @@
+{
+ "out": "typedocs",
+ "mode": "file",
+ "inputFiles": ["./src"],
+ "exclude": ["**/_test/**/*.*", "**/index.ts"],
+ "ignoreCompilerErrors": true,
+ "disableOutputCheck": true,
+ "excludeExternals": true,
+ "excludePrivate": true,
+ "excludeNotExported": true
+}