"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RecycleTree = exports.RenderErrorType = void 0;
const tslib_1 = require("tslib");
const fuzzy_1 = tslib_1.__importDefault(require("fuzzy"));
const react_1 = tslib_1.__importStar(require("react"));
const react_window_1 = require("react-window");
const ide_utils_1 = require("@opensumi/ide-utils");
const scrollbars_1 = require("../scrollbars");
const prompt_1 = require("./prompt");
const NewPromptHandle_1 = require("./prompt/NewPromptHandle");
const tree_1 = require("./tree");
const TreeNodeRendererWrap_1 = require("./TreeNodeRendererWrap");
const types_1 = require("./types");
var RenderErrorType;
(function (RenderErrorType) {
    RenderErrorType[RenderErrorType["RENDER_ITEM"] = 0] = "RENDER_ITEM";
    RenderErrorType[RenderErrorType["GET_RENDED_KEY"] = 1] = "GET_RENDED_KEY";
    RenderErrorType[RenderErrorType["RENDER_ERROR"] = 2] = "RENDER_ERROR";
})(RenderErrorType || (exports.RenderErrorType = RenderErrorType = {}));
const InnerElementType = react_1.default.forwardRef((props, ref) => {
    const { style, ...rest } = props;
    return (react_1.default.createElement("div", { ref: ref, style: {
            ...style,
            height: `${parseFloat(style.height) + RecycleTree.PADDING_BOTTOM_SIZE}px`,
        }, ...rest }));
});
class RecycleTree extends react_1.default.Component {
    constructor() {
        super(...arguments);
        this.idxToRendererPropsCache = new Map();
        this.dynamicSizeMap = new Map();
        this.listRef = react_1.default.createRef();
        this.disposables = new ide_utils_1.DisposableCollection();
        this.onErrorEmitter = new ide_utils_1.Emitter();
        this.onDidUpdateEmitter = new ide_utils_1.Emitter();
        this.onDidModelChangeEmitter = new ide_utils_1.Emitter();
        // 索引应该比目标折叠节点索引+1，即位于折叠节点下的首个节点
        this.newPromptInsertionIndex = -1;
        this.idToFilterRendererPropsCache = new Map();
        this.filterFlattenBranchChildrenCache = new Map();
        this.filterWatcherDisposeCollection = new ide_utils_1.DisposableCollection();
        this.queueUpdatePromise = null;
        this.updateCancelToken = new ide_utils_1.CancellationTokenSource();
        this.expandNodeCancelToken = new ide_utils_1.CancellationTokenSource();
        this.willUpdateTasks = 0;
        this.queueUpdateThrottler = new ide_utils_1.Throttler();
        // 批量更新Tree节点
        this.doBatchUpdate = (() => {
            const commitUpdate = (resolver, token) => {
                // 已经在 componentWillUnMount 中 disposed 了
                if (this.disposables.disposed) {
                    return;
                }
                const { root } = this.props.model;
                let newFilePromptInsertionIndex = -1;
                if (this.promptTargetID > -1 &&
                    this.promptHandle instanceof NewPromptHandle_1.NewPromptHandle &&
                    this.promptHandle.parent &&
                    this.promptHandle.parent.expanded &&
                    root.isItemVisibleAtSurface(this.promptHandle.parent) &&
                    !this.promptHandle.destroyed) {
                    const idx = root.getIndexAtTreeNodeId(this.promptTargetID);
                    if (idx > -1 || this.promptHandle.parent === root) {
                        // 如果新建节点类型为普通节点，则在可折叠节点后插入
                        if (this.promptHandle.type === types_1.TreeNodeType.TreeNode) {
                            const parent = this.promptHandle.parent;
                            if (parent) {
                                newFilePromptInsertionIndex = this.getNewPromptInsertIndex(idx, parent);
                            }
                            else {
                                newFilePromptInsertionIndex = idx + 1;
                            }
                        }
                        else {
                            newFilePromptInsertionIndex = idx + 1;
                        }
                    }
                    else {
                        this.promptTargetID = -1;
                    }
                }
                if (newFilePromptInsertionIndex === -1) {
                    // 新建节点，异常情况下的特殊处理，需要把promptHandle销毁，仅需要考虑NewPromptHandle情况
                    if (this.promptHandle && this.promptHandle.constructor === NewPromptHandle_1.NewPromptHandle && !this.promptHandle.destroyed) {
                        this.promptHandle.destroy();
                    }
                }
                this.newPromptInsertionIndex = newFilePromptInsertionIndex;
                // 清理cache，这里可以确保分支已更新完毕
                this.idxToRendererPropsCache.clear();
                // 更新React组件
                if (token.isCancellationRequested) {
                    resolver();
                    return;
                }
                this.forceUpdate(() => {
                    resolver();
                    // 如果存在过滤条件，同时筛选一下展示节点
                    if (this.props.filter && this.props.filterProvider && this.props.filterProvider.filterAlways) {
                        this.filterItems(this.props.filter);
                    }
                });
            };
            return () => {
                const doUpdate = (batchUpdatePromise, batchUpdateResolver) => {
                    const shouldUpdate = !!this.willUpdateTasks;
                    this.willUpdateTasks = 0;
                    if (!shouldUpdate) {
                        batchUpdateResolver.resolve();
                    }
                    else {
                        this.updateCancelToken.cancel();
                        this.updateCancelToken = new ide_utils_1.CancellationTokenSource();
                        const token = this.updateCancelToken.token;
                        requestAnimationFrame(() => {
                            if (token.isCancellationRequested) {
                                batchUpdateResolver();
                                return;
                            }
                            commitUpdate(batchUpdateResolver, token);
                        });
                    }
                    return batchUpdatePromise;
                };
                let batchUpdateResolver;
                const batchUpdatePromise = new Promise((res) => (batchUpdateResolver = res));
                batchUpdatePromise.then(() => {
                    this.onDidUpdateEmitter.fire();
                });
                doUpdate(batchUpdatePromise, batchUpdateResolver);
                return batchUpdatePromise;
            };
        })();
        this.batchUpdate = () => {
            this.willUpdateTasks++;
            this.queueUpdatePromise = this.queueUpdateThrottler.queue(this.doBatchUpdate);
            return this.queueUpdatePromise;
        };
        // 使用箭头函数绑定当前this
        this.promptNewTreeNode = (pathOrTreeNode) => this.promptNew(pathOrTreeNode);
        this.promptNewCompositeTreeNode = (pathOrTreeNode) => this.promptNew(pathOrTreeNode, types_1.TreeNodeType.CompositeTreeNode);
        this.promptRename = async (pathOrTreeNode, defaultName) => {
            var _a, _b;
            const { root } = this.props.model;
            const node = (typeof pathOrTreeNode === 'string' ? await root.getTreeNodeByPath(pathOrTreeNode) : pathOrTreeNode);
            if (!tree_1.TreeNode.is(node) || tree_1.CompositeTreeNode.isRoot(node)) {
                throw new TypeError(`Cannot rename object of type ${typeof node}`);
            }
            const promptHandle = new prompt_1.RenamePromptHandle(defaultName || node.name, node);
            this.promptHandle = promptHandle;
            this.promptTargetID = node.id;
            if (!root.isItemVisibleAtSurface(node)) {
                await node.parent.setExpanded(true);
            }
            else {
                await this.batchUpdate();
            }
            (_b = (_a = this.listRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.scrollToItem(root.getIndexAtTreeNodeId(this.promptTargetID));
            return this.promptHandle;
        };
        this.expandNode = async (pathOrCompositeTreeNode) => {
            const { root } = this.props.model;
            const directory = typeof pathOrCompositeTreeNode === 'string'
                ? root.getTreeNodeByPath(pathOrCompositeTreeNode)
                : pathOrCompositeTreeNode;
            if (this.expandNodeCancelToken.token.isCancellationRequested) {
                this.expandNodeCancelToken = new ide_utils_1.CancellationTokenSource();
            }
            if (directory && tree_1.CompositeTreeNode.is(directory) && !directory.disposed) {
                return directory.setExpanded(false, false, true, this.expandNodeCancelToken.token);
            }
        };
        this.collapseNode = async (pathOrCompositeTreeNode) => {
            const { root } = this.props.model;
            const directory = typeof pathOrCompositeTreeNode === 'string'
                ? root.getTreeNodeByPath(pathOrCompositeTreeNode)
                : pathOrCompositeTreeNode;
            if (directory && tree_1.CompositeTreeNode.is(directory) && !directory.disposed) {
                this.expandNodeCancelToken.cancel();
                return directory.setCollapsed();
            }
        };
        this._isEnsuring = false;
        this.ensureVisible = async (pathOrTreeNode, align = 'smart', untilStable = false) => {
            if (this._isEnsuring) {
                // 同一时间段只能让一次定位节点的操作生效
                return;
            }
            this._isEnsuring = true;
            const { root } = this.props.model;
            const node = typeof pathOrTreeNode === 'string'
                ? root.getTreeNodeByPath(pathOrTreeNode) || (await root.loadTreeNodeByPath(pathOrTreeNode))
                : pathOrTreeNode;
            if (node && !root.isItemVisibleAtSurface(node)) {
                await root.loadTreeNodeByPath(node.path);
            }
            if (!tree_1.TreeNode.is(node) || tree_1.CompositeTreeNode.isRoot(node)) {
                this._isEnsuring = false;
                // 节点展开操作被取消
                return;
            }
            if (untilStable) {
                this.tryScrollIntoViewWhileStable(node, align);
            }
            else {
                this.tryScrollIntoView(node, align);
            }
            this._isEnsuring = false;
            return node;
        };
        this.getItemAtIndex = (index) => {
            const { filter } = this.props;
            if (!!filter && this.filterFlattenBranch.length > 0) {
                return this.idToFilterRendererPropsCache.get(this.filterFlattenBranch[index]);
            }
            let cached = this.idxToRendererPropsCache.get(index);
            if (!cached) {
                const promptInsertionIdx = this.newPromptInsertionIndex;
                const { root } = this.props.model;
                // 存在新建输入框
                if (promptInsertionIdx > -1 &&
                    this.promptHandle &&
                    this.promptHandle.constructor === NewPromptHandle_1.NewPromptHandle &&
                    !this.promptHandle.destroyed) {
                    if (index === promptInsertionIdx) {
                        cached = {
                            itemType: types_1.TreeNodeType.NewPrompt,
                            item: this.promptHandle,
                        };
                    }
                    else {
                        const item = root.getTreeNodeAtIndex(index - (index >= promptInsertionIdx ? 1 : 0));
                        cached = {
                            itemType: tree_1.CompositeTreeNode.is(item) ? types_1.TreeNodeType.CompositeTreeNode : types_1.TreeNodeType.TreeNode,
                            item,
                        };
                    }
                }
                else {
                    const item = root.getTreeNodeAtIndex(index);
                    // 检查是否为重命名节点
                    if (item &&
                        item.id === this.promptTargetID &&
                        this.promptHandle &&
                        this.promptHandle.constructor === prompt_1.RenamePromptHandle &&
                        !this.promptHandle.destroyed) {
                        cached = {
                            itemType: types_1.TreeNodeType.RenamePrompt,
                            item: this.promptHandle,
                        };
                    }
                    else {
                        cached = {
                            itemType: tree_1.CompositeTreeNode.is(item) ? types_1.TreeNodeType.CompositeTreeNode : types_1.TreeNodeType.TreeNode,
                            item,
                        };
                    }
                }
                this.idxToRendererPropsCache.set(index, cached);
            }
            return cached;
        };
        this.handleListScroll = ({ scrollOffset }) => {
            const { model, height, itemHeight } = this.props;
            model.state.saveScrollOffset(scrollOffset);
            if (!this.promptHandle || !itemHeight) {
                return;
            }
            let promptIndex;
            if (this.promptHandle instanceof NewPromptHandle_1.NewPromptHandle && this.newPromptInsertionIndex > -1) {
                promptIndex = this.newPromptInsertionIndex;
            }
            else {
                promptIndex = model.root.getIndexAtTreeNodeId(this.promptHandle.id);
            }
            if (scrollOffset + height < promptIndex * itemHeight || promptIndex * itemHeight < scrollOffset) {
                this.promptHandle.destroy();
            }
        };
        this.getItemKey = (index) => {
            const node = this.getItemAtIndex(index);
            const { getItemKey } = this.props;
            const id = getItemKey ? getItemKey(node) : undefined;
            return id !== null && id !== void 0 ? id : index;
        };
        // 过滤Root节点展示
        this.filterItems = (filter) => {
            const { model: { root }, filterProvider, } = this.props;
            this.filterWatcherDisposeCollection.dispose();
            this.idToFilterRendererPropsCache.clear();
            if (!filter) {
                return;
            }
            const isPathFilter = /\//.test(filter);
            const idSets = new Set();
            const idToRenderTemplate = new Map();
            const nodes = [];
            for (let idx = 0, len = root.branchSize; idx < len; idx++) {
                const node = root.getTreeNodeAtIndex(idx);
                nodes.push(node);
            }
            if (isPathFilter) {
                nodes.forEach((node) => {
                    if (node && node.path.indexOf(filter) > -1) {
                        idSets.add(node.id);
                        let parent = node.parent;
                        // 不应包含根节点
                        while (parent && !tree_1.CompositeTreeNode.isRoot(parent)) {
                            idSets.add(parent.id);
                            parent = parent.parent;
                        }
                    }
                });
            }
            else {
                let fuzzyLists = [];
                if (filterProvider) {
                    fuzzyLists = fuzzy_1.default.filter(filter, nodes, filterProvider.fuzzyOptions());
                }
                else {
                    fuzzyLists = fuzzy_1.default.filter(filter, nodes, RecycleTree.FILTER_FUZZY_OPTIONS);
                }
                const showAllExpandedChild = (node) => {
                    const children = node.children || [];
                    if (children.length > 0) {
                        for (const child of children) {
                            idSets.add(child.id);
                            if (tree_1.CompositeTreeNode.is(child) && child.expanded) {
                                showAllExpandedChild(child);
                            }
                        }
                    }
                };
                fuzzyLists.forEach((item) => {
                    const node = item.original;
                    idSets.add(node.id);
                    let parent = node.parent;
                    idToRenderTemplate.set(node.id, () => (react_1.default.createElement("div", { style: {
                            flex: 1,
                            overflow: 'hidden',
                            textOverflow: 'ellipsis',
                            whiteSpace: 'nowrap',
                        }, dangerouslySetInnerHTML: { __html: item.string || '' } })));
                    if (tree_1.CompositeTreeNode.is(node)) {
                        // 让筛选到的节点目录也展示其子节点
                        showAllExpandedChild(node);
                    }
                    // 当子节点被筛选时，向上的所有父节点均应该被展示
                    while (parent && !tree_1.CompositeTreeNode.isRoot(parent)) {
                        idSets.add(parent.id);
                        parent = parent.parent;
                    }
                });
            }
            this.filterFlattenBranch = new Array(idSets.size);
            for (let flatTreeIdx = 0, idx = 0, len = root.branchSize; idx < len; idx++) {
                const node = root.getTreeNodeAtIndex(idx);
                if (node && idSets.has(node.id)) {
                    this.filterFlattenBranch[flatTreeIdx] = node.id;
                    if (tree_1.CompositeTreeNode.is(node)) {
                        this.idToFilterRendererPropsCache.set(node.id, {
                            item: node,
                            itemType: types_1.TreeNodeType.CompositeTreeNode,
                            template: idToRenderTemplate.has(node.id) ? idToRenderTemplate.get(node.id) : undefined,
                        });
                    }
                    else {
                        this.idToFilterRendererPropsCache.set(node.id, {
                            item: node,
                            itemType: types_1.TreeNodeType.TreeNode,
                            template: idToRenderTemplate.has(node.id) ? idToRenderTemplate.get(node.id) : undefined,
                        });
                    }
                    flatTreeIdx++;
                }
            }
            // 根据折叠情况变化裁剪filterFlattenBranch
            this.filterWatcherDisposeCollection.push(root.watcher.on(types_1.TreeNodeEvent.DidChangeExpansionState, (target, nowExpanded) => {
                const expandItemIndex = this.filterFlattenBranch.indexOf(target.id);
                if (!nowExpanded) {
                    const collapesArray = [];
                    for (let i = expandItemIndex + 1; i < this.filterFlattenBranch.length; i++) {
                        const node = root.getTreeNodeById(this.filterFlattenBranch[i]);
                        if (node && node.depth > target.depth) {
                            collapesArray.push(node.id);
                        }
                        else {
                            break;
                        }
                    }
                    this.filterFlattenBranchChildrenCache.set(target.id, collapesArray);
                    this.filterFlattenBranch = (0, tree_1.spliceArray)(this.filterFlattenBranch, expandItemIndex + 1, collapesArray.length);
                }
                else {
                    const spliceUint32Array = this.filterFlattenBranchChildrenCache.get(target.id);
                    if (spliceUint32Array && spliceUint32Array.length > 0) {
                        this.filterFlattenBranch = (0, tree_1.spliceArray)(this.filterFlattenBranch, expandItemIndex + 1, 0, spliceUint32Array);
                        this.filterFlattenBranchChildrenCache.delete(target.id);
                    }
                }
            }));
            this.filterWatcherDisposeCollection.push(ide_utils_1.Disposable.create(() => {
                this.filterFlattenBranchChildrenCache.clear();
            }));
        };
        this.renderItem = ({ index, style }) => {
            var _a, _b, _c, _d, _e, _f, _g;
            const { children, overflow = 'ellipsis', supportDynamicHeights } = this.props;
            const node = this.getItemAtIndex(index);
            const wrapRef = (0, react_1.useRef)(null);
            const setSize = (0, react_1.useMemo)(() => supportDynamicHeights
                ? () => {
                    let size = 0;
                    if (wrapRef.current) {
                        const ref = wrapRef.current;
                        size = Array.from(ref.children).reduce((pre, cur) => pre + cur.getBoundingClientRect().height, 0);
                    }
                    if (size) {
                        this.dynamicSizeMap.set(index, size);
                        this.layoutItem();
                    }
                    return Math.max(size, RecycleTree.DEFAULT_ITEM_HEIGHT);
                }
                : () => { }, [supportDynamicHeights]);
            (0, react_1.useEffect)(() => {
                if (wrapRef.current) {
                    setSize();
                }
            }, []);
            if (!node) {
                return react_1.default.createElement(react_1.default.Fragment, null);
            }
            const { item, itemType: type, template } = node;
            if (!item) {
                return react_1.default.createElement("div", { style: style });
            }
            let ariaInfo;
            // ref: https://developers.google.com/web/fundamentals/accessibility/semantics-aria/aria-labels-and-relationships
            if (tree_1.CompositeTreeNode.is(item)) {
                ariaInfo = {
                    'aria-label': (_a = item.accessibilityInformation) === null || _a === void 0 ? void 0 : _a.label,
                    'aria-expanded': item.expanded,
                    'aria-level': item.depth,
                    'aria-setsize': (_c = (_b = item.parent) === null || _b === void 0 ? void 0 : _b.children) === null || _c === void 0 ? void 0 : _c.length,
                    'aria-posinset': index,
                };
            }
            else if (tree_1.TreeNode.is(item)) {
                ariaInfo = {
                    'aria-label': (_d = item.accessibilityInformation) === null || _d === void 0 ? void 0 : _d.label,
                    'aria-level': item.depth,
                    'aria-setsize': (_f = (_e = item.parent) === null || _e === void 0 ? void 0 : _e.children) === null || _f === void 0 ? void 0 : _f.length,
                    'aria-posinset': index,
                };
            }
            const itemStyle = overflow === 'ellipsis' ? style : { ...style, width: 'auto', minWidth: '100%' };
            return (react_1.default.createElement("div", { ref: wrapRef, style: itemStyle, role: ((_g = item.accessibilityInformation) === null || _g === void 0 ? void 0 : _g.role) || 'treeiem', ...ariaInfo },
                react_1.default.createElement(TreeNodeRendererWrap_1.NodeRendererWrap, { item: item, depth: item.depth, itemType: type, template: template, hasPrompt: !!this.promptHandle && !this.promptHandle.destroyed, expanded: tree_1.CompositeTreeNode.is(item) ? item.expanded : void 0 }, children)));
        };
        this.layoutItem = () => {
            var _a, _b, _c, _d;
            if (!this.props.supportDynamicHeights) {
                return;
            }
            // eslint-disable-next-line no-unsafe-optional-chaining
            if (this.listRef && ((_a = this.listRef) === null || _a === void 0 ? void 0 : _a.current) && '_getRangeToRender' in ((_b = this.listRef) === null || _b === void 0 ? void 0 : _b.current)) {
                // _getRangeToRender 是 react-window 的内部方法，用于获取可视区域的下标范围
                // @ts-ignore
                const range = (_c = this.listRef) === null || _c === void 0 ? void 0 : _c.current._getRangeToRender();
                if (range) {
                    const start = range[0];
                    ((_d = this.listRef) === null || _d === void 0 ? void 0 : _d.current).resetAfterIndex(start);
                }
            }
        };
    }
    getNewPromptInsertIndex(startIndex, parent) {
        const { root } = this.props.model;
        let insertIndex = startIndex + 1;
        // 合并文件夹以及深层级子节点
        for (; insertIndex - startIndex <= parent.branchSize; insertIndex++) {
            const node = root.getTreeNodeAtIndex(insertIndex);
            if (!node) {
                return insertIndex;
            }
            if (tree_1.CompositeTreeNode.is(node)) {
                continue;
            }
            else if ((node === null || node === void 0 ? void 0 : node.depth) > parent.depth + 1) {
                continue;
            }
            else {
                return insertIndex;
            }
        }
        return insertIndex;
    }
    UNSAFE_componentWillUpdate(prevProps) {
        if (this.props.filter !== prevProps.filter) {
            this.filterItems(prevProps.filter);
        }
        if (this.props.model !== prevProps.model) {
            // model变化时，在渲染前清理缓存
            this.idxToRendererPropsCache.clear();
            this.idToFilterRendererPropsCache.clear();
            this.dynamicSizeMap.clear();
        }
    }
    componentDidUpdate(prevProps) {
        var _a, _b;
        if (this.props.model !== prevProps.model) {
            this.disposables.dispose();
            const { model } = this.props;
            (_b = (_a = this.listRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.scrollTo(model.state.scrollOffset);
            this.disposables.push(model.onChange(this.batchUpdate.bind(this)));
            this.disposables.push(model.state.onDidLoadState(() => {
                var _a, _b;
                (_b = (_a = this.listRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.scrollTo(model.state.scrollOffset);
            }));
            this.onDidModelChangeEmitter.fire({
                preModel: prevProps.model,
                nextModel: model,
            });
        }
    }
    async promptNew(pathOrTreeNode, type = types_1.TreeNodeType.TreeNode) {
        var _a, _b;
        const { root } = this.props.model;
        let node = typeof pathOrTreeNode === 'string' ? await root.getTreeNodeByPath(pathOrTreeNode) : pathOrTreeNode;
        if (type !== types_1.TreeNodeType.TreeNode && type !== types_1.TreeNodeType.CompositeTreeNode) {
            throw new TypeError(`Invalid type supplied. Expected 'TreeNodeType.TreeNode' or 'TreeNodeType.CompositeTreeNode', got ${type}`);
        }
        if (!!node && !tree_1.CompositeTreeNode.is(node)) {
            // 获取的子节点如果不是CompositeTreeNode，尝试获取父级节点
            node = node.parent;
            if (!tree_1.CompositeTreeNode.is(node)) {
                throw new TypeError(`Cannot create new prompt at object of type ${typeof node}`);
            }
        }
        if (!node) {
            throw new Error(`Cannot find node at ${pathOrTreeNode}`);
        }
        const promptHandle = new NewPromptHandle_1.NewPromptHandle(type, node);
        this.promptHandle = promptHandle;
        this.promptTargetID = node.id;
        if (node !== root &&
            (!node.expanded || !root.isItemVisibleAtSurface(node))) {
            // 调用setExpanded即会在之后调用batchUpdate函数
            await node.setExpanded(true);
        }
        else {
            await this.batchUpdate();
        }
        if (this.newPromptInsertionIndex >= 0) {
            // 说明已在输入框已在可视区域
            (_b = (_a = this.listRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.scrollToItem(this.newPromptInsertionIndex);
        }
        else {
            this.tryScrollIntoViewWhileStable(this.promptHandle);
        }
        return this.promptHandle;
    }
    tryScrollIntoView(node, align = 'auto') {
        var _a, _b, _c, _d;
        const { root } = this.props.model;
        if (node.constructor === NewPromptHandle_1.NewPromptHandle && !node.destroyed) {
            (_b = (_a = this.listRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.scrollToItem(this.newPromptInsertionIndex);
        }
        else if (root.isItemVisibleAtSurface(node)) {
            (_d = (_c = this.listRef) === null || _c === void 0 ? void 0 : _c.current) === null || _d === void 0 ? void 0 : _d.scrollToItem(root.getIndexAtTreeNode(node), align);
        }
    }
    tryScrollIntoViewWhileStable(node, align = 'smart') {
        const { root } = this.props.model;
        if (this.tryEnsureVisibleTimes > RecycleTree.TRY_ENSURE_VISIBLE_MAX_TIMES) {
            this.tryEnsureVisibleTimes = 0;
            return;
        }
        ide_utils_1.Event.once(this.props.model.onChange)(async () => {
            var _a, _b, _c, _d;
            await this.queueUpdatePromise;
            if (node.constructor === NewPromptHandle_1.NewPromptHandle && !node.destroyed) {
                (_b = (_a = this.listRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.scrollToItem(this.newPromptInsertionIndex);
            }
            else if (root.isItemVisibleAtSurface(node)) {
                (_d = (_c = this.listRef) === null || _c === void 0 ? void 0 : _c.current) === null || _d === void 0 ? void 0 : _d.scrollToItem(root.getIndexAtTreeNode(node), align);
                this.tryEnsureVisibleTimes = 0;
            }
            else {
                this.tryEnsureVisibleTimes++;
                this.tryScrollIntoViewWhileStable(node, align);
            }
        });
    }
    componentDidMount() {
        var _a, _b;
        const { model, onReady } = this.props;
        (_b = (_a = this.listRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.scrollTo(model.state.scrollOffset);
        this.disposables.push(model.onChange(this.batchUpdate.bind(this)));
        this.disposables.push(model.state.onDidLoadState(() => {
            var _a, _b;
            (_b = (_a = this.listRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.scrollTo(model.state.scrollOffset);
        }));
        if (typeof onReady === 'function') {
            const api = {
                promptNewTreeNode: this.promptNewTreeNode,
                promptNewCompositeTreeNode: this.promptNewCompositeTreeNode,
                promptRename: this.promptRename,
                expandNode: this.expandNode,
                collapseNode: this.collapseNode,
                ensureVisible: this.ensureVisible,
                getModel: () => this.props.model,
                layoutItem: this.layoutItem,
                getCurrentSize: () => ({
                    width: this.props.width,
                    height: this.props.height,
                }),
                onDidChangeModel: this.onDidModelChangeEmitter.event,
                onDidUpdate: this.onDidUpdateEmitter.event,
                onOnceDidUpdate: ide_utils_1.Event.once(this.onDidUpdateEmitter.event),
                onError: this.onErrorEmitter.event,
            };
            onReady(api);
        }
    }
    componentWillUnmount() {
        this.disposables.dispose();
    }
    set promptHandle(handle) {
        if (this._promptHandle === handle) {
            return;
        }
        if (this._promptHandle instanceof prompt_1.PromptHandle && !this._promptHandle.destroyed) {
            this._promptHandle.destroy();
        }
        handle.onDestroy(this.batchUpdate);
        this._promptHandle = handle;
    }
    get promptHandle() {
        return this._promptHandle;
    }
    // 根据是否携带新建输入框计算行数
    get adjustedRowCount() {
        const { root } = this.props.model;
        const { filter } = this.props;
        if (filter) {
            return this.filterFlattenBranch.length;
        }
        return this.newPromptInsertionIndex > -1 &&
            this.promptHandle &&
            this.promptHandle.constructor === NewPromptHandle_1.NewPromptHandle &&
            !this.promptHandle.destroyed
            ? root.branchSize + 1
            : root.branchSize;
    }
    getItemSize(index) {
        return this.dynamicSizeMap.get(index) || this.props.itemHeight;
    }
    render() {
        const { children, itemHeight, width = '100%', height, style, className, placeholder, overScanCount, leaveBottomBlank, supportDynamicHeights, } = this.props;
        if (placeholder && this.adjustedRowCount === 0) {
            const Placeholder = placeholder;
            return react_1.default.createElement(Placeholder, null);
        }
        const addonProps = {
            children,
            height,
            width,
            itemData: [],
            itemCount: this.adjustedRowCount,
            itemKey: this.getItemKey,
            overscanCount: overScanCount || RecycleTree.DEFAULT_OVER_SCAN_COUNT,
            onScroll: this.handleListScroll,
            style: {
                transform: 'translate3d(0px, 0px, 0px)',
                ...style,
            },
            className,
            outerElementType: scrollbars_1.ScrollbarsVirtualList,
        };
        if (leaveBottomBlank) {
            addonProps.innerElementType = InnerElementType;
        }
        return supportDynamicHeights ? (react_1.default.createElement(react_window_1.VariableSizeList, { ref: this.listRef, itemSize: this.getItemSize.bind(this), ...addonProps }, this.renderItem)) : (react_1.default.createElement(react_window_1.FixedSizeList, { ref: this.listRef, itemSize: itemHeight, ...addonProps }, this.renderItem));
    }
}
exports.RecycleTree = RecycleTree;
RecycleTree.PADDING_BOTTOM_SIZE = 22;
RecycleTree.DEFAULT_ITEM_HEIGHT = 22;
RecycleTree.TRY_ENSURE_VISIBLE_MAX_TIMES = 5;
RecycleTree.FILTER_FUZZY_OPTIONS = {
    pre: '<match>',
    post: '</match>',
    extract: (node) => (node === null || node === void 0 ? void 0 : node.name) || '',
};
RecycleTree.DEFAULT_OVER_SCAN_COUNT = 50;
//# sourceMappingURL=RecycleTree.js.map