Skip to content
API 风格:

Árbol ​

La conversión de datos planos a árbol y la propagación del marcado se resuelven en funciones puras de la capa core; el componente solo se ocupa del renderizado y del teclado.

Uso básico ​

Uso básico
Frontend
Vue
React
Operaciones
vue
<script setup>
import { ref } from 'vue'
const treeData = [
  { key: 'a', label: 'Frontend', children: [
    { key: 'a-1', label: 'Vue' },
    { key: 'a-2', label: 'React' },
  ] },
  { key: 'b', label: 'Backend', children: [{ key: 'b-1', label: 'Node' }] },
  { key: 'c', label: 'Operaciones', disabled: true },
]
</script>

<template>
  <!-- Uso básico -->
  <LhTree :data="treeData" :default-expanded-keys="['a']" />
</template>
vue
<script>
export default {
  data() {
    return {
      treeData: [
        { key: 'a', label: 'Frontend', children: [
          { key: 'a-1', label: 'Vue' },
          { key: 'a-2', label: 'React' },
        ] },
        { key: 'b', label: 'Backend', children: [{ key: 'b-1', label: 'Node' }] },
        { key: 'c', label: 'Operaciones', disabled: true },
      ],
    }
  },
}
</script>

<template>
  <!-- Uso básico -->
  <LhTree :data="treeData" :default-expanded-keys="['a']" />
</template>

Marcado en cascada ​

Al marcar un nodo padre se marca en cascada a todos sus descendientes; cuando todos los hijos están marcados, el padre se marca automáticamente, y con una selección parcial se muestra el estado intermedio (indeterminado).

Marcado en cascada
Frontend
Vue
React
Backend
Node
Operaciones
vue
<script setup lang="ts">
const treeData = [
  { key: 'a', label: 'Frontend', children: [
    { key: 'a-1', label: 'Vue' },
    { key: 'a-2', label: 'React' },
  ] },
  { key: 'b', label: 'Backend', children: [{ key: 'b-1', label: 'Node' }] },
  { key: 'c', label: 'Operaciones', disabled: true },
]
</script>

<template>
  <!-- Marcado en cascada -->
  <LhTree :data="treeData" show-checkbox :default-expanded-keys="['a', 'b']" />
</template>
vue
<script>
export default {
  data() {
    return {
      treeData: [
        { key: 'a', label: 'Frontend', children: [
          { key: 'a-1', label: 'Vue' },
          { key: 'a-2', label: 'React' },
        ] },
        { key: 'b', label: 'Backend', children: [{ key: 'b-1', label: 'Node' }] },
        { key: 'c', label: 'Operaciones', disabled: true },
      ],
    }
  },
}
</script>

<template>
  <!-- Marcado en cascada -->
  <LhTree :data="treeData" show-checkbox :default-expanded-keys="['a', 'b']" />
</template>

Carga diferida ​

Al activar lazy, expandir un nodo aún no cargado invoca la devolución de llamada load (resolve entrega los nodos secundarios y los expande; reject cancela); durante la carga, la flecha muestra un estado de rotación. Los nodos cargables deben declararse explícitamente con isLeaf: false (de lo contrario, los nodos sin children se deducen como hojas y no muestran flecha de expansión); los nodos secundarios descargados que sigan siendo ramas pendientes de carga también deben declarar isLeaf: false, mientras que las hojas reales declaran isLeaf: true.

Carga diferida
vue
<script setup>
import { ref } from 'vue'

const lazyData = ref([
  { key: 'lazy', label: 'Haz clic para cargar', isLeaf: false },
])

function loadNodes(node, resolve, reject) {
  // Simula una petición asíncrona
  setTimeout(() => {
    if (node.key === 'lazy') {
      resolve([
        { key: 'lazy-1', label: 'Nodo secundario 1', isLeaf: true },
        { key: 'lazy-2', label: 'Nodo secundario 2', isLeaf: true },
      ])
    } else {
      reject(new Error('nothing to load'))
    }
  }, 600)
}
</script>

<template>
  <!-- Carga diferida -->
  <LhTree :data="lazyData" lazy :load="loadNodes" />
</template>
vue
<script>
export default {
  data() {
    return {
      lazyData: [{ key: 'lazy', label: 'Haz clic para cargar', isLeaf: false }],
    }
  },
  methods: {
    // Simula una petición asíncrona
    loadNodes(node, resolve, reject) {
      setTimeout(() => {
        if (node.key === 'lazy') {
          resolve([
            { key: 'lazy-1', label: 'Nodo secundario 1', isLeaf: true },
            { key: 'lazy-2', label: 'Nodo secundario 2', isLeaf: true },
          ])
        } else {
          reject(new Error('nothing to load'))
        }
      }, 600)
    },
  },
}
</script>

<template>
  <!-- Carga diferida -->
  <LhTree :data="lazyData" lazy :load="loadNodes" />
</template>

Independencia padre-hijo (checkStrictly) ​

Al activar checkStrictly, el marcado solo afecta al propio nodo: no hay cascada a descendientes, no hay retroceso a ancestros y no existe estado intermedio.

checkStrictly
Frontend
Vue
React
Backend
Node
Operaciones
vue
<script setup lang="ts">
const treeData = [
  { key: 'a', label: 'Frontend', children: [
    { key: 'a-1', label: 'Vue' },
    { key: 'a-2', label: 'React' },
  ] },
  { key: 'b', label: 'Backend', children: [{ key: 'b-1', label: 'Node' }] },
  { key: 'c', label: 'Operaciones', disabled: true },
]
</script>

<template>
  <!-- Independencia padre-hijo -->
  <LhTree :data="treeData" show-checkbox check-strictly :default-expanded-keys="['a', 'b']" />
</template>
vue
<script>
export default {
  data() {
    return {
      treeData: [
        { key: 'a', label: 'Frontend', children: [
          { key: 'a-1', label: 'Vue' },
          { key: 'a-2', label: 'React' },
        ] },
        { key: 'b', label: 'Backend', children: [{ key: 'b-1', label: 'Node' }] },
        { key: 'c', label: 'Operaciones', disabled: true },
      ],
    }
  },
}
</script>

<template>
  <!-- Independencia padre-hijo -->
  <LhTree :data="treeData" show-checkbox check-strictly :default-expanded-keys="['a', 'b']" />
</template>

Selección y claves controladas ​

multiple permite la selección múltiple; expand-on-click-node expande/contrae al hacer clic en un nodo; default-selected-keys / v-model:selected-keys controlan la selección; default-checked-keys / v-model:checked-keys controlan el marcado; default-expanded-keys / v-model:expanded-keys controlan la expansión; disabled deshabilita todo el árbol.

Selección y marcado controlados
Diseño
Color
Tipografía
Ingeniería

Seleccionados: [ "x" ] | Marcados: [ "x-1" ]

Deshabilitar todo el árbol
Ingeniería
vue
<script setup>
import { ref } from 'vue'
const selKeys = ref(['x'])
const chkKeys = ref(['x-1'])
const ctrlData = [
  { key: 'x', label: 'Diseño', children: [
    { key: 'x-1', label: 'Color' },
    { key: 'x-2', label: 'Tipografía' },
  ] },
  { key: 'y', label: 'Ingeniería' },
]
</script>

<template>
  <!-- Selección y marcado controlados -->
  <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">Seleccionados: {{ selKeys }} | Marcados: {{ chkKeys }}</p>
  <!-- Deshabilitar todo el árbol -->
  <LhTree :data="ctrlData" disabled />
</template>
vue
<script>
export default {
  data() {
    return {
      selKeys: ['x'],
      chkKeys: ['x-1'],
      ctrlData: [
        { key: 'x', label: 'Diseño', children: [
          { key: 'x-1', label: 'Color' },
          { key: 'x-2', label: 'Tipografía' },
        ] },
        { key: 'y', label: 'Ingeniería' },
      ],
    }
  },
}
</script>

<template>
  <!-- Selección y marcado controlados -->
  <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">Seleccionados: {{ selKeys }} | Marcados: {{ chkKeys }}</p>
  <!-- Deshabilitar todo el árbol -->
  <LhTree :data="ctrlData" disabled />
</template>

API ​

LhTree Props ​

PropDescripciónTipoPredeterminado
dataDatos del árbol[LhTreeNode](#type-LhTreeNode)[]—
showCheckboxMuestra las casillas de marcadobooleanfalse
multipleSelección múltiple (al hacer clic en un nodo se conmuta la selección en lugar de reemplazarla)booleanfalse
expandOnClickNodeExpande/contrae al hacer clic en un nodo no hojabooleantrue
defaultExpandedKeysClaves expandidas por defecto (no controladas)string[]() => []
expandedKeysClaves expandidas controladasstring[]—
defaultSelectedKeysClaves seleccionadas por defecto (no controladas)string[]() => []
selectedKeysClaves seleccionadas controladasstring[]—
defaultCheckedKeysClaves marcadas por defecto (no controladas)string[]() => []
checkedKeysClaves marcadas controladasstring[]—
checkStrictlyMarcado independiente entre padres e hijos (solo afecta al propio nodo, sin cascada ni estado intermedio)booleanfalse
lazyModo de carga diferida (al expandir un nodo no cargado se invoca load)booleanfalse
loadDevolución de llamada de carga diferida (resolve entrega los nodos secundarios y los expande; reject cancela)(node: LhTreeNode, resolve: (children: LhTreeNode[]) => void, reject: (reason?: unknown) => void) => void—
disabledDeshabilitado por completobooleanfalse
sxPropiedades de diseño rápido sxLhSxProps—

Events ​

EventoDescripciónParámetros
update:expandedKeysSe dispara cuando cambia la lista de claves expandidas, para usarse con v-model:expanded-keys(keys: string[])
update:selectedKeysSe dispara cuando cambia la lista de claves seleccionadas, para usarse con v-model:selected-keys(keys: string[])
update:checkedKeysSe dispara cuando cambia la lista de claves marcadas, para usarse con v-model:checked-keys(keys: string[])
selectSe dispara cuando un nodo es seleccionado / se conmuta su selección(key: string, node: LhTreeNode)
checkSe dispara tras un cambio de marcado(keys: string[], node: LhTreeNode)
expandSe dispara tras expandir / contrair un nodo(key: string, expanded: boolean)
nodeLoadSe dispara cuando regresan los nodos secundarios de una carga diferida(node: LhTreeNode, children: LhTreeNode[])

Slots: node (con ámbito { node, level, expanded, selected }), empty.

LhTreeNode ​

CampoDescripciónTipo
keyClave únicastring
labelTexto mostradostring
childrenNodos secundariosLhTreeNode[]
disabledDeshabilitadoboolean
isLeafSi es un nodo hoja (si no se indica, se deduce a partir de children)boolean

Accesibilidad ​

  • role="tree" + role="treeitem", con aria-level / aria-expanded / aria-selected / aria-disabled expuestos por completo
  • Las casillas usan role="checkbox" + aria-checked (el estado intermedio es mixed)
  • Teclado (patrón tree de la APG): ↑/↓ se mueve entre elementos (saltando los deshabilitados, en bucle), → expande o entra al primer hijo, ← contrae o regresa al padre, Home/End van al primero/último, Enter/Space activan; roving tabindex

Definiciones de tipos ​

Este componente exporta las siguientes estructuras de tipos TypeScript (extraídas automáticamente de los fuentes packages/components/tree/src/types.ts y @lh-ui/share/core/*; abre cada entrada para ver la definición completa; consulta la tabla de Props de arriba para la descripción de los atributos):

LhTreeProps
ts
export interface LhTreeProps {
  /** Datos del árbol */
  data: LhTreeNode[]
  /** Muestra las casillas de marcado */
  showCheckbox?: boolean
  /** Selección múltiple (al hacer clic en un nodo se conmuta la selección en lugar de reemplazarla) */
  multiple?: boolean
  /** Expande/contrae al hacer clic en un nodo no hoja */
  expandOnClickNode?: boolean
  /** Claves expandidas por defecto (no controladas) */
  defaultExpandedKeys?: string[]
  /** Claves expandidas controladas */
  expandedKeys?: string[]
  /** Claves seleccionadas por defecto (no controladas) */
  defaultSelectedKeys?: string[]
  /** Claves seleccionadas controladas */
  selectedKeys?: string[]
  /** Claves marcadas por defecto (no controladas) */
  defaultCheckedKeys?: string[]
  /** Claves marcadas controladas */
  checkedKeys?: string[]
  /** Marcado independiente entre padres e hijos (solo afecta al propio nodo, sin cascada ni estado intermedio) */
  checkStrictly?: boolean
  /** Modo de carga diferida: al expandir un nodo no hoja aún no cargado se invoca load */
  lazy?: boolean
  /** Devolución de llamada de carga diferida: resolve entrega los nodos secundarios y los expande; reject (o una excepción) cancela */
  load?: (
    node: LhTreeNode,
    resolve: (children: LhTreeNode[]) => void,
    reject: (reason?: unknown) => void,
  ) => void
  /** Deshabilitado por completo */
  disabled?: boolean
  /** Propiedades de diseño rápido sx */
  sx?: LhSxProps
}
LhFlatTreeNode (de @lh-ui/share/core/tree)
ts
/** Nodo aplanado (para el renderizado) */
export interface LhFlatTreeNode {
  node: LhTreeNode
  key: string
  level: number
  parentKey: string | null
  /** Cadena de ancestros (sin incluirse a sí mismo) */
  ancestorKeys: string[]
  /** Si es un nodo hoja */
  isLeaf: boolean
}
LhTreeNode (de @lh-ui/share/core/tree)
ts
/** Datos del nodo del árbol */
export interface LhTreeNode {
  /** Clave única */
  key: string
  /** Texto mostrado */
  label: string
  /** Nodos secundarios */
  children?: LhTreeNode[]
  /** Deshabilitado */
  disabled?: boolean
  /** Si es un nodo hoja (si no se indica, se deduce a partir de children) */
  isLeaf?: boolean
}
LhTreeCheckState (de @lh-ui/share/core/tree)
ts
/** Estado del marcado */
export type LhTreeCheckState = 'checked' | 'indeterminate' | 'unchecked'

La belleza de la tradición, el cimiento del código