UNPKG

@mentor-apm/react-sortable-tree

Version:

Drag-and-drop sortable component for nested data and hierarchies

1,565 lines (1,554 loc) 61 kB
import React, { Children, cloneElement, Component } from 'react'; import withScrolling, { createScrollingComponent, createVerticalStrength, createHorizontalStrength } from '@nosferatu500/react-dnd-scrollzone'; import isEqual from 'lodash.isequal'; import { DragSource, DropTarget, DndContext, DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { Virtuoso } from 'react-virtuoso'; const defaultGetNodeKey = ({ treeIndex }) => treeIndex; const getReactElementText = (parent) => { if (typeof parent === "string") { return parent; } if (parent === void 0 || typeof parent !== "object" || !parent.props || !parent.props.children || typeof parent.props.children !== "string" && typeof parent.props.children !== "object") { return ""; } if (typeof parent.props.children === "string") { return parent.props.children; } return parent.props.children.map((child) => getReactElementText(child)).join(""); }; const stringSearch = (key, searchQuery, node, path, treeIndex) => { if (typeof node[key] === "function") { return String(node[key]({ node, path, treeIndex })).includes(searchQuery); } if (typeof node[key] === "object") { return getReactElementText(node[key]).includes(searchQuery); } return node[key] && String(node[key]).includes(searchQuery); }; const defaultSearchMethod = ({ node, path, treeIndex, searchQuery }) => { return stringSearch("title", searchQuery, node, path, treeIndex) || stringSearch("subtitle", searchQuery, node, path, treeIndex); }; var __defProp$5 = Object.defineProperty; var __defProps$2 = Object.defineProperties; var __getOwnPropDescs$2 = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols$5 = Object.getOwnPropertySymbols; var __hasOwnProp$5 = Object.prototype.hasOwnProperty; var __propIsEnum$5 = Object.prototype.propertyIsEnumerable; var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$5 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$5.call(b, prop)) __defNormalProp$5(a, prop, b[prop]); if (__getOwnPropSymbols$5) for (var prop of __getOwnPropSymbols$5(b)) { if (__propIsEnum$5.call(b, prop)) __defNormalProp$5(a, prop, b[prop]); } return a; }; var __spreadProps$2 = (a, b) => __defProps$2(a, __getOwnPropDescs$2(b)); const getNodeDataAtTreeIndexOrNextIndex = ({ targetIndex, node, currentIndex, getNodeKey, path = [], lowerSiblingCounts = [], ignoreCollapsed = true, isPseudoRoot = false }) => { const selfPath = !isPseudoRoot ? [...path, getNodeKey({ node, treeIndex: currentIndex })] : []; if (currentIndex === targetIndex) { return { node, lowerSiblingCounts, path: selfPath }; } if (!node.children || ignoreCollapsed && node.expanded !== true) { return { nextIndex: currentIndex + 1 }; } let childIndex = currentIndex + 1; const childCount = node.children.length; for (let i = 0; i < childCount; i += 1) { const result = getNodeDataAtTreeIndexOrNextIndex({ ignoreCollapsed, getNodeKey, targetIndex, node: node.children[i], currentIndex: childIndex, lowerSiblingCounts: [...lowerSiblingCounts, childCount - i - 1], path: selfPath }); if (result.node) { return result; } childIndex = result.nextIndex; } return { nextIndex: childIndex }; }; const getDescendantCount = ({ node, ignoreCollapsed = true }) => { return getNodeDataAtTreeIndexOrNextIndex({ getNodeKey: () => { }, ignoreCollapsed, node, currentIndex: 0, targetIndex: -1 }).nextIndex - 1; }; const walkDescendants = ({ callback, getNodeKey, ignoreCollapsed, isPseudoRoot = false, node, parentNode = void 0, currentIndex, path = [], lowerSiblingCounts = [] }) => { const selfPath = isPseudoRoot ? [] : [...path, getNodeKey({ node, treeIndex: currentIndex })]; const selfInfo = isPseudoRoot ? void 0 : { node, parentNode, path: selfPath, lowerSiblingCounts, treeIndex: currentIndex }; if (!isPseudoRoot) { const callbackResult = callback(selfInfo); if (callbackResult === false) { return false; } } if (!node.children || node.expanded !== true && ignoreCollapsed && !isPseudoRoot) { return currentIndex; } let childIndex = currentIndex; const childCount = node.children.length; if (typeof node.children !== "function") { for (let i = 0; i < childCount; i += 1) { childIndex = walkDescendants({ callback, getNodeKey, ignoreCollapsed, node: node.children[i], parentNode: isPseudoRoot ? void 0 : node, currentIndex: childIndex + 1, lowerSiblingCounts: [...lowerSiblingCounts, childCount - i - 1], path: selfPath }); if (childIndex === false) { return false; } } } return childIndex; }; const mapDescendants = ({ callback, getNodeKey, ignoreCollapsed, isPseudoRoot = false, node, parentNode = void 0, currentIndex, path = [], lowerSiblingCounts = [] }) => { const nextNode = __spreadValues$5({}, node); const selfPath = isPseudoRoot ? [] : [...path, getNodeKey({ node: nextNode, treeIndex: currentIndex })]; const selfInfo = { node: nextNode, parentNode, path: selfPath, lowerSiblingCounts, treeIndex: currentIndex }; if (!nextNode.children || nextNode.expanded !== true && ignoreCollapsed && !isPseudoRoot) { return { treeIndex: currentIndex, node: callback(selfInfo) }; } let childIndex = currentIndex; const childCount = nextNode.children.length; if (typeof nextNode.children !== "function") { nextNode.children = nextNode.children.map((child, i) => { const mapResult = mapDescendants({ callback, getNodeKey, ignoreCollapsed, node: child, parentNode: isPseudoRoot ? void 0 : nextNode, currentIndex: childIndex + 1, lowerSiblingCounts: [...lowerSiblingCounts, childCount - i - 1], path: selfPath }); childIndex = mapResult.treeIndex; return mapResult.node; }); } return { node: callback(selfInfo), treeIndex: childIndex }; }; const getVisibleNodeCount = ({ treeData }) => { const traverse = (node) => { if (!node.children || node.expanded !== true || typeof node.children === "function") { return 1; } return 1 + node.children.reduce((total, currentNode) => total + traverse(currentNode), 0); }; return treeData.reduce((total, currentNode) => total + traverse(currentNode), 0); }; const getVisibleNodeInfoAtIndex = ({ treeData, index: targetIndex, getNodeKey }) => { if (!treeData || treeData.length === 0) { return void 0; } const result = getNodeDataAtTreeIndexOrNextIndex({ targetIndex, getNodeKey, node: { children: treeData, expanded: true }, currentIndex: -1, path: [], lowerSiblingCounts: [], isPseudoRoot: true }); if (result.node) { return result; } return void 0; }; const walk = ({ treeData, getNodeKey, callback, ignoreCollapsed = true }) => { if (!treeData || treeData.length === 0) { return; } walkDescendants({ callback, getNodeKey, ignoreCollapsed, isPseudoRoot: true, node: { children: treeData }, currentIndex: -1, path: [], lowerSiblingCounts: [] }); }; const map = ({ treeData, getNodeKey, callback, ignoreCollapsed = true }) => { if (!treeData || treeData.length === 0) { return []; } return mapDescendants({ callback, getNodeKey, ignoreCollapsed, isPseudoRoot: true, node: { children: treeData }, currentIndex: -1, path: [], lowerSiblingCounts: [] }).node.children; }; const toggleExpandedForAll = ({ treeData, expanded = true }) => { return map({ treeData, callback: ({ node }) => __spreadProps$2(__spreadValues$5({}, node), { expanded }), getNodeKey: ({ treeIndex }) => treeIndex, ignoreCollapsed: false }); }; const changeNodeAtPath = ({ treeData, path, newNode, getNodeKey, ignoreCollapsed = true }) => { const RESULT_MISS = "RESULT_MISS"; const traverse = ({ isPseudoRoot = false, node, currentTreeIndex, pathIndex }) => { if (!isPseudoRoot && getNodeKey({ node, treeIndex: currentTreeIndex }) !== path[pathIndex]) { return RESULT_MISS; } if (pathIndex >= path.length - 1) { return typeof newNode === "function" ? newNode({ node, treeIndex: currentTreeIndex }) : newNode; } if (!node.children) { throw new Error("Path referenced children of node with no children."); } let nextTreeIndex = currentTreeIndex + 1; for (let i = 0; i < node.children.length; i += 1) { const result2 = traverse({ node: node.children[i], currentTreeIndex: nextTreeIndex, pathIndex: pathIndex + 1 }); if (result2 !== RESULT_MISS) { if (result2) { return __spreadProps$2(__spreadValues$5({}, node), { children: [ ...node.children.slice(0, i), result2, ...node.children.slice(i + 1) ] }); } return __spreadProps$2(__spreadValues$5({}, node), { children: [ ...node.children.slice(0, i), ...node.children.slice(i + 1) ] }); } nextTreeIndex += 1 + getDescendantCount({ node: node.children[i], ignoreCollapsed }); } return RESULT_MISS; }; const result = traverse({ node: { children: treeData }, currentTreeIndex: -1, pathIndex: -1, isPseudoRoot: true }); if (result === RESULT_MISS) { throw new Error("No node found at the given path."); } return result.children; }; const removeNodeAtPath = ({ treeData, path, getNodeKey, ignoreCollapsed = true }) => { return changeNodeAtPath({ treeData, path, getNodeKey, ignoreCollapsed, newNode: void 0 }); }; const removeNode = ({ treeData, path, getNodeKey, ignoreCollapsed = true }) => { let removedNode; let removedTreeIndex; const nextTreeData = changeNodeAtPath({ treeData, path, getNodeKey, ignoreCollapsed, newNode: ({ node, treeIndex }) => { removedNode = node; removedTreeIndex = treeIndex; return void 0; } }); return { treeData: nextTreeData, node: removedNode, treeIndex: removedTreeIndex }; }; const getNodeAtPath = ({ treeData, path, getNodeKey, ignoreCollapsed = true }) => { let foundNodeInfo; try { changeNodeAtPath({ treeData, path, getNodeKey, ignoreCollapsed, newNode: ({ node, treeIndex }) => { foundNodeInfo = { node, treeIndex }; return node; } }); } catch { } return foundNodeInfo; }; const addNodeUnderParent = ({ treeData, newNode, parentKey = void 0, getNodeKey, ignoreCollapsed = true, expandParent = false, addAsFirstChild = false }) => { if (parentKey === null || parentKey === void 0) { return addAsFirstChild ? { treeData: [newNode, ...treeData || []], treeIndex: 0 } : { treeData: [...treeData || [], newNode], treeIndex: (treeData || []).length }; } let insertedTreeIndex; let hasBeenAdded = false; const changedTreeData = map({ treeData, getNodeKey, ignoreCollapsed, callback: ({ node, treeIndex, path }) => { const key = path ? path[path.length - 1] : void 0; if (hasBeenAdded || key !== parentKey) { return node; } hasBeenAdded = true; const parentNode = __spreadValues$5({}, node); if (expandParent) { parentNode.expanded = true; } if (!parentNode.children) { insertedTreeIndex = treeIndex + 1; return __spreadProps$2(__spreadValues$5({}, parentNode), { children: [newNode] }); } if (typeof parentNode.children === "function") { throw new TypeError("Cannot add to children defined by a function"); } let nextTreeIndex = treeIndex + 1; for (let i = 0; i < parentNode.children.length; i += 1) { nextTreeIndex += 1 + getDescendantCount({ node: parentNode.children[i], ignoreCollapsed }); } insertedTreeIndex = nextTreeIndex; const children = addAsFirstChild ? [newNode, ...parentNode.children] : [...parentNode.children, newNode]; return __spreadProps$2(__spreadValues$5({}, parentNode), { children }); } }); if (!hasBeenAdded) { throw new Error("No node found with the given key."); } return { treeData: changedTreeData, treeIndex: insertedTreeIndex }; }; const addNodeAtDepthAndIndex = ({ targetDepth, minimumTreeIndex, newNode, ignoreCollapsed, expandParent, isPseudoRoot = false, isLastChild, node, currentIndex, currentDepth, getNodeKey, path = [] }) => { const selfPath = (n) => isPseudoRoot ? [] : [...path, getNodeKey({ node: n, treeIndex: currentIndex })]; if (currentIndex >= minimumTreeIndex - 1 || isLastChild && !(node.children && node.children.length > 0)) { if (typeof node.children === "function") { throw new TypeError("Cannot add to children defined by a function"); } else { const extraNodeProps = expandParent ? { expanded: true } : {}; const nextNode2 = __spreadProps$2(__spreadValues$5(__spreadValues$5({}, node), extraNodeProps), { children: node.children ? [newNode, ...node.children] : [newNode] }); return { node: nextNode2, nextIndex: currentIndex + 2, insertedTreeIndex: currentIndex + 1, parentPath: selfPath(nextNode2), parentNode: isPseudoRoot ? void 0 : nextNode2 }; } } if (currentDepth >= targetDepth - 1) { if (!node.children || typeof node.children === "function" || node.expanded !== true && ignoreCollapsed && !isPseudoRoot) { return { node, nextIndex: currentIndex + 1 }; } let childIndex2 = currentIndex + 1; let insertedTreeIndex2; let insertIndex; for (let i = 0; i < node.children.length; i += 1) { if (childIndex2 >= minimumTreeIndex) { insertedTreeIndex2 = childIndex2; insertIndex = i; break; } childIndex2 += 1 + getDescendantCount({ node: node.children[i], ignoreCollapsed }); } if (insertIndex === null || insertIndex === void 0) { if (childIndex2 < minimumTreeIndex && !isLastChild) { return { node, nextIndex: childIndex2 }; } insertedTreeIndex2 = childIndex2; insertIndex = node.children.length; } const nextNode2 = __spreadProps$2(__spreadValues$5({}, node), { children: [ ...node.children.slice(0, insertIndex), newNode, ...node.children.slice(insertIndex) ] }); return { node: nextNode2, nextIndex: childIndex2, insertedTreeIndex: insertedTreeIndex2, parentPath: selfPath(nextNode2), parentNode: isPseudoRoot ? void 0 : nextNode2 }; } if (!node.children || typeof node.children === "function" || node.expanded !== true && ignoreCollapsed && !isPseudoRoot) { return { node, nextIndex: currentIndex + 1 }; } let insertedTreeIndex; let pathFragment; let parentNode; let childIndex = currentIndex + 1; let newChildren = node.children; if (typeof newChildren !== "function") { newChildren = newChildren.map((child, i) => { if (insertedTreeIndex !== null && insertedTreeIndex !== void 0) { return child; } const mapResult = addNodeAtDepthAndIndex({ targetDepth, minimumTreeIndex, newNode, ignoreCollapsed, expandParent, isLastChild: isLastChild && i === newChildren.length - 1, node: child, currentIndex: childIndex, currentDepth: currentDepth + 1, getNodeKey, path: [] }); if ("insertedTreeIndex" in mapResult) { ({ insertedTreeIndex, parentNode, parentPath: pathFragment } = mapResult); } childIndex = mapResult.nextIndex; return mapResult.node; }); } const nextNode = __spreadProps$2(__spreadValues$5({}, node), { children: newChildren }); const result = { node: nextNode, nextIndex: childIndex }; if (insertedTreeIndex !== null && insertedTreeIndex !== void 0) { result.insertedTreeIndex = insertedTreeIndex; result.parentPath = [...selfPath(nextNode), ...pathFragment]; result.parentNode = parentNode; } return result; }; const insertNode = ({ treeData, depth: targetDepth, minimumTreeIndex, newNode, getNodeKey, ignoreCollapsed = true, expandParent = false }) => { if (!treeData && targetDepth === 0) { return { treeData: [newNode], treeIndex: 0, path: [getNodeKey({ node: newNode, treeIndex: 0 })], parentNode: void 0 }; } const insertResult = addNodeAtDepthAndIndex({ targetDepth, minimumTreeIndex, newNode, ignoreCollapsed, expandParent, getNodeKey, isPseudoRoot: true, isLastChild: true, node: { children: treeData }, currentIndex: -1, currentDepth: -1 }); if (!("insertedTreeIndex" in insertResult)) { throw new Error("No suitable position found to insert."); } const treeIndex = insertResult.insertedTreeIndex; return { treeData: insertResult.node.children, treeIndex, path: [ ...insertResult.parentPath, getNodeKey({ node: newNode, treeIndex }) ], parentNode: insertResult.parentNode }; }; const getFlatDataFromTree = ({ treeData, getNodeKey, ignoreCollapsed = true }) => { if (!treeData || treeData.length === 0) { return []; } const flattened = []; walk({ treeData, getNodeKey, ignoreCollapsed, callback: (nodeInfo) => { flattened.push(nodeInfo); } }); return flattened; }; const getTreeFromFlatData = ({ flatData, getKey = (node) => node.id, getParentKey = (node) => node.parentId, rootKey = "0" }) => { if (!flatData) { return []; } const childrenToParents = {}; for (const child of flatData) { const parentKey = getParentKey(child); if (parentKey in childrenToParents) { childrenToParents[parentKey].push(child); } else { childrenToParents[parentKey] = [child]; } } if (!(rootKey in childrenToParents)) { return []; } const trav = (parent) => { const parentKey = getKey(parent); if (parentKey in childrenToParents) { return __spreadProps$2(__spreadValues$5({}, parent), { children: childrenToParents[parentKey].map((child) => trav(child)) }); } return __spreadValues$5({}, parent); }; return childrenToParents[rootKey].map((child) => trav(child)); }; const isDescendant = (older, younger) => { return !!older.children && typeof older.children !== "function" && older.children.some((child) => child === younger || isDescendant(child, younger)); }; const getDepth = (node, depth = 0) => { if (!node.children) { return depth; } if (typeof node.children === "function") { return depth + 1; } return node.children.reduce((deepest, child) => Math.max(deepest, getDepth(child, depth + 1)), depth); }; const find = ({ getNodeKey, treeData, searchQuery, searchMethod, searchFocusOffset, expandAllMatchPaths = false, expandFocusMatchPaths = true }) => { let matchCount = 0; const trav = ({ isPseudoRoot = false, node, currentIndex, path = [] }) => { let matches = []; let isSelfMatch = false; let hasFocusMatch = false; const selfPath = isPseudoRoot ? [] : [...path, getNodeKey({ node, treeIndex: currentIndex })]; const extraInfo = isPseudoRoot ? void 0 : { path: selfPath, treeIndex: currentIndex }; const hasChildren = node.children && typeof node.children !== "function" && node.children.length > 0; if (!isPseudoRoot && searchMethod(__spreadProps$2(__spreadValues$5({}, extraInfo), { node, searchQuery }))) { if (matchCount === searchFocusOffset) { hasFocusMatch = true; } matchCount += 1; isSelfMatch = true; } let childIndex = currentIndex; const newNode = __spreadValues$5({}, node); if (hasChildren) { newNode.children = newNode.children.map((child) => { const mapResult = trav({ node: child, currentIndex: childIndex + 1, path: selfPath }); if (mapResult.node.expanded) { childIndex = mapResult.treeIndex; } else { childIndex += 1; } if (mapResult.matches.length > 0 || mapResult.hasFocusMatch) { matches = [...matches, ...mapResult.matches]; if (mapResult.hasFocusMatch) { hasFocusMatch = true; } if (expandAllMatchPaths && mapResult.matches.length > 0 || (expandAllMatchPaths || expandFocusMatchPaths) && mapResult.hasFocusMatch) { newNode.expanded = true; } } return mapResult.node; }); } if (!isPseudoRoot && !newNode.expanded) { matches = matches.map((match) => __spreadProps$2(__spreadValues$5({}, match), { treeIndex: void 0 })); } if (isSelfMatch) { matches = [__spreadProps$2(__spreadValues$5({}, extraInfo), { node: newNode }), ...matches]; } return { node: matches.length > 0 ? newNode : node, matches, hasFocusMatch, treeIndex: childIndex }; }; const result = trav({ node: { children: treeData }, isPseudoRoot: true, currentIndex: -1 }); return { matches: result.matches, treeData: result.node.children }; }; const classnames = (...classes) => classes.filter(Boolean).join(" "); var __defProp$4 = Object.defineProperty; var __getOwnPropSymbols$4 = Object.getOwnPropertySymbols; var __hasOwnProp$4 = Object.prototype.hasOwnProperty; var __propIsEnum$4 = Object.prototype.propertyIsEnumerable; var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$4 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$4.call(b, prop)) __defNormalProp$4(a, prop, b[prop]); if (__getOwnPropSymbols$4) for (var prop of __getOwnPropSymbols$4(b)) { if (__propIsEnum$4.call(b, prop)) __defNormalProp$4(a, prop, b[prop]); } return a; }; var __objRest$3 = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp$4.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols$4) for (var prop of __getOwnPropSymbols$4(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum$4.call(source, prop)) target[prop] = source[prop]; } return target; }; const defaultProps$3 = { isSearchMatch: false, isSearchFocus: false, canDrag: false, toggleChildrenVisibility: void 0, buttons: [], className: "", style: {}, parentNode: void 0, draggedNode: void 0, canDrop: false, title: void 0, subtitle: void 0 }; const NodeRendererDefault = (props) => { props = __spreadValues$4(__spreadValues$4({}, defaultProps$3), props); const _a = props, { scaffoldBlockPxWidth, toggleChildrenVisibility, connectDragPreview, connectDragSource, isDragging, canDrop, canDrag, node, title, subtitle, draggedNode, path, treeIndex, isSearchMatch, isSearchFocus, buttons, className, style, didDrop, treeId: treeIdOut, isOver: isOverOut, parentNode: parentNodeOut } = _a, otherProps = __objRest$3(_a, [ "scaffoldBlockPxWidth", "toggleChildrenVisibility", "connectDragPreview", "connectDragSource", "isDragging", "canDrop", "canDrag", "node", "title", "subtitle", "draggedNode", "path", "treeIndex", "isSearchMatch", "isSearchFocus", "buttons", "className", "style", "didDrop", "treeId", "isOver", "parentNode" ]); const nodeTitle = title || node.title; const nodeSubtitle = subtitle || node.subtitle; let handle; if (canDrag) { handle = typeof node.children === "function" && node.expanded ? /* @__PURE__ */ React.createElement("div", { className: "rst__loadingHandle" }, /* @__PURE__ */ React.createElement("div", { className: "rst__loadingCircle" }, [...Array.from({ length: 12 })].map((_, index) => /* @__PURE__ */ React.createElement("div", { key: index, className: "rst__loadingCirclePoint" })))) : connectDragSource(/* @__PURE__ */ React.createElement("div", { className: "rst__moveHandle" }), { dropEffect: "copy" }); } const isDraggedDescendant = draggedNode && isDescendant(draggedNode, node); const isLandingPadActive = !didDrop && isDragging; const buttonStyle = { left: -0.5 * scaffoldBlockPxWidth, right: 0 }; return /* @__PURE__ */ React.createElement("div", __spreadValues$4({ style: { height: "100%" } }, otherProps), toggleChildrenVisibility && node.children && (node.children.length > 0 || typeof node.children === "function") && /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("button", { type: "button", "aria-label": node.expanded ? "Collapse" : "Expand", className: node.expanded ? "rst__collapseButton" : "rst__expandButton", style: buttonStyle, onClick: () => toggleChildrenVisibility({ node, path, treeIndex }) }), node.expanded && !isDragging && /* @__PURE__ */ React.createElement("div", { style: { width: scaffoldBlockPxWidth }, className: "rst__lineChildren" })), /* @__PURE__ */ React.createElement("div", { className: "rst__rowWrapper" }, connectDragPreview(/* @__PURE__ */ React.createElement("div", { className: classnames("rst__row", isLandingPadActive ? "rst__rowLandingPad" : "", isLandingPadActive && !canDrop ? "rst__rowCancelPad" : "", isSearchMatch ? "rst__rowSearchMatch" : "", isSearchFocus ? "rst__rowSearchFocus" : "", className ?? ""), style: __spreadValues$4({ opacity: isDraggedDescendant ? 0.5 : 1 }, style) }, handle, /* @__PURE__ */ React.createElement("div", { className: classnames("rst__rowContents", !canDrag ? "rst__rowContentsDragDisabled" : "") }, /* @__PURE__ */ React.createElement("div", { className: "rst__rowLabel" }, /* @__PURE__ */ React.createElement("span", { className: classnames("rst__rowTitle", node.subtitle ? "rst__rowTitleWithSubtitle" : "") }, typeof nodeTitle === "function" ? nodeTitle({ node, path, treeIndex }) : nodeTitle), nodeSubtitle && /* @__PURE__ */ React.createElement("span", { className: "rst__rowSubtitle" }, typeof nodeSubtitle === "function" ? nodeSubtitle({ node, path, treeIndex }) : nodeSubtitle)), /* @__PURE__ */ React.createElement("div", { className: "rst__rowToolbar" }, buttons == null ? void 0 : buttons.map((btn, index) => /* @__PURE__ */ React.createElement("div", { key: index, className: "rst__toolbarButton" }, btn)))))))); }; var NodeRendererDefault$1 = NodeRendererDefault; var __defProp$3 = Object.defineProperty; var __getOwnPropSymbols$3 = Object.getOwnPropertySymbols; var __hasOwnProp$3 = Object.prototype.hasOwnProperty; var __propIsEnum$3 = Object.prototype.propertyIsEnumerable; var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$3 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$3.call(b, prop)) __defNormalProp$3(a, prop, b[prop]); if (__getOwnPropSymbols$3) for (var prop of __getOwnPropSymbols$3(b)) { if (__propIsEnum$3.call(b, prop)) __defNormalProp$3(a, prop, b[prop]); } return a; }; const defaultProps$2 = { isOver: false, canDrop: false }; const PlaceholderRendererDefault = (props) => { props = __spreadValues$3(__spreadValues$3({}, defaultProps$2), props); const { canDrop, isOver } = props; return /* @__PURE__ */ React.createElement("div", { className: classnames("rst__placeholder", canDrop ? "rst__placeholderLandingPad" : "", canDrop && !isOver ? "rst__placeholderCancelPad" : "") }); }; var PlaceholderRendererDefault$1 = PlaceholderRendererDefault; var __defProp$2 = Object.defineProperty; var __defProps$1 = Object.defineProperties; var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols$2 = Object.getOwnPropertySymbols; var __hasOwnProp$2 = Object.prototype.hasOwnProperty; var __propIsEnum$2 = Object.prototype.propertyIsEnumerable; var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$2 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$2.call(b, prop)) __defNormalProp$2(a, prop, b[prop]); if (__getOwnPropSymbols$2) for (var prop of __getOwnPropSymbols$2(b)) { if (__propIsEnum$2.call(b, prop)) __defNormalProp$2(a, prop, b[prop]); } return a; }; var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b)); var __objRest$2 = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp$2.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols$2) for (var prop of __getOwnPropSymbols$2(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum$2.call(source, prop)) target[prop] = source[prop]; } return target; }; const defaultProps$1 = { swapFrom: void 0, swapDepth: void 0, swapLength: void 0, canDrop: false, draggedNode: void 0 }; const TreeNodeComponent = (props) => { props = __spreadValues$2(__spreadValues$2({}, defaultProps$1), props); const _a = props, { children, listIndex, swapFrom, swapLength, swapDepth, scaffoldBlockPxWidth, lowerSiblingCounts, connectDropTarget, isOver, draggedNode, canDrop, treeIndex, rowHeight, treeId, getPrevRow, node, path } = _a, otherProps = __objRest$2(_a, [ "children", "listIndex", "swapFrom", "swapLength", "swapDepth", "scaffoldBlockPxWidth", "lowerSiblingCounts", "connectDropTarget", "isOver", "draggedNode", "canDrop", "treeIndex", "rowHeight", "treeId", "getPrevRow", "node", "path" ]); const scaffoldBlockCount = lowerSiblingCounts.length; const scaffold = []; for (const [i, lowerSiblingCount] of lowerSiblingCounts.entries()) { let lineClass = ""; if (lowerSiblingCount > 0) { if (listIndex === 0) { lineClass = "rst__lineHalfHorizontalRight rst__lineHalfVerticalBottom"; } else if (i === scaffoldBlockCount - 1) { lineClass = "rst__lineHalfHorizontalRight rst__lineFullVertical"; } else { lineClass = "rst__lineFullVertical"; } } else if (listIndex === 0) { lineClass = "rst__lineHalfHorizontalRight"; } else if (i === scaffoldBlockCount - 1) { lineClass = "rst__lineHalfVerticalTop rst__lineHalfHorizontalRight"; } scaffold.push(/* @__PURE__ */ React.createElement("div", { key: `pre_${1 + i}`, style: { width: scaffoldBlockPxWidth }, className: classnames("rst__lineBlock", lineClass) })); if (treeIndex !== listIndex && i === swapDepth) { let highlightLineClass = ""; if (listIndex === swapFrom + swapLength - 1) { highlightLineClass = "rst__highlightBottomLeftCorner"; } else if (treeIndex === swapFrom) { highlightLineClass = "rst__highlightTopLeftCorner"; } else { highlightLineClass = "rst__highlightLineVertical"; } const style2 = { width: scaffoldBlockPxWidth, left: scaffoldBlockPxWidth * i }; scaffold.push(/* @__PURE__ */ React.createElement("div", { key: i, style: style2, className: classnames("rst__absoluteLineBlock", highlightLineClass) })); } } const style = { left: scaffoldBlockPxWidth * scaffoldBlockCount }; let calculatedRowHeight = rowHeight; if (typeof rowHeight === "function") { calculatedRowHeight = rowHeight(treeIndex, node, path); } return connectDropTarget(/* @__PURE__ */ React.createElement("div", __spreadProps$1(__spreadValues$2({}, otherProps), { style: { height: `${calculatedRowHeight}px` }, className: "rst__node" }), scaffold, /* @__PURE__ */ React.createElement("div", { className: "rst__nodeContent", style }, Children.map(children, (child) => cloneElement(child, { isOver, canDrop, draggedNode }))))); }; var TreeNode = TreeNodeComponent; var __defProp$1 = Object.defineProperty; var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols; var __hasOwnProp$1 = Object.prototype.hasOwnProperty; var __propIsEnum$1 = Object.prototype.propertyIsEnumerable; var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$1 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$1.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); if (__getOwnPropSymbols$1) for (var prop of __getOwnPropSymbols$1(b)) { if (__propIsEnum$1.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); } return a; }; var __objRest$1 = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp$1.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols$1) for (var prop of __getOwnPropSymbols$1(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum$1.call(source, prop)) target[prop] = source[prop]; } return target; }; const defaultProps = { canDrop: false, draggedNode: void 0 }; const TreePlaceholder = (props) => { props = __spreadValues$1(__spreadValues$1({}, defaultProps), props); const _a = props, { children, connectDropTarget, treeId, drop } = _a, otherProps = __objRest$1(_a, ["children", "connectDropTarget", "treeId", "drop"]); return connectDropTarget(/* @__PURE__ */ React.createElement("div", null, Children.map(children, (child) => cloneElement(child, __spreadValues$1({}, otherProps))))); }; var TreePlaceholder$1 = TreePlaceholder; let rafId = 0; const nodeDragSourcePropInjection = (connect, monitor) => ({ connectDragSource: connect.dragSource(), connectDragPreview: connect.dragPreview(), isDragging: monitor.isDragging(), didDrop: monitor.didDrop() }); const wrapSource = (el, startDrag, endDrag, dndType) => { const nodeDragSource = { beginDrag: (props) => { startDrag(props); return { node: props.node, parentNode: props.parentNode, path: props.path, treeIndex: props.treeIndex, treeId: props.treeId }; }, endDrag: (props, monitor) => { endDrag(monitor.getDropResult()); }, isDragging: (props, monitor) => { const dropTargetNode = monitor.getItem().node; const draggedNode = props.node; return draggedNode === dropTargetNode; } }; return DragSource(dndType, nodeDragSource, nodeDragSourcePropInjection)(el); }; const propInjection = (connect, monitor) => { const dragged = monitor.getItem(); return { connectDropTarget: connect.dropTarget(), isOver: monitor.isOver(), canDrop: monitor.canDrop(), draggedNode: dragged ? dragged.node : void 0 }; }; const wrapPlaceholder = (el, treeId, drop, dndType) => { const placeholderDropTarget = { drop: (dropTargetProps, monitor) => { const { node, path, treeIndex } = monitor.getItem(); const result = { node, path, treeIndex, treeId, minimumTreeIndex: 0, depth: 0 }; drop(result); return result; } }; return DropTarget(dndType, placeholderDropTarget, propInjection)(el); }; const getTargetDepth = (dropTargetProps, monitor, component, canNodeHaveChildren, treeId, maxDepth) => { let dropTargetDepth = 0; const rowAbove = dropTargetProps.getPrevRow(); if (rowAbove) { const { node } = rowAbove; let { path } = rowAbove; const aboveNodeCannotHaveChildren = !canNodeHaveChildren(node); if (aboveNodeCannotHaveChildren) { path = path.slice(0, -1); } dropTargetDepth = Math.min(path.length, dropTargetProps.path.length); } let blocksOffset; let dragSourceInitialDepth = (monitor.getItem().path || []).length; if (monitor.getItem().treeId !== treeId) { dragSourceInitialDepth = 0; if (component) { const relativePosition = component.node.getBoundingClientRect(); const leftShift = monitor.getSourceClientOffset().x - relativePosition.left; blocksOffset = Math.round(leftShift / dropTargetProps.scaffoldBlockPxWidth); } else { blocksOffset = dropTargetProps.path.length; } } else { blocksOffset = Math.round(monitor.getDifferenceFromInitialOffset().x / dropTargetProps.scaffoldBlockPxWidth); } let targetDepth = Math.min(dropTargetDepth, Math.max(0, dragSourceInitialDepth + blocksOffset - 1)); if (typeof maxDepth !== "undefined" && maxDepth !== void 0) { const draggedNode = monitor.getItem().node; const draggedChildDepth = getDepth(draggedNode); targetDepth = Math.max(0, Math.min(targetDepth, maxDepth - draggedChildDepth - 1)); } return targetDepth; }; const canDrop = (dropTargetProps, monitor, canNodeHaveChildren, treeId, maxDepth, treeRefcanDrop) => { if (!monitor.isOver()) { return false; } const rowAbove = dropTargetProps.getPrevRow(); const abovePath = rowAbove ? rowAbove.path : []; const aboveNode = rowAbove ? rowAbove.node : {}; const targetDepth = getTargetDepth(dropTargetProps, monitor, void 0, canNodeHaveChildren, treeId, maxDepth); if (targetDepth >= abovePath.length && typeof aboveNode.children === "function") { return false; } if (typeof treeRefcanDrop === "function") { const { node } = monitor.getItem(); return treeRefcanDrop({ node, prevPath: monitor.getItem().path, prevParent: monitor.getItem().parentNode, prevTreeIndex: monitor.getItem().treeIndex, nextPath: dropTargetProps.children.props.path, nextParent: dropTargetProps.children.props.parentNode, nextTreeIndex: dropTargetProps.children.props.treeIndex }); } return true; }; const wrapTarget = (el, canNodeHaveChildren, treeId, maxDepth, treeRefcanDrop, drop, dragHover, dndType) => { const nodeDropTarget = { drop: (dropTargetProps, monitor, component) => { const result = { node: monitor.getItem().node, path: monitor.getItem().path, treeIndex: monitor.getItem().treeIndex, treeId, minimumTreeIndex: dropTargetProps.treeIndex, depth: getTargetDepth(dropTargetProps, monitor, component, canNodeHaveChildren, treeId, maxDepth) }; drop(result); return result; }, hover: (dropTargetProps, monitor, component) => { const targetDepth = getTargetDepth(dropTargetProps, monitor, component, canNodeHaveChildren, treeId, maxDepth); const draggedNode = monitor.getItem().node; const needsRedraw = dropTargetProps.node !== draggedNode || targetDepth !== dropTargetProps.path.length - 1; if (!needsRedraw) { return; } cancelAnimationFrame(rafId); rafId = requestAnimationFrame(() => { const item = monitor.getItem(); if (!item || !monitor.isOver()) { return; } dragHover({ node: draggedNode, path: item.path, minimumTreeIndex: dropTargetProps.listIndex, depth: targetDepth }); }); }, canDrop: (dropTargetProps, monitor) => canDrop(dropTargetProps, monitor, canNodeHaveChildren, treeId, maxDepth, treeRefcanDrop) }; return DropTarget(dndType, nodeDropTarget, propInjection)(el); }; const slideRows = (rows, fromIndex, toIndex, count = 1) => { const rowsWithoutMoved = [ ...rows.slice(0, fromIndex), ...rows.slice(fromIndex + count) ]; return [ ...rowsWithoutMoved.slice(0, toIndex), ...rows.slice(fromIndex, fromIndex + count), ...rowsWithoutMoved.slice(toIndex) ]; }; const memoize = (f) => { let savedArgsArray = []; let savedKeysArray = []; let savedResult; return (args) => { const keysArray = Object.keys(args).sort(); const argsArray = keysArray.map((key) => args[key]); if (argsArray.length !== savedArgsArray.length || argsArray.some((arg, index) => arg !== savedArgsArray[index]) || keysArray.some((key, index) => key !== savedKeysArray[index])) { savedArgsArray = argsArray; savedKeysArray = keysArray; savedResult = f(args); } return savedResult; }; }; const memoizedInsertNode = memoize(insertNode); const memoizedGetFlatDataFromTree = memoize(getFlatDataFromTree); const memoizedGetDescendantCount = memoize(getDescendantCount); var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; let treeIdCounter = 1; const mergeTheme = (props) => { const merged = __spreadProps(__spreadValues({}, props), { style: __spreadValues(__spreadValues({}, props.theme.style), props.style), innerStyle: __spreadValues(__spreadValues({}, props.theme.innerStyle), props.innerStyle) }); const overridableDefaults = { nodeContentRenderer: NodeRendererDefault$1, placeholderRenderer: PlaceholderRendererDefault$1, scaffoldBlockPxWidth: 44, slideRegionSize: 100, rowHeight: 62, treeNodeRenderer: TreeNode }; for (const propKey of Object.keys(overridableDefaults)) { if (props[propKey] === void 0) { merged[propKey] = typeof props.theme[propKey] !== "undefined" ? props.theme[propKey] : overridableDefaults[propKey]; } } return merged; }; class ReactSortableTree extends Component { constructor(props) { super(props); this.startDrag = ({ path }) => { this.setState((prevState) => { const { treeData: draggingTreeData, node: draggedNode, treeIndex: draggedMinimumTreeIndex } = removeNode({ treeData: prevState.instanceProps.treeData, path, getNodeKey: this.props.getNodeKey }); return { draggingTreeData, draggedNode, draggedDepth: path.length - 1, draggedMinimumTreeIndex, dragging: true }; }); }; this.dragHover = ({ node: draggedNode, depth: draggedDepth, minimumTreeIndex: draggedMinimumTreeIndex }) => { if (this.state.draggedDepth === draggedDepth && this.state.draggedMinimumTreeIndex === draggedMinimumTreeIndex) { return; } this.setState(({ draggingTreeData, instanceProps }) => { const newDraggingTreeData = draggingTreeData || instanceProps.treeData; const addedResult = memoizedInsertNode({ treeData: newDraggingTreeData, newNode: draggedNode, depth: draggedDepth, minimumTreeIndex: draggedMinimumTreeIndex, expandParent: true, getNodeKey: this.props.getNodeKey }); const rows = this.getRows(addedResult.treeData); const expandedParentPath = rows[addedResult.treeIndex].path; return { draggedNode, draggedDepth, draggedMinimumTreeIndex, draggingTreeData: changeNodeAtPath({ treeData: newDraggingTreeData, path: expandedParentPath.slice(0, -1), newNode: ({ node }) => __spreadProps(__spreadValues({}, node), { expanded: true }), getNodeKey: this.props.getNodeKey }), searchFocusTreeIndex: void 0, dragging: true }; }); }; this.endDrag = (dropResult) => { const { instanceProps } = this.state; if (!dropResult) { this.setState({ draggingTreeData: void 0, draggedNode: void 0, draggedMinimumTreeIndex: void 0, draggedDepth: void 0, dragging: false }); } else if (dropResult.treeId !== this.treeId) { const { node, path, treeIndex } = dropResult; let shouldCopy = this.props.shouldCopyOnOutsideDrop; if (typeof shouldCopy === "function") { shouldCopy = shouldCopy({ node, prevTreeIndex: treeIndex, prevPath: path }); } let treeData = this.state.draggingTreeData || instanceProps.treeData; if (shouldCopy) { treeData = changeNodeAtPath({ treeData: instanceProps.treeData, path, newNode: ({ node: copyNode }) => __spreadValues({}, copyNode), getNodeKey: this.props.getNodeKey }); } this.props.onChange(treeData); this.props.onMoveNode({ treeData, node, treeIndex: void 0, path: void 0, nextPath: void 0, nextTreeIndex: void 0, prevPath: path, prevTreeIndex: treeIndex }); } }; this.drop = (dropResult) => { this.moveNode(dropResult); }; this.canNodeHaveChildren = (node) => { const { canNodeHaveChildren } = this.props; if (canNodeHaveChildren) { return canNodeHaveChildren(node); } return true; }; this.listRef = React.createRef(); const { dndType, nodeContentRenderer, treeNodeRenderer, slideRegionSize } = mergeTheme(props); this.treeId = `rst__${treeIdCounter}`; treeIdCounter += 1; this.dndType = dndType || this.treeId; this.nodeContentRenderer = wrapSource(nodeContentRenderer, this.startDrag, this.endDrag, this.dndType); this.treePlaceholderRenderer = wrapPlaceholder(TreePlaceholder$1, this.treeId, this.drop, this.dndType); this.scrollZoneVirtualList = (createScrollingComponent || withScrolling)(React.forwardRef((props2, ref) => { const _a = props2, otherProps = __objRest(_a, ["dragDropManager", "rowHeight"]); return /* @__PURE__ */ React.createElement(Virtuoso, __spreadValues({ ref: this.listRef }, otherProps)); })); this.vStrength = createVerticalStrength(slideRegionSize); this.hStrength = createHorizontalStrength(slideRegionSize); this.state = { draggingTreeData: void 0, draggedNode: void 0, draggedMinimumTreeIndex: void 0, draggedDepth: void 0, searchMatches: [], searchFocusTreeIndex: void 0, dragging: false, instanceProps: { treeData: [], ignoreOneTreeUpdate: false, searchQuery: void 0, searchFocusOffset: void 0 } }; this.treeNodeRenderer = wrapTarget(treeNodeRenderer, this.canNodeHaveChildren, this.treeId, this.props.maxDepth, this.props.canDrop, this.drop, this.dragHover, this.dndType); this.toggleChildrenVisibility = this.toggleChildrenVisibility.bind(this); this.moveNode = this.moveNode.bind(this); this.startDrag = this.startDrag.bind(this); this.dragHover = this.dragHover.bind(this); this.endDrag = this.endDrag.bind(this); this.drop = this.drop.bind(this); this.handleDndMonitorChange = this.handleDndMonitorChange.bind(this); } static search(props, state, seekIndex, expand, singleSearch) { const { onChange, getNodeKey, searchFinishCallback, searchQuery, searchMethod, searchFocusOffset, onlyExpandSearchedNodes } = props; const { instanceProps } = state; if (!searchQuery && !searchMethod) { if (searchFinishCallback) { searchFinishCallback([]); } return { searchMatches: [] }; } const newState = { instanceProps: {} }; const { treeData: expandedTreeData, matches: searchMatches } = find({ getNodeKey, treeData: onlyExpandSearchedNodes ? toggleExpandedForAll({ treeData: instanceProps.treeData, expanded: false }) : instanceProps.treeData, searchQuery, searchMethod: searchMethod || defaultSearchMethod, searchFocusOffset, expandAllMatchPaths: expand && !singleSearch, expandFocusMat