UNPKG

flexlayout-react

Version:

A multi-tab docking layout manager

469 lines 19.9 kB
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