Skip to content
API 风格:

Дерево ​

Преобразование плоских данных в дерево и распространение отметок выполняются чистыми функциями в core; компонент отвечает только за рендеринг и работу с клавиатурой.

Базовое использование ​

Базовое использование
Фронтенд
Vue
React
Девопс
vue
<script setup>
import { ref } from 'vue'
const treeData = [
  { key: 'a', label: 'Фронтенд', children: [
    { key: 'a-1', label: 'Vue' },
    { key: 'a-2', label: 'React' },
  ] },
  { key: 'b', label: 'Бэкенд', children: [{ key: 'b-1', label: 'Node' }] },
  { key: 'c', label: 'Девопс', disabled: true },
]
</script>

<template>
  <!-- Базовое использование -->
  <LhTree :data="treeData" :default-expanded-keys="['a']" />
</template>
vue
<script>
export default {
  data() {
    return {
      treeData: [
        { key: 'a', label: 'Фронтенд', children: [
          { key: 'a-1', label: 'Vue' },
          { key: 'a-2', label: 'React' },
        ] },
        { key: 'b', label: 'Бэкенд', children: [{ key: 'b-1', label: 'Node' }] },
        { key: 'c', label: 'Девопс', disabled: true },
      ],
    }
  },
}
</script>

<template>
  <!-- Базовое использование -->
  <LhTree :data="treeData" :default-expanded-keys="['a']" />
</template>

Отметки (каскадные) ​

Отметка родителя каскадно распространяется на всех потомков; при выборе всех дочерних узлов родитель отмечается автоматически, а частичный выбор показывается неопределённым состоянием (indeterminate).

Каскадные отметки
Фронтенд
Vue
React
Бэкенд
Node
Девопс
vue
<script setup lang="ts">
const treeData = [
  { key: 'a', label: 'Фронтенд', children: [
    { key: 'a-1', label: 'Vue' },
    { key: 'a-2', label: 'React' },
  ] },
  { key: 'b', label: 'Бэкенд', children: [{ key: 'b-1', label: 'Node' }] },
  { key: 'c', label: 'Девопс', disabled: true },
]
</script>

<template>
  <!-- Каскадные отметки -->
  <LhTree :data="treeData" show-checkbox :default-expanded-keys="['a', 'b']" />
</template>
vue
<script>
export default {
  data() {
    return {
      treeData: [
        { key: 'a', label: 'Фронтенд', children: [
          { key: 'a-1', label: 'Vue' },
          { key: 'a-2', label: 'React' },
        ] },
        { key: 'b', label: 'Бэкенд', children: [{ key: 'b-1', label: 'Node' }] },
        { key: 'c', label: 'Девопс', disabled: true },
      ],
    }
  },
}
</script>

<template>
  <!-- Каскадные отметки -->
  <LhTree :data="treeData" show-checkbox :default-expanded-keys="['a', 'b']" />
</template>

Ленивая загрузка ​

При включённом lazy разворачивание незагруженного узла вызывает колбэк load (resolve принимает дочерние узлы и разворачивает их, reject отменяет); стрелка загрузки показывает вращающееся состояние. У узлов, которые требуется загружать, необходимо явно указать isLeaf: false (иначе узел без children будет считаться листом и стрелка развёртки не покажется); если загруженные дочерние узлы сами являются ветвями, ожидающими загрузки, для них также указывайте isLeaf: false, а настоящим листьям — isLeaf: true.

Ленивая загрузка
vue
<script setup>
import { ref } from 'vue'

const lazyData = ref([
  { key: 'lazy', label: 'Нажмите, чтобы загрузить', isLeaf: false },
])

function loadNodes(node, resolve, reject) {
  // Имитация асинхронного запроса
  setTimeout(() => {
    if (node.key === 'lazy') {
      resolve([
        { key: 'lazy-1', label: 'Дочерний узел один', isLeaf: true },
        { key: 'lazy-2', label: 'Дочерний узел два', isLeaf: true },
      ])
    } else {
      reject(new Error('nothing to load'))
    }
  }, 600)
}
</script>

<template>
  <!-- Ленивая загрузка -->
  <LhTree :data="lazyData" lazy :load="loadNodes" />
</template>
vue
<script>
export default {
  data() {
    return {
      lazyData: [{ key: 'lazy', label: 'Нажмите, чтобы загрузить', isLeaf: false }],
    }
  },
  methods: {
    // Имитация асинхронного запроса
    loadNodes(node, resolve, reject) {
      setTimeout(() => {
        if (node.key === 'lazy') {
          resolve([
            { key: 'lazy-1', label: 'Дочерний узел один', isLeaf: true },
            { key: 'lazy-2', label: 'Дочерний узел два', isLeaf: true },
          ])
        } else {
          reject(new Error('nothing to load'))
        }
      }, 600)
    },
  },
}
</script>

<template>
  <!-- Ленивая загрузка -->
  <LhTree :data="lazyData" lazy :load="loadNodes" />
</template>

Без связи родитель-потомок (checkStrictly) ​

При включённом checkStrictly отметка действует только на сам узел: без каскада потомкам, без обратного хода к предкам, без неопределённого состояния.

checkStrictly
Фронтенд
Vue
React
Бэкенд
Node
Девопс
vue
<script setup lang="ts">
const treeData = [
  { key: 'a', label: 'Фронтенд', children: [
    { key: 'a-1', label: 'Vue' },
    { key: 'a-2', label: 'React' },
  ] },
  { key: 'b', label: 'Бэкенд', children: [{ key: 'b-1', label: 'Node' }] },
  { key: 'c', label: 'Девопс', disabled: true },
]
</script>

<template>
  <!-- Без связи родитель-потомок -->
  <LhTree :data="treeData" show-checkbox check-strictly :default-expanded-keys="['a', 'b']" />
</template>
vue
<script>
export default {
  data() {
    return {
      treeData: [
        { key: 'a', label: 'Фронтенд', children: [
          { key: 'a-1', label: 'Vue' },
          { key: 'a-2', label: 'React' },
        ] },
        { key: 'b', label: 'Бэкенд', children: [{ key: 'b-1', label: 'Node' }] },
        { key: 'c', label: 'Девопс', disabled: true },
      ],
    }
  },
}
</script>

<template>
  <!-- Без связи родитель-потомок -->
  <LhTree :data="treeData" show-checkbox check-strictly :default-expanded-keys="['a', 'b']" />
</template>

Выбор и управляемые ключи ​

multiple разрешает множественный выбор; expand-on-click-node разворачивает/свёртывает узел по щелчку; default-selected-keys / v-model:selected-keys — управляемый выбор; default-checked-keys / v-model:checked-keys — управляемые отметки; default-expanded-keys / v-model:expanded-keys — управляемое развёртывание; disabled отключает всё дерево.

Управляемые выбор и отметки
Дизайн
Цвет
Шрифты
Инженерия

Выбрано: [ "x" ] | Отмечено: [ "x-1" ]

Отключение всего дерева
Инженерия
vue
<script setup>
import { ref } from 'vue'
const selKeys = ref(['x'])
const chkKeys = ref(['x-1'])
const ctrlData = [
  { key: 'x', label: 'Дизайн', children: [
    { key: 'x-1', label: 'Цвет' },
    { key: 'x-2', label: 'Шрифты' },
  ] },
  { key: 'y', label: 'Инженерия' },
]
</script>

<template>
  <!-- Управляемые выбор и отметки -->
  <LhTree
    v-model:selected-keys="selKeys"
    v-model:checked-keys="chkKeys"
    :data="ctrlData"
    multiple
    expand-on-click-node
    :default-expanded-keys="['x']"
    :default-selected-keys="['x']"
    :default-checked-keys="['x-1']"
  />
  <p class="demo-tree-log">Выбрано: {{ selKeys }} | Отмечено: {{ chkKeys }}</p>
  <!-- Отключение всего дерева -->
  <LhTree :data="ctrlData" disabled />
</template>
vue
<script>
export default {
  data() {
    return {
      selKeys: ['x'],
      chkKeys: ['x-1'],
      ctrlData: [
        { key: 'x', label: 'Дизайн', children: [
          { key: 'x-1', label: 'Цвет' },
          { key: 'x-2', label: 'Шрифты' },
        ] },
        { key: 'y', label: 'Инженерия' },
      ],
    }
  },
}
</script>

<template>
  <!-- Управляемые выбор и отметки -->
  <LhTree
    v-model:selected-keys="selKeys"
    v-model:checked-keys="chkKeys"
    :data="ctrlData"
    multiple
    expand-on-click-node
    :default-expanded-keys="['x']"
    :default-selected-keys="['x']"
    :default-checked-keys="['x-1']"
  />
  <p class="demo-tree-log">Выбрано: {{ selKeys }} | Отмечено: {{ chkKeys }}</p>
  <!-- Отключение всего дерева -->
  <LhTree :data="ctrlData" disabled />
</template>

API ​

LhTree Props ​

СвойствоОписаниеТипЗначение по умолчанию
dataданные дерева[LhTreeNode](#type-LhTreeNode)[]—
showCheckboxпоказывать чекбоксыbooleanfalse
multipleмножественный выбор (щелчок по узлу переключает выбор, а не заменяет его)booleanfalse
expandOnClickNodeразворачивать/свёртывать нелистовой узел по щелчкуbooleantrue
defaultExpandedKeysразвёрнутые по умолчанию ключи (неуправляемые)string[]() => []
expandedKeysуправляемые развёрнутые ключиstring[]—
defaultSelectedKeysвыбранные по умолчанию ключи (неуправляемые)string[]() => []
selectedKeysуправляемые выбранные ключиstring[]—
defaultCheckedKeysотмеченные по умолчанию ключи (неуправляемые)string[]() => []
checkedKeysуправляемые отмеченные ключиstring[]—
checkStrictlyотметки без связи родитель-потомок (действует только на сам узел, без каскада и неопределённого состояния)booleanfalse
lazyрежим ленивой загрузки (вызывает load при разворачивании незагруженного узла)booleanfalse
loadколбэк ленивой загрузки (resolve принимает дочерние узлы и разворачивает, reject отменяет)(node: LhTreeNode, resolve: (children: LhTreeNode[]) => void, reject: (reason?: unknown) => void) => void—
disabledполное отключениеbooleanfalse
sxсвойства быстрой раскладки sxLhSxProps—

Events ​

СобытиеОписаниеКолбэк
update:expandedKeysвызывается при изменении списка развёрнутых ключей, использовать совместно с v-model:expanded-keys(keys: string[])
update:selectedKeysвызывается при изменении списка выбранных ключей, использовать совместно с v-model:selected-keys(keys: string[])
update:checkedKeysвызывается при изменении списка отмеченных ключей, использовать совместно с v-model:checked-keys(keys: string[])
selectвызывается при выборе узла щелчком / переключении выбора(key: string, node: LhTreeNode)
checkвызывается после изменения отметок(keys: string[], node: LhTreeNode)
expandвызывается после разворачивания / сворачивания узла(key: string, expanded: boolean)
nodeLoadвызывается после возврата дочерних узлов при ленивой загрузке(node: LhTreeNode, children: LhTreeNode[])

Слоты: node (scoped-слот { node, level, expanded, selected }), empty.

LhTreeNode ​

ПолеОписаниеТип
keyуникальный ключstring
labelотображаемый текстstring
childrenдочерние узлыLhTreeNode[]
disabledотключеноboolean
isLeafявляется ли листом (если не указано, выводится по children)boolean

Доступность ​

  • role="tree" + role="treeitem", все aria-level / aria-expanded / aria-selected / aria-disabled экспонируются полностью
  • Чекбоксы: role="checkbox" + aria-checked (неопределённое состояние — mixed)
  • Клавиатура (паттерн APG tree): ↑/↓ — перемещение между узлами (с пропуском отключённых, зацикленное), → — разворачивает или входит в первого потомка, ← — сворачивает или возвращается к родителю, Home/End — к первому/последнему, Enter/Space — активация; roving tabindex

Определения типов ​

Ниже приведены структуры типов TypeScript, экспортируемые этим компонентом (автоматически извлечены из packages/components/tree/src/types.ts и исходников @lh-ui/share/core/*; раскройте каждый пункт, чтобы увидеть полное определение; описания свойств — в таблице Props выше):

LhTreeProps
ts
export interface LhTreeProps {
  /** данные дерева */
  data: LhTreeNode[]
  /** показывать чекбоксы */
  showCheckbox?: boolean
  /** множественный выбор (щелчок по узлу переключает выбор, а не заменяет его) */
  multiple?: boolean
  /** разворачивать/сворачивать нелистовой узел по щелчку */
  expandOnClickNode?: boolean
  /** развёрнутые по умолчанию ключи (неуправляемые) */
  defaultExpandedKeys?: string[]
  /** управляемые развёрнутые ключи */
  expandedKeys?: string[]
  /** выбранные по умолчанию ключи (неуправляемые) */
  defaultSelectedKeys?: string[]
  /** управляемые выбранные ключи */
  selectedKeys?: string[]
  /** отмеченные по умолчанию ключи (неуправляемые) */
  defaultCheckedKeys?: string[]
  /** управляемые отмеченные ключи */
  checkedKeys?: string[]
  /** отметки без связи родитель-потомок (действует только на сам узел, без каскада и неопределённого состояния) */
  checkStrictly?: boolean
  /** режим ленивой загрузки: вызывает load при разворачивании незагруженного нелистового узла */
  lazy?: boolean
  /** колбэк ленивой загрузки: resolve принимает дочерние узлы и разворачивает, reject (или исключение) отменяет */
  load?: (
    node: LhTreeNode,
    resolve: (children: LhTreeNode[]) => void,
    reject: (reason?: unknown) => void,
  ) => void
  /** полное отключение */
  disabled?: boolean
  /** sx: свойства быстрой раскладки */
  sx?: LhSxProps
}
LhFlatTreeNode (из @lh-ui/share/core/tree)
ts
/** уплощённый узел (для рендеринга) */
export interface LhFlatTreeNode {
  node: LhTreeNode
  key: string
  level: number
  parentKey: string | null
  /** цепочка предков (без самого узла) */
  ancestorKeys: string[]
  /** является ли листом */
  isLeaf: boolean
}
LhTreeNode (из @lh-ui/share/core/tree)
ts
/** данные узла дерева */
export interface LhTreeNode {
  /** уникальный ключ */
  key: string
  /** отображаемый текст */
  label: string
  /** дочерние узлы */
  children?: LhTreeNode[]
  /** отключено */
  disabled?: boolean
  /** является ли листом (если не указано, выводится по children) */
  isLeaf?: boolean
}
LhTreeCheckState (из @lh-ui/share/core/tree)
ts
/** состояние отметки */
export type LhTreeCheckState = 'checked' | 'indeterminate' | 'unchecked'

Красота традиций — основа кода