"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasicRecycleTree = void 0;
const tslib_1 = require("tslib");
const react_1 = tslib_1.__importStar(require("react"));
const react_ctxmenu_trigger_1 = tslib_1.__importDefault(require("react-ctxmenu-trigger"));
const click_outside_1 = require("../../click-outside");
const RecycleTree_1 = require("../RecycleTree");
const menubar_item_1 = require("./menubar-item");
const placements_1 = require("./placements");
const tree_node_1 = require("./tree-node");
const tree_node_define_1 = require("./tree-node.define");
const tree_service_1 = require("./tree-service");
require("./styles.less");
require("react-ctxmenu-trigger/assets/index.css");
tslib_1.__exportStar(require("./types"), exports);
const BasicRecycleTree = ({ width, height, itemHeight = 22, itemClassname, indent, baseIndent, containerClassname, onClick, onContextMenu, onTwistierClick, onIconClick, onDbClick, resolveChildren, sortComparator, treeData, inlineMenus, inlineMenuActuator, onReady, contextMenus, contextMenuActuator, treeName, leaveBottomBlank = true, }) => {
    const [showMenus, setShowMenus] = (0, react_1.useState)({ show: false });
    const [menubarItems, setMenubarItems] = (0, react_1.useState)([]);
    const [model, setModel] = (0, react_1.useState)();
    const isDisposed = (0, react_1.useRef)(false);
    const treeService = (0, react_1.useRef)();
    const treeHandle = (0, react_1.useRef)();
    const wrapperRef = (0, react_1.useRef)(null);
    const renderTreeNode = (0, react_1.useCallback)((props) => {
        var _a;
        return (react_1.default.createElement(tree_node_1.BasicTreeNodeRenderer, { item: props.item, itemType: props.itemType, itemHeight: itemHeight, indent: indent, baseIndent: baseIndent, className: itemClassname, inlineMenus: inlineMenus, inlineMenuActuator: inlineMenuActuator, onClick: handleItemClick, onDbClick: handleItemDbClick, onContextMenu: handleContextMenu, onTwistierClick: handleTwistierClick, onIconClick: handleItemIconClick, decorations: (_a = treeService.current) === null || _a === void 0 ? void 0 : _a.decorations.getDecorations(props.item) }));
    }, [model]);
    (0, react_1.useEffect)(() => {
        var _a, _b;
        treeService.current = new tree_service_1.BasicTreeService(treeData, resolveChildren, sortComparator, {
            treeName,
        });
        const disposable = (_a = treeService.current) === null || _a === void 0 ? void 0 : _a.onDidUpdateTreeModel((model) => {
            ensureLoaded(model);
        });
        const handleBlur = () => {
            var _a;
            (_a = treeService.current) === null || _a === void 0 ? void 0 : _a.enactiveFocusedDecoration();
        };
        (_b = wrapperRef.current) === null || _b === void 0 ? void 0 : _b.addEventListener('blur', handleBlur, true);
        return () => {
            var _a, _b;
            isDisposed.current = true;
            (_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.removeEventListener('blur', handleBlur, true);
            disposable.dispose();
            (_b = treeService.current) === null || _b === void 0 ? void 0 : _b.dispose();
        };
    }, []);
    (0, react_1.useEffect)(() => {
        var _a;
        (_a = treeService.current) === null || _a === void 0 ? void 0 : _a.updateTreeData(treeData);
    }, [treeData]);
    const ensureLoaded = async (model) => {
        if (model) {
            await model.ensureReady;
        }
        if (!isDisposed.current) {
            setModel(model);
        }
    };
    const selectItem = async (item) => {
        var _a;
        (_a = treeService.current) === null || _a === void 0 ? void 0 : _a.activeFocusedDecoration(item);
        if (tree_node_define_1.BasicCompositeTreeNode.is(item)) {
            toggleDirectory(item);
        }
    };
    const handleTreeReady = (0, react_1.useCallback)((handle) => {
        if (onReady) {
            onReady({
                ...handle,
                selectItem,
                focusItem: async (nodePath) => {
                    var _a;
                    const path = `/${treeName}/${nodePath}`;
                    await (model === null || model === void 0 ? void 0 : model.ensureReady);
                    const node = (await handle.ensureVisible(path, 'auto', true));
                    if (node) {
                        (_a = treeService.current) === null || _a === void 0 ? void 0 : _a.activeFocusedDecoration(node);
                    }
                },
            });
        }
        treeHandle.current = handle;
    }, [treeService.current]);
    const handleItemClick = (0, react_1.useCallback)((event, item) => {
        selectItem(item);
        if (onClick) {
            onClick(event, item);
        }
    }, [onClick]);
    const handleItemIconClick = (0, react_1.useCallback)((event, item) => {
        if (onIconClick) {
            onIconClick(event, item);
        }
    }, [onIconClick]);
    const handleItemDbClick = (0, react_1.useCallback)((event, item) => {
        if (onDbClick) {
            onDbClick(event, item);
        }
    }, [onDbClick]);
    const handleContextMenu = (0, react_1.useCallback)((event, item) => {
        var _a, _b;
        if (item) {
            (_a = treeService.current) === null || _a === void 0 ? void 0 : _a.activeContextMenuDecoration(item);
        }
        else {
            (_b = treeService.current) === null || _b === void 0 ? void 0 : _b.enactiveFocusedDecoration();
        }
        if (onContextMenu) {
            onContextMenu(event, item);
        }
        else {
            // let menus: IBasicTreeMenu[] = [];
            let rawMenus = [];
            if (Array.isArray(contextMenus)) {
                rawMenus = contextMenus;
            }
            else if (typeof contextMenus === 'function') {
                rawMenus = contextMenus(item);
            }
            const groups = new Set();
            const menusMap = {};
            for (const menu of rawMenus) {
                groups.add(menu.group || '-1');
                if (!menusMap[menu.group || '-1']) {
                    menusMap[menu.group || '-1'] = [];
                }
                menusMap[menu.group || '-1'].push(menu);
            }
            const sortGroup = Array.from(groups).sort((a, b) => a.localeCompare(b, 'kn', { numeric: true }));
            let menus = [];
            for (const group of sortGroup) {
                menus = menus.concat(menusMap[group].map((menu) => ({ id: menu.id, label: menu.title, group: menu.group })));
                menus = menus.concat([{ id: `${group}_divider`, label: '', type: 'divider' }]);
            }
            menus.pop();
            if (JSON.stringify(menus) !== JSON.stringify(menubarItems)) {
                setMenubarItems(menus);
            }
            const { x, y } = event.nativeEvent;
            setShowMenus({ show: true, point: { pageX: x, pageY: y }, activeNode: item });
        }
    }, [onDbClick]);
    const toggleDirectory = (0, react_1.useCallback)((item) => {
        var _a, _b;
        if (item.expanded) {
            (_a = treeHandle.current) === null || _a === void 0 ? void 0 : _a.collapseNode(item);
        }
        else {
            (_b = treeHandle.current) === null || _b === void 0 ? void 0 : _b.expandNode(item);
        }
    }, []);
    const handleTwistierClick = (0, react_1.useCallback)((event, item) => {
        if (tree_node_define_1.BasicCompositeTreeNode.is(item)) {
            toggleDirectory(item);
        }
        if (onTwistierClick) {
            onTwistierClick(event, item);
        }
    }, [onTwistierClick]);
    const handleOuterClick = (0, react_1.useCallback)(() => {
        var _a;
        (_a = treeService.current) === null || _a === void 0 ? void 0 : _a.enactiveFocusedDecoration();
    }, []);
    const handleOuterContextMenu = (0, react_1.useCallback)((event, item) => {
        if (onContextMenu) {
            onContextMenu(event, item);
        }
    }, []);
    const handleMouseLeave = (0, react_1.useCallback)(() => {
        setShowMenus({ ...showMenus, show: false });
    }, [showMenus]);
    const renderContextMenu = (0, react_1.useCallback)(() => {
        if (!contextMenus) {
            return null;
        }
        return (react_1.default.createElement(react_ctxmenu_trigger_1.default, { popupPlacement: 'bottomLeft', popupVisible: showMenus.show, action: ['contextMenu'], popupAlign: {
                overflow: {
                    adjustX: 1,
                    adjustY: 1,
                },
                offset: [window.scrollX, window.scrollY],
            }, point: showMenus.point || {}, builtinPlacements: placements_1.placements, popup: react_1.default.createElement(click_outside_1.ClickOutside, { className: 'basic_tree_menubars', mouseEvents: ['click', 'contextmenu'], onOutsideClick: handleMouseLeave }, menubarItems.map(({ id, label, type }) => (react_1.default.createElement(menubar_item_1.BasicMenuItem, { key: id, id: id, label: label, type: type, focusMode: showMenus.show, onClick: (id) => {
                    if (contextMenuActuator) {
                        contextMenuActuator(showMenus.activeNode, id);
                    }
                    setShowMenus({ show: false });
                } })))), alignPoint: true }));
    }, [menubarItems, contextMenuActuator, showMenus]);
    return (react_1.default.createElement("div", { className: 'basic_tree', tabIndex: -1, ref: wrapperRef, onClick: handleOuterClick, onContextMenu: handleOuterContextMenu },
        renderContextMenu(),
        model ? (react_1.default.createElement(RecycleTree_1.RecycleTree, { height: height, width: width, itemHeight: itemHeight, model: model, onReady: handleTreeReady, className: containerClassname, leaveBottomBlank: leaveBottomBlank }, renderTreeNode)) : null));
};
exports.BasicRecycleTree = BasicRecycleTree;
//# sourceMappingURL=index.js.map