Reka UI logoReka
backdrop
组件

Alpha
树视图组件显示项目的分层列表,可以展开或折叠以显示或隐藏其子项目,例如在文件系统导航器中。

    目录结构

  • components
  • app.vue
  • nuxt.config.ts

特性

  • 可以受控或不受控。
  • 焦点完全由系统管理。
  • 完整的键盘导航。
  • 支持从右到左的方向。
  • 支持多选。
  • 不同的选择行为。

安装

从命令行安装此组件。

sh
$ npm add reka-ui

结构

导入所有部分并将其组合。

vue
<script setup>
import { TreeItem, TreeRoot, TreeVirtualizer } from 'reka-ui'
</script>

<template>
  <TreeRoot>
    <TreeItem />

    <!-- or with virtual -->
    <TreeVirtualizer>
      <TreeItem />
    </TreeVirtualizer>
  </TreeRoot>
</template>

API 参考

包含树的所有部分。

属性默认类型
as
'ul'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

bubbleSelect
布尔值

true 时,选择子项将更新父项状态。

defaultExpanded
string[]

初始渲染时展开树的值。当您不需要控制展开树的状态时使用。

defaultValue
Record<string, any> | Record<string, any>[]

初始渲染时树的值。当您不需要控制树的状态时使用。

dir
'ltr' | 'rtl'

列表框的阅读方向(适用时)。
如果省略,则全局继承自 ConfigProvider 或假定为 LTR(从左到右)阅读模式。

disabled
布尔值

true 时,阻止用户与树进行交互。

expanded
string[]

展开项的受控值。可以与 v-model 绑定。

getChildren
val.children
((val: Record<string, any>) => Record<string, any>[])

此函数传入每个项目的索引,并应返回该项目的子项列表。

getKey*
(val: Record<string, any>): string

此函数传入每个项目的索引,并应返回该项目的唯一键。

items
Record<string, any>[]

项目列表

modelValue
Record<string, any> | Record<string, any>[]

树的受控值。可以与 v-model 绑定。

multiple
布尔值

是否可以选择多个选项。

propagateSelect
布尔值

true 时,选择父项将选择其后代。

selectionBehavior
'toggle'
'toggle' | 'replace'

多选在此集合中的行为方式。

事件触发载荷
update:expanded
[val: string[]]
update:modelValue
[val: Record<string, any> | Record<string, any>[]]

当切换组件的值改变时调用的事件处理程序。

插槽(默认)载荷
flattenItems
FlattenedItem<Record<string, any>>[]
modelValue
Record<string, any> | Record<string, any>[]
expanded
string[]

项目组件。

属性默认类型
as
'li'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

level*
数字

深度级别

值*
Record<string, any>

赋予此项的值

事件触发载荷
select
[event: SelectEvent<Record<string, any>>]

选择项时调用的事件处理程序。
可以通过调用 event.preventDefault 来阻止它。

toggle
[event: ToggleEvent<Record<string, any>>]

选择项时调用的事件处理程序。
可以通过调用 event.preventDefault 来阻止它。

插槽(默认)载荷
isExpanded
布尔值
isSelected
布尔值
isIndeterminate
boolean | undefined
handleToggle
(): void
handleSelect
(): void
数据属性
[data-indent]Number
[data-expanded]展开时存在
[data-selected]选中时存在

虚拟化器

用于实现列表虚拟化的虚拟容器。

属性默认类型
estimateSize
数字

每个项的估计大小(以像素为单位)

overscan
数字

在可见区域之外渲染的项目数量

textContent
((item: Record<string, any>) => string)

每个项的文本内容,以实现预输入功能

插槽(默认)载荷
item
FlattenedItem<Record<string, any>>
virtualizer
Virtualizer<Element | Window, Element>
virtualItem
VirtualItem

示例

选择多个项目

Tree 组件允许您选择多个项目。您可以通过提供一个值数组而不是单个值,并将 multiple="true" 来启用此功能。

vue
<script setup lang="ts">
import { TreeRoot } from 'reka-ui'
import { ref } from '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>

虚拟列表

渲染长列表项目可能会拖慢应用程序,因此使用虚拟化将显著提高性能。

有关虚拟化的更多通用信息,请参阅虚拟化指南

vue
<script setup lang="ts">
import { TreeItem, TreeRoot, TreeVirtualizer } from 'reka-ui'
import { ref } from 'vue'
</script>

<template>
  <TreeRoot :items>
    <TreeVirtualizer
      v-slot="{ item }"
      :text-content="(opt) => opt.name"
    >
      <TreeItem v-bind="item.bind">
        {{ person.name }}
      </TreeItem>
    </TreeVirtualizer>
  </TreeRoot>
</template>

带复选框

某些 Tree 组件可能希望显示 选中/不确定 状态的复选框。我们可以通过使用一些属性和 preventDefault 事件来改变 Tree 组件的行为。

我们将 propagateSelect 设置为 true,因为我们希望父复选框能够选择/取消选择其后代。然后,我们添加一个触发 select 事件的复选框。

vue
<script setup lang="ts">
import { TreeItem, TreeRoot } from 'reka-ui'
import { ref } from '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 中,

vue
<script setup lang="ts">
import { TreeItem } from 'reka-ui'

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

vue
<template>
  <TreeRoot
    :items="items"
    :get-key="(item) => item.title"
  >
    <Tree :tree-items="items" />
  </TreeRoot>
</template>

自定义子项模式

默认情况下,<TreeRoot /> 要求您通过为每个节点传递 children 列表来提供节点的子项列表。您可以通过提供 getChildren 属性来覆盖此行为。

提示

如果节点没有子项,getChildren 应该返回 undefined 而不是空数组。

vue
<script setup lang="ts">
import { TreeRoot } from 'reka-ui'
import { ref } from '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 作为处理拖放的核心包。

Stackblitz 演示

可访问性

遵循 树 WAI-ARIA 设计模式

键盘交互

描述
回车键
当焦点在 TreeItem 上时,选择该焦点项目。
向下箭头键
当焦点在 TreeItem 上时,将焦点移到下一个项目。
向上箭头键
当焦点在 TreeItem 上时,将焦点移到上一个项目。
向右箭头
当焦点在关闭的 TreeItem(节点)上时,它会打开节点而不移动焦点。当在打开的节点上时,它将焦点移到第一个子节点。当在末尾节点上时,它不做任何操作。
向左箭头
当焦点在打开的 TreeItem(节点)上时,关闭节点。当焦点在既是末尾节点又是关闭节点的子节点上时,将焦点移到其父节点。当焦点在既是末尾节点又是关闭节点的根节点上时,不做任何操作。
HomePageUp
将焦点移到第一个 TreeItem
EndPageDown
将焦点移到最后一个 TreeItem