UNPKG

@lonli-lokli/react-mosaic-component

Version:
450 lines (448 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/util/mosaicUpdates.ts var mosaicUpdates_exports = {}; __export(mosaicUpdates_exports, { buildSpecFromUpdate: () => buildSpecFromUpdate, convertToDropInfo: () => convertToDropInfo, createAddChildUpdate: () => createAddChildUpdate, createDragToUpdates: () => createDragToUpdates, createExpandUpdate: () => createExpandUpdate, createHideUpdate: () => createHideUpdate, createRemoveUpdate: () => createRemoveUpdate, updateTree: () => updateTree }); module.exports = __toCommonJS(mosaicUpdates_exports); var import_immutability_helper = __toESM(require("immutability-helper"), 1); var import_lodash_es = require("lodash-es"); var import_internalTypes = require("../internalTypes.cjs"); var import_mosaicUtilities = require("./mosaicUtilities.cjs"); var import_assertNever = require("./assertNever.cjs"); function buildSpecFromUpdate(mosaicUpdate) { if (mosaicUpdate.path.length > 0) { return (0, import_lodash_es.set)({}, mosaicUpdate.path, mosaicUpdate.spec); } else { return mosaicUpdate.spec; } } function buildSpecFromPath(tree, path, spec) { return path.reduceRight((acc, pathSegment, index) => { const parentPath = path.slice(0, index); const parentNode = (0, import_mosaicUtilities.getNodeAtPath)(tree, parentPath); let property = "children"; if (parentNode && typeof parentNode === "object" && "type" in parentNode && parentNode.type === "tabs") { property = "tabs"; } return { [property]: { [pathSegment]: acc } }; }, spec); } function updateTree(tree, updates) { if (updates.length === 0) { return tree; } let currentTree = tree; for (const { path, spec } of updates) { if (path.length === 0) { currentTree = (0, import_immutability_helper.default)(currentTree, spec); } else { const nestedSpec = buildSpecFromPath(currentTree, path, spec); currentTree = (0, import_immutability_helper.default)(currentTree, nestedSpec); } } return currentTree; } function createRemoveUpdate(root, path) { if (path.length === 0) { throw new Error("Cannot remove root node"); } const parentInfo = (0, import_mosaicUtilities.getParentAndChildIndex)(root, path); if (!parentInfo) { throw new Error("Cannot find parent for removal"); } const { parent, childIndex } = parentInfo; if ((0, import_mosaicUtilities.isSplitNode)(parent)) { if (parent.children.length === 2) { const siblingIndex = childIndex === 0 ? 1 : 0; const sibling = parent.children[siblingIndex]; return { path: (0, import_lodash_es.dropRight)(path), spec: { $set: sibling } }; } else { const newChildren = parent.children.filter( (_, index) => index !== childIndex ); const oldPercentages = parent.splitPercentages || Array(parent.children.length).fill(100 / parent.children.length); const removedPercentage = oldPercentages[childIndex]; const redistributeAmount = removedPercentage / newChildren.length; const newPercentages = oldPercentages.filter((_, index) => index !== childIndex).map((percentage) => percentage + redistributeAmount); return { path: (0, import_lodash_es.dropRight)(path), spec: { children: { $set: newChildren }, splitPercentages: { $set: newPercentages } } }; } } else if ((0, import_mosaicUtilities.isTabsNode)(parent)) { const newTabs = parent.tabs.filter((_, index) => index !== childIndex); if (newTabs.length === 0) { throw new Error("Cannot remove last tab from tabs node"); } let newActiveTabIndex = parent.activeTabIndex; if (childIndex === parent.activeTabIndex) { newActiveTabIndex = childIndex > 0 ? childIndex - 1 : 0; } else if (childIndex < parent.activeTabIndex) { newActiveTabIndex = parent.activeTabIndex - 1; } return { path: (0, import_lodash_es.dropRight)(path), spec: { tabs: { $set: newTabs }, activeTabIndex: { $set: newActiveTabIndex } } }; } throw new Error("Invalid parent node type for removal"); } function createDragToUpdates(root, sourcePath, destinationPath, dropInfo) { if ((0, import_lodash_es.isEqual)(sourcePath, destinationPath)) { return []; } const destinationNode = (0, import_mosaicUtilities.getAndAssertNodeAtPathExists)(root, destinationPath); const sourceNode = (0, import_mosaicUtilities.getAndAssertNodeAtPathExists)(root, sourcePath); switch (dropInfo.type) { case "tab-container": case "tab-reorder": { if (!(0, import_mosaicUtilities.isTabsNode)(destinationNode)) { throw new Error( `Expected tab container at destination path ${destinationPath.join(", ")}, but found: ${JSON.stringify(destinationNode)}` ); } const updates = []; updates.push(createRemoveUpdate(root, sourcePath)); let adjustedDestinationPath = adjustPathAfterRemoval( sourcePath, destinationPath ); const rootAfterRemoval = updateTree(root, updates); let destinationAfterRemoval = (0, import_mosaicUtilities.getNodeAtPath)( rootAfterRemoval, adjustedDestinationPath ); if (!destinationAfterRemoval || !(0, import_mosaicUtilities.isTabsNode)(destinationAfterRemoval)) { let findTabsNodeInTree2 = function(node, currentPath = []) { if (node && typeof node === "object") { if ((0, import_mosaicUtilities.isTabsNode)(node) && (0, import_mosaicUtilities.isTabsNode)(destinationNode) && node.tabs && JSON.stringify(node.tabs) === JSON.stringify(destinationNode.tabs)) { return { node, path: currentPath }; } if ((0, import_mosaicUtilities.isSplitNode)(node)) { for (let i = 0; i < node.children.length; i++) { const result2 = findTabsNodeInTree2(node.children[i], [ ...currentPath, i ]); if (result2) { return result2; } } } } return null; }; var findTabsNodeInTree = findTabsNodeInTree2; const result = findTabsNodeInTree2(rootAfterRemoval); if (result) { adjustedDestinationPath = result.path; destinationAfterRemoval = result.node; } } if (!destinationAfterRemoval || !(0, import_mosaicUtilities.isTabsNode)(destinationAfterRemoval)) { throw new Error( `Could not find tabs container after removal. Original path: ${destinationPath.join(", ")}, Adjusted path: ${adjustedDestinationPath.join(", ")}` ); } if (dropInfo.type === "tab-reorder") { const newTabs = [...destinationAfterRemoval.tabs]; newTabs.splice(dropInfo.insertIndex, 0, sourceNode); updates.push({ path: adjustedDestinationPath, spec: { tabs: { $set: newTabs }, activeTabIndex: { $set: dropInfo.insertIndex } // Make the inserted tab active } }); } else { updates.push({ path: adjustedDestinationPath, spec: { tabs: { $push: [sourceNode] }, activeTabIndex: { $set: destinationAfterRemoval.tabs.length } } }); } return updates; } case "split": { const updates = []; updates.push(createRemoveUpdate(root, sourcePath)); let adjustedDestinationPath = adjustPathAfterRemoval( sourcePath, destinationPath ); const rootAfterRemoval = updateTree(root, updates); let destinationAfterRemoval = (0, import_mosaicUtilities.getNodeAtPath)( rootAfterRemoval, adjustedDestinationPath ); if (!destinationAfterRemoval) { for (let i = adjustedDestinationPath.length - 1; i >= 0; i--) { const shorterPath = adjustedDestinationPath.slice(0, i); const nodeAtShorterPath = (0, import_mosaicUtilities.getNodeAtPath)( rootAfterRemoval, shorterPath ); if (nodeAtShorterPath) { adjustedDestinationPath = shorterPath; destinationAfterRemoval = nodeAtShorterPath; break; } } if (!destinationAfterRemoval) { adjustedDestinationPath = []; destinationAfterRemoval = rootAfterRemoval; } } if (!destinationAfterRemoval) { console.error( "Could not find valid destination after removing source from:", sourcePath, "original destination was:", destinationPath ); throw new Error( `Could not find valid destination after removing source from [${sourcePath.join(", ")}]` ); } let direction = "column"; if (dropInfo.position === import_internalTypes.MosaicDropTargetPosition.LEFT || dropInfo.position === import_internalTypes.MosaicDropTargetPosition.RIGHT) { direction = "row"; } if ((0, import_mosaicUtilities.isSplitNode)(destinationAfterRemoval) && destinationAfterRemoval.direction === direction) { const insertIndex = dropInfo.position === import_internalTypes.MosaicDropTargetPosition.LEFT || dropInfo.position === import_internalTypes.MosaicDropTargetPosition.TOP ? 0 : destinationAfterRemoval.children.length; updates.push( createAddChildUpdate( adjustedDestinationPath, sourceNode, insertIndex ) ); } else { let first; let second; if (dropInfo.position === import_internalTypes.MosaicDropTargetPosition.LEFT || dropInfo.position === import_internalTypes.MosaicDropTargetPosition.TOP) { first = sourceNode; second = destinationAfterRemoval; } else { first = destinationAfterRemoval; second = sourceNode; } updates.push({ path: adjustedDestinationPath, spec: { $set: { type: "split", direction, children: [first, second], splitPercentages: [50, 50] } } }); } return updates; } default: (0, import_assertNever.assertNever)(dropInfo); } } function adjustPathAfterRemoval(removedPath, targetPath) { if (removedPath.length === 0 || targetPath.length === 0) { return targetPath; } if ((0, import_lodash_es.isEqual)(removedPath, targetPath)) { return []; } const removedParentPath = removedPath.slice(0, -1); const targetParentPath = targetPath.slice(0, -1); if (!(0, import_lodash_es.isEqual)(removedParentPath, targetParentPath)) { return targetPath; } const removedIndex = removedPath[removedPath.length - 1]; const targetIndex = targetPath[targetPath.length - 1]; if (typeof removedIndex === "number" && typeof targetIndex === "number") { if (targetIndex > removedIndex) { const adjustedPath = [...targetPath]; adjustedPath[adjustedPath.length - 1] = targetIndex - 1; return adjustedPath; } } return targetPath; } function createHideUpdate(root, path) { if (path.length === 0) { throw new Error("Cannot hide root node"); } const parentInfo = (0, import_mosaicUtilities.getParentAndChildIndex)(root, path); if (!parentInfo) { throw new Error("Cannot hide node: parent not found"); } const { parent, childIndex } = parentInfo; if ((0, import_mosaicUtilities.isSplitNode)(parent)) { const currentPercentages = parent.splitPercentages || Array(parent.children.length).fill(100 / parent.children.length); const hidePercentage = currentPercentages[childIndex]; const otherIndices = currentPercentages.map((_, i) => i).filter((i) => i !== childIndex); if (otherIndices.length === 0) { throw new Error("Cannot hide the only child of a split node"); } const redistributeAmount = hidePercentage / otherIndices.length; const newPercentages = currentPercentages.map((percentage, index) => { if (index === childIndex) { return 0; } else { return percentage + redistributeAmount; } }); return { path: (0, import_lodash_es.dropRight)(path), spec: { splitPercentages: { $set: newPercentages } } }; } else if ((0, import_mosaicUtilities.isTabsNode)(parent)) { if (parent.tabs.length <= 1) { throw new Error("Cannot hide the only tab in a tab container"); } let newActiveTabIndex = parent.activeTabIndex; if (childIndex === parent.activeTabIndex) { newActiveTabIndex = childIndex > 0 ? childIndex - 1 : childIndex + 1; } return { path: (0, import_lodash_es.dropRight)(path), spec: { activeTabIndex: { $set: newActiveTabIndex } } }; } throw new Error("Cannot hide node: parent is not a split or tabs node"); } function createExpandUpdate(path, percentage) { let spec = {}; for (let i = path.length - 1; i >= 0; i--) { const childIndex = typeof path[i] === "number" ? path[i] : Number(path[i]); spec = { splitPercentages: { $apply: (currentPercentages) => { if (!currentPercentages) { throw new Error( "Cannot expand: parent node has no splitPercentages defined" ); } const newPercentages = [...currentPercentages]; const targetChildPercentage = percentage; const otherChildrenCount = newPercentages.length - 1; if (otherChildrenCount === 0) { return [100]; } const remainingPercentage = 100 - targetChildPercentage; const otherChildPercentage = remainingPercentage / otherChildrenCount; for (let j = 0; j < newPercentages.length; j++) { if (j === childIndex) { newPercentages[j] = targetChildPercentage; } else { newPercentages[j] = otherChildPercentage; } } return newPercentages; } }, children: { [childIndex]: spec } }; } return { spec, path: [] }; } function createAddChildUpdate(path, newChild, insertIndex) { return { path, spec: { children: { $apply: (currentChildren) => { const newChildren = [...currentChildren]; const index = insertIndex !== void 0 ? insertIndex : newChildren.length; newChildren.splice(index, 0, newChild); return newChildren; } }, splitPercentages: { $apply: (currentPercentages) => { const currentLength = currentPercentages ? currentPercentages.length : 0; const newLength = currentLength + 1; const equalPercentage = 100 / newLength; if (!currentPercentages) { return Array(newLength).fill(equalPercentage); } const newPercentages = Array(newLength).fill(equalPercentage); return newPercentages; } } } }; } function convertToDropInfo(position, tabReorderIndex) { if (tabReorderIndex !== void 0) { return { type: "tab-reorder", insertIndex: tabReorderIndex }; } else if (position === void 0) { return { type: "tab-container" }; } else { return { type: "split", position }; } }