flexlayout-react
Version:
A multi-tab docking layout manager
425 lines (361 loc) • 13.9 kB
text/typescript
import { Attribute } from "../Attribute";
import { AttributeDefinitions } from "../AttributeDefinitions";
import { Rect } from "../Rect";
import { BorderNode } from "./BorderNode";
import { IDraggable } from "./IDraggable";
import { IJsonTabNode } from "./IJsonModel";
import { Model } from "./Model";
import { Node } from "./Node";
import { TabSetNode } from "./TabSetNode";
export class TabNode extends Node implements IDraggable {
static readonly TYPE = "tab";
/** @internal */
static fromJson(json: any, model: Model, addToModel: boolean = true) {
const newLayoutNode = new TabNode(model, json, addToModel);
return newLayoutNode;
}
/** @internal */
private tabRect: Rect = Rect.empty();
/** @internal */
private moveableElement: HTMLElement | null;
/** @internal */
private tabStamp: HTMLElement | null;
/** @internal */
private renderedName?: string;
/** @internal */
private extra: Record<string, any>;
/** @internal */
private visible: boolean;
/** @internal */
private rendered: boolean;
/** @internal */
private scrollTop?: number;
/** @internal */
private scrollLeft?: number;
/** @internal */
constructor(model: Model, json: any, addToModel: boolean = true) {
super(model);
this.extra = {}; // extra data added to node not saved in json
this.moveableElement = null;
this.tabStamp = null;
this.rendered = false;
this.visible = false;
TabNode.attributeDefinitions.fromJson(json, this.attributes);
if (addToModel === true) {
model.addNode(this);
}
}
getName() {
return this.getAttr("name") as string;
}
getHelpText() {
return this.getAttr("helpText") as string | undefined;
}
getComponent() {
return this.getAttr("component") as string | undefined;
}
getWindowId() {
if (this.parent instanceof TabSetNode) {
return this.parent.getWindowId();
}
return Model.MAIN_WINDOW_ID;
}
getWindow() : Window | undefined {
const layoutWindow = this.model.getwindowsMap().get(this.getWindowId());
if (layoutWindow) {
return layoutWindow.window;
}
return undefined;
}
/**
* Returns the config attribute that can be used to store node specific data that
* WILL be saved to the json. The config attribute should be changed via the action Actions.updateNodeAttributes rather
* than directly, for example:
* this.state.model.doAction(
* FlexLayout.Actions.updateNodeAttributes(node.getId(), {config:myConfigObject}));
*/
getConfig() {
return this.attributes.config;
}
/**
* Returns an object that can be used to store transient node specific data that will
* NOT be saved in the json.
*/
getExtraData() {
return this.extra;
}
isPoppedOut() {
return this.getWindowId() !== Model.MAIN_WINDOW_ID;
}
isSelected() {
return (this.getParent() as TabSetNode | BorderNode).getSelectedNode() === this;
}
getIcon() {
return this.getAttr("icon") as string | undefined;
}
isEnableClose() {
return this.getAttr("enableClose") as boolean;
}
getCloseType() {
return this.getAttr("closeType") as number;
}
isEnablePopout() {
return this.getAttr("enablePopout") as boolean;
}
isEnablePopoutIcon() {
return this.getAttr("enablePopoutIcon") as boolean;
}
isEnablePopoutOverlay() {
return this.getAttr("enablePopoutOverlay") as boolean;
}
isEnableDrag() {
return this.getAttr("enableDrag") as boolean;
}
isEnableRename() {
return this.getAttr("enableRename") as boolean;
}
isEnableWindowReMount() {
return this.getAttr("enableWindowReMount") as boolean;
}
getClassName() {
return this.getAttr("className") as string | undefined;
}
getContentClassName() {
return this.getAttr("contentClassName") as string | undefined;
}
getTabSetClassName() {
return this.getAttr("tabsetClassName") as string | undefined;
}
isEnableRenderOnDemand() {
return this.getAttr("enableRenderOnDemand") as boolean;
}
getMinWidth() {
return this.getAttr("minWidth") as number;
}
getMinHeight() {
return this.getAttr("minHeight") as number;
}
getMaxWidth() {
return this.getAttr("maxWidth") as number;
}
getMaxHeight() {
return this.getAttr("maxHeight") as number;
}
isVisible() {
return this.visible;
}
toJson(): IJsonTabNode {
const json = {};
TabNode.attributeDefinitions.toJson(json, this.attributes);
return json;
}
/** @internal */
saveScrollPosition() {
if (this.moveableElement) {
this.scrollLeft = this.moveableElement.scrollLeft;
this.scrollTop = this.moveableElement.scrollTop;
// console.log("save", this.getName(), this.scrollTop);
}
}
/** @internal */
restoreScrollPosition() {
if (this.scrollTop) {
requestAnimationFrame(() => {
if (this.moveableElement) {
if (this.scrollTop) {
// console.log("restore", this.getName(), this.scrollTop);
this.moveableElement.scrollTop = this.scrollTop;
this.moveableElement.scrollLeft = this.scrollLeft!;
}
}
});
}
}
/** @internal */
setRect(rect: Rect) {
if (!rect.equals(this.rect)) {
this.fireEvent("resize", {rect});
this.rect = rect;
}
}
/** @internal */
setVisible(visible: boolean) {
if (visible !== this.visible) {
this.visible = visible;
this.fireEvent("visibility", { visible });
}
}
/** @internal */
getScrollTop() {
return this.scrollTop;
}
/** @internal */
setScrollTop(scrollTop: number | undefined) {
this.scrollTop = scrollTop;
}
/** @internal */
getScrollLeft() {
return this.scrollLeft;
}
/** @internal */
setScrollLeft(scrollLeft: number | undefined) {
this.scrollLeft = scrollLeft;
}
/** @internal */
isRendered() {
return this.rendered;
}
/** @internal */
setRendered(rendered: boolean) {
this.rendered = rendered;
}
/** @internal */
getTabRect() {
return this.tabRect;
}
/** @internal */
setTabRect(rect: Rect) {
this.tabRect = rect;
}
/** @internal */
getTabStamp() {
return this.tabStamp;
}
/** @internal */
setTabStamp(stamp: HTMLElement | null) {
this.tabStamp = stamp;
}
/** @internal */
getMoveableElement() {
return this.moveableElement;
}
/** @internal */
setMoveableElement(element: HTMLElement | null) {
this.moveableElement = element;
}
/** @internal */
setRenderedName(name: string) {
this.renderedName = name;
}
/** @internal */
getNameForOverflowMenu() {
const altName = this.getAttr("altName") as string;
if (altName !== undefined) {
return altName;
}
return this.renderedName;
}
/** @internal */
setName(name: string) {
this.attributes.name = name;
}
/** @internal */
delete() {
(this.parent as TabSetNode | BorderNode).remove(this);
this.fireEvent("close", {});
}
/** @internal */
updateAttrs(json: any) {
TabNode.attributeDefinitions.update(json, this.attributes);
}
/** @internal */
getAttributeDefinitions() {
return TabNode.attributeDefinitions;
}
/** @internal */
setBorderWidth(width: number) {
this.attributes.borderWidth = width;
}
/** @internal */
setBorderHeight(height: number) {
this.attributes.borderHeight = height;
}
/** @internal */
static getAttributeDefinitions() {
return TabNode.attributeDefinitions;
}
/** @internal */
private static attributeDefinitions: AttributeDefinitions = TabNode.createAttributeDefinitions();
/** @internal */
private static createAttributeDefinitions(): AttributeDefinitions {
const attributeDefinitions = new AttributeDefinitions();
attributeDefinitions.add("type", TabNode.TYPE, true).setType(Attribute.STRING).setFixed();
attributeDefinitions.add("id", undefined).setType(Attribute.STRING).setDescription(
`the unique id of the tab, if left undefined a uuid will be assigned`
);
attributeDefinitions.add("name", "[Unnamed Tab]").setType(Attribute.STRING).setDescription(
`name of tab to be displayed in the tab button`
);
attributeDefinitions.add("altName", undefined).setType(Attribute.STRING).setDescription(
`if there is no name specifed then this value will be used in the overflow menu`
);
attributeDefinitions.add("helpText", undefined).setType(Attribute.STRING).setDescription(
`An optional help text for the tab to be displayed upon tab hover.`
);
attributeDefinitions.add("component", undefined).setType(Attribute.STRING).setDescription(
`string identifying which component to run (for factory)`
);
attributeDefinitions.add("config", undefined).setType("any").setDescription(
`a place to hold json config for the hosted component`
);
attributeDefinitions.add("tabsetClassName", undefined).setType(Attribute.STRING).setDescription(
`class applied to parent tabset when this is the only tab and it is stretched to fill the tabset`
);
attributeDefinitions.add("enableWindowReMount", false).setType(Attribute.BOOLEAN).setDescription(
`if enabled the tab will re-mount when popped out/in`
);
attributeDefinitions.addInherited("enableClose", "tabEnableClose").setType(Attribute.BOOLEAN).setDescription(
`allow user to close tab via close button`
);
attributeDefinitions.addInherited("closeType", "tabCloseType").setType("ICloseType").setDescription(
`see values in ICloseType`
);
attributeDefinitions.addInherited("enableDrag", "tabEnableDrag").setType(Attribute.BOOLEAN).setDescription(
`allow user to drag tab to new location`
);
attributeDefinitions.addInherited("enableRename", "tabEnableRename").setType(Attribute.BOOLEAN).setDescription(
`allow user to rename tabs by double clicking`
);
attributeDefinitions.addInherited("className", "tabClassName").setType(Attribute.STRING).setDescription(
`class applied to tab button`
);
attributeDefinitions.addInherited("contentClassName", "tabContentClassName").setType(Attribute.STRING).setDescription(
`class applied to tab content`
);
attributeDefinitions.addInherited("icon", "tabIcon").setType(Attribute.STRING).setDescription(
`the tab icon`
);
attributeDefinitions.addInherited("enableRenderOnDemand", "tabEnableRenderOnDemand").setType(Attribute.BOOLEAN).setDescription(
`whether to avoid rendering component until tab is visible`
);
attributeDefinitions.addInherited("enablePopout", "tabEnablePopout").setType(Attribute.BOOLEAN).setAlias("enableFloat").setDescription(
`enable popout (in popout capable browser)`
);
attributeDefinitions.addInherited("enablePopoutIcon", "tabEnablePopoutIcon").setType(Attribute.BOOLEAN).setDescription(
`whether to show the popout icon in the tabset header if this tab enables popouts`
);
attributeDefinitions.addInherited("enablePopoutOverlay", "tabEnablePopoutOverlay").setType(Attribute.BOOLEAN).setDescription(
`if this tab will not work correctly in a popout window when the main window is backgrounded (inactive)
then enabling this option will gray out this tab`
);
attributeDefinitions.addInherited("borderWidth", "tabBorderWidth").setType(Attribute.NUMBER).setDescription(
`width when added to border, -1 will use border size`
);
attributeDefinitions.addInherited("borderHeight", "tabBorderHeight").setType(Attribute.NUMBER).setDescription(
`height when added to border, -1 will use border size`
);
attributeDefinitions.addInherited("minWidth", "tabMinWidth").setType(Attribute.NUMBER).setDescription(
`the min width of this tab`
);
attributeDefinitions.addInherited("minHeight", "tabMinHeight").setType(Attribute.NUMBER).setDescription(
`the min height of this tab`
);
attributeDefinitions.addInherited("maxWidth", "tabMaxWidth").setType(Attribute.NUMBER).setDescription(
`the max width of this tab`
);
attributeDefinitions.addInherited("maxHeight", "tabMaxHeight").setType(Attribute.NUMBER).setDescription(
`the max height of this tab`
);
return attributeDefinitions;
}
}