Arborescence
La conversion de données aplaties en arbre ainsi que la propagation de la coche sont implémentées dans des fonctions pures de la couche core ; le composant ne gère que le rendu et le clavier.
Utilisation de base
<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: 'Exploitation', disabled: true },
]
</script>
<template>
<!-- Utilisation de base -->
<LhTree :data="treeData" :default-expanded-keys="['a']" />
</template><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: 'Exploitation', disabled: true },
],
}
},
}
</script>
<template>
<!-- Utilisation de base -->
<LhTree :data="treeData" :default-expanded-keys="['a']" />
</template>Coche en cascade
Cocher un parent cascade sur tous ses descendants ; lorsque tous les enfants sont cochés, le parent est automatiquement coché, et une sélection partielle affiche l'état intermédiaire (indeterminate).
<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: 'Exploitation', disabled: true },
]
</script>
<template>
<!-- Coche en cascade -->
<LhTree :data="treeData" show-checkbox :default-expanded-keys="['a', 'b']" />
</template><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: 'Exploitation', disabled: true },
],
}
},
}
</script>
<template>
<!-- Coche en cascade -->
<LhTree :data="treeData" show-checkbox :default-expanded-keys="['a', 'b']" />
</template>Chargement paresseux
Avec lazy activé, l'expansion d'un nœud non chargé appelle le rappel load (resolve soumet les nœuds enfants et déclenche l'expansion, reject l'annule) ; la flèche affiche un état de rotation pendant le chargement. Un nœud chargeable doit déclarer explicitement isLeaf: false (sinon un nœud sans children est déduit comme feuille et n'affiche pas de flèche d'expansion) ; les nœuds enfants chargés qui sont encore des branches en attente de chargement doivent également déclarer isLeaf: false, tandis que les vraies feuilles déclarent isLeaf: true.
<script setup>
import { ref } from 'vue'
const lazyData = ref([
{ key: 'lazy', label: 'Cliquer pour charger', isLeaf: false },
])
function loadNodes(node, resolve, reject) {
// Simulation d'une requête asynchrone
setTimeout(() => {
if (node.key === 'lazy') {
resolve([
{ key: 'lazy-1', label: 'Nœud enfant 1', isLeaf: true },
{ key: 'lazy-2', label: 'Nœud enfant 2', isLeaf: true },
])
} else {
reject(new Error('nothing to load'))
}
}, 600)
}
</script>
<template>
<!-- Chargement paresseux -->
<LhTree :data="lazyData" lazy :load="loadNodes" />
</template><script>
export default {
data() {
return {
lazyData: [{ key: 'lazy', label: 'Cliquer pour charger', isLeaf: false }],
}
},
methods: {
// Simulation d'une requête asynchrone
loadNodes(node, resolve, reject) {
setTimeout(() => {
if (node.key === 'lazy') {
resolve([
{ key: 'lazy-1', label: 'Nœud enfant 1', isLeaf: true },
{ key: 'lazy-2', label: 'Nœud enfant 2', isLeaf: true },
])
} else {
reject(new Error('nothing to load'))
}
}, 600)
},
},
}
</script>
<template>
<!-- Chargement paresseux -->
<LhTree :data="lazyData" lazy :load="loadNodes" />
</template>Sans cascade parent-enfant (checkStrictly)
Avec checkStrictly activé, la coche ne s'applique qu'au nœud lui-même : pas de cascade vers les descendants, pas de remontée vers les ancêtres, et pas d'état intermédiaire.
<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: 'Exploitation', disabled: true },
]
</script>
<template>
<!-- Sans cascade parent-enfant -->
<LhTree :data="treeData" show-checkbox check-strictly :default-expanded-keys="['a', 'b']" />
</template><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: 'Exploitation', disabled: true },
],
}
},
}
</script>
<template>
<!-- Sans cascade parent-enfant -->
<LhTree :data="treeData" show-checkbox check-strictly :default-expanded-keys="['a', 'b']" />
</template>Sélection et clés contrôlées
multiple autorise la sélection multiple ; expand-on-click-node étend ou réduit l'arbre au clic sur un nœud ; default-selected-keys / v-model:selected-keys contrôlent la sélection ; default-checked-keys / v-model:checked-keys contrôlent la coche ; default-expanded-keys / v-model:expanded-keys contrôlent l'expansion ; disabled désactive l'arbre entier.
Sélection : [ "x" ] | Coche : [ "x-1" ]
<script setup>
import { ref } from 'vue'
const selKeys = ref(['x'])
const chkKeys = ref(['x-1'])
const ctrlData = [
{ key: 'x', label: 'Design', children: [
{ key: 'x-1', label: 'Couleurs' },
{ key: 'x-2', label: 'Polices' },
] },
{ key: 'y', label: 'Ingénierie' },
]
</script>
<template>
<!-- Sélection et coche contrôlées -->
<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">Sélection : {{ selKeys }} | Coche : {{ chkKeys }}</p>
<!-- Arbre entièrement désactivé -->
<LhTree :data="ctrlData" disabled />
</template><script>
export default {
data() {
return {
selKeys: ['x'],
chkKeys: ['x-1'],
ctrlData: [
{ key: 'x', label: 'Design', children: [
{ key: 'x-1', label: 'Couleurs' },
{ key: 'x-2', label: 'Polices' },
] },
{ key: 'y', label: 'Ingénierie' },
],
}
},
}
</script>
<template>
<!-- Sélection et coche contrôlées -->
<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">Sélection : {{ selKeys }} | Coche : {{ chkKeys }}</p>
<!-- Arbre entièrement désactivé -->
<LhTree :data="ctrlData" disabled />
</template>API
LhTree Props
| Prop | Description | Type | Valeur par défaut |
|---|---|---|---|
| data | Données de l'arbre | [LhTreeNode](#type-LhTreeNode)[] | — |
| showCheckbox | Affiche les cases à cocher | boolean | false |
| multiple | Sélection multiple (le clic sur un nœud bascule la sélection au lieu de la remplacer) | boolean | false |
| expandOnClickNode | Étend ou réduit au clic sur un nœud non feuille | boolean | true |
| defaultExpandedKeys | Clés étendues par défaut (non contrôlées) | string[] | () => [] |
| expandedKeys | Clés étendues contrôlées | string[] | — |
| defaultSelectedKeys | Clés sélectionnées par défaut (non contrôlées) | string[] | () => [] |
| selectedKeys | Clés sélectionnées contrôlées | string[] | — |
| defaultCheckedKeys | Clés cochées par défaut (non contrôlées) | string[] | () => [] |
| checkedKeys | Clés cochées contrôlées | string[] | — |
| checkStrictly | Coche sans cascade parent-enfant (la coche ne s'applique qu'au nœud lui-même, pas de cascade ni d'état intermédiaire) | boolean | false |
| lazy | Mode de chargement paresseux (appelle load lors de l'expansion d'un nœud non chargé) | boolean | false |
| load | Rappel de chargement paresseux (resolve soumet les nœuds enfants et déclenche l'expansion, reject annule) | (node: LhTreeNode, resolve: (children: LhTreeNode[]) => void, reject: (reason?: unknown) => void) => void | — |
| disabled | Désactivé globalement | boolean | false |
| sx | Propriétés de mise en page rapide sx | LhSxProps | — |
Events
| Événement | Description | Paramètre de rappel |
|---|---|---|
| update:expandedKeys | Émis lorsque la liste des clés étendues change, à utiliser avec v-model:expanded-keys | (keys: string[]) |
| update:selectedKeys | Émis lorsque la liste des clés sélectionnées change, à utiliser avec v-model:selected-keys | (keys: string[]) |
| update:checkedKeys | Émis lorsque la liste des clés cochées change, à utiliser avec v-model:checked-keys | (keys: string[]) |
| select | Émis après la sélection ou la bascule de sélection d'un nœud au clic | (key: string, node: LhTreeNode) |
| check | Émis après un changement de coche | (keys: string[], node: LhTreeNode) |
| expand | Émis après l'expansion ou la réduction d'un nœud | (key: string, expanded: boolean) |
| nodeLoad | Émis après le retour des nœuds enfants en chargement paresseux | (node: LhTreeNode, children: LhTreeNode[]) |
Slots : node (portée { node, level, expanded, selected }), empty.
LhTreeNode
| Champ | Description | Type |
|---|---|---|
| key | Clé unique | string |
| label | Texte affiché | string |
| children | Nœuds enfants | LhTreeNode[] |
| disabled | Désactivé | boolean |
| isLeaf | Feuille ou non (déduit des children lorsque non renseigné) | boolean |
Accessibilité
role="tree"+role="treeitem",aria-level/aria-expanded/aria-selected/aria-disabledtous exposés- Case à cocher
role="checkbox"+aria-checked(l'état intermédiaire vautmixed) - Clavier (pattern tree des APG) : ↑/↓ pour passer d'un élément à l'autre (en sautant les désactivés, en bouclant), → pour étendre ou entrer dans le premier enfant, ← pour réduire ou remonter au parent, Home/End pour le premier et le dernier, Enter/Space pour activer ; roving tabindex
Définitions de types
Les structures de types TypeScript exportées par ce composant sont les suivantes (extraites automatiquement de packages/components/tree/src/types.ts et de @lh-ui/share/core/* ; cliquez sur chaque entrée pour afficher la définition complète ; les descriptions des props se trouvent dans le tableau Props ci-dessus) :
LhTreeProps
export interface LhTreeProps {
/** Données de l'arbre */
data: LhTreeNode[]
/** Affiche les cases à cocher */
showCheckbox?: boolean
/** Sélection multiple (le clic sur un nœud bascule la sélection au lieu de la remplacer) */
multiple?: boolean
/** Étend ou réduit au clic sur un nœud non feuille */
expandOnClickNode?: boolean
/** Clés étendues par défaut (non contrôlées) */
defaultExpandedKeys?: string[]
/** Clés étendues contrôlées */
expandedKeys?: string[]
/** Clés sélectionnées par défaut (non contrôlées) */
defaultSelectedKeys?: string[]
/** Clés sélectionnées contrôlées */
selectedKeys?: string[]
/** Clés cochées par défaut (non contrôlées) */
defaultCheckedKeys?: string[]
/** Clés cochées contrôlées */
checkedKeys?: string[]
/** Coche sans cascade parent-enfant (la coche ne s'applique qu'au nœud lui-même, pas de cascade ni d'état intermédiaire) */
checkStrictly?: boolean
/** Mode de chargement paresseux : appelle load lors de l'expansion d'un nœud non feuille non chargé */
lazy?: boolean
/** Rappel de chargement paresseux : resolve soumet les nœuds enfants et déclenche l'expansion, reject (ou une erreur) annule */
load?: (
node: LhTreeNode,
resolve: (children: LhTreeNode[]) => void,
reject: (reason?: unknown) => void,
) => void
/** Désactivé globalement */
disabled?: boolean
/** Propriétés de mise en page rapide sx */
sx?: LhSxProps
}LhFlatTreeNode (provenant de @lh-ui/share/core/tree)
/** Nœud aplati (pour le rendu) */
export interface LhFlatTreeNode {
node: LhTreeNode
key: string
level: number
parentKey: string | null
/** Chaîne des ancêtres (elle-même non comprise) */
ancestorKeys: string[]
/** Feuille ou non */
isLeaf: boolean
}LhTreeNode (provenant de @lh-ui/share/core/tree)
/** Données d'un nœud de l'arbre */
export interface LhTreeNode {
/** Clé unique */
key: string
/** Texte affiché */
label: string
/** Nœuds enfants */
children?: LhTreeNode[]
/** Désactivé */
disabled?: boolean
/** Feuille ou non (déduit des children lorsque non renseigné) */
isLeaf?: boolean
}LhTreeCheckState (provenant de @lh-ui/share/core/tree)
/** État de la coche */
export type LhTreeCheckState = 'checked' | 'indeterminate' | 'unchecked'