输入类

树选择器

树选择器用于多层级树形数据的结构化展示 & 选取,例如显示文件夹与文件的列表、显示组织架构成员列表等等。

使用场景

当需要选择的选项为树形结构时,可以使用树选择器,例如部门层级、学科体系、分类目录等。

代码演示

如何引入

import { TreeSelect } from '@fibrous/semi-ui-solid';

基本用法

最简单的用法,默认为单选模式,每一级菜单项均可选择。

代码
import { TreeSelect } from '@fibrous/semi-ui-solid';

const treeData = [
    {
        label: '亚洲',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: '中国',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: '北京',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: '上海',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                ],
            },
        ],
    },
    {
        label: '北美洲',
        value: 'North America',
        key: '1',
    },
];

export default function TreeSelectDemo() {
    return <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} placeholder="请选择" />;
}

多选

设置 multiple,可以进行多选。多选情况下所有子项都被选择时,自动勾选显示其父项。

通过 leafOnly 属性,可以设置只展示叶子节点,同时 onChange 的回调入参也会只有叶子节点的值。





代码
import { TreeSelect } from '@fibrous/semi-ui-solid';

const treeData = [
    {
        label: 'Asia',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: 'China',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: 'Beijing',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: 'Shanghai',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                    {
                        label: 'Chengdu',
                        value: 'Chengdu',
                        key: '0-0-2',
                    },
                ],
            },
            {
                label: 'Japan',
                value: 'Japan',
                key: '0-1',
                children: [
                    {
                        label: 'Osaka',
                        value: 'Osaka',
                        key: '0-1-0',
                    },
                ],
            },
        ],
    },
    {
        label: 'North America',
        value: 'North America',
        key: '1',
        children: [
            {
                label: 'United States',
                value: 'United States',
                key: '1-0',
            },
            {
                label: 'Canada',
                value: 'Canada',
                key: '1-1',
            },
        ],
    },
];

export default function TreeSelectDemo() {
    return (
        <div>
            <TreeSelect style={{ width: '300px' }} multiple dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} placeholder="请选择" />
            <br />
            <br />
            <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} multiple leafOnly placeholder="只渲染叶子节点" />
            <br />
            <br />
        </div>
    );
}

限制标签展示数量

在多选的场景中,利用 maxTagCount 可以限制展示的标签数量,超出部分将以 +N 的方式展示。
使用 showRestTagsPopover (>= v2.22.0) 可以设置在超出 maxTagCount 后,hover +N 是否显示 Popover,默认为 false。并且,还可以在 restTagsPopoverProps 属性中配置 Popover。

maxTagCount=2:

maxTagCount=2, showRestTagsPopover:

代码
import { TreeSelect } from '@fibrous/semi-ui-solid';

const treeData = [
    {
        label: 'Asia',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: 'China',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: 'Beijing',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: 'Shanghai',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                    {
                        label: 'Chengdu',
                        value: 'Chengdu',
                        key: '0-0-2',
                    },
                ],
            },
            {
                label: 'Japan',
                value: 'Japan',
                key: '0-1',
                children: [
                    {
                        label: 'Osaka',
                        value: 'Osaka',
                        key: '0-1-0',
                    },
                ],
            },
        ],
    },
    {
        label: 'North America',
        value: 'North America',
        key: '1',
        children: [
            {
                label: 'United States',
                value: 'United States',
                key: '1-0',
            },
            {
                label: 'Canada',
                value: 'Canada',
                key: '1-1',
            },
        ],
    },
];

const textStyle = { margin: '20px 0 10px' };

export default function TreeSelectDemo() {
    return (
        <div>
            <h4 style={textStyle}>maxTagCount=2:</h4>
            <TreeSelect multiple maxTagCount={2} style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} placeholder="当选中标签超过两个将折叠" defaultValue={['Beijing', 'Chengdu', 'Canada']} />
            <h4 style={textStyle}>maxTagCount=2, showRestTagsPopover:</h4>
            <TreeSelect showRestTagsPopover restTagsPopoverProps={{ position: 'top' }} multiple maxTagCount={2} style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} placeholder="hover +N 查看" defaultValue={['Beijing', 'Chengdu', 'Canada']} />
        </div>
    );
}

可搜索的

通过设置 filterTreeNode 属性可支持搜索功能。默认对 label 值进行搜索,可通过 treeNodeFilterProp 更改。

如果只希望展示过滤后的结果,可以设置 showFilteredOnly 。

如果想要获取搜索结果的具体信息,可使用 onSearch 回调函数,函数具体参数见 API 列表。

showFilteredOnly






代码
import { createSignal } from 'solid-js';
import { Switch, TreeSelect, type TreeNodeData } from '@fibrous/semi-ui-solid';

const treeData = [
    {
        label: 'Asia',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: 'China',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: 'Beijing',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: 'Shanghai',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                ],
            },
            {
                label: 'Japan',
                value: 'Japan',
                key: '0-1',
                children: [
                    {
                        label: 'Osaka',
                        value: 'Osaka',
                        key: '0-1-0',
                    },
                ],
            },
        ],
    },
    {
        label: 'North America',
        value: 'North America',
        key: '1',
        children: [
            {
                label: 'United States',
                value: 'United States',
                key: '1-0',
            },
            {
                label: 'Canada',
                value: 'Canada',
                key: '1-1',
            },
        ],
    },
];

function onSearch(inputValue: string, filteredExpandedKeys: string[], filteredNodes: TreeNodeData[]) {
    console.log('onSearch', inputValue, filteredExpandedKeys, filteredNodes);
}

export default function TreeSelectDemo() {
    const [showFilteredOnly, setShowFilteredOnly] = createSignal(false);

    return (
        <>
            <span id="treeselect-searchable-filtered-only">showFilteredOnly</span>
            <Switch aria-labelledby="treeselect-searchable-filtered-only" checked={showFilteredOnly()} onChange={(checked) => setShowFilteredOnly(checked)} size="small" />
            <br />
            <br />
            <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} filterTreeNode showFilteredOnly={showFilteredOnly()} placeholder="单选可搜索的" onSearch={onSearch} />
            <br />
            <br />
            <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} multiple filterTreeNode maxTagCount={2} showFilteredOnly={showFilteredOnly()} placeholder="多选可搜索的" searchPlaceholder="请输入关键字开始搜索" onSearch={onSearch} />
            <br />
            <br />
            <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} multiple filterTreeNode maxTagCount={2} showFilteredOnly={showFilteredOnly()} placeholder="搜索框autofocus" searchPlaceholder="autofocus" searchAutoFocus onSearch={onSearch} />
        </>
    );
}

远程搜索

通过设置 remote 属性可启用远程搜索。开启后,输入时不会执行本地过滤,而是仅触发 onSearch 回调,用户可自行处理远程数据获取并更新 treeData。

搜索框位置

可以使用 searchPosition 来设置搜索框的位置,可选: dropdown(默认)、trigger。

当输入框位于 trigger 时:

  1. 搜索框占位符由 placeholder 控制;
  2. showClear=true 时,点击输入框的清空按钮,将同时清空 inputValue 和 value。
单选


代码
import { TreeSelect } from '@fibrous/semi-ui-solid';

const treeData = [
    {
        label: 'Asia',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: 'China',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: 'Beijing',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: 'Shanghai',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                ],
            },
            {
                label: 'Japan',
                value: 'Japan',
                key: '0-1',
                children: [
                    {
                        label: 'Osaka',
                        value: 'Osaka',
                        key: '0-1-0',
                    },
                ],
            },
        ],
    },
    {
        label: 'North America',
        value: 'North America',
        key: '1',
        children: [
            {
                label: 'United States',
                value: 'United States',
                key: '1-0',
            },
            {
                label: 'Canada',
                value: 'Canada',
                key: '1-1',
            },
        ],
    },
];

export default function TreeSelectDemo() {
    return (
        <>
            <TreeSelect searchPosition="trigger" style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} filterTreeNode placeholder="单选" />
            <br />
            <br />
            <TreeSelect searchPosition="trigger" style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} multiple filterTreeNode maxTagCount={2} placeholder="多选" />
        </>
    );
}

Trigger 内多行换行(triggerTagWrap)

当你在 多选 + 搜索框位于 trigger 的场景下,选择了较多项或输入较长文本时,默认 trigger 可能会更倾向于保持单行展示。

开启 triggerTagWrap 后,可以让 trigger 内的已选标签支持自动换行(多行展示)。

China
代码
import { TreeSelect } from '@fibrous/semi-ui-solid';

const treeData = [
    {
        label: 'Asia',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: 'China',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: 'Beijing',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: 'Shanghai',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                    {
                        label: 'Shenzhen',
                        value: 'Shenzhen',
                        key: '0-0-2',
                    },
                    {
                        label: 'Guangzhou',
                        value: 'Guangzhou',
                        key: '0-0-3',
                    },
                ],
            },
            {
                label: 'Japan',
                value: 'Japan',
                key: '0-1',
                children: [
                    {
                        label: 'Osaka',
                        value: 'Osaka',
                        key: '0-1-0',
                    },
                ],
            },
        ],
    },
    {
        label: 'North America',
        value: 'North America',
        key: '1',
        children: [
            {
                label: 'United States',
                value: 'United States',
                key: '1-0',
            },
            {
                label: 'Canada',
                value: 'Canada',
                key: '1-1',
            },
        ],
    },
];

export default function TreeSelectDemo() {
    return <TreeSelect searchPosition="trigger" triggerTagWrap style={{ width: '260px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} multiple filterTreeNode placeholder="请选择多个选项或输入长文本" defaultValue={['Beijing', 'Shanghai', 'Shenzhen', 'Guangzhou']} />;
}

尺寸大小

可以通过 size 设置尺寸大小,可选: 'small'、'default'、'large'





代码
import { TreeSelect } from '@fibrous/semi-ui-solid';

const treeData = [
    {
        label: '亚洲',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: '中国',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: '北京',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: '上海',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                ],
            },
        ],
    },
    {
        label: '北美洲',
        value: 'North America',
        key: '1',
    },
];

export default function TreeSelectDemo() {
    return (
        <div>
            <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} multiple size="small" placeholder="small" />
            <br />
            <br />
            <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} multiple size="default" placeholder="default" />
            <br />
            <br />
            <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} multiple size="large" placeholder="large" />
        </div>
    );
}

默认展开

defaultExpandAll 和 expandAll 均可以设置 TreeSelect 的默认展开/收起状态。二者的区别是,defaultExpandAll 只在初始化时生效,而 expandAll 不仅会在初始化时生效,当数据(treeData)发生动态更新时,expandAll 也仍然生效。

在下面的 demo 中,TreeData 更新后,defaultExpandAll 失效,expandAll 仍然生效。

代码
import { createSignal, onCleanup, onMount } from 'solid-js';
import { TreeSelect, type TreeNodeData } from '@fibrous/semi-ui-solid';

const treeData: TreeNodeData[] = [
    {
        label: '亚洲',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: '中国',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: '北京',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: '上海',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                ],
            },
        ],
    },
    {
        label: '北美洲',
        value: 'North America',
        key: '1',
    },
];

export default function TreeSelectDemo() {
    const [data, setData] = createSignal<TreeNodeData[]>([]);

    onMount(() => {
        const timer = setTimeout(() => setData(treeData), 500);
        onCleanup(() => clearTimeout(timer));
    });

    return (
        <>
            <TreeSelect style={{ width: '300px', 'margin-bottom': '20px' }} expandAll treeData={data()} placeholder="expandAll" />
            <TreeSelect style={{ width: '300px' }} defaultExpandAll treeData={data()} placeholder="defaultExpandAll" />
        </>
    );
}

禁用





代码
import { TreeSelect } from '@fibrous/semi-ui-solid';

const treeData = [
    {
        label: '亚洲',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: '中国',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: '北京',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: '上海',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                ],
            },
        ],
    },
    {
        label: '北美洲',
        value: 'North America',
        key: '1',
    },
];

export default function TreeSelectDemo() {
    return (
        <div>
            <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} disabled placeholder="禁用下拉菜单" />
            <br />
            <br />
            <TreeSelect style={{ width: '300px' }} defaultValue="Shanghai" dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} disabled />
            <br />
            <br />
            <TreeSelect style={{ width: '300px' }} defaultValue={['Shanghai', 'North America']} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} multiple disabled />
        </div>
    );
}

严格禁用

可以使用 disableStrictly 来开启严格禁用。开启严格禁用后,当节点是 disabled 的时候,则不能通过子级或者父级的关系改变选中状态。

以下面的 demo 为例,节点"中国"开启了严格禁用,因此,当我们改变其父节点"亚洲"的选中状态时,也不会影响到节点"中国"的选中状态。

代码
import { TreeSelect } from '@fibrous/semi-ui-solid';

const treeData = [
    {
        label: '亚洲',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: '中国',
                value: 'China',
                key: '0-0',
                disabled: true,
                children: [
                    {
                        label: '北京',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: '上海',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                ],
            },
            {
                label: '日本',
                value: 'Japan',
                key: '0-1',
            },
        ],
    },
    {
        label: '北美洲',
        value: 'North America',
        key: '1',
    },
];

export default function TreeSelectDemo() {
    return (
        <div>
            <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} disableStrictly multiple defaultValue={['Shanghai']} />
        </div>
    );
}

受控

传入 value 时即为受控组件,可以配合 onChange 使用。

代码
import { createSignal } from 'solid-js';
import { TreeSelect } from '@fibrous/semi-ui-solid';

const treeData = [
    {
        label: '亚洲',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: '中国',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: '北京',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: '上海',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                ],
            },
        ],
    },
    {
        label: '北美洲',
        value: 'North America',
        key: '1',
    },
];

export default function TreeSelectDemo() {
    const [value, setValue] = createSignal<string | undefined>('Shanghai');
    return <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} value={value()} placeholder="请选择" onChange={(next) => setValue(next)} />;
}

节点选中关系

版本:>= 2.5.0

多选时,可以使用 checkRelation 来设置节点之间选中关系的类型,可选:'related'(默认)、'unRelated'。当选中关系为 'unRelated' 时,意味着节点之间的选中互不影响。

代码
import { TreeSelect } from '@fibrous/semi-ui-solid';

const treeData = [
    {
        label: '亚洲',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: '中国',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: '北京',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: '上海',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                ],
            },
        ],
    },
    {
        label: '北美洲',
        value: 'North America',
        key: '1',
    },
];

export default function TreeSelectDemo() {
    return <TreeSelect multiple defaultValue={['Asia']} checkRelation="unRelated" style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData} />;
}

开启搜索的展开受控

传入 expandedKeys 时即为展开受控组件,可以配合 onExpand 使用。当展开受控时,如果开启 filterTreeNode 并进行搜索是不会再自动展开节点的,此时,节点的展开完全由 expandedKeys 来控制。你可以利用 onSearch 的入参 filteredExpandedKeys 来实现展开受控时的搜索展开效果。

虚拟化

列表虚拟化,用于大量树节点的情况。开启后,动画效果将被关闭。

virtualize 是一个包含下列值的对象:

  • height: 高度值,如果为 string 必须有计算高度才能被渲染出来,即其父节点有 offsetHeight。建议传入数值。
  • width: 宽度值,默认 100%
  • itemSize: 每行的treeNode的高度,必传

如果带搜索框,建议开启 showFilteredOnly 减少多余节点的渲染。

共 0 个节点

代码
import { createSignal, Show } from 'solid-js';
import { Button, TreeSelect, type TreeNodeData } from '@fibrous/semi-ui-solid';

// x: nodes per level. y: nodes with children per level. z: depth (0 is one level)
function generateData(x = 5, y = 4, z = 3) {
    const data: TreeNodeData[] = [];

    function fill(level: number, preKey: string, nodes: TreeNodeData[]) {
        const parents: string[] = [];
        for (let i = 0; i < x; i++) {
            const key = `${preKey}-${i}`;
            nodes.push({ label: `${key}-标签`, key: `${key}-key`, value: `${key}-value` });
            if (i < y) {
                parents.push(key);
            }
        }
        if (level < 0) {
            return;
        }
        parents.forEach((key, index) => {
            const children: TreeNodeData[] = [];
            nodes[index].children = children;
            fill(level - 1, key, children);
        });
    }
    fill(z, '0', data);

    let total = 0;
    for (let n = z + 1; n >= 0; n--) {
        total += x * y ** n;
    }
    return { gData: data, total };
}

export default function TreeSelectDemo() {
    const [gData, setGData] = createSignal<TreeNodeData[]>([]);
    const [total, setTotal] = createSignal(0);

    function onGen() {
        const generated = generateData();
        setGData(generated.gData);
        setTotal(generated.total);
    }

    return (
        <div style={{ padding: '0 20px' }}>
            <Button onClick={onGen}>生成数据: </Button>
            <span>共 {total()} 个节点</span>
            <br />
            <br />
            <Show when={gData().length > 0}>
                <TreeSelect
                    style={{ width: '300px' }}
                    treeData={gData()}
                    filterTreeNode
                    showFilteredOnly
                    placeholder="Please select"
                    dropdownStyle={{ overflow: 'hidden' }}
                    virtualize={{
                        itemSize: 28,
                        // Dropdown height 300 minus the search box and 8 * 2 padding; a set height fills the rest
                        height: 236,
                    }}
                />
            </Show>
        </div>
    );
}

动态更新数据



代码
import { createSignal } from 'solid-js';
import { Button, TreeSelect, type TreeNodeData } from '@fibrous/semi-ui-solid';

export default function TreeSelectDemo() {
    const [treeData, setTreeData] = createSignal<TreeNodeData[]>([]);

    function add() {
        const itemLength = Math.floor(Math.random() * 5) + 1;
        const nextTreeData = Array.from({ length: itemLength }, (_v, i) => {
            const length = Math.floor(Math.random() * 3);
            const children = Array.from({ length }, (_cv, ci) => {
                const child = {
                    key: `${i}-${ci}`,
                    label: `Leaf-${i}-${ci}`,
                    value: `${i}-${ci}`,
                };
                return child;
            });
            const item = {
                key: `${i}`,
                label: `Item-${i}`,
                value: `${i}`,
                children,
            };
            return item;
        });
        setTreeData(nextTreeData);
    }

    return (
        <>
            <TreeSelect style={{ width: '300px' }} dropdownStyle={{ 'max-height': '400px', overflow: 'auto' }} treeData={treeData()} placeholder="请选择" />
            <br />
            <br />
            <Button onClick={add}>动态改变数据</Button>
        </>
    );
}

异步加载数据

通过设置 loadData 可以动态加载数据,此时需要在数据中传入 isLeaf 标明叶子节点。

代码
import { createSignal } from 'solid-js';
import { TreeSelect, type TreeNodeData } from '@fibrous/semi-ui-solid';

const initialData: TreeNodeData[] = [
    {
        label: 'Expand to load',
        value: '0',
        key: '0',
    },
    {
        label: 'Expand to load',
        value: '1',
        key: '1',
    },
    {
        label: 'Leaf Node',
        value: '2',
        key: '2',
        isLeaf: true,
    },
];

function updateTreeData(list: TreeNodeData[], key: string | undefined, children: TreeNodeData[]): TreeNodeData[] {
    return list.map((node) => {
        if (node.key === key) {
            return { ...node, children };
        }
        if (node.children) {
            return { ...node, children: updateTreeData(node.children, key, children) };
        }
        return node;
    });
}

export default function TreeSelectDemo() {
    const [treeData, setTreeData] = createSignal<TreeNodeData[]>(initialData);

    function onLoadData(treeNode?: TreeNodeData) {
        return new Promise<void>((resolve) => {
            if (!treeNode || treeNode.children) {
                resolve();
                return;
            }
            const key = treeNode.key;
            setTimeout(() => {
                setTreeData((origin) =>
                    updateTreeData(origin, key, [
                        {
                            label: 'Child Node',
                            key: `${key}-0`,
                        },
                        {
                            label: 'Child Node',
                            key: `${key}-1`,
                        },
                    ])
                );
                resolve();
            }, 1000);
        });
    }

    return <TreeSelect loadData={onLoadData} treeData={treeData()} style={{ width: '300px' }} placeholder="请选择" />;
}

自定义 Trigger

如果默认的触发器样式满足不了你的需求,可以用 triggerRender 自定义选择框的展示。入参是活对象,在返回的 JSX 中读取它的字段,不要解构。

triggerRender 入参如下:

interface TriggerRenderProps {
    componentProps: Omit<TreeSelectProps, 'ref'>; // 所有用户传给 TreeSelect 的 props
    disabled: boolean; // 是否禁用 TreeSelect
    value: TreeNodeData[]; // 已选中的 node 的数据
    inputValue: string; // 当前 input 框的输入值
    placeholder?: string; // placeholder
    // 用于清空值的函数;绑定在触发器内的控件上时传入点击事件,避免这次点击同时打开下拉
    onClear: (e?: MouseEvent) => void;
    // 删除单个 item 时调用的函数,以 item 的 key 作为入参
    onRemove: (key: string) => void;
    // 用于在 Input 框值更新时启动搜索,需要设置 filterTreeNode 非 false,
    // searchPosition 为 'trigger'
    onSearch: (inputValue: string) => void;
}
代码
import { Tag, TagInput, TreeSelect, type TreeNodeData, type TreeSelectTriggerRenderProps } from '@fibrous/semi-ui-solid';

const treeData: TreeNodeData[] = [
    {
        label: '亚洲',
        value: '亚洲',
        key: '0',
        children: [
            {
                label: '中国',
                value: '中国',
                key: '0-0',
                children: [
                    {
                        label: '北京',
                        value: '北京',
                        key: '0-0-0',
                    },
                    {
                        label: '上海',
                        value: '上海',
                        key: '0-0-1',
                    },
                ],
            },
        ],
    },
    {
        label: '北美洲',
        value: '北美洲',
        key: '1',
    },
];

// The render props are a live object: read them in the returned JSX so the TagInput follows the selection
function renderTrigger(props: TreeSelectTriggerRenderProps) {
    return (
        <TagInput
            value={props.value.flatMap<string>((item) => item.key ?? [])}
            inputValue={props.inputValue}
            onInputChange={props.onSearch}
            renderTagItem={(key) => (
                <Tag style={{ 'margin-left': '2px' }} tagKey={key} closable onClose={() => props.onRemove(key)}>
                    {props.value.find((item) => item.key === key)?.label}
                </Tag>
            )}
        />
    );
}

export default function TreeSelectDemo() {
    return <TreeSelect triggerRender={renderTrigger} filterTreeNode searchPosition="trigger" multiple treeData={treeData} placeholder="Custom Trigger" onChange={(value) => console.log('onChange', value)} style={{ width: '300px' }} />;
}

自定义渲染已选项

你可以通过 renderSelectedItem 自定义选择框中已选项标签的渲染结构。

  • 单选时 renderSelectedItem(treeNode: TreeNodeData) => content: JSX.Element
  • 多选时 renderSelectedItem(treeNode: TreeNodeData, { index: number | string, onClose: (tagContent, e?) => void }) => { isRenderInTag: boolean, content: JSX.Element }
    • isRenderInTag 为 true 时,会自动将 content 包裹在 Tag 中渲染(带有背景色以及关闭按钮)
    • isRenderInTag 为 false 时,将直接渲染返回的 content

单选

多选+ isRenderInTag=true

多选 + isRenderInTag=false

代码
import { Tag, TreeSelect, type TreeNodeData } from '@fibrous/semi-ui-solid';

const treeData: TreeNodeData[] = [
    {
        label: '亚洲',
        value: 'Asia',
        key: '0',
        children: [
            {
                label: '中国',
                value: 'China',
                key: '0-0',
                children: [
                    {
                        label: '北京',
                        value: 'Beijing',
                        key: '0-0-0',
                    },
                    {
                        label: '上海',
                        value: 'Shanghai',
                        key: '0-0-1',
                    },
                ],
            },
        ],
    },
    {
        label: '北美洲',
        value: 'North America',
        key: '1',
    },
    {
        label: '南美洲',
        value: 'South America',
        key: '2',
    },
    {
        label: '南极洲',
        value: 'Antarctica',
        key: '3',
    },
];

export default function TreeSelectDemo() {
    return (
        <>
            <h4>单选</h4>
            <TreeSelect style={{ width: '300px' }} treeData={treeData} renderSelectedItem={(item) => item.label} />
            <h4>多选+ isRenderInTag=true</h4>
            <TreeSelect style={{ width: '300px' }} treeData={treeData} multiple renderSelectedItem={(item) => ({ content: item.label, isRenderInTag: true })} />
            <h4>多选 + isRenderInTag=false</h4>
            <TreeSelect
                style={{ width: '300px' }}
                treeData={treeData}
                multiple
                maxTagCount={2}
                renderSelectedItem={(item, { onClose }) => ({
                    content: (
                        <Tag color="white" closable onClose={onClose}>
                            {item.value}
                        </Tag>
                    ),
                    isRenderInTag: false,
                })}
            />
        </>
    );
}

API参考

TreeSelect

Props

属性说明类型默认值
aria-labelcombobox 的可访问名称:搜索框在选择框内时是搜索框,否则是触发器。string-
arrowIcon自定义右侧下拉箭头图标,传 null 不显示箭头。showClear 开启且有已选值时,hover 优先显示清除图标。JSX.Element-
autoAdjustOverflow弹出层被遮挡时是否自动调整方向。booleantrue
autoExpandParent是否自动展开父节点。booleanfalse
autoMergeValue是否合并值:开启后选中父节点时,值中不包含其后代节点。仅在 leafOnly 为 false 时生效。booleantrue
borderless是否使用无边框样式。booleanfalse
checkRelation多选时节点选中状态的关系,可选 related、unRelated。unRelated 时节点之间的选中互不影响。CheckRelation"related"
clearIcon自定义清除按钮的图标,showClear 为 true 时有效。JSX.Element-
clickToHide选中后是否自动关闭下拉面板,仅在单选时生效。booleantrue
clickTriggerToHide面板打开时,点击触发器后是否关闭面板。booleantrue
defaultExpandAll初始化时是否展开全部节点。数据(treeData)变化后此属性不再生效,需要时使用 expandAll。boolean-
defaultExpandedKeys默认展开的节点 key,展开后显示直接子节点。string[]-
defaultOpen是否默认打开下拉面板。booleanfalse
defaultValue默认选中的值,只在挂载时读取;多选时为数组。TypeModeValue<T, Multiple>-
disabled是否禁用。boolean-
disableStrictly严格禁用:节点禁用时,它的选中状态不会因为父或子节点的变化而改变。booleanfalse
dropdownClassName下拉面板的类名。string-
dropdownMatchSelectWidth下拉面板的最小宽度是否与选择框相同。booleantrue
dropdownStyle下拉面板的样式。JSX.CSSProperties | string-
emptyContent无数据或搜索无结果时的空状态内容,未设置时显示语言包中的文案;传 null 时不显示。JSX.Element-
expandAction节点的展开方式,可选 false(只有点击展开图标才展开)、click、doubleClick。ExpandActionfalse
expandAll是否默认展开全部节点。数据(treeData)变化后仍受此属性影响。booleanfalse
expandedKeys(受控)展开的节点 key,展开后显示直接子节点。string[]-
expandIcon自定义展开图标,可传元素或函数。传元素时由组件给它加上展开按钮的类名与点击事件;传函数时入参为 class、onClick 与 expanded,由函数自己渲染按钮,并把它们挂到元素上。TreeProps['expandIcon']-
filterTreeNode是否可搜索,也可以传入函数自定义搜索逻辑,函数的第三个参数为节点数据 data。Typeboolean | ((inputValue: string, treeNodeString: string, data?: TreeNodeData) => boolean)false
icon自定义节点图标,同 Tree 的 icon。传入元素时所有节点使用同一图标;传入函数时每行调用一次,参数为该行的属性(含节点数据),返回该行的图标。TypeJSX.Element | ((row: TreeRowProps) => JSX.Element)-
insetLabel选择框内的标签,同时作为下拉树的名称。JSX.Element-
insetLabelIdinsetLabel 外层元素的 id,供 aria-labelledby 引用。string-
keyMaps自定义节点中 key、label、value、disabled、children、isLeaf 与 icon 字段的名称。如果自定义了 label 的字段名并开启搜索,需要把 treeNodeFilterProp 设为 treeData 中的某个字段,或通过 filterTreeNode 传入自定义搜索函数。KeyMapProps-
labelEllipsis标签溢出时是否省略。未设置时,虚拟化模式下为 true,否则为 false。boolean-
leafOnly多选时是否只展示叶子节点的标签,onChange 的入参也只包含叶子节点的值。boolean-
loadData异步加载子节点,返回 Promise。需要在 treeData 中用 isLeaf 标明叶子节点。Type(treeNode?: TreeNodeData) => Promise<void>-
loadedKeys(受控)已加载完成的节点 key,与 loadData 配合使用。string[]-
maxTagCount最多显示的标签数量,超出部分以 +N 的形式展示。number-
motion下拉面板打开和关闭时是否带动画。booleantrue
motionExpand节点展开和收起时是否带动画。booleantrue
multiple是否多选。Multiplefalse
onBlurTreeSelect 失去焦点时的回调,每次 onFocus 之后触发一次。焦点离开触发器与下拉时入参为原生 FocusEvent,点击外部时为原生 MouseEvent,选中后关闭下拉时为选中的点击或按键事件。Type(e: MouseEvent | KeyboardEvent | FocusEvent) => void-
onChange选中值变化时的回调,入参为节点的 value(多选时为数组)、节点数据与事件。关闭标签时事件为 null,键盘选中时为 KeyboardEvent,清空单选时值为 undefined。onChangeWithObject 为 true 时,入参变为节点数据与事件。Type(...args: OnChangeArgs<T, Multiple, WithObject>) => void-
onChangeWithObjectonChange 的入参是否为节点的全部属性。为 true 时,onChange 的入参变为 (node, e)。WithObjectfalse
onClear点击清除按钮时的回调。Type(e: MouseEvent) => void-
onExpand展开或收起节点时的回调,入参为展开的 key 数组与 { expanded, node }。Type(expandedKeys: string[], expandedOtherProps: ExpandedOtherProps) => void-
onFocus焦点从外部进入触发器(包括搜索框与清除按钮)或下拉面板时的回调,入参为原生 FocusEvent;在它们之间移动焦点不会再次触发。选中后关闭下拉,又通过点击或按键重新打开时,同样触发一次,入参为该点击或按键事件;挂载时 searchAutoFocus 聚焦的入参为 null。Type(e: MouseEvent | KeyboardEvent | FocusEvent | null) => void-
onLoad节点加载完成时的回调。Type(loadedKeys: Set<string>, treeNode: TreeNodeData) => void-
onSearch搜索值变化时的回调。入参为搜索值、filteredExpandedKeys(因搜索或 value 而展开的节点 key,展开受控时可用于实现搜索展开)与 filteredNodes(命中搜索的节点)。Type(searchValue: string, filteredExpandedKeys: string[], filteredNodes: TreeNodeData[]) => void-
onSelect节点被选中或取消选中时的回调,入参为节点的 key、是否选中与节点数据。Type(selectedKey: string, selected: boolean, selectedNode: TreeNodeData) => void-
onVisibleChange下拉面板显示或隐藏时的回调,入参为是否可见。Type(isVisible: boolean) => void-
optionListStyle选项列表的样式。JSX.CSSProperties | string-
outerBottomSlot渲染在下拉面板底部的自定义内容,与选项列表同级。JSX.Element-
outerTopSlot渲染在下拉面板顶部的自定义内容,与选项列表同级。开启 filterTreeNode 时它会替代搜索框,需要自定义搜索时可以通过 ref 的 search 方法实现。JSX.Element-
placeholder输入框的占位文本。string-
position弹出层的位置,可选值参考 Tooltip 的 position,默认 bottomLeft(RTL 下为 bottomRight)。Position-
prefix选择框前面的内容。JSX.Element-
preventScroll组件内部调用 focus 时是否阻止浏览器滚动文档以显示新聚焦的元素,不作用于用户传入的元素。boolean-
ref根元素,带有 search 与 close 方法,类型为 TreeSelectElement。元素的 focus() 聚焦 combobox(搜索框在触发器内时是搜索框),不展开下拉面板。TypeRef<TreeSelectElement>-
remote是否开启远程搜索。开启后输入时不做本地过滤,只触发 onSearch,由调用方获取数据并更新 treeData。booleanfalse
renderFullLabel自定义整行的渲染,入参为活对象,返回值外层会包一层带 role="treeitem" 与状态属性的行元素。用法与 Tree 的 renderFullLabel 相同。Type(renderFullLabelProps: RenderFullLabelProps) => JSX.Element-
renderLabel自定义节点文本的渲染,入参为原文本、节点数据与搜索词。Type(label?: string | number, treeNode?: TreeNodeData, searchWord?: string) => JSX.Element-
renderSelectedItem自定义选择框中已选项的渲染。单选时返回内容;多选时返回 { content, isRenderInTag },isRenderInTag 为 true 时内容自动包进带背景色与关闭按钮的 Tag 中。TypeByMultiple<Multiple, RenderSelectedItemInSingle, RenderSelectedItemInMultiple>-
restTagsPopoverProps超出 maxTagCount 后折叠标签的 Popover 配置,见 Popover。PopoverProps-
searchAutoFocus搜索框挂载后是否自动聚焦。booleanfalse
searchPlaceholder下拉面板中搜索框的占位文本。string-
searchPosition搜索框的位置,可选 dropdown、trigger。'dropdown' | 'trigger'"dropdown"
searchRender自定义搜索框,入参为活对象,它的 ref 与 ARIA 属性需要写在输入框上;传 false 时不渲染搜索框。Typeboolean | ((inputProps: SearchRenderProps) => JSX.Element)-
showClear值不为空时,触发器是否显示清除按钮。booleanfalse
showFilteredOnly搜索时是否只展示过滤后的结果。boolean-
showLine选项面板中的节点是否显示连接线。boolean-
showRestTagsPopover标签数量超过 maxTagCount 后,hover 到 +N 时是否用 Popover 展示其余标签。booleanfalse
showSearchClear下拉面板中的搜索框是否支持清空。booleantrue
size输入框的尺寸,可选 large、small、default。Size"default"
stopPropagation是否阻止下拉面板上的点击事件冒泡。boolean | stringtrue
style触发器的内联样式,对象的键使用 kebab-case,如 'margin-right'。JSX.CSSProperties | string-
suffix选择框后面的内容。JSX.Element-
treeData节点数据,key 在整棵树内唯一。只有传入新数组才会更新,原地修改数组或节点不会生效。TreeNodeData[]-
treeNodeFilterProp搜索时匹配的 TreeNodeData 属性。string"label"
treeNodeLabelProp显示文本所用的 TreeNodeData 属性。string"label"
triggerRender自定义触发器,入参为 TriggerRenderProps 活对象,在返回的 JSX 中读取它的字段。Type(props: TriggerRenderProps) => JSX.Element-
triggerTagWrap已选标签在触发器内是否允许换行。仅在 multiple、开启搜索且 searchPosition 为 trigger 时生效。booleanfalse
validateStatus校验状态,可选 warning、error、default,只影响组件的背景色。ValidateStatus-
value(受控)当前选中的值,传入后即为受控组件,可配合 onChange 使用;多选时为数组。TypeModeValue<T, Multiple>-
virtualize虚拟列表配置,对象包含 itemSize(必传,每行的高度)、height(下拉高度,建议传数值)与 width。虚拟化时动画关闭。Virtualize-

Methods

方法说明类型
close关闭下拉面板。Type() => void
search按输入了该值来搜索,先打开下拉面板,需要同时设置 filterTreeNode。Type(value: string) => void

class、style 与原生属性(如 id、data-*)落在根元素(触发器)上。aria-label、aria-labelledby、aria-describedby、aria-errormessage、aria-invalid 与 aria-required 落在当前的 combobox 上:不可搜索时是根元素,搜索框在触发器内时是搜索框的 input。onFocus、onBlur、onChange 与 onSelect 是组件自己的回调,不会作为原生事件绑定到根元素。

TreeNodeData

不同 TreeNodeData 的 key 值要求必填且唯一。label 允许重复。value 值非必填。此时 onChange, value, defaultValue 及 onChangeWithObject 中所取的 value 属性值将改为 key 值。为了保证行为的符合预期,treeData 中的 value 值或者全部不填写,或者全部填写且唯一,不建议混写。

属性说明类型默认值
value属性值string|number-
label展示的文本,自定义内容使用 renderLabelstring|number-
disabled是否禁用,多选状态下支持booleanfalse
keyrequired且要求唯一string-
isLeaf是否为叶子节点boolean-
children子节点TreeNodeData[]-

节点图标使用 TreeSelect 的 icon 属性提供,不在数据中声明:传入元素时所有节点使用同一图标,传入函数时每行调用一次,参数为该行的属性(含节点数据)。

Methods

TreeSelect 的 ref 得到根元素(触发器),方法挂在这个元素上,类型为 TreeSelectElement(即 HTMLDivElement & TreeSelectHandle)。

方法说明
search(value: string)如果需要在外部自定义搜索框,可以在自定义搜索框值变更时主动调用该方法,改变筛选结果。按输入了该值来搜索,先打开下拉面板,需要同时设置 filterTreeNode
close()关闭下拉面板
import { TreeSelect, type TreeSelectElement } from '@fibrous/semi-ui-solid';

function App() {
    let treeSelect: TreeSelectElement | undefined;
    return (
        <>
            <TreeSelect ref={treeSelect} filterTreeNode />
            <button onClick={() => treeSelect?.close()}>关闭</button>
        </>
    );
}

Accessibility

ARIA

  • 按 ARIA 1.2 的 combobox 写法,任一时刻只有一个元素是 combobox。它的名称按优先级取:aria-labelledby、aria-label、实际渲染出的 insetLabel(Vue 为 #insetLabel,未传 insetLabelId 时组件生成 id;有 prefix 时 insetLabel 不渲染,也不作名称来源),都没有时取语言包中的名称:
    • 不可搜索或 searchPosition="dropdown" 时,触发器是 select-only combobox;下拉内的搜索框保持文本框角色,带 aria-controls、aria-autocomplete、aria-activedescendant
    • searchPosition="trigger" 时,搜索框是 combobox 和唯一的 Tab 停靠点,带 aria-autocomplete="list",触发器不再有角色。单选时搜索框在输入前为空,已选文本以 aria-describedby 关联为它的描述。点击触发器任意位置会把焦点交给搜索框;
  • TreeSelect 允许用户设置 aria-describedby、aria-errormessage、aria-invalid、aria-labelledby、aria-required,另外,Form 会为 Form.TreeSelect 自动设置这些属性;
  • 下拉中 role="tree" 的列表与 combobox 使用同一个名称来源:同一组 aria-labelledby、同一个 aria-label 或同一个 insetLabel;都没有时与 combobox 一样取语言包中的名称;
  • 下拉中的节点与 Tree 相同:role="treeitem",带 aria-level、aria-setsize、aria-posinset、aria-disabled,可展开的节点带 aria-expanded;单选时用 aria-selected,多选时用 aria-checked(半选为 mixed)。
  • 节点前的展开箭头和多选复选框只是状态的图示,对读屏软件隐藏,也不接收焦点。

键盘交互

TreeSelect 按 WAI-ARIA Combobox 处理键盘,弹出层是一棵树:焦点始终留在触发器或搜索框上,aria-activedescendant 指向当前节点,当前节点显示焦点环。下拉刚打开时没有当前节点。

  • 下拉关闭时:ArrowDown、ArrowUp、Enter、Space 打开下拉;Home、End 打开下拉并移到第一个、最后一个可见节点
  • ArrowDown、ArrowUp:移到下一个、上一个可见节点,第一次按 ArrowDown 移到第一个节点;Home、End:移到第一个、最后一个可见节点
  • ArrowRight:展开收起的节点;节点已展开时移到它的第一个子节点(RTL 下与 ArrowLeft 互换)
  • ArrowLeft:收起展开的节点;否则移到父节点
  • Enter、Space:选中当前节点,多选时勾选或取消勾选;禁用节点不响应
  • 输入字符:下拉打开时,从当前节点往后,移到第一个以输入内容开头的可见节点;快速连续输入的字符合成一个搜索词。下拉关闭时不响应
  • Esc:关闭下拉

焦点在搜索框里时,只有 ArrowDown、ArrowUp、Enter 用于移动和选择,其余按键(含字符、Space、Home、End、ArrowLeft、ArrowRight)用于编辑文字。

showClear 开启、有已选值或搜索文本且未禁用时,触发器内渲染清除按钮。它是原生 button,名称为语言包中的「清空已选值」,在 hover、下拉打开或焦点位于 TreeSelect 内时显示。从触发器或搜索框按 Tab 移到清除按钮,Enter 或 Space 清空,效果与鼠标点击相同,清空后焦点回到触发器或搜索框。多选时也可在搜索框中用 Backspace 删除标签。

自定义 searchRender 的入参是活对象,其中带有这组 ARIA 属性(aria-activedescendant、aria-controls、aria-autocomplete、名称;在触发器内时为整组 combobox 属性,含 role、aria-expanded),请把它们展开到输入框上,否则读屏软件不会读出当前节点。入参里的 ref 是回调,同样要写到输入框(Solid Input、原生 input 或 textarea)上,点击触发器内的已选文本时才能把焦点交给它。

示例:

<TreeSelect aria-label="example treeSelect" treeData={treeData} />

设计变量

下表是组件内部的 Sass 变量,不能覆写。定制样式请覆写 --semi-* CSS 变量(见 Design Token 页),或写不分层的 CSS 规则。
Sass 变量值用法
$color-treeSelect_default-bg-defaultvar(--semi-color-fill-0)树选择器选择框背景颜色 - 默认
$color-treeSelect_default-bg-hovervar(--semi-color-fill-1)树选择器选择框背景颜色 - 悬停
$color-treeSelect_default-bg-activevar(--semi-color-fill-2)树选择器选择框背景颜色 - 按下
$color-treeSelect_default-bg-focusvar(--semi-color-fill-0)树选择器选择框背景颜色 - 选中
$color-treeSelect_default-border-defaulttransparent树选择器选择框描边颜色 - 默认
$color-treeSelect_selection-text-defaultvar(--semi-color-text-0)树选择器选择框回填内容文本颜色 - 默认
$color-treeSelect_default-icon-defaultvar(--semi-color-text-2)树选择器选择框图标颜色 - 默认
$color-treeSelect_default-icon-hovervar(--semi-color-primary-hover)树选择器选择框清空按钮颜色 - 悬停
$color-treeSelect_default-icon-activevar(--semi-color-primary-active)树选择器选择框清空按钮颜色 - 按下
$color-treeSelect_default-border-hovertransparent树选择器选择框描边颜色 - 悬浮
$color-treeSelect_default-border-focusvar(--semi-color-focus-border)树选择器选择框描边颜色 - 选中
$color-treeSelect_search-border-defaultvar(--semi-color-fill-0)树选择器菜单搜索框描边颜色 - 默认
$color-treeSelect_tag_disabled-bg-defaulttransparent禁用树选择器菜单标签背景颜色
$color-treeSelect_input_disabled-bg-defaultvar(--semi-color-disabled-fill)禁用树选择器选择框背景颜色
$color-treeSelect_input_disabled-text-defaultvar(--semi-color-disabled-text)禁用树选择器选择框文本颜色
$color-treeSelect_input_disabled-bg-hovervar(--semi-color-disabled-fill)禁用树选择器选择框背景颜色 - 悬浮
$color-treeSelect_input_placeholder-text-defaultvar(--semi-color-text-2)树选择器选择框占位文本颜色
$color-treeSelect_warning-bg-defaultvar(--semi-color-warning-light-default)警告树选择器选择框背景颜色 - 默认
$color-treeSelect_warning-border-defaultvar(--semi-color-warning-light-default)警告树选择器选择框描边颜色 - 默认
$color-treeSelect_warning-bg-hovervar(--semi-color-warning-light-hover)警告树选择器选择框背景颜色 - 悬浮
$color-treeSelect_warning-border-hovervar(--semi-color-warning-light-hover)警告树选择器选择框描边颜色 - 悬浮
$color-treeSelect_warning-bg-focusvar(--semi-color-warning-light-default)警告树选择器选择框背景颜色 - 选中
$color-treeSelect_warning-border-focusvar(--semi-color-warning)警告树选择器选择框描边颜色 - 选中
$color-treeSelect_warning-bg-activevar(--semi-color-warning-light-active)警告树选择器选择框背景颜色 - 激活
$color-treeSelect_warning-border-activevar(--semi-color-warning-light-active)警告树选择器选择框描边颜色 - 激活
$color-treeSelect_danger-bg-defaultvar(--semi-color-danger-light-default)错误树选择器选择框背景颜色 - 默认
$color-treeSelect_danger-border-defaultvar(--semi-color-danger-light-default)错误树选择器选择框描边颜色 - 默认
$color-treeSelect_danger-bg-hovervar(--semi-color-danger-light-hover)错误树选择器选择框背景颜色 - 悬浮
$color-treeSelect_danger-border-hovervar(--semi-color-danger-light-hover)错误树选择器选择框描边颜色 - 悬浮
$color-treeSelect_danger-bg-focusvar(--semi-color-danger-light-default)错误树选择器选择框背景颜色 - 选中
$color-treeSelect_danger-border-focusvar(--semi-color-danger)错误树选择器选择框描边颜色 - 选中
$color-treeSelect_danger-bg-activevar(--semi-color-danger-light-active)错误树选择器选择框背景颜色 - 激活
$color-treeSelect_danger-border-activevar(--semi-color-danger-light-active)错误树选择器选择框描边颜色 - 激活
$radius-treeSelectvar(--semi-border-radius-small)树选择器圆角
$width-treeSelect-border1px树选择器描边宽度
$width-treeSelect_search_wrapper-border1px树选择器搜索框描边宽度
$width-treeSelect_hover-border$border-thickness-control-focus树选择器选择框描边宽度 - 悬浮
$width-treeSelect_focus-border$width-treeSelect_hover-border树选择器选择框描边宽度 - 选中
$width-treeSelect_default80px树选择器最小宽度
$height-treeSelect_default$height-control-default树选择器选择器高度 - 默认
$font-treeSelect_default-lineHeight$height-treeSelect_default树选择器选择器文本行高
$font-treeSelect_default-fontWeight$font-weight-regular树选择器选择器文本字重
$height-treeSelect_small$height-control-small小尺寸树选择器选择器高度
$font-treeSelect_small-lineHeight$height-treeSelect_small小尺寸树选择器选择器文本行高
$height-treeSelect_large$height-control-large大尺寸树选择器选择器文本行高
$font-treeSelect_large-lineHeight$height-treeSelect_large大尺寸树选择器选择器文本行高
$font-treeSelect_prefix_suffix_fontWeight$font-weight-bold树选择器前缀后缀文本字重
$spacing-treeSelect_selection_tagInput_wrapper-paddingX$spacing-extra-tight树选择器多选标签容器水平内边距
$spacing-treeSelect_selection_tagInput_empty-marginLeft$spacing-extra-tight树选择器多选标签容器为空时左侧外边距
$spacing-treeSelect_selection_tagInput_notEmpty-marginLeft-$spacing-extra-tight树选择器多选标签容器不为空时左侧外边距
$spacing-treeSelect_selection-paddingLeft$spacing-base-tight树选择器单选左侧内边距
$spacing-treeSelect_tag-marginX$spacing-super-tight树选择器多选标签水平外边距
$spacing-treeSelect_tag-marginY1px树选择器多选标签垂直外边距
$spacing-treeSelect_selection_multiple-paddingLeft4px树选择器多选标签左侧内边距
$spacing-treeSelect_placeholder_multiple-paddingLeft8px树选择器多选占位文本左侧外边距
$spacing-treeSelect_prefix_text-marginY0px树选择器前缀文本垂直外边距
$spacing-treeSelect_prefix_text-marginX$spacing-base-tight树选择器前缀文本水平外边距
$spacing-treeSelect_prefix_icon-marginY0px树选择器前缀图标垂直外边距
$spacing-treeSelect_prefix_icon-marginX$spacing-tight树选择器前缀图标水平外边距
$spacing-treeSelect_selection_withSuffix-paddingRight0px树选择器有后缀时内容右侧内边距
$color-treeSelect_selection_TriggerSearchItem_disabled-text-defaultvar(--semi-color-disabled-text)带搜索的树选择器触发器占位文本默认颜色
$color-treeSelect_inputTrigger-border-defaultnone带搜索的树选择器触发器描边颜色
$color-treeSelect_inputTrigger-bg-defaulttransparent带搜索的树选择器触发器背景颜色
$color-treeSelect_prefix_suffix_text-defaultvar(--semi-color-text-2)带搜索的树选择器前后缀文本颜色
$width-treeSelect_arrow32px树选择器展开箭头宽度
$width-treeSelect_option230px树选择器菜单项宽度
$height-treeSelect_popover300px树选择器菜单高度
$height-treeSelect_selection_tagInput_wrapper_small22px树选择器多选搜索,搜索框在选择框时选择框最小高度 - 小尺寸
$height-treeSelect_selection_tagInput_wrapper_default30px树选择器多选搜索,搜索框在选择框时选择框最小高度 - 默认尺寸
$height-treeSelect_selection_tagInput_wrapper_large38px树选择器多选搜索,搜索框在选择框时选择框最小高度 - 大尺寸