flexlayout-react
Version:
A multi-tab docking layout manager
469 lines • 19.9 kB
JavaScript
import { Attribute } from "../Attribute";
import { AttributeDefinitions } from "../AttributeDefinitions";
import { DockLocation } from "../DockLocation";
import { DropInfo } from "../DropInfo";
import { Orientation } from "../Orientation";
import { Rect } from "../Rect";
import { CLASSES } from "../Types";
import { canDockToWindow } from "../view/Utils";
import { BorderNode } from "./BorderNode";
import { Model } from "./Model";
import { Node } from "./Node";
import { RowNode } from "./RowNode";
import { TabNode } from "./TabNode";
import { adjustSelectedIndex } from "./Utils";
export class TabSetNode extends Node {
/** @internal */
static fromJson(json, model, layoutWindow) {
const newLayoutNode = new TabSetNode(model, json);
if (json.children != null) {
for (const jsonChild of json.children) {
const child = TabNode.fromJson(jsonChild, model);
newLayoutNode.addChild(child);
}
}
if (newLayoutNode.children.length === 0) {
newLayoutNode.setSelected(-1);
}
if (json.maximized && json.maximized === true) {
layoutWindow.maximizedTabSet = newLayoutNode;
}
if (json.active && json.active === true) {
layoutWindow.activeTabSet = newLayoutNode;
}
return newLayoutNode;
}
/** @internal */
constructor(model, json) {
super(model);
/** @internal */
this.tabStripRect = Rect.empty();
/** @internal */
this.contentRect = Rect.empty();
this.calculatedMinHeight = 0;
this.calculatedMinWidth = 0;
this.calculatedMaxHeight = 0;
this.calculatedMaxWidth = 0;
TabSetNode.attributeDefinitions.fromJson(json, this.attributes);
model.addNode(this);
}
getName() {
return this.getAttr("name");
}
isEnableActiveIcon() {
return this.getAttr("enableActiveIcon");
}
getSelected() {
const selected = this.attributes.selected;
if (selected !== undefined) {
return selected;
}
return -1;
}
getSelectedNode() {
const selected = this.getSelected();
if (selected !== -1) {
return this.children[selected];
}
return undefined;
}
getWeight() {
return this.getAttr("weight");
}
getAttrMinWidth() {
return this.getAttr("minWidth");
}
getAttrMinHeight() {
return this.getAttr("minHeight");
}
getMinWidth() {
return this.calculatedMinWidth;
}
getMinHeight() {
return this.calculatedMinHeight;
}
/** @internal */
getMinSize(orientation) {
if (orientation === Orientation.HORZ) {
return this.getMinWidth();
}
else {
return this.getMinHeight();
}
}
getAttrMaxWidth() {
return this.getAttr("maxWidth");
}
getAttrMaxHeight() {
return this.getAttr("maxHeight");
}
getMaxWidth() {
return this.calculatedMaxWidth;
}
getMaxHeight() {
return this.calculatedMaxHeight;
}
/** @internal */
getMaxSize(orientation) {
if (orientation === Orientation.HORZ) {
return this.getMaxWidth();
}
else {
return this.getMaxHeight();
}
}
/**
* 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;
}
isMaximized() {
return this.model.getMaximizedTabset(this.getWindowId()) === this;
}
isActive() {
return this.model.getActiveTabset(this.getWindowId()) === this;
}
isEnableDeleteWhenEmpty() {
return this.getAttr("enableDeleteWhenEmpty");
}
isEnableDrop() {
return this.getAttr("enableDrop");
}
isEnableTabWrap() {
return this.getAttr("enableTabWrap");
}
isEnableDrag() {
return this.getAttr("enableDrag");
}
isEnableDivide() {
return this.getAttr("enableDivide");
}
isEnableMaximize() {
return this.getAttr("enableMaximize");
}
isEnableClose() {
return this.getAttr("enableClose");
}
isEnableSingleTabStretch() {
return this.getAttr("enableSingleTabStretch");
}
isEnableTabStrip() {
return this.getAttr("enableTabStrip");
}
isAutoSelectTab() {
return this.getAttr("autoSelectTab");
}
isEnableTabScrollbar() {
return this.getAttr("enableTabScrollbar");
}
getClassNameTabStrip() {
return this.getAttr("classNameTabStrip");
}
getTabLocation() {
return this.getAttr("tabLocation");
}
toJson() {
const json = {};
TabSetNode.attributeDefinitions.toJson(json, this.attributes);
json.children = this.children.map((child) => child.toJson());
if (this.isActive()) {
json.active = true;
}
if (this.isMaximized()) {
json.maximized = true;
}
return json;
}
/** @internal */
calcMinMaxSize() {
this.calculatedMinHeight = this.getAttrMinHeight();
this.calculatedMinWidth = this.getAttrMinWidth();
this.calculatedMaxHeight = this.getAttrMaxHeight();
this.calculatedMaxWidth = this.getAttrMaxWidth();
for (const child of this.children) {
const c = child;
this.calculatedMinWidth = Math.max(this.calculatedMinWidth, c.getMinWidth());
this.calculatedMinHeight = Math.max(this.calculatedMinHeight, c.getMinHeight());
this.calculatedMaxWidth = Math.min(this.calculatedMaxWidth, c.getMaxWidth());
this.calculatedMaxHeight = Math.min(this.calculatedMaxHeight, c.getMaxHeight());
}
this.calculatedMinHeight += this.tabStripRect.height;
this.calculatedMaxHeight += this.tabStripRect.height;
}
/** @internal */
canMaximize() {
if (this.isEnableMaximize()) {
// always allow maximize toggle if already maximized
if (this.getModel().getMaximizedTabset(this.getWindowId()) === this) {
return true;
}
// only one tabset, so disable
if (this.getParent() === this.getModel().getRoot(this.getWindowId()) && this.getModel().getRoot(this.getWindowId()).getChildren().length === 1) {
return false;
}
return true;
}
return false;
}
/** @internal */
setContentRect(rect) {
this.contentRect = rect;
}
/** @internal */
getContentRect() {
return this.contentRect;
}
/** @internal */
setTabStripRect(rect) {
this.tabStripRect = rect;
}
/** @internal */
setWeight(weight) {
this.attributes.weight = weight;
}
/** @internal */
setSelected(index) {
this.attributes.selected = index;
}
getWindowId() {
return this.parent.getWindowId();
}
/** @internal */
canDrop(dragNode, x, y) {
let dropInfo;
if (dragNode === this) {
const dockLocation = DockLocation.CENTER;
const outlineRect = this.tabStripRect;
dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
}
else if (this.getWindowId() !== Model.MAIN_WINDOW_ID && !canDockToWindow(dragNode)) {
return undefined;
}
else if (this.contentRect.contains(x, y)) {
let dockLocation = DockLocation.CENTER;
if (this.model.getMaximizedTabset(this.parent.getWindowId()) === undefined) {
dockLocation = DockLocation.getLocation(this.contentRect, x, y);
}
const outlineRect = dockLocation.getDockRect(this.rect);
dropInfo = new DropInfo(this, outlineRect, dockLocation, -1, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
}
else if (this.tabStripRect != null && this.tabStripRect.contains(x, y)) {
let r;
let yy;
let h;
if (this.children.length === 0) {
r = this.tabStripRect.clone();
yy = r.y + 3;
h = r.height - 4;
r.width = 2;
}
else {
let child = this.children[0];
r = child.getTabRect();
yy = r.y;
h = r.height;
let p = this.tabStripRect.x;
let childCenter = 0;
for (let i = 0; i < this.children.length; i++) {
child = this.children[i];
r = child.getTabRect();
if (r.y !== yy) {
yy = r.y;
p = this.tabStripRect.x;
}
childCenter = r.x + r.width / 2;
if (p <= x && x < childCenter && r.y < y && y < r.getBottom()) {
const dockLocation = DockLocation.CENTER;
const outlineRect = new Rect(r.x - 2, r.y, 3, r.height);
if (this.rect.x < r.x && r.x < this.rect.getRight()) {
dropInfo = new DropInfo(this, outlineRect, dockLocation, i, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
break;
}
else {
return undefined;
}
}
p = childCenter;
}
}
if (dropInfo == null && r.getRight() < this.rect.getRight()) {
const dockLocation = DockLocation.CENTER;
const outlineRect = new Rect(r.getRight() - 2, yy, 3, h);
dropInfo = new DropInfo(this, outlineRect, dockLocation, this.children.length, CLASSES.FLEXLAYOUT__OUTLINE_RECT);
}
}
if (!dragNode.canDockInto(dragNode, dropInfo)) {
return undefined;
}
return dropInfo;
}
/** @internal */
delete() {
this.parent.removeChild(this);
}
/** @internal */
remove(node) {
const removedIndex = this.removeChild(node);
this.model.tidy();
adjustSelectedIndex(this, removedIndex);
}
/** @internal */
drop(dragNode, location, index, select) {
const dockLocation = location;
if (this === dragNode) {
// tabset drop into itself
return; // dock back to itself
}
let dragParent = dragNode.getParent();
let fromIndex = 0;
if (dragParent !== undefined) {
fromIndex = dragParent.removeChild(dragNode);
// if selected node in border is being docked into tabset then deselect border tabs
if (dragParent instanceof BorderNode && dragParent.getSelected() === fromIndex) {
dragParent.setSelected(-1);
}
else {
adjustSelectedIndex(dragParent, fromIndex);
}
}
// if dropping a tab back to same tabset and moving to forward position then reduce insertion index
if (dragNode instanceof TabNode && dragParent === this && fromIndex < index && index > 0) {
index--;
}
// simple_bundled dock to existing tabset
if (dockLocation === DockLocation.CENTER) {
let insertPos = index;
if (insertPos === -1) {
insertPos = this.children.length;
}
if (dragNode instanceof TabNode) {
this.addChild(dragNode, insertPos);
if (select || (select !== false && this.isAutoSelectTab())) {
this.setSelected(insertPos);
}
// console.log("added child at : " + insertPos);
}
else if (dragNode instanceof RowNode) {
dragNode.forEachNode((child, level) => {
if (child instanceof TabNode) {
this.addChild(child, insertPos);
// console.log("added child at : " + insertPos);
insertPos++;
}
}, 0);
}
else {
for (let i = 0; i < dragNode.getChildren().length; i++) {
const child = dragNode.getChildren()[i];
this.addChild(child, insertPos);
// console.log("added child at : " + insertPos);
insertPos++;
}
if (this.getSelected() === -1 && this.children.length > 0) {
this.setSelected(0);
}
}
this.model.setActiveTabset(this, this.parent.getWindowId());
}
else {
let moveNode = dragNode;
if (dragNode instanceof TabNode) {
// create new tabset parent
// console.log("create a new tabset");
const callback = this.model.getOnCreateTabSet();
moveNode = new TabSetNode(this.model, callback ? callback(dragNode) : {});
moveNode.addChild(dragNode);
// console.log("added child at end");
dragParent = moveNode;
}
else if (dragNode instanceof RowNode) {
const parent = this.getParent();
// need to turn round if same orientation unless docking oposite direction
if (dragNode.getOrientation() === parent.getOrientation() &&
(location.getOrientation() === parent.getOrientation() || location === DockLocation.CENTER)) {
const node = new RowNode(this.model, this.getWindowId(), {});
node.addChild(dragNode);
moveNode = node;
}
}
else {
moveNode = dragNode;
}
const parentRow = this.parent;
const pos = parentRow.getChildren().indexOf(this);
if (parentRow.getOrientation() === dockLocation.orientation) {
moveNode.setWeight(this.getWeight() / 2);
this.setWeight(this.getWeight() / 2);
// console.log("added child 50% size at: " + pos + dockLocation.indexPlus);
parentRow.addChild(moveNode, pos + dockLocation.indexPlus);
}
else {
// create a new row to host the new tabset (it will go in the opposite direction)
// console.log("create a new row");
const newRow = new RowNode(this.model, this.getWindowId(), {});
newRow.setWeight(this.getWeight());
newRow.addChild(this);
this.setWeight(50);
moveNode.setWeight(50);
// console.log("added child 50% size at: " + dockLocation.indexPlus);
newRow.addChild(moveNode, dockLocation.indexPlus);
parentRow.removeChild(this);
parentRow.addChild(newRow, pos);
}
if (moveNode instanceof TabSetNode) {
this.model.setActiveTabset(moveNode, this.getWindowId());
}
}
this.model.tidy();
}
/** @internal */
updateAttrs(json) {
TabSetNode.attributeDefinitions.update(json, this.attributes);
}
/** @internal */
getAttributeDefinitions() {
return TabSetNode.attributeDefinitions;
}
/** @internal */
static getAttributeDefinitions() {
return TabSetNode.attributeDefinitions;
}
/** @internal */
static createAttributeDefinitions() {
const attributeDefinitions = new AttributeDefinitions();
attributeDefinitions.add("type", TabSetNode.TYPE, true).setType(Attribute.STRING).setFixed();
attributeDefinitions.add("id", undefined).setType(Attribute.STRING).setDescription(`the unique id of the tab set, if left undefined a uuid will be assigned`);
attributeDefinitions.add("weight", 100).setType(Attribute.NUMBER).setDescription(`relative weight for sizing of this tabset in parent row`);
attributeDefinitions.add("selected", 0).setType(Attribute.NUMBER).setDescription(`index of selected/visible tab in tabset`);
attributeDefinitions.add("name", undefined).setType(Attribute.STRING);
attributeDefinitions.add("config", undefined).setType("any").setDescription(`a place to hold json config used in your own code`);
attributeDefinitions.addInherited("enableDeleteWhenEmpty", "tabSetEnableDeleteWhenEmpty").setDescription(`whether to delete this tabset when is has no tabs`);
attributeDefinitions.addInherited("enableDrop", "tabSetEnableDrop").setDescription(`allow user to drag tabs into this tabset`);
attributeDefinitions.addInherited("enableDrag", "tabSetEnableDrag").setDescription(`allow user to drag tabs out this tabset`);
attributeDefinitions.addInherited("enableDivide", "tabSetEnableDivide").setDescription(`allow user to drag tabs to region of this tabset, splitting into new tabset`);
attributeDefinitions.addInherited("enableMaximize", "tabSetEnableMaximize").setDescription(`allow user to maximize tabset to fill view via maximize button`);
attributeDefinitions.addInherited("enableClose", "tabSetEnableClose").setDescription(`allow user to close tabset via a close button`);
attributeDefinitions.addInherited("enableSingleTabStretch", "tabSetEnableSingleTabStretch").setDescription(`if the tabset has only a single tab then stretch the single tab to fill area and display in a header style`);
attributeDefinitions.addInherited("classNameTabStrip", "tabSetClassNameTabStrip").setDescription(`a class name to apply to the tab strip`);
attributeDefinitions.addInherited("enableTabStrip", "tabSetEnableTabStrip").setDescription(`enable tab strip and allow multiple tabs in this tabset`);
attributeDefinitions.addInherited("minWidth", "tabSetMinWidth").setDescription(`minimum width (in px) for this tabset`);
attributeDefinitions.addInherited("minHeight", "tabSetMinHeight").setDescription(`minimum height (in px) for this tabset`);
attributeDefinitions.addInherited("maxWidth", "tabSetMaxWidth").setDescription(`maximum width (in px) for this tabset`);
attributeDefinitions.addInherited("maxHeight", "tabSetMaxHeight").setDescription(`maximum height (in px) for this tabset`);
attributeDefinitions.addInherited("enableTabWrap", "tabSetEnableTabWrap").setDescription(`wrap tabs onto multiple lines`);
attributeDefinitions.addInherited("tabLocation", "tabSetTabLocation").setDescription(`the location of the tabs either top or bottom`);
attributeDefinitions.addInherited("autoSelectTab", "tabSetAutoSelectTab").setType(Attribute.BOOLEAN).setDescription(`whether to select new/moved tabs in tabset`);
attributeDefinitions.addInherited("enableActiveIcon", "tabSetEnableActiveIcon").setType(Attribute.BOOLEAN).setDescription(`whether the active icon (*) should be displayed when the tabset is active`);
attributeDefinitions.addInherited("enableTabScrollbar", "tabSetEnableTabScrollbar").setType(Attribute.BOOLEAN).setDescription(`whether to show a mini scrollbar for the tabs`);
return attributeDefinitions;
}
}
TabSetNode.TYPE = "tabset";
/** @internal */
TabSetNode.attributeDefinitions = TabSetNode.createAttributeDefinitions();
//# sourceMappingURL=TabSetNode.js.map