树
Alpha- composables
- components
- Home
- app.vue
- nuxt.config.ts
目录结构
<script setup lang="ts">
import { TreeItem, TreeRoot } from 'radix-vue'
import { Icon } from '@iconify/vue'
const items = [
{
title: 'composables',
icon: 'lucide:folder',
children: [
{ title: 'useAuth.ts', icon: 'vscode-icons:file-type-typescript' },
{ title: 'useUser.ts', icon: 'vscode-icons:file-type-typescript' },
],
},
{
title: 'components',
icon: 'lucide:folder',
children: [
{
title: 'Home',
icon: 'lucide:folder',
children: [
{ title: 'Card.vue', icon: 'vscode-icons:file-type-vue' },
{ title: 'Button.vue', icon: 'vscode-icons:file-type-vue' },
],
},
],
},
{ title: 'app.vue', icon: 'vscode-icons:file-type-vue' },
{ title: 'nuxt.config.ts', icon: 'vscode-icons:file-type-nuxt' },
]
</script>
<template>
<TreeRoot
v-slot="{ flattenItems }"
class="list-none select-none w-56 bg-white text-blackA11 rounded-lg p-2 text-sm font-medium"
:items="items"
:get-key="(item) => item.title"
:default-expanded="['components']"
>
<h2 class="font-semibold !text-base text-blackA11 px-2 pt-1">
Directory Structure
</h2>
<TreeItem
v-for="item in flattenItems"
v-slot="{ isExpanded }"
:key="item._id"
:style="{ 'padding-left': `${item.level - 0.5}rem` }"
v-bind="item.bind"
class="flex items-center py-1 px-2 my-0.5 rounded outline-none focus:ring-grass8 focus:ring-2 data-[selected]:bg-grass4"
>
<template v-if="item.hasChildren">
<Icon
v-if="!isExpanded"
icon="lucide:folder"
class="h-4 w-4"
/>
<Icon
v-else
icon="lucide:folder-open"
class="h-4 w-4"
/>
</template>
<Icon
v-else
:icon="item.value.icon || 'lucide:file'"
class="h-4 w-4"
/>
<div class="pl-2">
{{ item.value.title }}
</div>
</TreeItem>
</TreeRoot>
</template>
特性
- 可以是受控或不受控的。
- 焦点完全管理。
- 完全键盘导航。
- 支持从右到左的方向。
- 支持多选。
- 不同的选择行为。
安装
从命令行安装组件。
$ npm add radix-vue
解剖
导入所有部分并将它们拼凑在一起。
<script setup>
import { TreeItem, TreeRoot, TreeVirtualizer } from 'radix-vue'
</script>
<template>
<TreeRoot>
<TreeItem />
<!-- or with virtual -->
<TreeVirtualizer>
<TreeItem />
</TreeVirtualizer>
</TreeRoot>
</template>
API 参考
根
包含树的所有部分。
道具 | 默认 | 类型 |
---|---|---|
as | 'ul' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 将默认渲染的元素更改为作为子元素传递的元素,合并它们的属性和行为。 阅读我们的 组合 指南以了解更多详细信息。 | |
defaultExpanded | string[] 初始渲染时展开树的值。 当您不需要控制展开树的状态时使用 | |
defaultValue | Record<string, any> | Record<string, any>[] 初始渲染时树的值。 当您不需要控制树的状态时使用 | |
dir | 'ltr' | 'rtl' 列表框的阅读方向(如果适用)。 | |
disabled | 布尔值 当 | |
expanded | string[] 展开项目的受控值。 可以使用 | |
getKey* | (val: Record<string, any>) => string 此函数传递每个项目的索引,并应为该项目返回一个唯一的键 | |
getChildren | (val: Record<string, any>) => Record<string, any>[] | undefined 此函数传递每个项目的索引,并应为该项目返回一个子项目列表 | |
items | Record<string, any>[] 项目列表 | |
modelValue | Record<string, any> | Record<string, any>[] 树的受控值。 可以使用 | |
multiple | 布尔值 是否可以选择多个选项。 | |
propagateSelect | 布尔值 当 | |
selectionBehavior | 'toggle' | 'replace' | 'toggle' 集合中多选应如何表现。 |
发射 | 有效载荷 |
---|---|
update:expanded | [val: string[]] |
update:modelValue | [val: Record<string, any>] 当值更改时调用的事件处理程序。 |
插槽 (默认) | 有效载荷 |
---|---|
flattenItems | FlattenedItem<Record<string, any>>[] |
modelValue | Record<string, any> | Record<string, any>[] |
expanded | string[] |
项目
项目组件。
道具 | 默认 | 类型 |
---|---|---|
as | 'li' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 将默认渲染的元素更改为作为子元素传递的元素,合并它们的属性和行为。 阅读我们的 组合 指南以了解更多详细信息。 | |
level* | 号码 深度级别 | |
value* | Record<string, any> 赋予该项目的价值 |
发射 | 有效载荷 |
---|---|
select | [event: SelectEvent<Record<string, any>>] 选择项目时调用的事件处理程序。 |
toggle | [event: ToggleEvent<Record<string, any>>] 选择项目时调用的事件处理程序。 |
插槽 (默认) | 有效载荷 |
---|---|
isExpanded | 布尔值 |
isSelected | 布尔值 |
isIndeterminate | 布尔值 | 未定义 |
handleToggle |
|
handleSelect |
|
数据属性 | 价值 |
---|---|
[data-indent] | 号码 |
[data-expanded] | 展开时出现 |
[data-selected] | 选中时出现 |
虚拟化
虚拟容器以实现列表虚拟化。
道具 | 默认 | 类型 |
---|---|---|
estimateSize | 号码 每个项目的估计大小(以像素为单位) | |
textContent | ((item: Record<string, any>) => string) 每个项目的文本内容,以实现提前输入功能 |
插槽 (默认) | 有效载荷 |
---|---|
item | FlattenedItem<Record<string, any>> |
例子
选择多个项目
Tree
组件允许您选择多个项目。 您可以通过提供一个值数组而不是单个值来启用此功能。
<script setup lang="ts">
import { ref } from 'vue'
import { TreeRoot } from 'radix-vue'
const people = [
{ id: 1, name: 'Durward Reynolds' },
{ id: 2, name: 'Kenton Towne' },
{ id: 3, name: 'Therese Wunsch' },
{ id: 4, name: 'Benedict Kessler' },
{ id: 5, name: 'Katelyn Rohan' },
]
const selectedPeople = ref([people[0], people[1]])
</script>
<template>
<TreeRoot
v-model="selectedPeople"
multiple
>
...
</TreeRoot>
</template>
虚拟列表
渲染大量项目列表可能会减慢应用程序的速度,因此使用虚拟化将显着提高性能。
<script setup lang="ts">
import { ref } from 'vue'
import { TreeItem, TreeRoot, TreeVirtualizer } from 'radix-vue'
</script>
<template>
<TreeRoot :items>
<!-- checkout https://radix-vue.com/components/tree.html#virtualizer -->
<TreeVirtualizer
v-slot="{ item }"
:text-content="(opt) => opt.name"
>
<TreeItem v-bind="item.bind">
{{ person.name }}
</TreeItem>
</TreeVirtualizer>
</TreeRoot>
</template>
带复选框
某些 Tree
组件可能希望显示 toggled/indeterminate
复选框。 我们可以使用一些属性和 preventDefault
事件来改变 Tree
组件的行为。
我们将 propagateSelect
设置为 true
,因为我们希望父级复选框选择/取消选择其后代。 然后,我们添加一个触发 select
事件的复选框。
<script setup lang="ts">
import { ref } from 'vue'
import { TreeItem, TreeRoot } from 'radix-vue'
</script>
<template>
<TreeRoot
v-slot="{ flattenItems }"
:items
multiple
propagate-select
>
<TreeItem
v-for="item in flattenItems"
:key="item._id"
v-bind="item.bind"
v-slot="{ handleSelect, isSelected, isIndeterminate }"
@select="(event) => {
if (event.detail.originalEvent.type === 'click')
event.preventDefault()
}"
@toggle="(event) => {
if (event.detail.originalEvent.type === 'keydown')
event.preventDefault()
}"
>
<Icon
v-if="item.hasChildren"
icon="radix-icons:chevron-down"
/>
<button
tabindex="-1"
@click.stop
@change="handleSelect"
>
<Icon
v-if="isSelected"
icon="radix-icons:check"
/>
<Icon
v-else-if="isIndeterminate"
icon="radix-icons:dash"
/>
<Icon
v-else
icon="radix-icons:box"
/>
</button>
<div class="pl-2">
{{ item.value.title }}
</div>
</TreeItem>
</TreeRoot>
</template>
嵌套树节点
默认示例显示扁平化树项目和节点,这使得 虚拟化 和自定义功能(如拖放)更容易。 但是,您也可以构建它以拥有嵌套的 DOM 节点。
在 Tree.vue
中,
<script setup lang="ts">
import { TreeItem } from 'radix-vue'
interface TreeNode {
title: string
icon: string
children?: TreeNode[]
}
withDefaults(defineProps<{
treeItems: TreeNode[]
level?: number
}>(), { level: 0 })
</script>
<template>
<li
v-for=" tree in treeItems"
:key="tree.title"
>
<TreeItem
v-slot="{ isExpanded }"
as-child
:level="level"
:value="tree"
>
<button>…</button>
<ul v-if="isExpanded && tree.children">
<Tree
:tree-items="tree.children"
:level="level + 1"
/>
</ul>
</TreeItem>
</li>
</template>
在 CustomTree.vue
中
<template>
<TreeRoot
:items="items"
:get-key="(item) => item.title"
>
<Tree :tree-items="items" />
</TreeRoot>
</template>
自定义子项模式
默认情况下,<TreeRoot />
期待您通过为每个节点传递 children
列表来提供节点子项列表。 您可以通过提供 getChildren
道具来覆盖它。
::: 注意 如果节点没有子节点,getChildren
应返回 undefined
而不是空数组。 ::
<script setup lang="ts">
import { ref } from 'vue'
import { TreeRoot } from 'radix-vue'
interface FileNode {
title: string
icon: string
}
interface DirectoryNode {
title: string
icon: string
directories?: DirectoryNode[]
files?: FileNode[]
}
</script>
<template>
<TreeRoot
:items="items"
:get-key="(item) => item.title"
:get-children="(item) => (!item.files) ? item.directories : (!item.directories) ? item.files : [...item.directories, ...item.files]"
>
...
</TreeRoot>
</template>
可拖动/可排序树
对于更复杂的可拖动 Tree
组件,在本例中,我们将使用 pragmatic-drag-and-drop 作为处理 DND 的核心软件包。
无障碍性
符合 树 WAI-ARIA 设计模式。
键盘交互
键 | 描述 |
---|---|
Enter | 当在 TreeItem 上突出显示时,选择聚焦的项目。 |
ArrowDown | 当焦点在 TreeItem 上时,将焦点移动到下一个项目。 |
ArrowUp | 当焦点在 TreeItem 上时,将焦点移动到上一个项目。 |
ArrowRight | 当焦点在一个封闭的 TreeItem (节点)上时,它会打开节点而不会移动焦点。 当在打开的节点上时,它会将焦点移动到第一个子节点。 当在末端节点上时,它什么也不做。 |
ArrowLeft | 当焦点在一个打开的 TreeItem (节点)上时,会关闭节点。 当焦点在一个子节点上,该子节点也是一个末端节点或一个封闭节点时,它会将焦点移动到其父节点。 当焦点在一个根节点上,该根节点也是一个末端节点或一个封闭节点时,它什么也不做。 |
首页PageUp | 将焦点移动到第一个 TreeItem |
EndPageDown | 将焦点移动到最后一个 TreeItem |