跳至内容

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

    目录结构

  • components
  • app.vue
  • nuxt.config.ts
vue
<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>

特性

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

安装

从命令行安装组件。

sh
$ npm add radix-vue

解剖

导入所有部分并将它们拼凑在一起。

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 覆盖

asChild
布尔值

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

阅读我们的 组合 指南以了解更多详细信息。

defaultExpanded
string[]

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

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

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

dir
'ltr' | 'rtl'

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

disabled
布尔值

true 时,阻止用户与树交互

expanded
string[]

展开项目的受控值。 可以使用 v-model 进行绑定。

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>[]

树的受控值。 可以使用 v-model 进行绑定。

multiple
布尔值

是否可以选择多个选项。

propagateSelect
布尔值

true 时,选择父级将选择子级。

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 覆盖

asChild
布尔值

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

阅读我们的 组合 指南以了解更多详细信息。

level*
号码

深度级别

value*
Record<string, any>

赋予该项目的价值

发射有效载荷
select
[event: SelectEvent<Record<string, any>>]

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

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

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

插槽 (默认)有效载荷
isExpanded
布尔值
isSelected
布尔值
isIndeterminate
布尔值 | 未定义
handleToggle
handleSelect
数据属性价值
[data-indent]号码
[data-expanded]展开时出现
[data-selected]选中时出现

虚拟化

虚拟容器以实现列表虚拟化。

道具默认类型
estimateSize
号码

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

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

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

插槽 (默认)有效载荷
item
FlattenedItem<Record<string, any>>

例子

选择多个项目

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

vue
<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>

虚拟列表

渲染大量项目列表可能会减慢应用程序的速度,因此使用虚拟化将显着提高性能。

vue
<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 事件的复选框。

vue
<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 中,

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

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 { 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 的核心软件包。

Stackblitz 演示

无障碍性

符合 树 WAI-ARIA 设计模式

键盘交互

描述
Enter
当在 TreeItem 上突出显示时,选择聚焦的项目。
ArrowDown
当焦点在 TreeItem 上时,将焦点移动到下一个项目。
ArrowUp
当焦点在 TreeItem 上时,将焦点移动到上一个项目。
ArrowRight
当焦点在一个封闭的 TreeItem(节点)上时,它会打开节点而不会移动焦点。 当在打开的节点上时,它会将焦点移动到第一个子节点。 当在末端节点上时,它什么也不做。
ArrowLeft
当焦点在一个打开的 TreeItem(节点)上时,会关闭节点。 当焦点在一个子节点上,该子节点也是一个末端节点或一个封闭节点时,它会将焦点移动到其父节点。 当焦点在一个根节点上,该根节点也是一个末端节点或一个封闭节点时,它什么也不做。
首页PageUp
将焦点移动到第一个 TreeItem
EndPageDown
将焦点移动到最后一个 TreeItem