Utilisation
Utilisez la directive v-model pour contrôler la valeur de la boîte de liste ou la prop default-value pour définir la valeur initiale lorsque vous n'avez pas besoin de contrôler son état.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
},
{
label: 'Netherlands',
icon: 'i-lucide-map-pin',
value: 'NL'
},
{
label: 'Poland',
icon: 'i-lucide-map-pin',
value: 'PL'
},
{
label: 'Belgium',
icon: 'i-lucide-map-pin',
value: 'BE'
},
{
label: 'Portugal',
icon: 'i-lucide-map-pin',
value: 'PT'
},
{
label: 'Austria',
icon: 'i-lucide-map-pin',
value: 'AT'
},
{
label: 'Sweden',
icon: 'i-lucide-map-pin',
value: 'SE'
}
])
const value = ref({
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
})
</script>
<template>
<UListbox v-model="value" :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
},
{
label: 'Netherlands',
icon: 'i-lucide-map-pin',
value: 'NL'
},
{
label: 'Poland',
icon: 'i-lucide-map-pin',
value: 'PL'
},
{
label: 'Belgium',
icon: 'i-lucide-map-pin',
value: 'BE'
},
{
label: 'Portugal',
icon: 'i-lucide-map-pin',
value: 'PT'
},
{
label: 'Austria',
icon: 'i-lucide-map-pin',
value: 'AT'
},
{
label: 'Sweden',
icon: 'i-lucide-map-pin',
value: 'SE'
}
])
const value = ref({
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
})
</script>
<template>
<UListbox v-model="value" :items="items" />
</template>Éléments
Utilisez le prop items comme un tableau d'objets avec les propriétés suivantes:
- xx
label?: stringxx{lang="ts-type"} Xph060xxxdescription?: stringx{lang="ts-type"}x Xph067xxxtype?: "label" | "separator" | "item"x{lang="ts-type"}x Xph074xxxicon?: stringx{lang="ts-type"}x Xph081xxxavatar?: AvatarPropsx{lang="ts-type"}x Xph088xxxchip?: ChipPropsx{lang="ts-type"}x - xx
disabled?: booleanxx{lang="ts-type"} - x
onSelect?: (e: Event) => voidx{lang="ts-type"} - x
class?: anyx{lang="ts-type"} - x
ui?: { label?: ClassNameValue, separator?: ClassNameValue, item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, ... }x{lang="ts-type"}
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
description: 'The Hexagon',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
description: 'The Federal Republic',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
description: 'The Boot',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
description: 'The Bull Skin',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
</script>
<template>
<UListbox :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
description: 'The Hexagon',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
description: 'The Federal Republic',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
description: 'The Boot',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
description: 'The Bull Skin',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
</script>
<template>
<UListbox :items="items" />
</template>Vous pouvez également passer un tableau de tableaux au prop items pour afficher des groupes d'éléments séparés.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[][]>([
[
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
}
],
[
{
label: 'Brazil',
icon: 'i-lucide-map-pin',
value: 'BR'
},
{
label: 'Argentina',
icon: 'i-lucide-map-pin',
value: 'AR'
}
]
])
</script>
<template>
<UListbox :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[][]>([
[
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
}
],
[
{
label: 'Brazil',
icon: 'i-lucide-map-pin',
value: 'BR'
},
{
label: 'Argentina',
icon: 'i-lucide-map-pin',
value: 'AR'
}
]
])
</script>
<template>
<UListbox :items="items" />
</template>multiple
Utilisez la prop multiple pour permettre de sélectionner plusieurs éléments. Lorsqu 'elle est activée, la v-model sera un tableau.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
</script>
<template>
<UListbox multiple :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
</script>
<template>
<UListbox multiple :items="items" />
</template>Clé ### Value
Vous pouvez choisir de lier une propriété unique de l'objet plutôt que l'objet entier en utilisant la prop. value-key.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
const value = ref('FR')
</script>
<template>
<UListbox v-model="value" value-key="value" :items="items" class="w-full" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
const value = ref('FR')
</script>
<template>
<UListbox v-model="value" value-key="value" :items="items" class="w-full" />
</template>Filtre
Utilisez la prop filter pour afficher une entrée de filtre ou passer un objet pour personnaliser le composant Input.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
},
{
label: 'Netherlands',
icon: 'i-lucide-map-pin',
value: 'NL'
},
{
label: 'Poland',
icon: 'i-lucide-map-pin',
value: 'PL'
}
])
</script>
<template>
<UListbox :filter="{
placeholder: 'Filter...',
icon: 'i-lucide-search'
}" :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
},
{
label: 'Netherlands',
icon: 'i-lucide-map-pin',
value: 'NL'
},
{
label: 'Poland',
icon: 'i-lucide-map-pin',
value: 'PL'
}
])
</script>
<template>
<UListbox :filter="{
placeholder: 'Filter...',
icon: 'i-lucide-search'
}" :items="items" />
</template>Selected Icône
Utilisez la prop selected-icon pour personnaliser l'icône lorsqu 'un élément est sélectionné. Par défaut i-lucide-check.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
const value = ref('FR')
</script>
<template>
<UListbox v-model="value" selected-icon="i-lucide-flame" value-key="value" :items="items" class="w-full" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
const value = ref('FR')
</script>
<template>
<UListbox v-model="value" selected-icon="i-lucide-flame" value-key="value" :items="items" class="w-full" />
</template>Size
Utilisez la prop size pour modifier la taille de la boîte de liste.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
</script>
<template>
<UListbox size="xl" :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
</script>
<template>
<UListbox size="xl" :items="items" />
</template>Chargement
Utilisez la prop loading pour afficher un indicateur de chargement. Utilisez la prop loading-icon pour personnaliser l'icône.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
}
])
</script>
<template>
<UListbox loading :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
}
])
</script>
<template>
<UListbox loading :items="items" />
</template>Disabled
Utilisez la prop disabled pour empêcher toute interaction de l'utilisateur avec la boîte de liste.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
</script>
<template>
<UListbox disabled :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
</script>
<template>
<UListbox disabled :items="items" />
</template>Exemples
Avec type d'éléments
Vous pouvez utiliser la propriété type avec separator pour afficher un séparateur entre les éléments ou label pour afficher une étiquette.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[][]>([
[
{
type: 'label',
label: 'Fruits'
},
{
label: 'Apple'
},
{
label: 'Banana'
},
{
label: 'Blueberry'
},
{
label: 'Grapes'
},
{
label: 'Pineapple'
}
],
[
{
type: 'label',
label: 'Vegetables'
},
{
label: 'Aubergine'
},
{
label: 'Broccoli'
},
{
label: 'Carrot'
},
{
label: 'Courgette'
},
{
label: 'Leek'
}
]
])
</script>
<template>
<UListbox :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[][]>([
[
{
type: 'label',
label: 'Fruits'
},
{
label: 'Apple'
},
{
label: 'Banana'
},
{
label: 'Blueberry'
},
{
label: 'Grapes'
},
{
label: 'Pineapple'
}
],
[
{
type: 'label',
label: 'Vegetables'
},
{
label: 'Aubergine'
},
{
label: 'Broccoli'
},
{
label: 'Carrot'
},
{
label: 'Courgette'
},
{
label: 'Leek'
}
]
])
</script>
<template>
<UListbox :items="items" />
</template>label comme en-têtes de groupe, passez un tableau de tableaux afin qu 'une étiquette soit filtrée avec son groupe lors de la recherche.With icône dans les éléments
Vous pouvez utiliser la propriété icon pour afficher un Icon à l'intérieur des éléments.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'Backlog',
icon: 'i-lucide-circle-help',
value: 'backlog'
},
{
label: 'Todo',
icon: 'i-lucide-circle-plus',
value: 'todo'
},
{
label: 'In Progress',
icon: 'i-lucide-circle-arrow-up',
value: 'in_progress'
},
{
label: 'Done',
icon: 'i-lucide-circle-check',
value: 'done'
}
])
</script>
<template>
<UListbox :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'Backlog',
icon: 'i-lucide-circle-help',
value: 'backlog'
},
{
label: 'Todo',
icon: 'i-lucide-circle-plus',
value: 'todo'
},
{
label: 'In Progress',
icon: 'i-lucide-circle-arrow-up',
value: 'in_progress'
},
{
label: 'Done',
icon: 'i-lucide-circle-check',
value: 'done'
}
])
</script>
<template>
<UListbox :items="items" />
</template>Avec avatar dans les éléments
Vous pouvez utiliser la propriété avatar pour afficher un Avatar à l'intérieur des éléments.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'benjamincanac',
avatar: {
src: 'https://github.com/benjamincanac.png'
}
},
{
label: 'HugoRCD',
avatar: {
src: 'https://github.com/HugoRCD.png'
}
},
{
label: 'atinux',
avatar: {
src: 'https://github.com/atinux.png'
}
},
{
label: 'romhml',
avatar: {
src: 'https://github.com/romhml.png'
}
}
])
</script>
<template>
<UListbox :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'benjamincanac',
avatar: {
src: 'https://github.com/benjamincanac.png'
}
},
{
label: 'HugoRCD',
avatar: {
src: 'https://github.com/HugoRCD.png'
}
},
{
label: 'atinux',
avatar: {
src: 'https://github.com/atinux.png'
}
},
{
label: 'romhml',
avatar: {
src: 'https://github.com/romhml.png'
}
}
])
</script>
<template>
<UListbox :items="items" />
</template>Avec puce dans les articles
Vous pouvez utiliser la propriété chip pour afficher un Chip à l'intérieur des éléments.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'bug',
chip: {
color: 'error'
}
},
{
label: 'feature',
chip: {
color: 'success'
}
},
{
label: 'enhancement',
chip: {
color: 'info'
}
}
])
</script>
<template>
<UListbox :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'bug',
chip: {
color: 'error'
}
},
{
label: 'feature',
chip: {
color: 'success'
}
},
{
label: 'enhancement',
chip: {
color: 'info'
}
}
])
</script>
<template>
<UListbox :items="items" />
</template>Avec la description dans les articles
Vous pouvez utiliser la propriété description pour afficher du texte supplémentaire sous l'étiquette.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
description: 'The Hexagon',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
description: 'The Federal Republic',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
description: 'The Boot',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
description: 'The Bull Skin',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
</script>
<template>
<UListbox :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items = ref<ListboxItem[]>([
{
label: 'France',
description: 'The Hexagon',
icon: 'i-lucide-map-pin',
value: 'FR'
},
{
label: 'Germany',
description: 'The Federal Republic',
icon: 'i-lucide-map-pin',
value: 'DE'
},
{
label: 'Italy',
description: 'The Boot',
icon: 'i-lucide-map-pin',
value: 'IT'
},
{
label: 'Spain',
description: 'The Bull Skin',
icon: 'i-lucide-map-pin',
value: 'ES'
}
])
</script>
<template>
<UListbox :items="items" />
</template>Control élément sélectionné (s)
Vous pouvez contrôler l'élément sélectionné à l'aide de la prop default-value ou de la directive v-model.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items: ListboxItem[] = [
{ label: 'France', icon: 'i-lucide-map-pin', value: 'FR' },
{ label: 'Germany', icon: 'i-lucide-map-pin', value: 'DE' },
{ label: 'Italy', icon: 'i-lucide-map-pin', value: 'IT' },
{ label: 'Spain', icon: 'i-lucide-map-pin', value: 'ES' },
{ label: 'Netherlands', icon: 'i-lucide-map-pin', value: 'NL' },
{ label: 'Poland', icon: 'i-lucide-map-pin', value: 'PL' }
]
const value = ref<ListboxItem>(items[2]!)
</script>
<template>
<UListbox
v-model="value"
:items="items"
class="w-full"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items: ListboxItem[] = [
{ label: 'France', icon: 'i-lucide-map-pin', value: 'FR' },
{ label: 'Germany', icon: 'i-lucide-map-pin', value: 'DE' },
{ label: 'Italy', icon: 'i-lucide-map-pin', value: 'IT' },
{ label: 'Spain', icon: 'i-lucide-map-pin', value: 'ES' },
{ label: 'Netherlands', icon: 'i-lucide-map-pin', value: 'NL' },
{ label: 'Poland', icon: 'i-lucide-map-pin', value: 'PL' }
]
const value = ref<ListboxItem>(items[2]!)
</script>
<template>
<UListbox
v-model="value"
:items="items"
class="w-full"
/>
</template>
Control terme de recherche
Utilisez la directive v-model:search-term pour contrôler le terme de recherche.
<script setup lang="ts">
const searchTerm = ref('F')
const items = ref([
{ label: 'France', icon: 'i-lucide-map-pin', value: 'FR' },
{ label: 'Germany', icon: 'i-lucide-map-pin', value: 'DE' },
{ label: 'Italy', icon: 'i-lucide-map-pin', value: 'IT' },
{ label: 'Spain', icon: 'i-lucide-map-pin', value: 'ES' },
{ label: 'Netherlands', icon: 'i-lucide-map-pin', value: 'NL' },
{ label: 'Poland', icon: 'i-lucide-map-pin', value: 'PL' }
])
const value = ref()
</script>
<template>
<UListbox v-model="value" v-model:search-term="searchTerm" filter :items="items" class="w-full" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
const searchTerm = ref('F')
const items = ref([
{ label: 'France', icon: 'i-lucide-map-pin', value: 'FR' },
{ label: 'Germany', icon: 'i-lucide-map-pin', value: 'DE' },
{ label: 'Italy', icon: 'i-lucide-map-pin', value: 'IT' },
{ label: 'Spain', icon: 'i-lucide-map-pin', value: 'ES' },
{ label: 'Netherlands', icon: 'i-lucide-map-pin', value: 'NL' },
{ label: 'Poland', icon: 'i-lucide-map-pin', value: 'PL' }
])
const value = ref()
</script>
<template>
<UListbox v-model="value" v-model:search-term="searchTerm" filter :items="items" class="w-full" />
</template>
With ignore le filtre
Réglez la prop ignore-filter sur true pour désactiver la recherche interne et utiliser votre propre logique de recherche.
<script setup lang="ts">
import { refDebounced } from '@vueuse/core'
const searchTerm = ref('')
const searchTermDebounced = refDebounced(searchTerm, 200)
const { data: users, status, execute } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
key: 'listbox-users-search',
params: { q: searchTermDebounced },
transform: (data: { id: number, name: string }[]) => {
return data?.map(user => ({
label: user.name,
value: String(user.id),
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}`, loading: 'lazy' as const }
}))
},
immediate: false
})
onMounted(() => {
execute()
})
</script>
<template>
<UListbox
v-model:search-term="searchTerm"
:items="users || []"
:filter="{
icon: 'i-lucide-search',
loading: status === 'pending'
}"
ignore-filter
class="w-full"
/>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { refDebounced } from '@vueuse/core'
const searchTerm = ref('')
const searchTermDebounced = refDebounced(searchTerm, 200)
const { data: users, status, execute } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
key: 'listbox-users-search',
params: { q: searchTermDebounced },
transform: (data: { id: number, name: string }[]) => {
return data?.map(user => ({
label: user.name,
value: String(user.id),
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}`, loading: 'lazy' as const }
}))
},
immediate: false
})
onMounted(() => {
execute()
})
</script>
<template>
<UListbox
v-model:search-term="searchTerm"
:items="users || []"
:filter="{
icon: 'i-lucide-search',
loading: status === 'pending'
}"
ignore-filter
class="w-full"
/>
</template>
refDebounced pour déboulonner les appels d'API.With filter fields
Utilisez le prop filter-fields avec un tableau de champs pour filtrer.
<script setup lang="ts">
const { data: users, status, execute } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
key: 'typicode-users-email',
transform: (data: { id: number, name: string, email: string }[]) => {
return data?.map(user => ({
label: user.name,
email: user.email,
value: String(user.id),
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}`, loading: 'lazy' as const }
}))
},
immediate: false
})
onMounted(() => {
execute()
})
</script>
<template>
<UListbox
:items="users || []"
:loading="status === 'pending'"
:filter-fields="['label', 'email']"
filter
class="w-full"
>
<template #item-label="{ item }">
{{ item.label }}
<span class="text-muted">
{{ item.email }}
</span>
</template>
</UListbox>
</template>
<script setup lang="ts">
import { onMounted } from 'vue'
const { data: users, status, execute } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
key: 'typicode-users-email',
transform: (data: { id: number, name: string, email: string }[]) => {
return data?.map(user => ({
label: user.name,
email: user.email,
value: String(user.id),
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}`, loading: 'lazy' as const }
}))
},
immediate: false
})
onMounted(() => {
execute()
})
</script>
<template>
<UListbox
:items="users || []"
:loading="status === 'pending'"
:filter-fields="['label', 'email']"
filter
class="w-full"
>
<template #item-label="{ item }">
{{ item.label }}
<span class="text-muted">
{{ item.email }}
</span>
</template>
</UListbox>
</template>
Avec virtualisation
Utilisez la prop virtualize pour activer la virtualisation de grandes listes en tant que booléen ou objet avec des options telles que { estimateSize: 32, overscan: 12 }.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items: ListboxItem[] = Array.from({ length: 1000 }, (_, i) => ({
label: `Item ${i + 1}`,
icon: 'i-lucide-file',
value: i + 1
}))
const value = ref<ListboxItem[]>([])
</script>
<template>
<UListbox
v-model="value"
:items="items"
multiple
virtualize
class="w-full"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items: ListboxItem[] = Array.from({ length: 1000 }, (_, i) => ({
label: `Item ${i + 1}`,
icon: 'i-lucide-file',
value: i + 1
}))
const value = ref<ListboxItem[]>([])
</script>
<template>
<UListbox
v-model="value"
:items="items"
multiple
virtualize
class="w-full"
/>
</template>
As une liste de transfert
Vous pouvez composer deux composants Listbox avec des contrôles Button pour créer un modèle de liste de transfert.
<script setup lang="ts">
import type { ListboxItem } from '@nuxt/ui'
const items: ListboxItem[] = [
{ label: 'France', icon: 'i-lucide-map-pin', value: 'FR' },
{ label: 'Germany', icon: 'i-lucide-map-pin', value: 'DE' },
{ label: 'Italy', icon: 'i-lucide-map-pin', value: 'IT' },
{ label: 'Spain', icon: 'i-lucide-map-pin', value: 'ES' },
{ label: 'Netherlands', icon: 'i-lucide-map-pin', value: 'NL' },
{ label: 'Poland', icon: 'i-lucide-map-pin', value: 'PL' },
{ label: 'Belgium', icon: 'i-lucide-map-pin', value: 'BE' },
{ label: 'Portugal', icon: 'i-lucide-map-pin', value: 'PT' }
]
const targetItems = ref<ListboxItem[]>([])
const sourceSelection = ref<ListboxItem[]>([])
const targetSelection = ref<ListboxItem[]>([])
const sourceItems = computed(() => items.filter(item => !targetItems.value.some(t => t.value === item.value)))
function transferSelected() {
targetItems.value = [...targetItems.value, ...sourceSelection.value]
sourceSelection.value = []
}
function removeSelected() {
targetItems.value = targetItems.value.filter(item => !targetSelection.value.some(t => t.value === item.value))
targetSelection.value = []
}
</script>
<template>
<div class="flex items-stretch gap-4 w-full">
<div class="flex flex-col flex-1 gap-1">
<span class="text-sm font-medium text-highlighted">Available</span>
<UListbox
v-model="sourceSelection"
:items="sourceItems"
multiple
filter
class="size-full"
/>
</div>
<div class="flex flex-col items-center justify-center gap-1">
<UButton
icon="i-lucide-chevron-right"
color="neutral"
variant="outline"
:disabled="!sourceSelection.length"
@click="transferSelected"
/>
<UButton
icon="i-lucide-chevron-left"
color="neutral"
variant="outline"
:disabled="!targetSelection.length"
@click="removeSelected"
/>
</div>
<div class="flex flex-col flex-1 gap-1">
<span class="text-sm font-medium text-highlighted">Selected</span>
<UListbox
v-model="targetSelection"
:items="targetItems"
multiple
filter
class="size-full"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { ListboxItem } from '@nuxt/ui'
const items: ListboxItem[] = [
{ label: 'France', icon: 'i-lucide-map-pin', value: 'FR' },
{ label: 'Germany', icon: 'i-lucide-map-pin', value: 'DE' },
{ label: 'Italy', icon: 'i-lucide-map-pin', value: 'IT' },
{ label: 'Spain', icon: 'i-lucide-map-pin', value: 'ES' },
{ label: 'Netherlands', icon: 'i-lucide-map-pin', value: 'NL' },
{ label: 'Poland', icon: 'i-lucide-map-pin', value: 'PL' },
{ label: 'Belgium', icon: 'i-lucide-map-pin', value: 'BE' },
{ label: 'Portugal', icon: 'i-lucide-map-pin', value: 'PT' }
]
const targetItems = ref<ListboxItem[]>([])
const sourceSelection = ref<ListboxItem[]>([])
const targetSelection = ref<ListboxItem[]>([])
const sourceItems = computed(() => items.filter(item => !targetItems.value.some(t => t.value === item.value)))
function transferSelected() {
targetItems.value = [...targetItems.value, ...sourceSelection.value]
sourceSelection.value = []
}
function removeSelected() {
targetItems.value = targetItems.value.filter(item => !targetSelection.value.some(t => t.value === item.value))
targetSelection.value = []
}
</script>
<template>
<div class="flex items-stretch gap-4 w-full">
<div class="flex flex-col flex-1 gap-1">
<span class="text-sm font-medium text-highlighted">Available</span>
<UListbox
v-model="sourceSelection"
:items="sourceItems"
multiple
filter
class="size-full"
/>
</div>
<div class="flex flex-col items-center justify-center gap-1">
<UButton
icon="i-lucide-chevron-right"
color="neutral"
variant="outline"
:disabled="!sourceSelection.length"
@click="transferSelected"
/>
<UButton
icon="i-lucide-chevron-left"
color="neutral"
variant="outline"
:disabled="!targetSelection.length"
@click="removeSelected"
/>
</div>
<div class="flex flex-col flex-1 gap-1">
<span class="text-sm font-medium text-highlighted">Selected</span>
<UListbox
v-model="targetSelection"
:items="targetItems"
multiple
filter
class="size-full"
/>
</div>
</div>
</template>
API is
Props and
| Prop | Default | Type |
|---|---|---|
as | 'div' | anyThe element or component this component should render as. |
id | string | |
color | 'primary' | "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral" |
size | 'md' | "sm" | "md" | "xs" | "lg" | "xl" |
items | TThe items to display in the list. | |
modelValue | _Number<_Optional<_Nullable<GetModelValue<T, VK, M, undefined>, Mod>, Mod>, Mod>The controlled value of the Listbox. Can be bound with | |
modelModifiers | Mod | |
defaultValue | _Number<_Optional<_Nullable<GetModelValue<T, VK, M, undefined>, Mod>, Mod>, Mod>The default value when not controlled. | |
multiple | false | MWhether multiple items can be selected. |
valueKey | undefined | VKWhen |
labelKey | 'label' | keyof Extract<NestedItem<T>, object> & string | DotPathKeys<Extract<NestedItem<T>, object>>The key used to get the label from the item. |
descriptionKey | 'description' | keyof Extract<NestedItem<T>, object> & string | DotPathKeys<Extract<NestedItem<T>, object>>The key used to get the description from the item. |
loading | boolean Whether the list is in a loading state. | |
loadingIcon | appConfig.ui.icons.loading | anyThe icon displayed when loading. |
filter | false | boolean | Omit<InputProps<AcceptableValue, ModelModifiers>, "modelValue" | "defaultValue"> Whether to display a filter input or not.
Can be an object to pass additional props to the input.
|
filterFields | [labelKey] | string[]The fields to filter by. |
ignoreFilter | false | boolean When |
selectedIcon | appConfig.ui.icons.check | anyThe icon displayed when an item is selected. |
virtualize | false | boolean | { overscan?: number ; estimateSize?: number | ((index: number) => number) | undefined; } | undefinedEnable virtualization for large lists. |
highlight | boolean Highlight the ring color like a focus state. | |
autofocus | boolean | |
autofocusDelay | 0 | number |
by | string | (a: AcceptableValue, b: AcceptableValue): booleanUse this to compare objects by a particular field, or pass your own comparison function for complete control over how objects are compared. | |
disabled | boolean When | |
highlightOnHover | true | boolean When |
name | stringThe name of the field. Submitted with its owning form as part of a name/value pair. | |
orientation | "vertical" | "horizontal"The orientation of the listbox. | |
required | boolean When | |
selectionBehavior | 'toggle' | "replace" | "toggle"How multiple selection should behave in the collection. |
searchTerm | '' | string |
ui | { root?: SlotClass; input?: SlotClass; content?: SlotClass; group?: SlotClass; label?: SlotClass; separator?: SlotClass; empty?: SlotClass; loading?: SlotClass; loadingIcon?: SlotClass; item?: SlotClass; itemLeadingIcon?: SlotClass; itemLeadingAvatar?: SlotClass; itemLeadingAvatarSize?: SlotClass; itemLeadingChip?: SlotClass; itemLeadingChipSize?: SlotClass; itemWrapper?: SlotClass; itemLabel?: SlotClass; itemDescription?: SlotClass; itemTrailing?: SlotClass; itemTrailingIcon?: SlotClass; } |
Slots
| Slot | Type |
|---|---|
loading | {} |
empty | { searchTerm: string; } |
item | { item: NestedItem<T>; index: number; ui: object; } |
item-leading | { item: NestedItem<T>; index: number; ui: object; } |
item-label | { item: NestedItem<T>; index: number; } |
item-description | { item: NestedItem<T>; index: number; } |
item-trailing | { item: NestedItem<T>; index: number; ui: object; } |
Emits
| Event | Type |
|---|---|
entryFocus | [event: CustomEvent<any>] |
highlight | [payload: { ref: HTMLElement; value: AcceptableValue; } | undefined] |
leave | [event: Event] |
change | [event: Event] |
update:modelValue | [value: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, undefined>, Mod>, Mod>, Mod>] |
update:searchTerm | [value: string] |
Thème
export default defineAppConfig({
ui: {
listbox: {
slots: {
root: 'flex flex-col min-h-0 min-w-0 ring ring-inset ring-default rounded-lg overflow-hidden',
input: 'border-b border-default',
content: 'relative overflow-y-auto flex-1 max-h-60 scroll-py-1 focus:outline-none',
group: 'p-1 isolate',
label: 'font-semibold text-highlighted',
separator: '-mx-1 my-1 h-px bg-border',
empty: 'text-center text-muted',
loading: 'flex items-center justify-center text-muted',
loadingIcon: 'animate-spin shrink-0',
item: [
'group relative w-full flex items-start select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-default data-highlighted:not-data-disabled:text-highlighted data-highlighted:not-data-disabled:before:bg-elevated/50',
'transition-colors before:transition-colors'
],
itemLeadingIcon: [
'shrink-0 text-dimmed group-data-highlighted:not-group-data-disabled:text-default',
'transition-colors'
],
itemLeadingAvatar: 'shrink-0',
itemLeadingAvatarSize: '',
itemLeadingChip: 'shrink-0',
itemLeadingChipSize: '',
itemWrapper: 'flex-1 flex flex-col min-w-0',
itemLabel: 'truncate',
itemDescription: 'truncate text-muted',
itemTrailing: 'ms-auto inline-flex gap-1.5 items-center',
itemTrailingIcon: 'shrink-0'
},
variants: {
size: {
xs: {
label: 'p-1 text-[10px]/3 gap-1',
empty: 'py-3 text-xs',
loading: 'py-3',
loadingIcon: 'size-4',
item: 'p-1 text-xs gap-1',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemLeadingChip: 'size-4',
itemLeadingChipSize: 'sm',
itemTrailingIcon: 'size-4'
},
sm: {
label: 'p-1.5 text-[10px]/3 gap-1.5',
empty: 'py-4 text-xs',
loading: 'py-4',
loadingIcon: 'size-4',
item: 'p-1.5 text-xs gap-1.5',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemLeadingChip: 'size-4',
itemLeadingChipSize: 'sm',
itemTrailingIcon: 'size-4'
},
md: {
label: 'p-1.5 text-xs gap-1.5',
empty: 'py-6 text-sm',
loading: 'py-6',
loadingIcon: 'size-5',
item: 'p-1.5 text-sm gap-1.5',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemLeadingChip: 'size-5',
itemLeadingChipSize: 'md',
itemTrailingIcon: 'size-5'
},
lg: {
label: 'p-2 text-xs gap-2',
empty: 'py-7 text-sm',
loading: 'py-7',
loadingIcon: 'size-5',
item: 'p-2 text-sm gap-2',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemLeadingChip: 'size-5',
itemLeadingChipSize: 'md',
itemTrailingIcon: 'size-5'
},
xl: {
label: 'p-2 text-sm gap-2',
empty: 'py-8 text-base',
loading: 'py-8',
loadingIcon: 'size-6',
item: 'p-2 text-base gap-2',
itemLeadingIcon: 'size-6',
itemLeadingAvatarSize: 'xs',
itemLeadingChip: 'size-6',
itemLeadingChipSize: 'lg',
itemTrailingIcon: 'size-6',
itemDescription: 'text-sm'
}
},
color: {
primary: {
root: 'outline-primary/25 has-focus-visible:outline-3 has-focus-visible:ring-primary'
},
secondary: {
root: 'outline-secondary/25 has-focus-visible:outline-3 has-focus-visible:ring-secondary'
},
success: {
root: 'outline-success/25 has-focus-visible:outline-3 has-focus-visible:ring-success'
},
info: {
root: 'outline-info/25 has-focus-visible:outline-3 has-focus-visible:ring-info'
},
warning: {
root: 'outline-warning/25 has-focus-visible:outline-3 has-focus-visible:ring-warning'
},
error: {
root: 'outline-error/25 has-focus-visible:outline-3 has-focus-visible:ring-error'
},
neutral: {
root: 'outline-inverted/25 has-focus-visible:outline-3 has-focus-visible:ring-inverted'
}
},
virtualize: {
true: {
content: 'p-1 isolate'
},
false: {
content: 'divide-y divide-default'
}
},
disabled: {
true: {
root: 'opacity-75 cursor-not-allowed'
}
},
highlight: {
true: ''
}
},
compoundVariants: [
{
color: 'primary',
highlight: true,
class: {
root: 'ring ring-inset ring-primary'
}
},
{
color: 'neutral',
highlight: true,
class: {
root: 'ring ring-inset ring-inverted'
}
}
],
defaultVariants: {
color: 'primary',
size: 'md'
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
listbox: {
slots: {
root: 'flex flex-col min-h-0 min-w-0 ring ring-inset ring-default rounded-lg overflow-hidden',
input: 'border-b border-default',
content: 'relative overflow-y-auto flex-1 max-h-60 scroll-py-1 focus:outline-none',
group: 'p-1 isolate',
label: 'font-semibold text-highlighted',
separator: '-mx-1 my-1 h-px bg-border',
empty: 'text-center text-muted',
loading: 'flex items-center justify-center text-muted',
loadingIcon: 'animate-spin shrink-0',
item: [
'group relative w-full flex items-start select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-default data-highlighted:not-data-disabled:text-highlighted data-highlighted:not-data-disabled:before:bg-elevated/50',
'transition-colors before:transition-colors'
],
itemLeadingIcon: [
'shrink-0 text-dimmed group-data-highlighted:not-group-data-disabled:text-default',
'transition-colors'
],
itemLeadingAvatar: 'shrink-0',
itemLeadingAvatarSize: '',
itemLeadingChip: 'shrink-0',
itemLeadingChipSize: '',
itemWrapper: 'flex-1 flex flex-col min-w-0',
itemLabel: 'truncate',
itemDescription: 'truncate text-muted',
itemTrailing: 'ms-auto inline-flex gap-1.5 items-center',
itemTrailingIcon: 'shrink-0'
},
variants: {
size: {
xs: {
label: 'p-1 text-[10px]/3 gap-1',
empty: 'py-3 text-xs',
loading: 'py-3',
loadingIcon: 'size-4',
item: 'p-1 text-xs gap-1',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemLeadingChip: 'size-4',
itemLeadingChipSize: 'sm',
itemTrailingIcon: 'size-4'
},
sm: {
label: 'p-1.5 text-[10px]/3 gap-1.5',
empty: 'py-4 text-xs',
loading: 'py-4',
loadingIcon: 'size-4',
item: 'p-1.5 text-xs gap-1.5',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemLeadingChip: 'size-4',
itemLeadingChipSize: 'sm',
itemTrailingIcon: 'size-4'
},
md: {
label: 'p-1.5 text-xs gap-1.5',
empty: 'py-6 text-sm',
loading: 'py-6',
loadingIcon: 'size-5',
item: 'p-1.5 text-sm gap-1.5',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemLeadingChip: 'size-5',
itemLeadingChipSize: 'md',
itemTrailingIcon: 'size-5'
},
lg: {
label: 'p-2 text-xs gap-2',
empty: 'py-7 text-sm',
loading: 'py-7',
loadingIcon: 'size-5',
item: 'p-2 text-sm gap-2',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemLeadingChip: 'size-5',
itemLeadingChipSize: 'md',
itemTrailingIcon: 'size-5'
},
xl: {
label: 'p-2 text-sm gap-2',
empty: 'py-8 text-base',
loading: 'py-8',
loadingIcon: 'size-6',
item: 'p-2 text-base gap-2',
itemLeadingIcon: 'size-6',
itemLeadingAvatarSize: 'xs',
itemLeadingChip: 'size-6',
itemLeadingChipSize: 'lg',
itemTrailingIcon: 'size-6',
itemDescription: 'text-sm'
}
},
color: {
primary: {
root: 'outline-primary/25 has-focus-visible:outline-3 has-focus-visible:ring-primary'
},
secondary: {
root: 'outline-secondary/25 has-focus-visible:outline-3 has-focus-visible:ring-secondary'
},
success: {
root: 'outline-success/25 has-focus-visible:outline-3 has-focus-visible:ring-success'
},
info: {
root: 'outline-info/25 has-focus-visible:outline-3 has-focus-visible:ring-info'
},
warning: {
root: 'outline-warning/25 has-focus-visible:outline-3 has-focus-visible:ring-warning'
},
error: {
root: 'outline-error/25 has-focus-visible:outline-3 has-focus-visible:ring-error'
},
neutral: {
root: 'outline-inverted/25 has-focus-visible:outline-3 has-focus-visible:ring-inverted'
}
},
virtualize: {
true: {
content: 'p-1 isolate'
},
false: {
content: 'divide-y divide-default'
}
},
disabled: {
true: {
root: 'opacity-75 cursor-not-allowed'
}
},
highlight: {
true: ''
}
},
compoundVariants: [
{
color: 'primary',
highlight: true,
class: {
root: 'ring ring-inset ring-primary'
}
},
{
color: 'neutral',
highlight: true,
class: {
root: 'ring ring-inset ring-inverted'
}
}
],
defaultVariants: {
color: 'primary',
size: 'md'
}
}
}
})
]
})