"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __importStar(require("react"));
var prop_types_1 = __importDefault(require("prop-types"));
var classnames_1 = __importDefault(require("classnames"));
var react_virtualized_1 = require("react-virtualized");
var ramda_1 = require("ramda");
var frontend_collective_react_dnd_scrollzone_1 = __importStar(require("frontend-collective-react-dnd-scrollzone"));
var react_dnd_1 = require("react-dnd");
var withStyles_1 = __importDefault(require("@mui/styles/withStyles"));
var TreeNode_1 = require("./components/TreeNode");
var NodeRendererDefault_1 = require("./components/NodeRendererDefault");
var TreePlaceholder_1 = require("./components/TreePlaceholder");
var PlaceholderRendererDefault_1 = require("./components/PlaceholderRendererDefault");
var tree_1 = require("./helpers/tree");
var memoizedTreeData_1 = require("./helpers/memoizedTreeData");
var generic_1 = require("./helpers/generic");
var defaultHandlers_1 = require("./helpers/defaultHandlers");
var dndManager_1 = __importDefault(require("./helpers/dndManager"));
var styles_1 = require("./styles");
var treeIdCounter = 1;
var mergeTheme = function (props) {
    var merged = __assign(__assign({}, props), { style: __assign(__assign({}, props.theme.style), props.style), innerStyle: __assign(__assign({}, props.theme.innerStyle), props.innerStyle), reactVirtualizedListProps: __assign(__assign({}, props.theme.reactVirtualizedListProps), props.reactVirtualizedListProps) });
    var overridableDefaults = {
        nodeContentRenderer: NodeRendererDefault_1.NodeRendererDefault,
        placeholderRenderer: PlaceholderRendererDefault_1.PlaceholderRendererDefault,
        rowHeight: 62,
        scaffoldBlockPxWidth: 44,
        slideRegionSize: 100,
        treeNodeRenderer: TreeNode_1.TreeNode
    };
    Object.keys(overridableDefaults).forEach(function (propKey) {
        // If prop has been specified, do not change it
        // If prop is specified in theme, use the theme setting
        // If all else fails, fall back to the default
        if (props[propKey] === null) {
            merged[propKey] =
                typeof props.theme[propKey] !== 'undefined' ? props.theme[propKey] : overridableDefaults[propKey];
        }
    });
    return merged;
};
var ReactSortableTree = /** @class */ (function (_super) {
    __extends(ReactSortableTree, _super);
    function ReactSortableTree(props) {
        var _this = _super.call(this, props) || this;
        var _a = mergeTheme(props), dndType = _a.dndType, nodeContentRenderer = _a.nodeContentRenderer, treeNodeRenderer = _a.treeNodeRenderer, isVirtualized = _a.isVirtualized, slideRegionSize = _a.slideRegionSize;
        _this.dndManager = new dndManager_1.default(_this);
        // Wrapping classes for use with react-dnd
        _this.treeId = "rst__".concat(treeIdCounter);
        treeIdCounter += 1;
        _this.dndType = dndType || _this.treeId;
        _this.nodeContentRenderer = _this.dndManager.wrapSource(nodeContentRenderer);
        _this.treePlaceholderRenderer = _this.dndManager.wrapPlaceholder(TreePlaceholder_1.TreePlaceholder);
        _this.treeNodeRenderer = _this.dndManager.wrapTarget(treeNodeRenderer);
        // Prepare scroll-on-drag options for this list
        if (isVirtualized) {
            _this.scrollZoneVirtualList = (frontend_collective_react_dnd_scrollzone_1.createScrollingComponent || frontend_collective_react_dnd_scrollzone_1.default)(react_virtualized_1.List);
            _this.vStrength = (0, frontend_collective_react_dnd_scrollzone_1.createVerticalStrength)(slideRegionSize);
            _this.hStrength = (0, frontend_collective_react_dnd_scrollzone_1.createHorizontalStrength)(slideRegionSize);
        }
        _this.state = {
            draggingTreeData: null,
            draggedNode: null,
            draggedMinimumTreeIndex: null,
            draggedDepth: null,
            searchMatches: [],
            searchFocusTreeIndex: null,
            dragging: false,
            draggingExpandedPaths: [],
            ignoreOneTreeUpdate: false,
            // props that need to be used in gDSFP or static functions will be stored here
            instanceProps: {
                treeData: _this.props.treeData || [],
                searchQuery: null,
                searchFocusOffset: null,
                ignoreTreeChanges: false
            }
        };
        _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);
        return _this;
    }
    ReactSortableTree.prototype.componentDidMount = function () {
        ReactSortableTree.loadLazyChildren(this.props);
        var stateUpdate = ReactSortableTree.search(this.props, this.state, true, true, false);
        this.setState(stateUpdate);
        // Hook into react-dnd state changes to detect when the drag ends
        // TODO: This is very brittle, so it needs to be replaced if react-dnd
        // offers a more official way to detect when a drag ends
        this.clearMonitorSubscription = this.props.dragDropManager
            .getMonitor()
            .subscribeToStateChange(this.handleDndMonitorChange);
    };
    ReactSortableTree.getDerivedStateFromProps = function (nextProps, prevState) {
        var instanceProps = prevState.instanceProps;
        var newState = {};
        if (!(0, ramda_1.equals)(instanceProps.searchQuery, nextProps.searchQuery)) {
            Object.assign(newState, ReactSortableTree.search(nextProps, prevState, true, true, false));
        }
        else if (instanceProps.searchFocusOffset !== nextProps.searchFocusOffset) {
            Object.assign(newState, ReactSortableTree.search(nextProps, prevState, true, true, true));
        }
        instanceProps.searchQuery = nextProps.searchQuery;
        instanceProps.searchFocusOffset = nextProps.searchFocusOffset;
        newState.instanceProps = instanceProps;
        return newState;
    };
    // listen to dragging
    ReactSortableTree.prototype.componentDidUpdate = function (prevProps, prevState) {
        var _this = this;
        // if it is not the same then call the onDragStateChanged
        if (this.state.dragging !== prevState.dragging) {
            if (this.props.onDragStateChanged) {
                this.props.onDragStateChanged({
                    isDragging: this.state.dragging,
                    draggedNode: this.state.draggedNode
                });
            }
        }
        if (this.state.dragging) {
            var isTreeDataEqual = (0, ramda_1.equals)(this.props.treeData, prevProps.treeData);
            if (!isTreeDataEqual) {
                var draggingTreeData_1 = (0, tree_1.removeNode)({
                    treeData: this.props.treeData,
                    path: prevState.draggedPath,
                    getNodeKey: this.props.getNodeKey
                }).treeData;
                this.setState(function (_a) {
                    var draggingExpandedPaths = _a.draggingExpandedPaths;
                    return ({
                        draggingTreeData: draggingExpandedPaths.reduce(function (treeData, path) {
                            var newTree;
                            try {
                                newTree = (0, tree_1.changeNodeAtPath)({
                                    treeData: treeData,
                                    path: path,
                                    newNode: function (_a) {
                                        var node = _a.node;
                                        return (__assign(__assign({}, node), { expanded: true }));
                                    },
                                    getNodeKey: _this.props.getNodeKey
                                });
                            }
                            catch (_a) {
                                newTree = treeData;
                            }
                            return newTree;
                        }, draggingTreeData_1)
                    });
                });
            }
            else if (this.state.draggingTreeData && prevState.draggingTreeData !== this.state.draggingTreeData) {
                (0, tree_1.walk)({
                    treeData: this.state.draggingTreeData,
                    getNodeKey: this.props.getNodeKey,
                    callback: function (_a) {
                        var node = _a.node, path = _a.path, lowerSiblingCounts = _a.lowerSiblingCounts, treeIndex = _a.treeIndex;
                        if (node.pending && node.expanded) {
                            _this.props.onLazyLoadChildren({
                                node: node,
                                path: path,
                                lowerSiblingCounts: lowerSiblingCounts,
                                treeIndex: treeIndex
                            });
                        }
                    }
                });
            }
        }
        else if (this.props.treeData !== prevProps.treeData) {
            var isTreeDataEqual = (0, ramda_1.equals)(this.props.treeData, prevProps.treeData);
            var _a = this.state, ignoreOneTreeUpdate = _a.ignoreOneTreeUpdate, instanceProps = _a.instanceProps;
            var newState = {};
            if (!isTreeDataEqual) {
                if (ignoreOneTreeUpdate) {
                    newState.ignoreOneTreeUpdate = false;
                }
                else {
                    newState.searchFocusTreeIndex = null;
                    ReactSortableTree.loadLazyChildren(this.props);
                    Object.assign(newState, ReactSortableTree.search(this.props, this.state, false, false, false));
                }
                newState.draggingTreeData = null;
                newState.draggedNode = null;
                newState.draggedMinimumTreeIndex = null;
                newState.draggedDepth = null;
                newState.dragging = false;
                newState.draggingExpandedPaths = [];
                newState.instanceProps = __assign(__assign({}, instanceProps), { treeData: this.props.treeData });
                this.setState(newState);
            }
        }
    };
    ReactSortableTree.prototype.componentWillUnmount = function () {
        this.clearMonitorSubscription();
        this.dndManager.resetDragVariables();
    };
    ReactSortableTree.prototype.getRows = function (treeData) {
        return (0, memoizedTreeData_1.memoizedGetFlatDataFromTree)({
            ignoreCollapsed: true,
            getNodeKey: this.props.getNodeKey,
            treeData: treeData
        });
    };
    ReactSortableTree.prototype.handleDndMonitorChange = function () {
        var monitor = this.props.dragDropManager.getMonitor();
        // If the drag ends and the tree is still in a mid-drag state,
        // it means that the drag was canceled or the dragSource dropped
        // elsewhere, and we should reset the state of this tree
        if (!monitor.isDragging() && this.state.draggingTreeData) {
            this.endDrag();
        }
    };
    ReactSortableTree.prototype.toggleChildrenVisibility = function (_a) {
        var targetNode = _a.node, path = _a.path;
        var instanceProps = this.state.instanceProps;
        var treeData = (0, tree_1.changeNodeAtPath)({
            treeData: instanceProps.treeData,
            path: path,
            newNode: function (_a) {
                var node = _a.node;
                return (__assign(__assign({}, node), { expanded: !node.expanded }));
            },
            getNodeKey: this.props.getNodeKey
        });
        this.props.onChange(treeData);
        this.props.onVisibilityToggle({
            treeData: treeData,
            node: targetNode,
            expanded: !targetNode.expanded,
            path: path
        });
    };
    ReactSortableTree.prototype.moveNode = function (_a) {
        var _this = this;
        var node = _a.node, prevPath = _a.path, prevTreeIndex = _a.treeIndex, depth = _a.depth, minimumTreeIndex = _a.minimumTreeIndex;
        var _b = (0, tree_1.insertNode)({
            treeData: this.state.draggingTreeData,
            newNode: node,
            depth: depth,
            minimumTreeIndex: minimumTreeIndex,
            expandParent: true,
            getNodeKey: this.props.getNodeKey
        }), treeData = _b.treeData, treeIndex = _b.treeIndex, path = _b.path, nextParentNode = _b.parentNode;
        this.setState({
            draggingTreeData: null,
            draggedNode: null,
            draggedMinimumTreeIndex: null,
            draggedDepth: null,
            dragging: false,
            draggingExpandedPaths: []
        }, function () {
            _this.props.onChange(treeData);
            _this.props.onMoveNode({
                treeData: treeData,
                node: node,
                treeIndex: treeIndex,
                path: path,
                nextPath: path,
                nextTreeIndex: treeIndex,
                prevPath: prevPath,
                prevTreeIndex: prevTreeIndex,
                nextParentNode: nextParentNode
            });
        });
    };
    // returns the new state after search
    ReactSortableTree.search = function (props, state, seekIndex, expand, singleSearch) {
        var onChange = props.onChange, getNodeKey = props.getNodeKey, searchFinishCallback = props.searchFinishCallback, searchQuery = props.searchQuery, searchMethod = props.searchMethod, searchFocusOffset = props.searchFocusOffset, onlyExpandSearchedNodes = props.onlyExpandSearchedNodes;
        var instanceProps = state.instanceProps;
        // Skip search if no conditions are specified
        if (!searchQuery && !searchMethod) {
            if (searchFinishCallback) {
                searchFinishCallback([]);
            }
            return { searchMatches: [] };
        }
        var newState = {};
        // if onlyExpandSearchedNodes collapse the tree and search
        var _a = (0, tree_1.find)({
            getNodeKey: getNodeKey,
            treeData: onlyExpandSearchedNodes
                ? (0, tree_1.toggleExpandedForAll)({
                    treeData: instanceProps.treeData,
                    expanded: false
                })
                : instanceProps.treeData,
            searchQuery: searchQuery,
            searchMethod: searchMethod || defaultHandlers_1.defaultSearchMethod,
            searchFocusOffset: searchFocusOffset,
            expandAllMatchPaths: expand && !singleSearch,
            expandFocusMatchPaths: !!expand
        }), expandedTreeData = _a.treeData, searchMatches = _a.matches;
        // Update the tree with data leaving all paths leading to matching nodes open
        if (expand) {
            newState.ignoreOneTreeUpdate = true; // Prevents infinite loop
            onChange(expandedTreeData);
        }
        if (searchFinishCallback) {
            searchFinishCallback(searchMatches);
        }
        var searchFocusTreeIndex = null;
        if (seekIndex && searchFocusOffset !== null && searchFocusOffset < searchMatches.length) {
            searchFocusTreeIndex = searchMatches[searchFocusOffset].treeIndex;
        }
        newState.searchMatches = searchMatches;
        newState.searchFocusTreeIndex = searchFocusTreeIndex;
        return newState;
    };
    ReactSortableTree.prototype.startDrag = function (_a) {
        var _this = this;
        var path = _a.path;
        this.setState(function (prevState) {
            var _a = (0, tree_1.removeNode)({
                treeData: prevState.instanceProps.treeData,
                path: path,
                getNodeKey: _this.props.getNodeKey
            }), draggingTreeData = _a.treeData, draggedNode = _a.node, draggedMinimumTreeIndex = _a.treeIndex;
            return {
                draggingTreeData: draggingTreeData,
                draggedNode: draggedNode,
                draggedDepth: path.length - 1,
                draggedMinimumTreeIndex: draggedMinimumTreeIndex,
                draggedPath: path,
                dragging: true
            };
        });
    };
    ReactSortableTree.prototype.dragHover = function (_a) {
        var _this = this;
        var draggedNode = _a.node, draggedDepth = _a.depth, draggedMinimumTreeIndex = _a.minimumTreeIndex;
        // Ignore this hover if it is at the same position as the last hover
        if (this.state.draggedDepth === draggedDepth &&
            this.state.draggedMinimumTreeIndex === draggedMinimumTreeIndex) {
            return;
        }
        this.setState(function (_a) {
            var draggingTreeData = _a.draggingTreeData, instanceProps = _a.instanceProps, draggingExpandedPaths = _a.draggingExpandedPaths;
            // Fall back to the tree data if something is being dragged in from
            //  an external element
            var newDraggingTreeData = draggingTreeData || instanceProps.treeData;
            var addedResult = (0, memoizedTreeData_1.memoizedInsertNode)({
                treeData: newDraggingTreeData,
                newNode: draggedNode,
                depth: draggedDepth,
                minimumTreeIndex: draggedMinimumTreeIndex,
                expandParent: true,
                getNodeKey: _this.props.getNodeKey
            });
            var rows = _this.getRows(addedResult.treeData);
            var expandedParentPath = rows[addedResult.treeIndex].path;
            var nodePath = expandedParentPath.slice(0, -1);
            var newDraggingExpandedPaths = (0, ramda_1.pipe)((0, ramda_1.when)((0, ramda_1.always)((0, ramda_1.gt)(nodePath.length, 0)), (0, ramda_1.append)(nodePath)), (0, ramda_1.uniqWith)((0, ramda_1.eqBy)(String)))(draggingExpandedPaths);
            return {
                draggedNode: draggedNode,
                draggedDepth: draggedDepth,
                draggedMinimumTreeIndex: draggedMinimumTreeIndex,
                draggingTreeData: (0, tree_1.changeNodeAtPath)({
                    treeData: newDraggingTreeData,
                    path: nodePath,
                    newNode: function (_a) {
                        var node = _a.node;
                        return (__assign(__assign({}, node), { expanded: true }));
                    },
                    getNodeKey: _this.props.getNodeKey
                }),
                // reset the scroll focus so it doesn't jump back
                // to a search result while dragging
                searchFocusTreeIndex: null,
                dragging: true,
                draggingExpandedPaths: newDraggingExpandedPaths
            };
        });
    };
    ReactSortableTree.prototype.endDrag = function (dropResult) {
        var _this = this;
        var instanceProps = this.state.instanceProps;
        var resetTree = function () {
            return _this.setState({
                draggingTreeData: null,
                draggedNode: null,
                draggedMinimumTreeIndex: null,
                draggedDepth: null,
                dragging: false
            });
        };
        // Drop was cancelled
        if (!dropResult) {
            resetTree();
        }
        else if (dropResult.treeId !== this.treeId) {
            // The node was dropped in an external drop target or tree
            var node = dropResult.node, path = dropResult.path, treeIndex = dropResult.treeIndex;
            var shouldCopy = this.props.shouldCopyOnOutsideDrop;
            if (typeof shouldCopy === 'function') {
                shouldCopy = shouldCopy({
                    node: node,
                    prevTreeIndex: treeIndex,
                    prevPath: path
                });
            }
            var treeData = this.state.draggingTreeData || instanceProps.treeData;
            // If copying is enabled, a drop outside leaves behind a copy in the
            //  source tree
            if (shouldCopy) {
                treeData = (0, tree_1.changeNodeAtPath)({
                    treeData: instanceProps.treeData, // use treeData unaltered by the drag operation
                    path: path,
                    newNode: function (_a) {
                        var copyNode = _a.node;
                        return (__assign({}, copyNode));
                    }, // create a shallow copy of the node
                    getNodeKey: this.props.getNodeKey
                });
            }
            this.props.onChange(treeData);
            this.props.onMoveNode({
                treeData: treeData,
                node: node,
                treeIndex: null,
                path: null,
                nextPath: null,
                nextTreeIndex: null,
                prevPath: path,
                prevTreeIndex: treeIndex
            });
        }
    };
    ReactSortableTree.prototype.drop = function (dropResult) {
        this.moveNode(dropResult);
    };
    ReactSortableTree.prototype.canNodeHaveChildren = function (node) {
        var canNodeHaveChildren = this.props.canNodeHaveChildren;
        if (canNodeHaveChildren) {
            return canNodeHaveChildren(node);
        }
        return true;
    };
    // Load any children in the tree that are given by a function
    // calls the onChange callback on the new treeData
    ReactSortableTree.loadLazyChildren = function (props) {
        (0, tree_1.walk)({
            treeData: props.treeData,
            getNodeKey: props.getNodeKey,
            callback: function (_a) {
                var node = _a.node, path = _a.path, lowerSiblingCounts = _a.lowerSiblingCounts, treeIndex = _a.treeIndex;
                // If the node has children defined by a function, and is either expanded
                //  or set to load even before expansion, run the function.
                if (node.pending && (node.expanded || props.loadCollapsedLazyChildren)) {
                    // Call the children fetching function
                    props.onLazyLoadChildren({
                        node: node,
                        path: path,
                        lowerSiblingCounts: lowerSiblingCounts,
                        treeIndex: treeIndex,
                        // Provide a helper to append the new data when it is received
                        done: function (childrenArray) {
                            return props.onChange((0, tree_1.changeNodeAtPath)({
                                treeData: props.treeData,
                                path: path,
                                newNode: function (_a) {
                                    var oldNode = _a.node;
                                    // Only replace the old node if it's the one we set off to find children
                                    //  for in the first place
                                    return oldNode === node
                                        ? __assign(__assign({}, oldNode), { children: childrenArray }) : oldNode;
                                },
                                getNodeKey: props.getNodeKey
                            }), true);
                        }
                    });
                }
            }
        });
    };
    ReactSortableTree.prototype.renderRow = function (row, _a) {
        var listIndex = _a.listIndex, style = _a.style, getPrevRow = _a.getPrevRow, matchKeys = _a.matchKeys, swapFrom = _a.swapFrom, swapDepth = _a.swapDepth, swapLength = _a.swapLength;
        var node = row.node, parentNode = row.parentNode, path = row.path, lowerSiblingCounts = row.lowerSiblingCounts, treeIndex = row.treeIndex;
        var _b = mergeTheme(this.props), canDrag = _b.canDrag, generateNodeProps = _b.generateNodeProps, scaffoldBlockPxWidth = _b.scaffoldBlockPxWidth, searchFocusOffset = _b.searchFocusOffset, rowDirection = _b.rowDirection;
        var TreeNodeRenderer = this.treeNodeRenderer;
        var NodeContentRenderer = this.nodeContentRenderer;
        var nodeKey = path[path.length - 1];
        var isSearchMatch = nodeKey in matchKeys;
        var isSearchFocus = isSearchMatch && matchKeys[nodeKey] === searchFocusOffset;
        var callbackParams = {
            node: node,
            parentNode: parentNode,
            path: path,
            lowerSiblingCounts: lowerSiblingCounts,
            treeIndex: treeIndex,
            isSearchMatch: isSearchMatch,
            isSearchFocus: isSearchFocus
        };
        var nodeProps = !generateNodeProps ? {} : generateNodeProps(callbackParams);
        var rowCanDrag = typeof canDrag !== 'function' ? canDrag : canDrag(callbackParams);
        var sharedProps = {
            treeIndex: treeIndex,
            scaffoldBlockPxWidth: scaffoldBlockPxWidth,
            node: node,
            path: path,
            treeId: this.treeId,
            rowDirection: rowDirection
        };
        return (react_1.default.createElement(TreeNodeRenderer, __assign({ style: style, key: nodeKey, listIndex: listIndex, getPrevRow: getPrevRow, lowerSiblingCounts: lowerSiblingCounts, swapFrom: swapFrom, swapLength: swapLength, swapDepth: swapDepth }, sharedProps),
            react_1.default.createElement(NodeContentRenderer, __assign({ parentNode: parentNode, isSearchMatch: isSearchMatch, isSearchFocus: isSearchFocus, canDrag: rowCanDrag, toggleChildrenVisibility: this.toggleChildrenVisibility }, sharedProps, nodeProps))));
    };
    ReactSortableTree.prototype.render = function () {
        var _this = this;
        var _a = mergeTheme(this.props), dragDropManager = _a.dragDropManager, style = _a.style, className = _a.className, innerStyle = _a.innerStyle, rowHeight = _a.rowHeight, isVirtualized = _a.isVirtualized, placeholderRenderer = _a.placeholderRenderer, reactVirtualizedListProps = _a.reactVirtualizedListProps, getNodeKey = _a.getNodeKey, rowDirection = _a.rowDirection;
        var _b = this.state, searchMatches = _b.searchMatches, searchFocusTreeIndex = _b.searchFocusTreeIndex, draggedNode = _b.draggedNode, draggedDepth = _b.draggedDepth, draggedMinimumTreeIndex = _b.draggedMinimumTreeIndex;
        var treeData = this.state.draggingTreeData || this.props.treeData;
        var rowDirectionClass = rowDirection === 'rtl' ? 'rst__rtl' : null;
        var rows;
        var swapFrom = null;
        var swapLength = null;
        if (draggedNode && draggedMinimumTreeIndex !== null) {
            var addedResult = (0, memoizedTreeData_1.memoizedInsertNode)({
                treeData: treeData,
                newNode: draggedNode,
                depth: draggedDepth,
                minimumTreeIndex: draggedMinimumTreeIndex,
                expandParent: true,
                getNodeKey: getNodeKey
            });
            var swapTo = draggedMinimumTreeIndex;
            swapFrom = addedResult.treeIndex;
            swapLength = 1 + (0, memoizedTreeData_1.memoizedGetDescendantCount)({ node: draggedNode });
            rows = (0, generic_1.slideRows)(this.getRows(addedResult.treeData), swapFrom, swapTo, swapLength);
        }
        else {
            rows = this.getRows(treeData);
        }
        // Get indices for rows that match the search conditions
        var matchKeys = {};
        searchMatches.forEach(function (_a, i) {
            var path = _a.path;
            matchKeys[path[path.length - 1]] = i;
        });
        // Seek to the focused search result if there is one specified
        var scrollToInfo = searchFocusTreeIndex !== null ? { scrollToIndex: searchFocusTreeIndex } : {};
        var containerStyle = style;
        var list;
        if (rows.length < 1) {
            var Placeholder = this.treePlaceholderRenderer;
            var PlaceholderContent = placeholderRenderer;
            list = (react_1.default.createElement(Placeholder, { treeId: this.treeId, drop: this.drop },
                react_1.default.createElement(PlaceholderContent, null)));
        }
        else if (isVirtualized) {
            containerStyle = __assign({ height: '100%' }, containerStyle);
            var ScrollZoneVirtualList_1 = this.scrollZoneVirtualList;
            // Render list with react-virtualized
            list = (react_1.default.createElement(react_virtualized_1.AutoSizer, null, function (_a) {
                var height = _a.height, width = _a.width;
                return (react_1.default.createElement(ScrollZoneVirtualList_1, __assign({}, scrollToInfo, { dragDropManager: dragDropManager, verticalStrength: _this.vStrength, horizontalStrength: _this.hStrength, speed: 30, scrollToAlignment: "start", className: _this.props.classes.rst__virtualScrollOverride, width: width, onScroll: function (_a) {
                        var scrollTop = _a.scrollTop;
                        _this.scrollTop = scrollTop;
                    }, height: height, style: innerStyle, rowCount: rows.length, estimatedRowSize: typeof rowHeight !== 'function' ? rowHeight : undefined, rowHeight: typeof rowHeight !== 'function'
                        ? rowHeight
                        : function (_a) {
                            var index = _a.index;
                            return rowHeight({
                                index: index,
                                treeIndex: index,
                                node: rows[index].node,
                                path: rows[index].path
                            });
                        }, rowRenderer: function (_a) {
                        var index = _a.index, rowStyle = _a.style;
                        return _this.renderRow(rows[index], {
                            listIndex: index,
                            style: rowStyle,
                            getPrevRow: function () { return rows[index - 1] || null; },
                            matchKeys: matchKeys,
                            swapFrom: swapFrom,
                            swapDepth: draggedDepth,
                            swapLength: swapLength
                        });
                    } }, reactVirtualizedListProps)));
            }));
        }
        else {
            // Render list without react-virtualized
            list = rows.map(function (row, index) {
                return _this.renderRow(row, {
                    listIndex: index,
                    style: {
                        height: typeof rowHeight !== 'function'
                            ? rowHeight
                            : rowHeight({
                                index: index,
                                treeIndex: index,
                                node: row.node,
                                path: row.path
                            })
                    },
                    getPrevRow: function () { return rows[index - 1] || null; },
                    matchKeys: matchKeys,
                    swapFrom: swapFrom,
                    swapDepth: draggedDepth,
                    swapLength: swapLength
                });
            });
        }
        return (react_1.default.createElement("div", { className: (0, classnames_1.default)('rst__tree', className, rowDirectionClass), style: containerStyle }, list));
    };
    return ReactSortableTree;
}(react_1.Component));
ReactSortableTree.propTypes = {
    classes: prop_types_1.default.object,
    dragDropManager: prop_types_1.default.shape({
        getMonitor: prop_types_1.default.func
    }).isRequired,
    // Tree data in the following format:
    // [{title: 'main', subtitle: 'sub'}, { title: 'value2', expanded: true, children: [{ title: 'value3') }] }]
    // `title` is the primary label for the node
    // `subtitle` is a secondary label for the node
    // `expanded` shows children of the node if true, or hides them if false. Defaults to false.
    // `children` is an array of child nodes belonging to the node.
    treeData: prop_types_1.default.arrayOf(prop_types_1.default.object).isRequired,
    // Style applied to the container wrapping the tree (style defaults to {height: '100%'})
    style: prop_types_1.default.shape({}),
    // Class name for the container wrapping the tree
    className: prop_types_1.default.string,
    // Style applied to the inner, scrollable container (for padding, etc.)
    innerStyle: prop_types_1.default.shape({}),
    // Used by react-virtualized
    // Either a fixed row height (number) or a function that returns the
    // height of a row given its index: `({ index: number }): number`
    rowHeight: prop_types_1.default.oneOfType([prop_types_1.default.number, prop_types_1.default.func]),
    // Size in px of the region near the edges that initiates scrolling on dragover
    slideRegionSize: prop_types_1.default.number,
    // Custom properties to hand to the react-virtualized list
    // https://github.com/bvaughn/react-virtualized/blob/master/docs/List.md#prop-types
    reactVirtualizedListProps: prop_types_1.default.shape({}),
    // The width of the blocks containing the lines representing the structure of the tree.
    scaffoldBlockPxWidth: prop_types_1.default.number,
    // Maximum depth nodes can be inserted at. Defaults to infinite.
    maxDepth: prop_types_1.default.number,
    // The method used to search nodes.
    // Defaults to a function that uses the `searchQuery` string to search for nodes with
    // matching `title` or `subtitle` values.
    // NOTE: Changing `searchMethod` will not update the search, but changing the `searchQuery` will.
    searchMethod: prop_types_1.default.func,
    // Used by the `searchMethod` to highlight and scroll to matched nodes.
    // Should be a string for the default `searchMethod`, but can be anything when using a custom search.
    searchQuery: prop_types_1.default.any,
    // Outline the <`searchFocusOffset`>th node and scroll to it.
    searchFocusOffset: prop_types_1.default.number,
    // Get the nodes that match the search criteria. Used for counting total matches, etc.
    searchFinishCallback: prop_types_1.default.func,
    // Generate an object with additional props to be passed to the node renderer.
    // Use this for adding buttons via the `buttons` key,
    // or additional `style` / `className` settings.
    generateNodeProps: prop_types_1.default.func,
    // Set to false to disable virtualization.
    // NOTE: Auto-scrolling while dragging, and scrolling to the `searchFocusOffset` will be disabled.
    isVirtualized: prop_types_1.default.bool,
    treeNodeRenderer: prop_types_1.default.func,
    // Override the default component for rendering nodes (but keep the scaffolding generator)
    // This is an advanced option for complete customization of the appearance.
    // It is best to copy the component in `node-renderer-default.js` to use as a base, and customize as needed.
    nodeContentRenderer: prop_types_1.default.func,
    // Override the default component for rendering an empty tree
    // This is an advanced option for complete customization of the appearance.
    // It is best to copy the component in `placeholder-renderer-default.js` to use as a base,
    // and customize as needed.
    placeholderRenderer: prop_types_1.default.func,
    theme: prop_types_1.default.shape({
        style: prop_types_1.default.shape({}),
        innerStyle: prop_types_1.default.shape({}),
        reactVirtualizedListProps: prop_types_1.default.shape({}),
        scaffoldBlockPxWidth: prop_types_1.default.number,
        slideRegionSize: prop_types_1.default.number,
        rowHeight: prop_types_1.default.oneOfType([prop_types_1.default.number, prop_types_1.default.func]),
        treeNodeRenderer: prop_types_1.default.func,
        nodeContentRenderer: prop_types_1.default.func,
        placeholderRenderer: prop_types_1.default.func
    }),
    // Determine the unique key used to identify each node and
    // generate the `path` array passed in callbacks.
    // By default, returns the index in the tree (omitting hidden nodes).
    getNodeKey: prop_types_1.default.func,
    // Called whenever tree data changed.
    // Just like with React input elements, you have to update your
    // own component's data to see the changes reflected.
    onChange: prop_types_1.default.func.isRequired,
    // Called after node move operation.
    onMoveNode: prop_types_1.default.func,
    // Determine whether a node can be dragged. Set to false to disable dragging on all nodes.
    canDrag: prop_types_1.default.oneOfType([prop_types_1.default.func, prop_types_1.default.bool]),
    // Determine whether a node can be dropped based on its path and parents'.
    canDrop: prop_types_1.default.func,
    // Determine whether a node can have children
    canNodeHaveChildren: prop_types_1.default.func,
    // When true, or a callback returning true, dropping nodes to react-dnd
    // drop targets outside of this tree will not remove them from this tree
    shouldCopyOnOutsideDrop: prop_types_1.default.oneOfType([prop_types_1.default.func, prop_types_1.default.bool]),
    // Called after children nodes collapsed or expanded.
    onVisibilityToggle: prop_types_1.default.func,
    dndType: prop_types_1.default.string,
    // Called to track between dropped and dragging
    onDragStateChanged: prop_types_1.default.func,
    // Specify that nodes that do not match search will be collapsed
    onlyExpandSearchedNodes: prop_types_1.default.bool,
    // rtl support
    rowDirection: prop_types_1.default.string,
    onLazyLoadChildren: prop_types_1.default.func,
    ignoreTreeChanges: prop_types_1.default.bool
};
ReactSortableTree.defaultProps = {
    canDrag: true,
    canDrop: null,
    canNodeHaveChildren: function () { return true; },
    className: '',
    dndType: null,
    generateNodeProps: null,
    getNodeKey: defaultHandlers_1.defaultGetNodeKey,
    innerStyle: {},
    isVirtualized: true,
    maxDepth: null,
    treeNodeRenderer: null,
    nodeContentRenderer: null,
    onMoveNode: function () { },
    onVisibilityToggle: function () { },
    placeholderRenderer: null,
    reactVirtualizedListProps: {},
    rowHeight: null,
    scaffoldBlockPxWidth: null,
    searchFinishCallback: null,
    searchFocusOffset: null,
    searchMethod: null,
    searchQuery: null,
    shouldCopyOnOutsideDrop: false,
    slideRegionSize: null,
    style: {},
    theme: {},
    onDragStateChanged: function () { },
    onlyExpandSearchedNodes: false,
    rowDirection: 'ltr',
    onLazyLoadChildren: function () { },
    ignoreTreeChanges: false
};
var StyledReactSortableTree = (0, withStyles_1.default)(styles_1.styles)(ReactSortableTree);
var SortableTree = function (props) { return (react_1.default.createElement(react_dnd_1.DndContext.Consumer, null, function (_a) {
    var dragDropManager = _a.dragDropManager;
    return dragDropManager === undefined ? null : (react_1.default.createElement(StyledReactSortableTree, __assign({}, props, { dragDropManager: dragDropManager })));
})); };
exports.default = SortableTree;
