import { h } from "@stencil/core";
/**
 * Tree 树形组件
 * 文件夹、组织架构、分类导航等层级结构的展示与选择
 */
export class Tree {
    el;
    /**
     * 可选项数据源（递归树结构）
     */
    data = [];
    /**
     * （受控）展开的节点 key 集合
     */
    expandedKeys = [];
    /**
     * （受控）选中的节点 key 集合
     */
    selectedKeys = [];
    /**
     * 是否显示 checkbox 复选框
     * @default false
     */
    checkable = false;
    /**
     * （受控）勾选的节点 key 集合
     */
    checkedKeys = [];
    /**
     * 是否默认展开全部节点
     * @default false
     */
    defaultExpandAll = false;
    /**
     * 是否显示连接线
     * @default false
     */
    showLine = false;
    internalExpanded = null;
    internalSelected = null;
    internalChecked = null;
    /**
     * 选中节点变化事件（detail: 选中节点 key 数组）
     */
    hbSelect;
    /**
     * 展开节点变化事件（detail: 展开节点 key 数组）
     */
    hbExpand;
    /**
     * 勾选节点变化事件（detail: 勾选节点 key 数组）
     */
    hbCheck;
    componentWillLoad() {
        if (this.defaultExpandAll) {
            const all = [];
            this.collectExpandableKeys(this.data, all);
            this.internalExpanded = all;
        }
    }
    /**
     * data 变化时，若 defaultExpandAll=true，把所有含 children 的节点 key
     * 合并进 expandedKeys（保留用户已手动展开的节点，不收起任何已展开项）。
     * 解决「先 mount 再赋 data」时 componentWillLoad 基于空数据计算导致不展开的问题。
     */
    handleDataChange() {
        if (!this.defaultExpandAll) {
            return;
        }
        const current = this.getExpandedKeys();
        const merged = [...current];
        this.collectExpandableKeys(this.data, merged);
        // 去重
        const deduped = Array.from(new Set(merged));
        this.internalExpanded = deduped;
        this.expandedKeys = deduped;
    }
    handleExpandedKeysChange() {
        this.internalExpanded = null;
    }
    handleSelectedKeysChange() {
        this.internalSelected = null;
    }
    handleCheckedKeysChange() {
        this.internalChecked = null;
    }
    collectExpandableKeys(nodes, out) {
        for (const node of nodes) {
            if (node.children && node.children.length > 0) {
                out.push(node.key);
                this.collectExpandableKeys(node.children, out);
            }
        }
    }
    getExpandedKeys() {
        return this.internalExpanded !== null ? this.internalExpanded : this.expandedKeys;
    }
    getSelectedKeys() {
        return this.internalSelected !== null ? this.internalSelected : this.selectedKeys;
    }
    getCheckedKeys() {
        return this.internalChecked !== null ? this.internalChecked : this.checkedKeys;
    }
    isExpanded = (key) => this.getExpandedKeys().includes(key);
    isSelected = (key) => this.getSelectedKeys().includes(key);
    isChecked = (key) => this.getCheckedKeys().includes(key);
    handleToggleExpand = (node, e) => {
        e.stopPropagation();
        if (node.disabled)
            return;
        const current = [...this.getExpandedKeys()];
        const idx = current.indexOf(node.key);
        if (idx >= 0) {
            current.splice(idx, 1);
        }
        else {
            current.push(node.key);
        }
        this.internalExpanded = current;
        this.expandedKeys = current;
        this.hbExpand.emit(current);
    };
    handleSelect = (node) => {
        if (node.disabled)
            return;
        const current = [...this.getSelectedKeys()];
        const idx = current.indexOf(node.key);
        if (idx >= 0) {
            current.splice(idx, 1);
        }
        else {
            current.push(node.key);
        }
        this.internalSelected = current;
        this.selectedKeys = current;
        this.hbSelect.emit(current);
    };
    handleCheck = (node, e) => {
        e.stopPropagation();
        if (node.disabled)
            return;
        const current = [...this.getCheckedKeys()];
        const wasChecked = current.includes(node.key);
        // 计算本次操作应进入/移出 checkedKeys 的所有 key 集合（含级联）。
        // 不可变：始终基于副本计算，绝不原地修改 current。
        const next = new Set(current);
        if (wasChecked) {
            // 取消：取消自身 + 取消所有后代（含禁用后代，因为它们当时是被父级带选的）。
            this.removeDescendantKeys(node, next);
            next.delete(node.key);
        }
        else {
            // 勾选：勾选自身 + 勾选所有非禁用后代。
            next.add(node.key);
            this.collectCheckableDescendantKeys(node, next);
        }
        // 父子联动自底向上修正祖先：所有非禁用子节点全选 → 父加入 checkedKeys；
        // 子节点部分选/全未选 → 父移出 checkedKeys（部分选体现为 indeterminate，由 detail 暴露）。
        this.reconcileAncestors(node, next);
        const result = Array.from(next);
        this.internalChecked = result;
        this.checkedKeys = result;
        this.hbCheck.emit(result);
    };
    /**
     * 从集合中移除某节点所有后代 key（含禁用项），用于取消父级时级联取消。
     * 不可变：仅修改传入的 Set 副本，不修改树本身。
     */
    removeDescendantKeys(node, out) {
        if (!node.children)
            return;
        for (const child of node.children) {
            out.delete(child.key);
            this.removeDescendantKeys(child, out);
        }
    }
    /**
     * 收集某节点所有「可勾选（非禁用）」后代 key，用于勾选父级时级联勾选。
     * 注意：禁用子节点本身不可手动勾选，但若其下还有非禁用后代，仍应被级联勾选。
     */
    collectCheckableDescendantKeys(node, out) {
        if (!node.children)
            return;
        for (const child of node.children) {
            if (!child.disabled) {
                out.add(child.key);
            }
            // 无论 child 是否禁用，继续向下寻找可勾选后代。
            this.collectCheckableDescendantKeys(child, out);
        }
    }
    /**
     * 自底向上修正 checkedKeys 中某节点的所有祖先勾选状态。
     * 规则：一个祖先节点，当且仅当其所有「非禁用子节点」均被勾选时，自身才被勾选；
     * 否则移出 checkedKeys（部分选情况由 indeterminate 集合在渲染层暴露，不进入 checkedKeys）。
     */
    reconcileAncestors(node, next) {
        const parents = this.findAncestors(this.data, node.key, []);
        // parents 为从直接父到根的数组；逐层判断。
        for (const parent of parents) {
            if (parent.disabled || !parent.children || parent.children.length === 0)
                continue;
            const checkableChildren = parent.children.filter((c) => !c.disabled);
            if (checkableChildren.length === 0)
                continue;
            const allChecked = checkableChildren.every((c) => next.has(c.key));
            if (allChecked) {
                next.add(parent.key);
            }
            else {
                next.delete(parent.key);
            }
        }
    }
    /**
     * 递归查找某 key 的祖先链（不含自身），返回从直接父到根的数组。
     * 找不到返回空数组（key 为根节点或不存在时）。
     */
    findAncestors(nodes, key, acc) {
        for (const node of nodes) {
            if (node.key === key) {
                return acc;
            }
            if (node.children && node.children.length > 0) {
                const found = this.findAncestors(node.children, key, [node, ...acc]);
                if (found)
                    return found;
            }
        }
        return null;
    }
    /**
     * 计算当前 checkedKeys 下处于「部分选中（indeterminate）」的节点 key 集合。
     * 一个节点为 indeterminate 当：其本身不在 checkedKeys，但其下（递归）存在被勾选的后代。
     * 用于渲染层暴露三方态（aria-checked="mixed" + class）。
     */
    computeIndeterminateKeys() {
        const checked = this.getCheckedKeys();
        const result = new Set();
        const walk = (node) => {
            if (!node.children || node.children.length === 0) {
                return { hasCheckedDescendant: false };
            }
            let hasCheckedDescendant = false;
            for (const child of node.children) {
                const childState = walk(child);
                if (checked.includes(child.key) || childState.hasCheckedDescendant) {
                    hasCheckedDescendant = true;
                }
            }
            // 节点本身未勾选，但有被勾选后代 → indeterminate
            if (hasCheckedDescendant && !checked.includes(node.key)) {
                result.add(node.key);
            }
            return { hasCheckedDescendant };
        };
        for (const root of this.data) {
            walk(root);
        }
        return result;
    }
    renderNode = (node, level, indeterminateKeys) => {
        const hasChildren = !!(node.children && node.children.length > 0);
        const expanded = this.isExpanded(node.key);
        const selected = this.isSelected(node.key);
        const checked = this.isChecked(node.key);
        const indeterminate = indeterminateKeys.has(node.key);
        const disabled = !!node.disabled;
        return (h("li", { class: {
                'hb-tree__node': true,
                'hb-tree__node--disabled': disabled,
            }, role: "treeitem", "aria-level": level + 1, "aria-expanded": hasChildren ? String(expanded) : undefined, "aria-selected": selected ? 'true' : 'false', "aria-checked": this.checkable
                ? indeterminate
                    ? 'mixed'
                    : checked
                        ? 'true'
                        : 'false'
                : undefined, "aria-disabled": disabled ? 'true' : undefined, tabindex: selected ? 0 : -1 }, h("div", { class: {
                'hb-tree__node-content': true,
                'hb-tree__node-content--selected': selected,
            }, style: { paddingLeft: `${level * 18 + 8}px` } }, h("span", { class: "hb-tree__switcher", onClick: (e) => hasChildren && this.handleToggleExpand(node, e) }, hasChildren ? (h("span", { class: { 'hb-tree__arrow': true, 'hb-tree__arrow--expanded': expanded } }, "\u25B6")) : this.showLine ? (h("span", { class: "hb-tree__line-dot" })) : null), this.checkable && (h("span", { class: {
                'hb-tree__checkbox': true,
                'hb-tree__checkbox--checked': checked,
                'hb-tree__checkbox--indeterminate': indeterminate,
                'hb-tree__checkbox--disabled': disabled,
            }, onClick: (e) => this.handleCheck(node, e) }, checked ? '✓' : indeterminate ? '–' : '')), h("span", { class: "hb-tree__title", onClick: () => this.handleSelect(node) }, node.title)), hasChildren && expanded && (h("ul", { class: "hb-tree__children", role: "group" }, node.children.map((child) => this.renderNode(child, level + 1, indeterminateKeys))))));
    };
    render() {
        const indeterminateKeys = this.checkable ? this.computeIndeterminateKeys() : new Set();
        return (h("ul", { key: '6e40f05b44667fb23f0b6a74f647553fa518cca4', class: { 'hb-tree': true, 'hb-tree--show-line': this.showLine }, role: "tree" }, this.data.map((node) => this.renderNode(node, 0, indeterminateKeys))));
    }
    static get is() { return "hb-tree"; }
    static get encapsulation() { return "shadow"; }
    static get originalStyleUrls() {
        return {
            "$": ["tree.css"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["tree.css"]
        };
    }
    static get properties() {
        return {
            "data": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "TreeNode[]",
                    "resolved": "TreeNode[]",
                    "references": {
                        "TreeNode": {
                            "location": "local",
                            "path": "/Users/vscoderwhy/Desktop/product/web-common/huibo-ui/src/components/Tree/Tree.tsx",
                            "id": "src/components/Tree/Tree.tsx::TreeNode"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "\u53EF\u9009\u9879\u6570\u636E\u6E90\uFF08\u9012\u5F52\u6811\u7ED3\u6784\uFF09"
                },
                "getter": false,
                "setter": false,
                "defaultValue": "[]"
            },
            "expandedKeys": {
                "type": "unknown",
                "mutable": true,
                "complexType": {
                    "original": "string[]",
                    "resolved": "string[]",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "\uFF08\u53D7\u63A7\uFF09\u5C55\u5F00\u7684\u8282\u70B9 key \u96C6\u5408"
                },
                "getter": false,
                "setter": false,
                "defaultValue": "[]"
            },
            "selectedKeys": {
                "type": "unknown",
                "mutable": true,
                "complexType": {
                    "original": "string[]",
                    "resolved": "string[]",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "\uFF08\u53D7\u63A7\uFF09\u9009\u4E2D\u7684\u8282\u70B9 key \u96C6\u5408"
                },
                "getter": false,
                "setter": false,
                "defaultValue": "[]"
            },
            "checkable": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "default",
                            "text": "false"
                        }],
                    "text": "\u662F\u5426\u663E\u793A checkbox \u590D\u9009\u6846"
                },
                "getter": false,
                "setter": false,
                "attribute": "checkable",
                "reflect": false,
                "defaultValue": "false"
            },
            "checkedKeys": {
                "type": "unknown",
                "mutable": true,
                "complexType": {
                    "original": "string[]",
                    "resolved": "string[]",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "\uFF08\u53D7\u63A7\uFF09\u52FE\u9009\u7684\u8282\u70B9 key \u96C6\u5408"
                },
                "getter": false,
                "setter": false,
                "defaultValue": "[]"
            },
            "defaultExpandAll": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "default",
                            "text": "false"
                        }],
                    "text": "\u662F\u5426\u9ED8\u8BA4\u5C55\u5F00\u5168\u90E8\u8282\u70B9"
                },
                "getter": false,
                "setter": false,
                "attribute": "default-expand-all",
                "reflect": false,
                "defaultValue": "false"
            },
            "showLine": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "default",
                            "text": "false"
                        }],
                    "text": "\u662F\u5426\u663E\u793A\u8FDE\u63A5\u7EBF"
                },
                "getter": false,
                "setter": false,
                "attribute": "show-line",
                "reflect": false,
                "defaultValue": "false"
            }
        };
    }
    static get states() {
        return {
            "internalExpanded": {},
            "internalSelected": {},
            "internalChecked": {}
        };
    }
    static get events() {
        return [{
                "method": "hbSelect",
                "name": "hbSelect",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "\u9009\u4E2D\u8282\u70B9\u53D8\u5316\u4E8B\u4EF6\uFF08detail: \u9009\u4E2D\u8282\u70B9 key \u6570\u7EC4\uFF09"
                },
                "complexType": {
                    "original": "string[]",
                    "resolved": "string[]",
                    "references": {}
                }
            }, {
                "method": "hbExpand",
                "name": "hbExpand",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "\u5C55\u5F00\u8282\u70B9\u53D8\u5316\u4E8B\u4EF6\uFF08detail: \u5C55\u5F00\u8282\u70B9 key \u6570\u7EC4\uFF09"
                },
                "complexType": {
                    "original": "string[]",
                    "resolved": "string[]",
                    "references": {}
                }
            }, {
                "method": "hbCheck",
                "name": "hbCheck",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "\u52FE\u9009\u8282\u70B9\u53D8\u5316\u4E8B\u4EF6\uFF08detail: \u52FE\u9009\u8282\u70B9 key \u6570\u7EC4\uFF09"
                },
                "complexType": {
                    "original": "string[]",
                    "resolved": "string[]",
                    "references": {}
                }
            }];
    }
    static get elementRef() { return "el"; }
    static get watchers() {
        return [{
                "propName": "data",
                "methodName": "handleDataChange"
            }, {
                "propName": "expandedKeys",
                "methodName": "handleExpandedKeysChange"
            }, {
                "propName": "selectedKeys",
                "methodName": "handleSelectedKeysChange"
            }, {
                "propName": "checkedKeys",
                "methodName": "handleCheckedKeysChange"
            }];
    }
}
//# sourceMappingURL=Tree.js.map
