flexlayout-react-v7-react-19
Version:
A multi-tab docking layout manager
908 lines • 43.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Layout = void 0;
const React = require("react");
const react_dom_1 = require("react-dom");
const DockLocation_1 = require("../DockLocation");
const DragDrop_1 = require("../DragDrop");
const Actions_1 = require("../model/Actions");
const BorderNode_1 = require("../model/BorderNode");
const SplitterNode_1 = require("../model/SplitterNode");
const TabNode_1 = require("../model/TabNode");
const TabSetNode_1 = require("../model/TabSetNode");
const Rect_1 = require("../Rect");
const Types_1 = require("../Types");
const BorderTabSet_1 = require("./BorderTabSet");
const Splitter_1 = require("./Splitter");
const Tab_1 = require("./Tab");
const TabSet_1 = require("./TabSet");
const FloatingWindow_1 = require("./FloatingWindow");
const FloatingWindowTab_1 = require("./FloatingWindowTab");
const TabFloating_1 = require("./TabFloating");
const Orientation_1 = require("../Orientation");
const Icons_1 = require("./Icons");
const TabButtonStamp_1 = require("./TabButtonStamp");
const defaultIcons = {
close: React.createElement(Icons_1.CloseIcon, null),
closeTabset: React.createElement(Icons_1.CloseIcon, null),
popout: React.createElement(Icons_1.PopoutIcon, null),
maximize: React.createElement(Icons_1.MaximizeIcon, null),
restore: React.createElement(Icons_1.RestoreIcon, null),
more: React.createElement(Icons_1.OverflowIcon, null),
edgeArrow: React.createElement(Icons_1.EdgeIcon, null)
};
// Popout windows work in latest browsers based on webkit (Chrome, Opera, Safari, latest Edge) and Firefox. They do
// not work on any version if IE or the original Edge browser
// Assume any recent desktop browser not IE or original Edge will work
/** @internal */
// @ts-ignore
const isIEorEdge = typeof window !== "undefined" && (window.document.documentMode || /Edge\//.test(window.navigator.userAgent));
/** @internal */
const isDesktop = typeof window !== "undefined" && window.matchMedia && window.matchMedia("(hover: hover) and (pointer: fine)").matches;
/** @internal */
const defaultSupportsPopout = isDesktop && !isIEorEdge;
/**
* A React component that hosts a multi-tabbed layout
*/
class Layout extends React.Component {
constructor(props) {
super(props);
/** @internal */
this.firstMove = false;
/** @internal */
this.dragRectRendered = true;
/** @internal */
this.dragDivText = undefined;
/** @internal */
this.edgeRectLength = 100;
/** @internal */
this.edgeRectWidth = 10;
/** @internal */
this.onModelChange = (action) => {
this.forceUpdate();
if (this.props.onModelChange) {
this.props.onModelChange(this.props.model, action);
}
};
/** @internal */
this.updateRect = (domRect) => {
if (!domRect) {
domRect = this.getDomRect();
}
if (!domRect) {
// no dom rect available, return.
return;
}
const rect = new Rect_1.Rect(0, 0, domRect.width, domRect.height);
if (!rect.equals(this.state.rect) && rect.width !== 0 && rect.height !== 0) {
this.setState({ rect });
}
};
/** @internal */
this.updateLayoutMetrics = () => {
if (this.findHeaderBarSizeRef.current) {
const headerBarSize = this.findHeaderBarSizeRef.current.getBoundingClientRect().height;
if (headerBarSize !== this.state.calculatedHeaderBarSize) {
this.setState({ calculatedHeaderBarSize: headerBarSize });
}
}
if (this.findTabBarSizeRef.current) {
const tabBarSize = this.findTabBarSizeRef.current.getBoundingClientRect().height;
if (tabBarSize !== this.state.calculatedTabBarSize) {
this.setState({ calculatedTabBarSize: tabBarSize });
}
}
if (this.findBorderBarSizeRef.current) {
const borderBarSize = this.findBorderBarSizeRef.current.getBoundingClientRect().height;
if (borderBarSize !== this.state.calculatedBorderBarSize) {
this.setState({ calculatedBorderBarSize: borderBarSize });
}
}
};
/** @internal */
this.getClassName = (defaultClassName) => {
if (this.props.classNameMapper === undefined) {
return defaultClassName;
}
else {
return this.props.classNameMapper(defaultClassName);
}
};
/** @internal */
this.onCloseWindow = (id) => {
this.doAction(Actions_1.Actions.unFloatTab(id));
try {
this.props.model.getNodeById(id)._setWindow(undefined);
}
catch (e) {
// catch incase it was a model change
}
};
/** @internal */
this.onSetWindow = (id, window) => {
this.props.model.getNodeById(id)._setWindow(window);
};
/** @internal */
this.onCancelAdd = () => {
var _a, _b;
const rootdiv = this.selfRef.current;
if (rootdiv && this.dragDiv) {
rootdiv.removeChild(this.dragDiv);
}
this.dragDiv = undefined;
this.hidePortal();
if (this.fnNewNodeDropped != null) {
this.fnNewNodeDropped();
this.fnNewNodeDropped = undefined;
}
try {
(_b = (_a = this.customDrop) === null || _a === void 0 ? void 0 : _a.invalidated) === null || _b === void 0 ? void 0 : _b.call(_a);
}
catch (e) {
console.error(e);
}
DragDrop_1.DragDrop.instance.hideGlass();
this.newTabJson = undefined;
this.customDrop = undefined;
};
/** @internal */
this.onCancelDrag = (wasDragging) => {
var _a, _b;
if (wasDragging) {
const rootdiv = this.selfRef.current;
const outlineDiv = this.outlineDiv;
if (rootdiv && outlineDiv) {
try {
rootdiv.removeChild(outlineDiv);
}
catch (e) { }
}
const dragDiv = this.dragDiv;
if (rootdiv && dragDiv) {
try {
rootdiv.removeChild(dragDiv);
}
catch (e) { }
}
this.dragDiv = undefined;
this.hidePortal();
this.setState({ showEdges: false });
if (this.fnNewNodeDropped != null) {
this.fnNewNodeDropped();
this.fnNewNodeDropped = undefined;
}
try {
(_b = (_a = this.customDrop) === null || _a === void 0 ? void 0 : _a.invalidated) === null || _b === void 0 ? void 0 : _b.call(_a);
}
catch (e) {
console.error(e);
}
DragDrop_1.DragDrop.instance.hideGlass();
this.newTabJson = undefined;
this.customDrop = undefined;
}
this.setState({ showHiddenBorder: DockLocation_1.DockLocation.CENTER });
};
/** @internal */
this.onDragDivMouseDown = (event) => {
event.preventDefault();
this.dragStart(event, this.dragDivText, TabNode_1.TabNode._fromJson(this.newTabJson, this.props.model, false), true, undefined, undefined);
};
/** @internal */
this.dragStart = (event, dragDivText, node, allowDrag, onClick, onDoubleClick) => {
var _a, _b;
if (!allowDrag) {
DragDrop_1.DragDrop.instance.startDrag(event, undefined, undefined, undefined, undefined, onClick, onDoubleClick, this.currentDocument, (_a = this.selfRef.current) !== null && _a !== void 0 ? _a : undefined);
}
else {
this.dragNode = node;
this.dragDivText = dragDivText;
DragDrop_1.DragDrop.instance.startDrag(event, this.onDragStart, this.onDragMove, this.onDragEnd, this.onCancelDrag, onClick, onDoubleClick, this.currentDocument, (_b = this.selfRef.current) !== null && _b !== void 0 ? _b : undefined);
}
};
/** @internal */
this.dragRectRender = (text, node, json, onRendered) => {
let content;
if (text !== undefined) {
content = React.createElement("div", { style: { whiteSpace: "pre" } }, text.replace("<br>", "\n"));
}
else {
if (node && node instanceof TabNode_1.TabNode) {
content = (React.createElement(TabButtonStamp_1.TabButtonStamp, { node: node, layout: this, iconFactory: this.props.iconFactory, titleFactory: this.props.titleFactory }));
}
}
if (this.props.onRenderDragRect !== undefined) {
const customContent = this.props.onRenderDragRect(content, node, json);
if (customContent !== undefined) {
content = customContent;
}
}
// hide div until the render is complete
this.dragRectRendered = false;
const dragDiv = this.dragDiv;
if (dragDiv) {
dragDiv.style.visibility = "hidden";
this.showPortal(React.createElement(DragRectRenderWrapper
// wait for it to be rendered
, {
// wait for it to be rendered
onRendered: () => {
this.dragRectRendered = true;
onRendered === null || onRendered === void 0 ? void 0 : onRendered();
} }, content), dragDiv);
}
};
/** @internal */
this.showPortal = (control, element) => {
const portal = (0, react_dom_1.createPortal)(control, element);
this.setState({ portal });
};
/** @internal */
this.hidePortal = () => {
this.setState({ portal: undefined });
};
/** @internal */
this.onDragStart = () => {
var _a;
this.dropInfo = undefined;
this.customDrop = undefined;
const rootdiv = this.selfRef.current;
this.outlineDiv = this.currentDocument.createElement("div");
this.outlineDiv.className = this.getClassName(Types_1.CLASSES.FLEXLAYOUT__OUTLINE_RECT);
this.outlineDiv.style.visibility = "hidden";
if (rootdiv) {
rootdiv.appendChild(this.outlineDiv);
}
if (this.dragDiv == null) {
this.dragDiv = this.currentDocument.createElement("div");
this.dragDiv.className = this.getClassName(Types_1.CLASSES.FLEXLAYOUT__DRAG_RECT);
this.dragDiv.setAttribute("data-layout-path", "/drag-rectangle");
this.dragRectRender(this.dragDivText, this.dragNode, this.newTabJson);
if (rootdiv) {
rootdiv.appendChild(this.dragDiv);
}
}
// add edge indicators
if (this.props.model.getMaximizedTabset() === undefined) {
this.setState({ showEdges: this.props.model.isEnableEdgeDock() });
}
if (this.dragNode && this.outlineDiv && this.dragNode instanceof TabNode_1.TabNode && this.dragNode.getTabRect() !== undefined) {
(_a = this.dragNode.getTabRect()) === null || _a === void 0 ? void 0 : _a.positionElement(this.outlineDiv);
}
this.firstMove = true;
return true;
};
/** @internal */
this.onDragMove = (event) => {
var _a, _b, _c, _d, _e, _f, _g;
if (this.firstMove === false) {
const speed = this.props.model._getAttribute("tabDragSpeed");
if (this.outlineDiv) {
this.outlineDiv.style.transition = `top ${speed}s, left ${speed}s, width ${speed}s, height ${speed}s`;
}
}
this.firstMove = false;
const clientRect = (_a = this.selfRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
const pos = {
x: event.clientX - ((_b = clientRect === null || clientRect === void 0 ? void 0 : clientRect.left) !== null && _b !== void 0 ? _b : 0),
y: event.clientY - ((_c = clientRect === null || clientRect === void 0 ? void 0 : clientRect.top) !== null && _c !== void 0 ? _c : 0),
};
this.checkForBorderToShow(pos.x, pos.y);
// keep it between left & right
const dragRect = (_e = (_d = this.dragDiv) === null || _d === void 0 ? void 0 : _d.getBoundingClientRect()) !== null && _e !== void 0 ? _e : new DOMRect(0, 0, 100, 100);
let newLeft = pos.x - dragRect.width / 2;
if (newLeft + dragRect.width > ((_f = clientRect === null || clientRect === void 0 ? void 0 : clientRect.width) !== null && _f !== void 0 ? _f : 0)) {
newLeft = ((_g = clientRect === null || clientRect === void 0 ? void 0 : clientRect.width) !== null && _g !== void 0 ? _g : 0) - dragRect.width;
}
newLeft = Math.max(0, newLeft);
if (this.dragDiv) {
this.dragDiv.style.left = newLeft + "px";
this.dragDiv.style.top = pos.y + 5 + "px";
if (this.dragRectRendered && this.dragDiv.style.visibility === "hidden") {
// make visible once the drag rect has been rendered
this.dragDiv.style.visibility = "visible";
}
}
let dropInfo = this.props.model._findDropTargetNode(this.dragNode, pos.x, pos.y);
if (dropInfo) {
if (this.props.onTabDrag) {
this.handleCustomTabDrag(dropInfo, pos, event);
}
else {
this.dropInfo = dropInfo;
if (this.outlineDiv) {
this.outlineDiv.className = this.getClassName(dropInfo.className);
dropInfo.rect.positionElement(this.outlineDiv);
this.outlineDiv.style.visibility = "visible";
}
}
}
};
/** @internal */
this.onDragEnd = (event) => {
const rootdiv = this.selfRef.current;
if (rootdiv) {
if (this.outlineDiv) {
rootdiv.removeChild(this.outlineDiv);
}
if (this.dragDiv) {
rootdiv.removeChild(this.dragDiv);
}
}
this.dragDiv = undefined;
this.hidePortal();
this.setState({ showEdges: false });
DragDrop_1.DragDrop.instance.hideGlass();
if (this.dropInfo) {
if (this.customDrop) {
this.newTabJson = undefined;
try {
const { callback, dragging, over, x, y, location } = this.customDrop;
callback(dragging, over, x, y, location);
if (this.fnNewNodeDropped != null) {
this.fnNewNodeDropped();
this.fnNewNodeDropped = undefined;
}
}
catch (e) {
console.error(e);
}
}
else if (this.newTabJson !== undefined) {
const newNode = this.doAction(Actions_1.Actions.addNode(this.newTabJson, this.dropInfo.node.getId(), this.dropInfo.location, this.dropInfo.index));
if (this.fnNewNodeDropped != null) {
this.fnNewNodeDropped(newNode, event);
this.fnNewNodeDropped = undefined;
}
this.newTabJson = undefined;
}
else if (this.dragNode !== undefined) {
this.doAction(Actions_1.Actions.moveNode(this.dragNode.getId(), this.dropInfo.node.getId(), this.dropInfo.location, this.dropInfo.index));
}
}
this.setState({ showHiddenBorder: DockLocation_1.DockLocation.CENTER });
};
this.props.model._setChangeListener(this.onModelChange);
this.tabIds = [];
this.selfRef = React.createRef();
this.findHeaderBarSizeRef = React.createRef();
this.findTabBarSizeRef = React.createRef();
this.findBorderBarSizeRef = React.createRef();
this.supportsPopout = props.supportsPopout !== undefined ? props.supportsPopout : defaultSupportsPopout;
this.popoutURL = props.popoutURL ? props.popoutURL : "popout.html";
this.icons = Object.assign(Object.assign({}, defaultIcons), props.icons);
this.state = {
rect: new Rect_1.Rect(0, 0, 0, 0),
calculatedHeaderBarSize: 25,
calculatedTabBarSize: 26,
calculatedBorderBarSize: 30,
editingTab: undefined,
showHiddenBorder: DockLocation_1.DockLocation.CENTER,
showEdges: false,
};
this.onDragEnter = this.onDragEnter.bind(this);
}
/** @internal */
styleFont(style) {
if (this.props.font) {
if (this.selfRef.current) {
if (this.props.font.size) {
this.selfRef.current.style.setProperty("--font-size", this.props.font.size);
}
if (this.props.font.family) {
this.selfRef.current.style.setProperty("--font-family", this.props.font.family);
}
}
if (this.props.font.style) {
style.fontStyle = this.props.font.style;
}
if (this.props.font.weight) {
style.fontWeight = this.props.font.weight;
}
}
return style;
}
/** @internal */
doAction(action) {
if (this.props.onAction !== undefined) {
const outcome = this.props.onAction(action);
if (outcome !== undefined) {
return this.props.model.doAction(outcome);
}
return undefined;
}
else {
return this.props.model.doAction(action);
}
}
/** @internal */
componentDidMount() {
this.updateRect();
this.updateLayoutMetrics();
// need to re-render if size changes
this.currentDocument = this.selfRef.current.ownerDocument;
this.currentWindow = this.currentDocument.defaultView;
this.resizeObserver = new ResizeObserver(entries => {
this.updateRect(entries[0].contentRect);
});
const selfRefCurr = this.selfRef.current;
if (selfRefCurr) {
this.resizeObserver.observe(selfRefCurr);
}
}
/** @internal */
componentDidUpdate() {
this.updateLayoutMetrics();
if (this.props.model !== this.previousModel) {
if (this.previousModel !== undefined) {
this.previousModel._setChangeListener(undefined); // stop listening to old model
}
this.props.model._setChangeListener(this.onModelChange);
this.previousModel = this.props.model;
}
// console.log("Layout time: " + this.layoutTime + "ms Render time: " + (Date.now() - this.start) + "ms");
}
/** @internal */
getCurrentDocument() {
return this.currentDocument;
}
/** @internal */
getDomRect() {
var _a;
return (_a = this.selfRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
}
/** @internal */
getRootDiv() {
return this.selfRef.current;
}
/** @internal */
isSupportsPopout() {
return this.supportsPopout;
}
/** @internal */
isRealtimeResize() {
var _a;
return (_a = this.props.realtimeResize) !== null && _a !== void 0 ? _a : false;
}
/** @internal */
onTabDrag(...args) {
var _a, _b;
return (_b = (_a = this.props).onTabDrag) === null || _b === void 0 ? void 0 : _b.call(_a, ...args);
}
/** @internal */
getPopoutURL() {
return this.popoutURL;
}
/** @internal */
componentWillUnmount() {
var _a;
const selfRefCurr = this.selfRef.current;
if (selfRefCurr) {
(_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.unobserve(selfRefCurr);
}
}
/** @internal */
setEditingTab(tabNode) {
this.setState({ editingTab: tabNode });
}
/** @internal */
getEditingTab() {
return this.state.editingTab;
}
/** @internal */
render() {
// first render will be used to find the size (via selfRef)
if (!this.selfRef.current) {
return (React.createElement("div", { ref: this.selfRef, className: this.getClassName(Types_1.CLASSES.FLEXLAYOUT__LAYOUT) }, this.metricsElements()));
}
this.props.model._setPointerFine(window && window.matchMedia && window.matchMedia("(pointer: fine)").matches);
// this.start = Date.now();
const borderComponents = [];
const tabSetComponents = [];
const floatingWindows = [];
const tabComponents = {};
const splitterComponents = [];
const metrics = {
headerBarSize: this.state.calculatedHeaderBarSize,
tabBarSize: this.state.calculatedTabBarSize,
borderBarSize: this.state.calculatedBorderBarSize
};
this.props.model._setShowHiddenBorder(this.state.showHiddenBorder);
this.centerRect = this.props.model._layout(this.state.rect, metrics);
this.renderBorder(this.props.model.getBorderSet(), borderComponents, tabComponents, floatingWindows, splitterComponents);
this.renderChildren("", this.props.model.getRoot(), tabSetComponents, tabComponents, floatingWindows, splitterComponents);
const nextTopIds = [];
const nextTopIdsMap = {};
// Keep any previous tabs in the same DOM order as before, removing any that have been deleted
for (const t of this.tabIds) {
if (tabComponents[t]) {
nextTopIds.push(t);
nextTopIdsMap[t] = t;
}
}
this.tabIds = nextTopIds;
// Add tabs that have been added to the DOM
for (const t of Object.keys(tabComponents)) {
if (!nextTopIdsMap[t]) {
this.tabIds.push(t);
}
}
const edges = [];
const arrowIcon = this.icons.edgeArrow;
if (this.state.showEdges) {
const r = this.centerRect;
const length = this.edgeRectLength;
const width = this.edgeRectWidth;
const offset = this.edgeRectLength / 2;
const className = this.getClassName(Types_1.CLASSES.FLEXLAYOUT__EDGE_RECT);
const radius = 50;
edges.push(React.createElement("div", { key: "North", style: { top: r.y, left: r.x + r.width / 2 - offset, width: length, height: width, borderBottomLeftRadius: radius, borderBottomRightRadius: radius }, className: className + " " + this.getClassName(Types_1.CLASSES.FLEXLAYOUT__EDGE_RECT_TOP) },
React.createElement("div", { style: { transform: "rotate(180deg)" } }, arrowIcon)));
edges.push(React.createElement("div", { key: "West", style: { top: r.y + r.height / 2 - offset, left: r.x, width: width, height: length, borderTopRightRadius: radius, borderBottomRightRadius: radius }, className: className + " " + this.getClassName(Types_1.CLASSES.FLEXLAYOUT__EDGE_RECT_LEFT) },
React.createElement("div", { style: { transform: "rotate(90deg)" } }, arrowIcon)));
edges.push(React.createElement("div", { key: "South", style: { top: r.y + r.height - width, left: r.x + r.width / 2 - offset, width: length, height: width, borderTopLeftRadius: radius, borderTopRightRadius: radius }, className: className + " " + this.getClassName(Types_1.CLASSES.FLEXLAYOUT__EDGE_RECT_BOTTOM) },
React.createElement("div", null, arrowIcon)));
edges.push(React.createElement("div", { key: "East", style: { top: r.y + r.height / 2 - offset, left: r.x + r.width - width, width: width, height: length, borderTopLeftRadius: radius, borderBottomLeftRadius: radius }, className: className + " " + this.getClassName(Types_1.CLASSES.FLEXLAYOUT__EDGE_RECT_RIGHT) },
React.createElement("div", { style: { transform: "rotate(-90deg)" } }, arrowIcon)));
}
// this.layoutTime = (Date.now() - this.start);
return (React.createElement("div", { ref: this.selfRef, className: this.getClassName(Types_1.CLASSES.FLEXLAYOUT__LAYOUT), onDragEnter: this.props.onExternalDrag ? this.onDragEnter : undefined },
tabSetComponents,
this.tabIds.map((t) => {
return tabComponents[t];
}),
borderComponents,
splitterComponents,
edges,
floatingWindows,
this.metricsElements(),
this.state.portal));
}
/** @internal */
metricsElements() {
// used to measure the tab and border tab sizes
const fontStyle = this.styleFont({ visibility: "hidden" });
return (React.createElement(React.Fragment, null,
React.createElement("div", { key: "findHeaderBarSize", ref: this.findHeaderBarSizeRef, style: fontStyle, className: this.getClassName(Types_1.CLASSES.FLEXLAYOUT__TABSET_HEADER_SIZER) }, "FindHeaderBarSize"),
React.createElement("div", { key: "findTabBarSize", ref: this.findTabBarSizeRef, style: fontStyle, className: this.getClassName(Types_1.CLASSES.FLEXLAYOUT__TABSET_SIZER) }, "FindTabBarSize"),
React.createElement("div", { key: "findBorderBarSize", ref: this.findBorderBarSizeRef, style: fontStyle, className: this.getClassName(Types_1.CLASSES.FLEXLAYOUT__BORDER_SIZER) }, "FindBorderBarSize")));
}
/** @internal */
renderBorder(borderSet, borderComponents, tabComponents, floatingWindows, splitterComponents) {
for (const border of borderSet.getBorders()) {
const borderPath = `/border/${border.getLocation().getName()}`;
if (border.isShowing()) {
borderComponents.push(React.createElement(BorderTabSet_1.BorderTabSet, { key: `border_${border.getLocation().getName()}`, path: borderPath, border: border, layout: this, iconFactory: this.props.iconFactory, titleFactory: this.props.titleFactory, icons: this.icons }));
const drawChildren = border._getDrawChildren();
let i = 0;
let tabCount = 0;
for (const child of drawChildren) {
if (child instanceof SplitterNode_1.SplitterNode) {
let path = borderPath + "/s";
splitterComponents.push(React.createElement(Splitter_1.Splitter, { key: child.getId(), layout: this, node: child, path: path }));
}
else if (child instanceof TabNode_1.TabNode) {
let path = borderPath + "/t" + tabCount++;
if (this.supportsPopout && child.isFloating()) {
const rect = this._getScreenRect(child);
const tabBorderWidth = child._getAttr("borderWidth");
const tabBorderHeight = child._getAttr("borderHeight");
if (rect) {
if (tabBorderWidth !== -1 && border.getLocation().getOrientation() === Orientation_1.Orientation.HORZ) {
rect.width = tabBorderWidth;
}
else if (tabBorderHeight !== -1 && border.getLocation().getOrientation() === Orientation_1.Orientation.VERT) {
rect.height = tabBorderHeight;
}
}
floatingWindows.push(React.createElement(FloatingWindow_1.FloatingWindow, { key: child.getId(), url: this.popoutURL, rect: rect, title: child.getName(), id: child.getId(), onSetWindow: this.onSetWindow, onCloseWindow: this.onCloseWindow },
React.createElement(FloatingWindowTab_1.FloatingWindowTab, { layout: this, node: child, factory: this.props.factory })));
tabComponents[child.getId()] = React.createElement(TabFloating_1.TabFloating, { key: child.getId(), layout: this, path: path, node: child, selected: i === border.getSelected() });
}
else {
tabComponents[child.getId()] = React.createElement(Tab_1.Tab, { key: child.getId(), layout: this, path: path, node: child, selected: i === border.getSelected(), factory: this.props.factory });
}
}
i++;
}
}
}
}
/** @internal */
renderChildren(path, node, tabSetComponents, tabComponents, floatingWindows, splitterComponents) {
const drawChildren = node._getDrawChildren();
let splitterCount = 0;
let tabCount = 0;
let rowCount = 0;
for (const child of drawChildren) {
if (child instanceof SplitterNode_1.SplitterNode) {
const newPath = path + "/s" + (splitterCount++);
splitterComponents.push(React.createElement(Splitter_1.Splitter, { key: child.getId(), layout: this, path: newPath, node: child }));
}
else if (child instanceof TabSetNode_1.TabSetNode) {
const newPath = path + "/ts" + (rowCount++);
tabSetComponents.push(React.createElement(TabSet_1.TabSet, { key: child.getId(), layout: this, path: newPath, node: child, iconFactory: this.props.iconFactory, titleFactory: this.props.titleFactory, icons: this.icons }));
this.renderChildren(newPath, child, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
}
else if (child instanceof TabNode_1.TabNode) {
const newPath = path + "/t" + (tabCount++);
const selectedTab = child.getParent().getChildren()[child.getParent().getSelected()];
if (selectedTab === undefined) {
// this should not happen!
console.warn("undefined selectedTab should not happen");
}
if (this.supportsPopout && child.isFloating()) {
const rect = this._getScreenRect(child);
floatingWindows.push(React.createElement(FloatingWindow_1.FloatingWindow, { key: child.getId(), url: this.popoutURL, rect: rect, title: child.getName(), id: child.getId(), onSetWindow: this.onSetWindow, onCloseWindow: this.onCloseWindow },
React.createElement(FloatingWindowTab_1.FloatingWindowTab, { layout: this, node: child, factory: this.props.factory })));
tabComponents[child.getId()] = React.createElement(TabFloating_1.TabFloating, { key: child.getId(), layout: this, path: newPath, node: child, selected: child === selectedTab });
}
else {
tabComponents[child.getId()] = React.createElement(Tab_1.Tab, { key: child.getId(), layout: this, path: newPath, node: child, selected: child === selectedTab, factory: this.props.factory });
}
}
else {
// is row
const newPath = path + ((child.getOrientation() === Orientation_1.Orientation.HORZ) ? "/r" : "/c") + (rowCount++);
this.renderChildren(newPath, child, tabSetComponents, tabComponents, floatingWindows, splitterComponents);
}
}
}
/** @internal */
_getScreenRect(node) {
var _a;
const rect = node.getRect().clone();
const bodyRect = (_a = this.selfRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
if (!bodyRect) {
return null;
}
const navHeight = Math.min(80, this.currentWindow.outerHeight - this.currentWindow.innerHeight);
const navWidth = Math.min(80, this.currentWindow.outerWidth - this.currentWindow.innerWidth);
rect.x = rect.x + bodyRect.x + this.currentWindow.screenX + navWidth;
rect.y = rect.y + bodyRect.y + this.currentWindow.screenY + navHeight;
return rect;
}
/**
* Adds a new tab to the given tabset
* @param tabsetId the id of the tabset where the new tab will be added
* @param json the json for the new tab node
* @returns the added tab node or undefined
*/
addTabToTabSet(tabsetId, json) {
const tabsetNode = this.props.model.getNodeById(tabsetId);
if (tabsetNode !== undefined) {
const node = this.doAction(Actions_1.Actions.addNode(json, tabsetId, DockLocation_1.DockLocation.CENTER, -1));
return node;
}
return undefined;
}
/**
* Adds a new tab to the active tabset (if there is one)
* @param json the json for the new tab node
* @returns the added tab node or undefined
*/
addTabToActiveTabSet(json) {
const tabsetNode = this.props.model.getActiveTabset();
if (tabsetNode !== undefined) {
const node = this.doAction(Actions_1.Actions.addNode(json, tabsetNode.getId(), DockLocation_1.DockLocation.CENTER, -1));
return node;
}
return undefined;
}
/**
* Adds a new tab by dragging a labeled panel to the drop location, dragging starts immediatelly
* @param dragText the text to show on the drag panel
* @param json the json for the new tab node
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
*/
addTabWithDragAndDrop(dragText, json, onDrop) {
this.fnNewNodeDropped = onDrop;
this.newTabJson = json;
this.dragStart(undefined, dragText, TabNode_1.TabNode._fromJson(json, this.props.model, false), true, undefined, undefined);
}
/**
* Move a tab/tabset using drag and drop
* @param node the tab or tabset to drag
* @param dragText the text to show on the drag panel
*/
moveTabWithDragAndDrop(node, dragText) {
this.dragStart(undefined, dragText, node, true, undefined, undefined);
}
/**
* Adds a new tab by dragging a labeled panel to the drop location, dragging starts when you
* mouse down on the panel
*
* @param dragText the text to show on the drag panel
* @param json the json for the new tab node
* @param onDrop a callback to call when the drag is complete (node and event will be undefined if the drag was cancelled)
*/
addTabWithDragAndDropIndirect(dragText, json, onDrop) {
this.fnNewNodeDropped = onDrop;
this.newTabJson = json;
DragDrop_1.DragDrop.instance.addGlass(this.onCancelAdd);
this.dragDivText = dragText;
this.dragDiv = this.currentDocument.createElement("div");
this.dragDiv.className = this.getClassName(Types_1.CLASSES.FLEXLAYOUT__DRAG_RECT);
this.dragDiv.addEventListener("mousedown", this.onDragDivMouseDown);
this.dragDiv.addEventListener("touchstart", this.onDragDivMouseDown, { passive: false });
this.dragRectRender(this.dragDivText, undefined, this.newTabJson, () => {
if (this.dragDiv) {
// now it's been rendered into the dom it can be centered
this.dragDiv.style.visibility = "visible";
const domRect = this.dragDiv.getBoundingClientRect();
const r = new Rect_1.Rect(0, 0, domRect === null || domRect === void 0 ? void 0 : domRect.width, domRect === null || domRect === void 0 ? void 0 : domRect.height);
r.centerInRect(this.state.rect);
this.dragDiv.setAttribute("data-layout-path", "/drag-rectangle");
this.dragDiv.style.left = r.x + "px";
this.dragDiv.style.top = r.y + "px";
}
});
const rootdiv = this.selfRef.current;
rootdiv.appendChild(this.dragDiv);
}
/** @internal */
handleCustomTabDrag(dropInfo, pos, event) {
var _a, _b, _c;
let invalidated = (_a = this.customDrop) === null || _a === void 0 ? void 0 : _a.invalidated;
const currentCallback = (_b = this.customDrop) === null || _b === void 0 ? void 0 : _b.callback;
this.customDrop = undefined;
const dragging = this.newTabJson || (this.dragNode instanceof TabNode_1.TabNode ? this.dragNode : undefined);
if (dragging && (dropInfo.node instanceof TabSetNode_1.TabSetNode || dropInfo.node instanceof BorderNode_1.BorderNode) && dropInfo.index === -1) {
const selected = dropInfo.node.getSelectedNode();
const tabRect = selected === null || selected === void 0 ? void 0 : selected.getRect();
if (selected && (tabRect === null || tabRect === void 0 ? void 0 : tabRect.contains(pos.x, pos.y))) {
let customDrop = undefined;
try {
const dest = this.onTabDrag(dragging, selected, pos.x - tabRect.x, pos.y - tabRect.y, dropInfo.location, () => this.onDragMove(event));
if (dest) {
customDrop = {
rect: new Rect_1.Rect(dest.x + tabRect.x, dest.y + tabRect.y, dest.width, dest.height),
callback: dest.callback,
invalidated: dest.invalidated,
dragging: dragging,
over: selected,
x: pos.x - tabRect.x,
y: pos.y - tabRect.y,
location: dropInfo.location,
cursor: dest.cursor
};
}
}
catch (e) {
console.error(e);
}
if ((customDrop === null || customDrop === void 0 ? void 0 : customDrop.callback) === currentCallback) {
invalidated = undefined;
}
this.customDrop = customDrop;
}
}
this.dropInfo = dropInfo;
if (this.outlineDiv) {
this.outlineDiv.className = this.getClassName(this.customDrop ? Types_1.CLASSES.FLEXLAYOUT__OUTLINE_RECT : dropInfo.className);
if (this.customDrop) {
this.customDrop.rect.positionElement(this.outlineDiv);
}
else {
dropInfo.rect.positionElement(this.outlineDiv);
}
}
DragDrop_1.DragDrop.instance.setGlassCursorOverride((_c = this.customDrop) === null || _c === void 0 ? void 0 : _c.cursor);
if (this.outlineDiv) {
this.outlineDiv.style.visibility = "visible";
}
try {
invalidated === null || invalidated === void 0 ? void 0 : invalidated();
}
catch (e) {
console.error(e);
}
}
/** @internal */
onDragEnter(event) {
// DragDrop keeps track of number of dragenters minus the number of
// dragleaves. Only start a new drag if there isn't one already.
if (DragDrop_1.DragDrop.instance.isDragging())
return;
const drag = this.props.onExternalDrag(event);
if (drag) {
// Mimic addTabWithDragAndDrop, but pass in DragEvent
this.fnNewNodeDropped = drag.onDrop;
this.newTabJson = drag.json;
this.dragStart(event, drag.dragText, TabNode_1.TabNode._fromJson(drag.json, this.props.model, false), true, undefined, undefined);
}
}
/** @internal */
checkForBorderToShow(x, y) {
const r = this.props.model._getOuterInnerRects().outer;
const c = r.getCenter();
const margin = this.edgeRectWidth;
const offset = this.edgeRectLength / 2;
let overEdge = false;
if (this.props.model.isEnableEdgeDock() && this.state.showHiddenBorder === DockLocation_1.DockLocation.CENTER) {
if ((y > c.y - offset && y < c.y + offset) ||
(x > c.x - offset && x < c.x + offset)) {
overEdge = true;
}
}
let location = DockLocation_1.DockLocation.CENTER;
if (!overEdge) {
if (x <= r.x + margin) {
location = DockLocation_1.DockLocation.LEFT;
}
else if (x >= r.getRight() - margin) {
location = DockLocation_1.DockLocation.RIGHT;
}
else if (y <= r.y + margin) {
location = DockLocation_1.DockLocation.TOP;
}
else if (y >= r.getBottom() - margin) {
location = DockLocation_1.DockLocation.BOTTOM;
}
}
if (location !== this.state.showHiddenBorder) {
this.setState({ showHiddenBorder: location });
}
}
/** @internal */
maximize(tabsetNode) {
this.doAction(Actions_1.Actions.maximizeToggle(tabsetNode.getId()));
}
/** @internal */
customizeTab(tabNode, renderValues) {
if (this.props.onRenderTab) {
this.props.onRenderTab(tabNode, renderValues);
}
}
/** @internal */
customizeTabSet(tabSetNode, renderValues) {
if (this.props.onRenderTabSet) {
this.props.onRenderTabSet(tabSetNode, renderValues);
}
}
/** @internal */
i18nName(id, param) {
let message;
if (this.props.i18nMapper) {
message = this.props.i18nMapper(id, param);
}
if (message === undefined) {
message = id + (param === undefined ? "" : param);
}
return message;
}
/** @internal */
getOnRenderFloatingTabPlaceholder() {
return this.props.onRenderFloatingTabPlaceholder;
}
/** @internal */
getShowOverflowMenu() {
return this.props.onShowOverflowMenu;
}
/** @internal */
getTabSetPlaceHolderCallback() {
return this.props.onTabSetPlaceHolder;
}
/** @internal */
showContextMenu(node, event) {
if (this.props.onContextMenu) {
this.props.onContextMenu(node, event);
}
}
/** @internal */
auxMouseClick(node, event) {
if (this.props.onAuxMouseClick) {
this.props.onAuxMouseClick(node, event);
}
}
}
exports.Layout = Layout;
/** @internal */
const DragRectRenderWrapper = (props) => {
React.useEffect(() => {
var _a;
(_a = props.onRendered) === null || _a === void 0 ? void 0 : _a.call(props);
}, [props]);
return (React.createElement(React.Fragment, null, props.children));
};
//# sourceMappingURL=Layout.js.map