UNPKG

@lonli-lokli/react-mosaic-component

Version:
426 lines (424 loc) 16.9 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // libs/react-mosaic-component/src/lib/MosaicWindow.tsx var MosaicWindow_exports = {}; __export(MosaicWindow_exports, { InternalMosaicWindow: () => InternalMosaicWindow, MosaicWindow: () => MosaicWindow }); module.exports = __toCommonJS(MosaicWindow_exports); var import_classnames = __toESM(require("classnames"), 1); var import_lodash_es = require("lodash-es"); var import_react = __toESM(require("react"), 1); var import_react_dnd = require("react-dnd"); var import_defaultToolbarControls = require("./buttons/defaultToolbarControls.cjs"); var import_Separator = require("./buttons/Separator.cjs"); var import_contextTypes = require("./contextTypes.cjs"); var import_internalTypes = require("./internalTypes.cjs"); var import_MosaicDropTarget = require("./MosaicDropTarget.cjs"); var import_types = require("./types.cjs"); var import_mosaicUpdates = require("./util/mosaicUpdates.cjs"); var import_mosaicUtilities = require("./util/mosaicUtilities.cjs"); var import_OptionalBlueprint = require("./util/OptionalBlueprint.cjs"); var InternalMosaicWindow = class extends import_react.default.Component { static defaultProps = { additionalControlButtonText: "More", draggable: true, renderPreview: ({ title }) => /* @__PURE__ */ import_react.default.createElement("div", { className: "mosaic-preview" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mosaic-window-toolbar" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mosaic-window-title" }, title)), /* @__PURE__ */ import_react.default.createElement("div", { className: "mosaic-window-body" }, /* @__PURE__ */ import_react.default.createElement("h4", null, title), /* @__PURE__ */ import_react.default.createElement( import_OptionalBlueprint.OptionalBlueprint.Icon, { className: "default-preview-icon", size: "large", icon: "APPLICATION" } ))), renderToolbar: null }; static contextType = import_contextTypes.MosaicContext; state = { additionalControlsOpen: false }; rootElement = null; render() { const { className, isOver, renderPreview, additionalControls, connectDropTarget, connectDragPreview, draggedMosaicId, disableAdditionalControlsOverlay } = this.props; const root = this.context.mosaicActions.getRoot(); const parentNode = (0, import_mosaicUtilities.getParentNode)(root, this.props.path); const isInTabContainer = (0, import_mosaicUtilities.isTabsNode)(parentNode); return /* @__PURE__ */ import_react.default.createElement(import_contextTypes.MosaicWindowContext.Provider, { value: this.childContext }, connectDropTarget( /* @__PURE__ */ import_react.default.createElement( "div", { className: (0, import_classnames.default)( "mosaic-window", "mosaic-drop-target", className, { "drop-target-hover": isOver && draggedMosaicId === this.context.mosaicId, "additional-controls-open": this.state.additionalControlsOpen } ), ref: (element) => { this.rootElement = element; } }, this.renderToolbar(), /* @__PURE__ */ import_react.default.createElement("div", { className: "mosaic-window-body" }, this.props.children), !disableAdditionalControlsOverlay && /* @__PURE__ */ import_react.default.createElement( "div", { className: "mosaic-window-body-overlay", onClick: () => { this.setAdditionalControlsOpen(false); } } ), /* @__PURE__ */ import_react.default.createElement("div", { className: "mosaic-window-additional-actions-bar" }, additionalControls), connectDragPreview(renderPreview(this.props)), !isInTabContainer && /* @__PURE__ */ import_react.default.createElement("div", { className: (0, import_classnames.default)("drop-target-container", {}) }, (0, import_lodash_es.values)(import_internalTypes.MosaicDropTargetPosition).map( this.renderDropTarget )) ) )); } getToolbarControls() { const { toolbarControls, createNode, path } = this.props; if (toolbarControls) { return toolbarControls; } const root = this.context.mosaicActions.getRoot(); const parentNode = (0, import_mosaicUtilities.getParentNode)(root, path); if ((0, import_mosaicUtilities.isTabsNode)(parentNode)) { return import_defaultToolbarControls.DEFAULT_PANEL_CONTROLS_IN_TABS; } else if (createNode) { return import_defaultToolbarControls.DEFAULT_PANEL_CONTROLS_WITH_CREATION; } else { return import_defaultToolbarControls.DEFAULT_PANEL_CONTROLS_WITHOUT_CREATION; } } renderToolbar() { const { title, draggable, additionalControls, additionalControlButtonText, path, renderToolbar } = this.props; const { additionalControlsOpen } = this.state; const toolbarControls = this.getToolbarControls(); const root = this.context.mosaicActions.getRoot(); const parentNode = (0, import_mosaicUtilities.getParentNode)(root, path); const isDragAllowed = draggable && path.length > 0 && !(0, import_mosaicUtilities.isTabsNode)(parentNode); const connectIfDraggable = isDragAllowed ? this.props.connectDragSource : (el) => el; if (renderToolbar) { const connectedToolbar = connectIfDraggable( renderToolbar(this.props, draggable) ); return /* @__PURE__ */ import_react.default.createElement( "div", { className: (0, import_classnames.default)("mosaic-window-toolbar", { draggable: isDragAllowed }) }, connectedToolbar ); } const titleDiv = connectIfDraggable( /* @__PURE__ */ import_react.default.createElement("div", { title, className: "mosaic-window-title" }, title) ); const hasAdditionalControls = !!additionalControls; return /* @__PURE__ */ import_react.default.createElement( "div", { className: (0, import_classnames.default)("mosaic-window-toolbar", { draggable: isDragAllowed }) }, titleDiv, /* @__PURE__ */ import_react.default.createElement( "div", { className: (0, import_classnames.default)( "mosaic-window-controls", import_OptionalBlueprint.OptionalBlueprint.getClasses("BUTTON_GROUP") ) }, hasAdditionalControls && /* @__PURE__ */ import_react.default.createElement( "button", { onClick: () => this.setAdditionalControlsOpen(!additionalControlsOpen), className: (0, import_classnames.default)( import_OptionalBlueprint.OptionalBlueprint.getClasses( this.context.blueprintNamespace, "BUTTON", "MINIMAL" ), import_OptionalBlueprint.OptionalBlueprint.getIconClass( this.context.blueprintNamespace, "MORE" ), { [import_OptionalBlueprint.OptionalBlueprint.getClasses( this.context.blueprintNamespace, "ACTIVE" )]: additionalControlsOpen } ) }, /* @__PURE__ */ import_react.default.createElement("span", { className: "control-text" }, additionalControlButtonText) ), hasAdditionalControls && /* @__PURE__ */ import_react.default.createElement(import_Separator.Separator, null), toolbarControls ) ); } renderDropTarget = (position) => { const { path } = this.props; return /* @__PURE__ */ import_react.default.createElement(import_MosaicDropTarget.MosaicDropTarget, { position, path, key: position }); }; checkCreateNode() { if (this.props.createNode == null) { throw new Error("Operation invalid unless `createNode` is defined"); } } split = (...args) => { this.checkCreateNode(); const { path } = this.props; const { mosaicActions } = this.context; const root = mosaicActions.getRoot(); const parentNode = (0, import_mosaicUtilities.getParentNode)(root, path); if ((0, import_mosaicUtilities.isTabsNode)(parentNode)) { if (process.env.NODE_ENV !== "production") { console.warn( "Mosaic: Cannot split a panel that is already inside a tab group." ); } return Promise.resolve(); } const direction = this.rootElement.offsetWidth > this.rootElement.offsetHeight ? "row" : "column"; const currentNode = (0, import_mosaicUtilities.getNodeAtPath)(root, path); if (!currentNode) { throw new Error("Current node could not be found."); } return Promise.resolve(mosaicActions.createNode(...args)).then( (second) => { const newSplitNode = { type: "split", direction, children: [currentNode, second], splitPercentages: [50, 50] }; mosaicActions.replaceWith(path, newSplitNode); } ); }; addTab = (...args) => { this.checkCreateNode(); const { path, createNode } = this.props; const { mosaicActions } = this.context; const root = mosaicActions.getRoot(); const parentNode = (0, import_mosaicUtilities.getParentNode)(root, path); if ((0, import_mosaicUtilities.isTabsNode)(parentNode)) { if (process.env.NODE_ENV !== "production") { console.warn( `Mosaic: "Add Tab" from a panel toolbar is disabled when already in a tab group. Use the tab bar's "Add" button instead.` ); } return Promise.resolve(); } const currentNode = (0, import_mosaicUtilities.getNodeAtPath)(root, path); if (!currentNode) { throw new Error("Current node could not be found."); } return Promise.resolve(createNode(...args)).then((newNode) => { if (typeof newNode !== "string" && typeof newNode !== "number") { console.error( "createNode() for addTab should return a MosaicKey (string or number)." ); return; } if (typeof currentNode === "object" && currentNode.type === "tabs") { const newTabsNode = { type: "tabs", tabs: [...currentNode.tabs, newNode], activeTabIndex: currentNode.tabs.length // Make the new tab active }; mosaicActions.replaceWith(path, newTabsNode); } else { if (typeof currentNode !== "string" && typeof currentNode !== "number") { console.error("Cannot create a tab group from a non-panel node."); return; } const newTabsNode = { type: "tabs", tabs: [currentNode, newNode], activeTabIndex: 1 // Make the new tab active }; mosaicActions.replaceWith(path, newTabsNode); } }); }; getRoot = () => { const { mosaicActions } = this.context; return mosaicActions.getRoot(); }; swap = (...args) => { this.checkCreateNode(); const { mosaicActions } = this.context; const { path, createNode } = this.props; return Promise.resolve(createNode(...args)).then( (node) => mosaicActions.replaceWith(path, node) ); }; setAdditionalControlsOpen = (additionalControlsOpenOption) => { const additionalControlsOpen = additionalControlsOpenOption === "toggle" ? !this.state.additionalControlsOpen : additionalControlsOpenOption; this.setState({ additionalControlsOpen }); this.props.onAdditionalControlsToggle?.(additionalControlsOpen); }; getPath = () => this.props.path; connectDragSource = (connectedElements) => { const { connectDragSource } = this.props; return connectDragSource(connectedElements); }; childContext = { // @ts-expect-error we need it blueprintNamespace: this.context.blueprintNamespace, mosaicWindowActions: { split: this.split, addTab: this.addTab, getRoot: this.getRoot, replaceWithNew: this.swap, setAdditionalControlsOpen: this.setAdditionalControlsOpen, getPath: this.getPath, connectDragSource: this.connectDragSource } }; }; function ConnectedInternalMosaicWindow(props) { const { mosaicActions, mosaicId } = (0, import_react.useContext)(import_contextTypes.MosaicContext); const [, connectDragSource, connectDragPreview] = (0, import_react_dnd.useDrag)({ type: import_types.MosaicDragType.WINDOW, item: () => { if (props.onDragStart) { props.onDragStart(); } const hideTimer = window.setTimeout(() => { mosaicActions.hide(props.path, true); }, 50); return { mosaicId, hideTimer }; }, end: (item, monitor) => { window.clearTimeout(item.hideTimer); const ownPath = props.path; const dropResult = monitor.getDropResult() || {}; const { position, path: destinationPath } = dropResult; const dropped = destinationPath != null; const isSelfDrop = dropped && (0, import_lodash_es.isEqual)(destinationPath, ownPath); const isTabContainerSelfDrop = dropped && (() => { const root = mosaicActions.getRoot(); const ownParentPath = ownPath.slice(0, -1); const ownParentNode = (0, import_mosaicUtilities.getNodeAtPath)(root, ownParentPath); const destinationNode = (0, import_mosaicUtilities.getNodeAtPath)(root, destinationPath); return (0, import_mosaicUtilities.isTabsNode)(ownParentNode) && (0, import_mosaicUtilities.isTabsNode)(destinationNode) && (0, import_lodash_es.isEqual)(ownParentPath, destinationPath); })(); const isChildDrop = dropped && destinationPath.length > ownPath.length && (0, import_lodash_es.isEqual)( ownPath, (0, import_lodash_es.drop)(destinationPath, destinationPath.length - ownPath.length) ); if (dropped && !isSelfDrop && !isChildDrop && !isTabContainerSelfDrop) { const updates = (0, import_mosaicUpdates.createDragToUpdates)( mosaicActions.getRoot(), ownPath, destinationPath, dropResult.tabReorderIndex !== void 0 ? { type: "tab-reorder", insertIndex: dropResult.tabReorderIndex } : position === void 0 ? { type: "tab-container" } : { type: "split", position } ); mosaicActions.updateTree(updates, { shouldNormalize: true }); if (props.onDragEnd) { props.onDragEnd("drop"); } } else { mosaicActions.show(ownPath, true); if (props.onDragEnd) { props.onDragEnd("reset"); } } } }); const [{ isOver, draggedMosaicId }, connectDropTarget] = (0, import_react_dnd.useDrop)({ accept: import_types.MosaicDragType.WINDOW, collect: (monitor) => ({ isOver: monitor.isOver(), draggedMosaicId: monitor.getItem()?.mosaicId }) }); return /* @__PURE__ */ import_react.default.createElement( InternalMosaicWindow, { ...props, connectDragPreview, connectDragSource, connectDropTarget, isOver, draggedMosaicId } ); } var MosaicWindow = class extends import_react.default.PureComponent { render() { return /* @__PURE__ */ import_react.default.createElement( ConnectedInternalMosaicWindow, { ...this.props } ); } };