@annotationhub/react-golden-layout
Version:
A multi-screen javascript Layout manager https://golden-layout.com
661 lines (571 loc) • 25.1 kB
JavaScript
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { return function () { var Super = _getPrototypeOf(Derived), result; if (_isNativeReflectConstruct()) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
import AbstractContentItem from "../items/AbstractContentItem";
import RowOrColumn from "../items/RowOrColumn";
import Header from "../controls/Header";
import { fnBind, copy, indexOf } from "../utils/utils";
import $ from "jquery";
var Stack = /*#__PURE__*/function (_AbstractContentItem) {
_inherits(Stack, _AbstractContentItem);
var _super = _createSuper(Stack);
function Stack(layoutManager, config, parent) {
var _this;
_classCallCheck(this, Stack);
_this = _super.call(this, layoutManager, config, parent);
_this.element = $('<div class="lm_item lm_stack"></div>');
_this._activeContentItem = null;
var cfg = layoutManager.config;
_this._header = {
// defaults' reconstruction from old configuration style
show: cfg.settings.hasHeaders === true && config.hasHeaders !== false,
popout: cfg.settings.showPopoutIcon && cfg.labels.popout,
maximise: cfg.settings.showMaximiseIcon && cfg.labels.maximise,
close: cfg.settings.showCloseIcon && cfg.labels.close,
minimise: cfg.labels.minimise
};
if (cfg.header) // load simplified version of header configuration (https://github.com/deepstreamIO/golden-layout/pull/245)
copy(_this._header, cfg.header);
if (config.header) // load from stack
copy(_this._header, config.header);
if (config.content && config.content[0] && config.content[0].header) // load from component if stack omitted
copy(_this._header, config.content[0].header);
_this._dropZones = {};
_this._dropSegment = null;
_this._contentAreaDimensions = null;
_this._dropIndex = null;
_this.isStack = true;
_this.childElementContainer = $('<div class="lm_items"></div>');
_this.header = new Header(layoutManager, _assertThisInitialized(_this));
_this.element.on("mouseleave mouseenter", fnBind(function (event) {
if (this._docker && this._docker.docked) this.childElementContainer[this._docker.dimension](event.type == "mouseenter" ? this._docker.realSize : 0);
}, _assertThisInitialized(_this)));
_this.element.append(_this.header.element);
_this.element.append(_this.childElementContainer);
_this._setupHeaderPosition();
_this._$validateClosability();
return _this;
}
_createClass(Stack, [{
key: "dock",
value: function dock(mode) {
if (this._header.dock) if (this.parent instanceof RowOrColumn) this.parent.dock(this, mode);
}
}, {
key: "setSize",
value: function setSize() {
if (this.element.css("display") === "none") return;
var isDocked = this._docker && this._docker.docked,
content = {
width: this.element.width(),
height: this.element.height()
};
if (this._header.show) content[this._sided ? "width" : "height"] -= this.layoutManager.config.dimensions.headerHeight;
if (isDocked) content[this._docker.dimension] = this._docker.realSize;
if (!isDocked || this._docker.dimension == "height") this.childElementContainer.width(content.width);
if (!isDocked || this._docker.dimension == "width") this.childElementContainer.height(content.height);
for (var i = 0; i < this.contentItems.length; i++) {
this.contentItems[i].element.width(content.width);
this.contentItems[i].element.height(content.height);
}
this.emit("resize");
this.emitBubblingEvent("stateChanged");
}
}, {
key: "_$init",
value: function _$init() {
var i, initialItem;
if (this.isInitialised === true) return;
AbstractContentItem.prototype._$init.call(this);
for (i = 0; i < this.contentItems.length; i++) {
this.header.createTab(this.contentItems[i]);
this.contentItems[i]._$hide();
}
if (this.contentItems.length > 0) {
initialItem = this.contentItems[this.config.activeItemIndex || 0];
if (!initialItem) {
throw new Error("Configured activeItemIndex out of bounds");
}
this.setActiveContentItem(initialItem);
}
this._$validateClosability();
if (this.parent instanceof RowOrColumn) {
this.parent._validateDocking();
}
}
}, {
key: "setActiveContentItem",
value: function setActiveContentItem(contentItem) {
if (this._activeContentItem === contentItem) return;
if (indexOf(contentItem, this.contentItems) === -1) {
throw new Error("contentItem is not a child of this stack");
}
if (this._activeContentItem !== null) {
this._activeContentItem._$hide();
}
this._activeContentItem = contentItem;
this.header.setActiveContentItem(contentItem);
contentItem._$show();
this.emit("activeContentItemChanged", contentItem);
this.layoutManager.emit("activeContentItemChanged", contentItem);
this.emitBubblingEvent("stateChanged");
}
}, {
key: "getActiveContentItem",
value: function getActiveContentItem() {
return this.header.activeContentItem;
}
}, {
key: "addChild",
value: function addChild(contentItem, index) {
if (index > this.contentItems.length) {
/*
* UGLY PATCH: PR #428, commit a4e84ec5 fixed a bug appearing on touchscreens during the drag of a panel.
* The bug was caused by the physical removal of the element on drag: partial documentation is at issue #425.
* The fix introduced the function undisplayChild() (called 'undisplay' to differentiate it from jQuery.hide),
* which doesn't remove the element but only hides it: that's why when a tab is dragged & dropped into its
* original container (at the end), the index here could be off by one.
*/
index -= 1;
}
contentItem = this.layoutManager._$normalizeContentItem(contentItem, this);
AbstractContentItem.prototype.addChild.call(this, contentItem, index);
this.childElementContainer.append(contentItem.element);
this.header.createTab(contentItem, index);
this.setActiveContentItem(contentItem);
this.callDownwards("setSize");
this._$validateClosability();
if (this.parent instanceof RowOrColumn) this.parent._validateDocking();
this.emitBubblingEvent("stateChanged");
}
}, {
key: "removeChild",
value: function removeChild(contentItem, keepChild) {
var index = indexOf(contentItem, this.contentItems);
AbstractContentItem.prototype.removeChild.call(this, contentItem, keepChild);
this.header.removeTab(contentItem);
if (this.header.activeContentItem === contentItem) {
if (this.contentItems.length > 0) {
this.setActiveContentItem(this.contentItems[Math.max(index - 1, 0)]);
} else {
this._activeContentItem = null;
}
} else if (this.config.activeItemIndex >= this.contentItems.length) {
if (this.contentItems.length > 0) {
var activeIndex = indexOf(this.getActiveContentItem(), this.contentItems);
this.config.activeItemIndex = Math.max(activeIndex, 0);
}
} // Vega BEGIN
//
// If its not the active item and its index is less than current active
// item we need to decrement the active index by one. Otherwise when you
// reload the page the activeItemIndex will be greater than items.length
// causing the error on line 101
else if (index <= this.config.activeContentItem) {
this.config.activeItemIndex -= 1;
} // Vega END
this._$validateClosability();
if (this.parent instanceof RowOrColumn) this.parent._validateDocking();
this.emitBubblingEvent("stateChanged");
}
}, {
key: "undisplayChild",
value: function undisplayChild(contentItem) {
if (this.contentItems.length > 1) {
var index = indexOf(contentItem, this.contentItems);
contentItem._$hide && contentItem._$hide();
this.setActiveContentItem(this.contentItems[index === 0 ? index + 1 : index - 1]);
} else {
this.header.hideTab(contentItem);
contentItem._$hide && contentItem._$hide();
AbstractContentItem.prototype.undisplayChild.call(this, contentItem);
if (this.parent instanceof RowOrColumn) this.parent._validateDocking();
}
this.emitBubblingEvent("stateChanged");
}
/**
* Validates that the stack is still closable or not. If a stack is able
* to close, but has a non closable component added to it, the stack is no
* longer closable until all components are closable.
*
* @returns {void}
*/
}, {
key: "_$validateClosability",
value: function _$validateClosability() {
var contentItem, isClosable, len, i;
isClosable = this.header._isClosable();
for (i = 0, len = this.contentItems.length; i < len; i++) {
if (!isClosable) {
break;
}
isClosable = this.contentItems[i].config.isClosable;
}
this.header._$setClosable(isClosable);
}
}, {
key: "_$destroy",
value: function _$destroy() {
AbstractContentItem.prototype._$destroy.call(this);
this.header._$destroy();
this.element.off("mouseenter mouseleave");
}
/**
* Ok, this one is going to be the tricky one: The user has dropped {contentItem} onto this stack.
*
* It was dropped on either the stacks header or the top, right, bottom or left bit of the content area
* (which one of those is stored in this._dropSegment). Now, if the user has dropped on the header the case
* is relatively clear: We add the item to the existing stack... job done (might be good to have
* tab reordering at some point, but lets not sweat it right now)
*
* If the item was dropped on the content part things are a bit more complicated. If it was dropped on either the
* top or bottom region we need to create a new column and place the items accordingly.
* Unless, of course if the stack is already within a column... in which case we want
* to add the newly created item to the existing column...
* either prepend or append it, depending on wether its top or bottom.
*
* Same thing for rows and left / right drop segments... so in total there are 9 things that can potentially happen
* (left, top, right, bottom) * is child of the right parent (row, column) + header drop
*
* @param {lm.item} contentItem
*
* @returns {void}
*/
}, {
key: "_$onDrop",
value: function _$onDrop(contentItem) {
/*
* The item was dropped on the header area. Just add it as a child of this stack and
* get the hell out of this logic
*/
if (this._dropSegment === "header") {
this._resetHeaderDropZone();
this.addChild(contentItem, this._dropIndex);
return;
}
/*
* The stack is empty. Let's just add the element.
*/
if (this._dropSegment === "body") {
this.addChild(contentItem);
return;
}
/*
* The item was dropped on the top-, left-, bottom- or right- part of the content. Let's
* aggregate some conditions to make the if statements later on more readable
*/
var isVertical = this._dropSegment === "top" || this._dropSegment === "bottom",
isHorizontal = this._dropSegment === "left" || this._dropSegment === "right",
insertBefore = this._dropSegment === "top" || this._dropSegment === "left",
hasCorrectParent = isVertical && this.parent.isColumn || isHorizontal && this.parent.isRow,
type = isVertical ? "column" : "row",
dimension = isVertical ? "height" : "width",
index,
stack,
rowOrColumn;
/*
* The content item can be either a component or a stack. If it is a component, wrap it into a stack
*/
if (contentItem.isComponent) {
stack = this.layoutManager.createContentItem({
type: "stack",
header: contentItem.config.header || {}
}, this);
stack._$init();
stack.addChild(contentItem);
contentItem = stack;
}
/*
* If the contentItem that's being dropped is not dropped on a Stack (cases which just passed above and
* which would wrap the contentItem in a Stack) we need to check whether contentItem is a RowOrColumn.
* If it is, we need to re-wrap it in a Stack like it was when it was dragged by its Tab (it was dragged!).
*/
if (contentItem.config.type === "row" || contentItem.config.type === "column") {
stack = this.layoutManager.createContentItem({
type: "stack"
}, this);
stack.addChild(contentItem);
contentItem = stack;
}
/*
* If the item is dropped on top or bottom of a column or left and right of a row, it's already
* layd out in the correct way. Just add it as a child
*/
if (hasCorrectParent) {
index = indexOf(this, this.parent.contentItems);
this.parent.addChild(contentItem, insertBefore ? index : index + 1, true);
this.config[dimension] *= 0.5;
contentItem.config[dimension] = this.config[dimension];
this.parent.callDownwards("setSize");
/*
* This handles items that are dropped on top or bottom of a row or left / right of a column. We need
* to create the appropriate contentItem for them to live in
*/
} else {
type = isVertical ? "column" : "row";
rowOrColumn = this.layoutManager.createContentItem({
type: type
}, this);
this.parent.replaceChild(this, rowOrColumn);
rowOrColumn.addChild(contentItem, insertBefore ? 0 : undefined, true);
rowOrColumn.addChild(this, insertBefore ? undefined : 0, true);
this.config[dimension] = 50;
contentItem.config[dimension] = 50;
rowOrColumn.callDownwards("setSize");
}
this.parent._validateDocking();
}
/**
* If the user hovers above the header part of the stack, indicate drop positions for tabs.
* otherwise indicate which segment of the body the dragged item would be dropped on
*
* @param {Int} x Absolute Screen X
* @param {Int} y Absolute Screen Y
*
* @returns {void}
*/
}, {
key: "_$highlightDropZone",
value: function _$highlightDropZone(x, y) {
var segment, area;
for (segment in this._contentAreaDimensions) {
area = this._contentAreaDimensions[segment].hoverArea;
if (area.x1 < x && area.x2 > x && area.y1 < y && area.y2 > y) {
if (segment === "header") {
this._dropSegment = "header";
this._highlightHeaderDropZone(this._sided ? y : x);
} else {
this._resetHeaderDropZone();
this._highlightBodyDropZone(segment);
}
return;
}
}
}
}, {
key: "_$getArea",
value: function _$getArea() {
if (this.element.css("display") === "none") {
return null;
}
var getArea = AbstractContentItem.prototype._$getArea,
headerArea = getArea.call(this, this.header.element),
contentArea = getArea.call(this, this.childElementContainer),
contentWidth = contentArea.x2 - contentArea.x1,
contentHeight = contentArea.y2 - contentArea.y1;
this._contentAreaDimensions = {
header: {
hoverArea: {
x1: headerArea.x1,
y1: headerArea.y1,
x2: headerArea.x2,
y2: headerArea.y2
},
highlightArea: {
x1: headerArea.x1,
y1: headerArea.y1,
x2: headerArea.x2,
y2: headerArea.y2
}
}
};
/**
* If this Stack is a parent to rows, columns or other stacks only its
* header is a valid dropzone.
*/
if (this._activeContentItem && this._activeContentItem.isComponent === false) {
return headerArea;
}
/**
* Highlight the entire body if the stack is empty
*/
if (this.contentItems.length === 0) {
this._contentAreaDimensions.body = {
hoverArea: {
x1: contentArea.x1,
y1: contentArea.y1,
x2: contentArea.x2,
y2: contentArea.y2
},
highlightArea: {
x1: contentArea.x1,
y1: contentArea.y1,
x2: contentArea.x2,
y2: contentArea.y2
}
};
return getArea.call(this, this.element);
}
this._contentAreaDimensions.left = {
hoverArea: {
x1: contentArea.x1,
y1: contentArea.y1,
x2: contentArea.x1 + contentWidth * 0.25,
y2: contentArea.y2
},
highlightArea: {
x1: contentArea.x1,
y1: contentArea.y1,
x2: contentArea.x1 + contentWidth * 0.5,
y2: contentArea.y2
}
};
this._contentAreaDimensions.top = {
hoverArea: {
x1: contentArea.x1 + contentWidth * 0.25,
y1: contentArea.y1,
x2: contentArea.x1 + contentWidth * 0.75,
y2: contentArea.y1 + contentHeight * 0.5
},
highlightArea: {
x1: contentArea.x1,
y1: contentArea.y1,
x2: contentArea.x2,
y2: contentArea.y1 + contentHeight * 0.5
}
};
this._contentAreaDimensions.right = {
hoverArea: {
x1: contentArea.x1 + contentWidth * 0.75,
y1: contentArea.y1,
x2: contentArea.x2,
y2: contentArea.y2
},
highlightArea: {
x1: contentArea.x1 + contentWidth * 0.5,
y1: contentArea.y1,
x2: contentArea.x2,
y2: contentArea.y2
}
};
this._contentAreaDimensions.bottom = {
hoverArea: {
x1: contentArea.x1 + contentWidth * 0.25,
y1: contentArea.y1 + contentHeight * 0.5,
x2: contentArea.x1 + contentWidth * 0.75,
y2: contentArea.y2
},
highlightArea: {
x1: contentArea.x1,
y1: contentArea.y1 + contentHeight * 0.5,
x2: contentArea.x2,
y2: contentArea.y2
}
};
return getArea.call(this, this.element);
}
}, {
key: "_highlightHeaderDropZone",
value: function _highlightHeaderDropZone(x) {
var i,
tabElement,
tabsLength = this.header.tabs.length,
isAboveTab = false,
tabTop,
tabLeft,
offset,
placeHolderLeft,
headerOffset,
tabWidth,
halfX; // Empty stack
if (tabsLength === 0) {
headerOffset = this.header.element.offset();
this.layoutManager.dropTargetIndicator.highlightArea({
x1: headerOffset.left,
x2: headerOffset.left + 100,
y1: headerOffset.top + this.header.element.height() - 20,
y2: headerOffset.top + this.header.element.height()
});
return;
}
for (i = 0; i < tabsLength; i++) {
tabElement = this.header.tabs[i].element;
offset = tabElement.offset();
if (this._sided) {
tabLeft = offset.top;
tabTop = offset.left;
tabWidth = tabElement.height();
} else {
tabLeft = offset.left;
tabTop = offset.top;
tabWidth = tabElement.width();
}
if (x > tabLeft && x < tabLeft + tabWidth) {
isAboveTab = true;
break;
}
}
if (isAboveTab === false && x < tabLeft) {
return;
}
halfX = tabLeft + tabWidth / 2;
if (x < halfX) {
this._dropIndex = i;
tabElement.before(this.layoutManager.tabDropPlaceholder);
} else {
this._dropIndex = Math.min(i + 1, tabsLength);
tabElement.after(this.layoutManager.tabDropPlaceholder);
}
if (this._sided) {
var placeHolderTop = this.layoutManager.tabDropPlaceholder.offset().top;
this.layoutManager.dropTargetIndicator.highlightArea({
x1: tabTop,
x2: tabTop + tabElement.innerHeight(),
y1: placeHolderTop,
y2: placeHolderTop + this.layoutManager.tabDropPlaceholder.width()
});
return;
}
placeHolderLeft = this.layoutManager.tabDropPlaceholder.offset().left;
this.layoutManager.dropTargetIndicator.highlightArea({
x1: placeHolderLeft,
x2: placeHolderLeft + this.layoutManager.tabDropPlaceholder.width(),
y1: tabTop,
y2: tabTop + tabElement.innerHeight()
});
}
}, {
key: "_resetHeaderDropZone",
value: function _resetHeaderDropZone() {
this.layoutManager.tabDropPlaceholder.remove();
}
}, {
key: "toggleMaximise",
value: function toggleMaximise(e) {
if (!this.isMaximised) this.dock(false);
AbstractContentItem.prototype.toggleMaximise.call(this, e);
}
}, {
key: "_setupHeaderPosition",
value: function _setupHeaderPosition() {
var side = ["right", "left", "bottom"].indexOf(this._header.show) >= 0 && this._header.show;
this.header.element.toggle(!!this._header.show);
this._side = side;
this._sided = ["right", "left"].indexOf(this._side) >= 0;
this.element.removeClass("lm_left lm_right lm_bottom");
if (this._side) this.element.addClass("lm_" + this._side);
if (this.element.find(".lm_header").length && this.childElementContainer) {
var headerPosition = ["right", "bottom"].indexOf(this._side) >= 0 ? "before" : "after";
this.header.element[headerPosition](this.childElementContainer);
this.callDownwards("setSize");
}
}
}, {
key: "_highlightBodyDropZone",
value: function _highlightBodyDropZone(segment) {
var highlightArea = this._contentAreaDimensions[segment].highlightArea;
this.layoutManager.dropTargetIndicator.highlightArea(highlightArea);
this._dropSegment = segment;
}
}]);
return Stack;
}(AbstractContentItem);
export { Stack as default };