SélectionneMenu
Utilisation
Utilisez la directive v-model pour contrôler la valeur du SelectMenu 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">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" :items="items" />
</template>Select pour tirer parti du composant [Comboboxxph030https://reka-ui.com/docs/components/combobox) de Reka UI qui offre des fonctionnalités de recherche et de sélection multiple.InputMenu, mais il utilise une sélection au lieu d'une entrée avec la recherche dans le menu.Éléments
Utilisez la prop items comme tableau de chaînes, de nombres ou de booléens:
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" :items="items" class="w-48" />
</template>Vous pouvez également passer un tableau d'objets avec les propriétés suivantes:
- xx
label?: stringxx{lang="ts-type"} - xx
type?: "label" | "separator" | "item"x{lang="ts-type"}x - xx
icon?: stringx{lang="ts-type"}x Xph075xxxavatar?: AvatarPropsx{lang="ts-type"}x - x{lang="ts-type"}x{lang="ts-type"}x{lang="ts-type"}{lang="ts-type"}x{lang="ts-type"}x{lang="ts-type"}xxph0888xxxxxxph08888xxxx88xx88xxx88x8x8x8x8x8x8x8x8x8x8x8xx8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8
- xx
disabled?: booleanxx{lang="ts-type"} - xx
onSelect?: (e: Event) => voidxxx{lang="ts-type"} - xx
class?: anyxx{lang="ts-type"} - x
ui?: { label?: ClassNameValue, separator?: ClassNameValue, item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLabel?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingIcon?: ClassNameValue }x{lang="ts-type"}
<script setup lang="ts">
import type { SelectMenuItem } from '@nuxt/ui'
const items = ref<SelectMenuItem[]>([
{
label: 'Backlog'
},
{
label: 'Todo'
},
{
label: 'In Progress'
},
{
label: 'Done'
}
])
const value = ref({
label: 'Todo'
})
</script>
<template>
<USelectMenu v-model="value" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { SelectMenuItem } from '@nuxt/ui'
const items = ref<SelectMenuItem[]>([
{
label: 'Backlog'
},
{
label: 'Todo'
},
{
label: 'In Progress'
},
{
label: 'Done'
}
])
const value = ref({
label: 'Todo'
})
</script>
<template>
<USelectMenu v-model="value" :items="items" class="w-48" />
</template>Select, le SelectMenu s'attend à ce que l'objet entier soit passé à la directive v-model ou à la prop default-value par défaut.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">
const items = ref([
['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple'],
['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
])
const value = ref('Apple')
</script>
<template>
<USelectMenu v-model="value" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref([
['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple'],
['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
])
const value = ref('Apple')
</script>
<template>
<USelectMenu v-model="value" :items="items" class="w-48" />
</template>XPH153xValue Clé
Vous pouvez choisir de lier une seule propriété de l'objet plutôt que l'objet entier en utilisant la prop. value-key.
<script setup lang="ts">
import type { SelectMenuItem } from '@nuxt/ui'
const items = ref<SelectMenuItem[]>([
{
label: 'Backlog',
id: 'backlog'
},
{
label: 'Todo',
id: 'todo'
},
{
label: 'In Progress',
id: 'in_progress'
},
{
label: 'Done',
id: 'done'
}
])
const value = ref('todo')
</script>
<template>
<USelectMenu v-model="value" value-key="id" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { SelectMenuItem } from '@nuxt/ui'
const items = ref<SelectMenuItem[]>([
{
label: 'Backlog',
id: 'backlog'
},
{
label: 'Todo',
id: 'todo'
},
{
label: 'In Progress',
id: 'in_progress'
},
{
label: 'Done',
id: 'done'
}
])
const value = ref('todo')
</script>
<template>
<USelectMenu v-model="value" value-key="id" :items="items" class="w-48" />
</template>by pour comparer des objets par un champ au lieu de référence lorsque le model-value est un objet.Multiple équivalent
Utilisez la prop multiple pour permettre des sélections multiples, les éléments sélectionnés seront séparés par une virgule dans le déclencheur.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref(['Backlog', 'Todo'])
</script>
<template>
<USelectMenu v-model="value" multiple :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref(['Backlog', 'Todo'])
</script>
<template>
<USelectMenu v-model="value" multiple :items="items" class="w-48" />
</template>default-value ou à la directive v-model.Placeholder électronique
Utilisez la prop placeholder pour définir un texte d'espace réservé.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
</script>
<template>
<USelectMenu placeholder="Select status" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
</script>
<template>
<USelectMenu placeholder="Select status" :items="items" class="w-48" />
</template>Search entrée
Utilisez la prop search-input pour personnaliser ou masquer l'entrée de recherche (avec la valeur false).
Vous pouvez passer n'importe quelle propriété du composant Input pour le personnaliser.
<script setup lang="ts">
import type { SelectMenuItem } from '@nuxt/ui'
const items = ref<SelectMenuItem[]>([
{
label: 'Backlog',
icon: 'i-lucide-circle-help'
},
{
label: 'Todo',
icon: 'i-lucide-circle-plus'
},
{
label: 'In Progress',
icon: 'i-lucide-circle-arrow-up'
},
{
label: 'Done',
icon: 'i-lucide-circle-check'
}
])
const value = ref({
label: 'Backlog',
icon: 'i-lucide-circle-help'
})
</script>
<template>
<USelectMenu
v-model="value"
:search-input="{
placeholder: 'Filter...',
icon: 'i-lucide-search'
}"
:items="items"
class="w-48"
/>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { SelectMenuItem } from '@nuxt/ui'
const items = ref<SelectMenuItem[]>([
{
label: 'Backlog',
icon: 'i-lucide-circle-help'
},
{
label: 'Todo',
icon: 'i-lucide-circle-plus'
},
{
label: 'In Progress',
icon: 'i-lucide-circle-arrow-up'
},
{
label: 'Done',
icon: 'i-lucide-circle-check'
}
])
const value = ref({
label: 'Backlog',
icon: 'i-lucide-circle-help'
})
</script>
<template>
<USelectMenu
v-model="value"
:search-input="{
placeholder: 'Filter...',
icon: 'i-lucide-search'
}"
:items="items"
class="w-48"
/>
</template>search-input sur false pour masquer l'entrée de recherche.:search-input="{ autofocus: false }" pour éviter que la saisie de recherche ne soit focalisée lorsque le menu s'ouvre, par exemple pour éviter d'ouvrir le clavier virtuel sur les appareils tactiles.contenu
Use the content prop to control how the SelectMenu content is rendered, like its align or side for example.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu
v-model="value"
:content="{
align: 'center',
side: 'bottom',
sideOffset: 8
}"
:items="items"
class="w-48"
/>
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu
v-model="value"
:content="{
align: 'center',
side: 'bottom',
sideOffset: 8
}"
:items="items"
class="w-48"
/>
</template>flèche
Utilisez le prop arrow pour afficher une flèche sur le SelectMenu.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" arrow :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" arrow :items="items" class="w-48" />
</template>couleur
Utilisez le prop color pour changer la couleur de la bague lorsque le SelectMenu est focalisé.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" color="neutral" highlight :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" color="neutral" highlight :items="items" class="w-48" />
</template>highlight est utilisée ici pour afficher l'état de mise au point. Il est utilisé en interne lorsqu 'une erreur de validation se produit.Variant
Utilisez le prop variant pour modifier la variante du SelectMenu.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" color="neutral" variant="subtle" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" color="neutral" variant="subtle" :items="items" class="w-48" />
</template>Size
Utilisez la prop size pour modifier la taille du SelectMenu.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" size="xl" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" size="xl" :items="items" class="w-48" />
</template>Icône
Utilisez la prop icon pour afficher un Icon à l'intérieur du SelectMenu.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" icon="i-lucide-search" size="md" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" icon="i-lucide-search" size="md" :items="items" class="w-48" />
</template>Icône ### Trailing
Utilisez la prop trailing-icon pour personnaliser le Icon.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu
v-model="value"
trailing-icon="i-lucide-arrow-down"
size="md"
:items="items"
class="w-48"
/>
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu
v-model="value"
trailing-icon="i-lucide-arrow-down"
size="md"
:items="items"
class="w-48"
/>
</template>app.config.ts sous la touche ui.icons.chevronDown.vite.config.ts sous la touche ui.icons.chevronDown.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">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu
v-model="value"
selected-icon="i-lucide-flame"
size="md"
:items="items"
class="w-48"
/>
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu
v-model="value"
selected-icon="i-lucide-flame"
size="md"
:items="items"
class="w-48"
/>
</template>app.config.ts sous la touche ui.icons.check.vite.config.ts sous la touche ui.icons.check.Clear: badge{label="4.4+" class="align-text-top"}
Utilisez le prop clear pour afficher un bouton clair lorsqu 'une valeur est sélectionnée.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" clear :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" clear :items="items" class="w-48" />
</template>Clear Icône: badge{label="4.4+" class="align-text-top"}
Utilisez l'accessoire clear-icon pour personnaliser le bouton de nettoyage Icon.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" clear clear-icon="i-lucide-trash" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" clear clear-icon="i-lucide-trash" :items="items" class="w-48" />
</template>app.config.ts sous la touche ui.icons.close.vite.config.ts sous la touche ui.icons.close.Avatars
Utilisez la prop avatar pour afficher un Avatar dans le SelectMenu.
<script setup lang="ts">
const items = ref(['Nuxt', 'NuxtHub', 'NuxtLabs', 'Nuxt Modules', 'Nuxt Community'])
const value = ref('Nuxt')
</script>
<template>
<USelectMenu
v-model="value"
:avatar="{
src: 'https://github.com/nuxt.png',
loading: 'lazy'
}"
:items="items"
class="w-48"
/>
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Nuxt', 'NuxtHub', 'NuxtLabs', 'Nuxt Modules', 'Nuxt Community'])
const value = ref('Nuxt')
</script>
<template>
<USelectMenu
v-model="value"
:avatar="{
src: 'https://github.com/nuxt.png',
loading: 'lazy'
}"
:items="items"
class="w-48"
/>
</template>Loading
Utilisez le prop loading pour afficher une icône de chargement sur le SelectMenu.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" loading :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" loading :items="items" class="w-48" />
</template>Icône de chargement
Utilisez la prop loading-icon pour personnaliser l'icône de chargement. Par défaut, i-lucide-loader-circle.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" loading loading-icon="i-lucide-loader" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" loading loading-icon="i-lucide-loader" :items="items" class="w-48" />
</template>app.config.ts sous la touche ui.icons.loading.vite.config.ts sous la touche ui.icons.loading.Désactivé
Utilisez le prop disabled pour désactiver le SelectMenu.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
</script>
<template>
<USelectMenu disabled placeholder="Select status" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
</script>
<template>
<USelectMenu disabled placeholder="Select status" :items="items" class="w-48" />
</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 { SelectMenuItem } from '@nuxt/ui'
const items = ref<SelectMenuItem[]>([
[
{
type: 'label',
label: 'Fruits'
},
'Apple',
'Banana',
'Blueberry',
'Grapes',
'Pineapple'
],
[
{
type: 'label',
label: 'Vegetables'
},
'Aubergine',
'Broccoli',
'Carrot',
'Courgette',
'Leek'
]
])
const value = ref('Apple')
</script>
<template>
<USelectMenu v-model="value" :items="items" class="w-48" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { SelectMenuItem } from '@nuxt/ui'
const items = ref<SelectMenuItem[]>([
[
{
type: 'label',
label: 'Fruits'
},
'Apple',
'Banana',
'Blueberry',
'Grapes',
'Pineapple'
],
[
{
type: 'label',
label: 'Vegetables'
},
'Aubergine',
'Broccoli',
'Carrot',
'Courgette',
'Leek'
]
])
const value = ref('Apple')
</script>
<template>
<USelectMenu v-model="value" :items="items" class="w-48" />
</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 { SelectMenuItem } from '@nuxt/ui'
const items = ref([
{
label: 'Backlog',
value: 'backlog',
icon: 'i-lucide-circle-help'
},
{
label: 'Todo',
value: 'todo',
icon: 'i-lucide-circle-plus'
},
{
label: 'In Progress',
value: 'in_progress',
icon: 'i-lucide-circle-arrow-up'
},
{
label: 'Done',
value: 'done',
icon: 'i-lucide-circle-check'
}
] satisfies SelectMenuItem[])
const value = ref(items.value[0])
</script>
<template>
<USelectMenu v-model="value" :icon="value?.icon" :items="items" class="w-48" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectMenuItem } from '@nuxt/ui'
const items = ref([
{
label: 'Backlog',
value: 'backlog',
icon: 'i-lucide-circle-help'
},
{
label: 'Todo',
value: 'todo',
icon: 'i-lucide-circle-plus'
},
{
label: 'In Progress',
value: 'in_progress',
icon: 'i-lucide-circle-arrow-up'
},
{
label: 'Done',
value: 'done',
icon: 'i-lucide-circle-check'
}
] satisfies SelectMenuItem[])
const value = ref(items.value[0])
</script>
<template>
<USelectMenu v-model="value" :icon="value?.icon" :items="items" class="w-48" />
</template>
#leading pour afficher l'icône sélectionnée.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 { SelectMenuItem } from '@nuxt/ui'
const items = ref([
{
label: 'benjamincanac',
value: 'benjamincanac',
avatar: {
src: 'https://github.com/benjamincanac.png',
alt: 'benjamincanac',
loading: 'lazy' as const
}
},
{
label: 'HugoRCD',
value: 'HugoRCD',
avatar: {
src: 'https://github.com/HugoRCD.png',
alt: 'HugoRCD',
loading: 'lazy' as const
}
},
{
label: 'atinux',
value: 'atinux',
avatar: {
src: 'https://github.com/atinux.png',
alt: 'atinux',
loading: 'lazy' as const
}
},
{
label: 'romhml',
value: 'romhml',
avatar: {
src: 'https://github.com/romhml.png',
alt: 'romhml',
loading: 'lazy' as const
}
}
] satisfies SelectMenuItem[])
const value = ref(items.value[0])
</script>
<template>
<USelectMenu v-model="value" :avatar="value?.avatar" :items="items" class="w-48" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectMenuItem } from '@nuxt/ui'
const items = ref([
{
label: 'benjamincanac',
value: 'benjamincanac',
avatar: {
src: 'https://github.com/benjamincanac.png',
alt: 'benjamincanac',
loading: 'lazy' as const
}
},
{
label: 'HugoRCD',
value: 'HugoRCD',
avatar: {
src: 'https://github.com/HugoRCD.png',
alt: 'HugoRCD',
loading: 'lazy' as const
}
},
{
label: 'atinux',
value: 'atinux',
avatar: {
src: 'https://github.com/atinux.png',
alt: 'atinux',
loading: 'lazy' as const
}
},
{
label: 'romhml',
value: 'romhml',
avatar: {
src: 'https://github.com/romhml.png',
alt: 'romhml',
loading: 'lazy' as const
}
}
] satisfies SelectMenuItem[])
const value = ref(items.value[0])
</script>
<template>
<USelectMenu v-model="value" :avatar="value?.avatar" :items="items" class="w-48" />
</template>
#leading pour afficher l'avatar sélectionné.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 { SelectMenuItem, ChipProps } from '@nuxt/ui'
const items = ref([
{
label: 'bug',
value: 'bug',
chip: {
color: 'error'
}
},
{
label: 'feature',
value: 'feature',
chip: {
color: 'success'
}
},
{
label: 'enhancement',
value: 'enhancement',
chip: {
color: 'info'
}
}
] satisfies SelectMenuItem[])
const value = ref(items.value[0])
</script>
<template>
<USelectMenu v-model="value" :items="items" class="w-48">
<template #leading="{ modelValue, ui }">
<UChip
v-if="modelValue"
v-bind="modelValue.chip"
inset
standalone
:size="(ui.itemLeadingChipSize() as ChipProps['size'])"
:class="ui.itemLeadingChip()"
/>
</template>
</USelectMenu>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectMenuItem, ChipProps } from '@nuxt/ui'
const items = ref([
{
label: 'bug',
value: 'bug',
chip: {
color: 'error'
}
},
{
label: 'feature',
value: 'feature',
chip: {
color: 'success'
}
},
{
label: 'enhancement',
value: 'enhancement',
chip: {
color: 'info'
}
}
] satisfies SelectMenuItem[])
const value = ref(items.value[0])
</script>
<template>
<USelectMenu v-model="value" :items="items" class="w-48">
<template #leading="{ modelValue, ui }">
<UChip
v-if="modelValue"
v-bind="modelValue.chip"
inset
standalone
:size="(ui.itemLeadingChipSize() as ChipProps['size'])"
:class="ui.itemLeadingChip()"
/>
</template>
</USelectMenu>
</template>
#leading est utilisé pour afficher la puce sélectionnée.Control État ouvert
Vous pouvez contrôler l'état ouvert en utilisant la prop default-open ou la directive v-model:open.
<script setup lang="ts">
const open = ref(false)
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
defineShortcuts({
o: () => open.value = !open.value
})
</script>
<template>
<USelectMenu v-model="value" v-model:open="open" :items="items" class="w-48" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
const open = ref(false)
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
defineShortcuts({
o: () => open.value = !open.value
})
</script>
<template>
<USelectMenu v-model="value" v-model:open="open" :items="items" class="w-48" />
</template>
defineShortcuts, vous pouvez basculer le SelectMenu en appuyant sur: kbd{value="O"}.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('D')
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" v-model:search-term="searchTerm" :items="items" class="w-48" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
const searchTerm = ref('D')
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu v-model="value" v-model:search-term="searchTerm" :items="items" class="w-48" />
</template>
Avec icône tournante
Voici un exemple avec une icône tournante qui indique l'état ouvert du SelectMenu.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu
v-model="value"
:items="items"
:ui="{
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
}"
class="w-48"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>
<template>
<USelectMenu
v-model="value"
:items="items"
:ui="{
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
}"
class="w-48"
/>
</template>
With créer l'élément
Utilisez la prop create-item pour permettre aux utilisateurs d'ajouter des valeurs personnalisées qui ne sont pas dans les options prédéfinies.
<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
function onCreate(item: string) {
items.value.push(item)
value.value = item
}
</script>
<template>
<USelectMenu
v-model="value"
create-item
:items="items"
class="w-48"
@create="onCreate"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
function onCreate(item: string) {
items.value.push(item)
value.value = item
}
</script>
<template>
<USelectMenu
v-model="value"
create-item
:items="items"
class="w-48"
@create="onCreate"
/>
</template>
always pour l'afficher même lorsque des valeurs similaires existent.@create pour gérer la création de l'élément. Vous recevrez l'événement et l'élément en tant qu 'arguments.Avec les éléments récupérés
Vous pouvez récupérer des éléments à partir d'une API et les utiliser dans le SelectMenu.
<script setup lang="ts">
import type { AvatarProps } from '@nuxt/ui'
const { data: users, status, execute } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
key: 'typicode-users',
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
})
function onOpen() {
if (!users.value?.length) {
execute()
}
}
</script>
<template>
<USelectMenu
:items="users"
:loading="status === 'pending'"
icon="i-lucide-user"
placeholder="Select user"
class="w-48"
@update:open="onOpen"
>
<template #leading="{ modelValue, ui }">
<UAvatar
v-if="modelValue"
v-bind="modelValue.avatar"
:size="(ui.leadingAvatarSize() as AvatarProps['size'])"
:class="ui.leadingAvatar()"
/>
</template>
</USelectMenu>
</template>
useLazyFetch avec immediate: false pour récupérer les données uniquement lorsque le menu s'ouvre, évitant ainsi les appels d'API inutiles lors du chargement de la page.Avec filtre ignorer
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'
import type { AvatarProps } from '@nuxt/ui'
const searchTerm = ref('')
const searchTermDebounced = refDebounced(searchTerm, 200)
const { data: users, status, execute } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
key: 'select-menu-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
})
function onOpen() {
if (!users.value?.length) {
execute()
}
}
</script>
<template>
<USelectMenu
v-model:search-term="searchTerm"
:items="users"
:search-input="{
icon: 'i-lucide-search',
loading: status === 'pending'
}"
ignore-filter
icon="i-lucide-user"
placeholder="Select user"
class="w-48"
@update:open="onOpen"
>
<template #leading="{ modelValue, ui }">
<UAvatar
v-if="modelValue"
v-bind="modelValue.avatar"
:size="(ui.leadingAvatarSize() as AvatarProps['size'])"
:class="ui.leadingAvatar()"
/>
</template>
</USelectMenu>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { refDebounced } from '@vueuse/core'
import type { AvatarProps } from '@nuxt/ui'
const searchTerm = ref('')
const searchTermDebounced = refDebounced(searchTerm, 200)
const { data: users, status, execute } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
key: 'select-menu-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
})
function onOpen() {
if (!users.value?.length) {
execute()
}
}
</script>
<template>
<USelectMenu
v-model:search-term="searchTerm"
:items="users"
:search-input="{
icon: 'i-lucide-search',
loading: status === 'pending'
}"
ignore-filter
icon="i-lucide-user"
placeholder="Select user"
class="w-48"
@update:open="onOpen"
>
<template #leading="{ modelValue, ui }">
<UAvatar
v-if="modelValue"
v-bind="modelValue.avatar"
:size="(ui.leadingAvatarSize() as AvatarProps['size'])"
:class="ui.leadingAvatar()"
/>
</template>
</USelectMenu>
</template>
refDebounced pour déboulonner les appels de l'API. La récupération est différée avec immediate: false afin qu 'aucune requête ne soit effectuée avant l'ouverture du menu.Avec les champs de filtre
Utilisez le prop filter-fields avec un tableau de champs pour filtrer.
<script setup lang="ts">
import type { AvatarProps } from '@nuxt/ui'
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
})
function onOpen() {
if (!users.value?.length) {
execute()
}
}
</script>
<template>
<USelectMenu
:items="users"
:loading="status === 'pending'"
:filter-fields="['label', 'email']"
icon="i-lucide-user"
placeholder="Select user"
class="w-80"
@update:open="onOpen"
>
<template #leading="{ modelValue, ui }">
<UAvatar
v-if="modelValue"
v-bind="modelValue.avatar"
:size="(ui.leadingAvatarSize() as AvatarProps['size'])"
:class="ui.leadingAvatar()"
/>
</template>
<template #item-label="{ item }">
{{ item.label }}
<span class="text-muted">
{{ item.email }}
</span>
</template>
</USelectMenu>
</template>
useLazyFetch avec immediate: false pour récupérer les données uniquement lorsque le menu s'ouvre, évitant ainsi les appels d'API inutiles lors du chargement de la page.Avec virtualisation: badge{label="4.1+" class="align-text-top"}
Utilisez la prop virtualize pour activer la virtualisation de grandes listes en tant que booléen ou objet avec des options comme { estimateSize: 32, overscan: 12 }.
<script setup lang="ts">
import type { SelectMenuItem } from '@nuxt/ui'
const items: SelectMenuItem[] = Array(1000)
.fill(0)
.map((_, i) => ({
label: `item-${i}`,
value: i
}))
</script>
<template>
<USelectMenu virtualize :items="items" class="w-48" />
</template>Avec défilement infini: badge{label="4.4+" class="align-text-top"}
Vous pouvez utiliser le composable useInfiniteScroll pour charger plus de données au fur et à mesure que l'utilisateur fait défiler.
<script setup lang="ts">
import { useInfiniteScroll } from '@vueuse/core'
type User = {
firstName: string
}
type UserResponse = {
users: User[]
total: number
skip: number
limit: number
}
const skip = ref(0)
const { data, status, execute } = await useLazyFetch(
'https://dummyjson.com/users?limit=10&select=firstName',
{
key: 'select-menu-users-infinite-scroll',
params: { skip },
transform: (data?: UserResponse) => {
return data?.users.map((user) => user.firstName)
},
immediate: false
}
)
const users = ref<string[]>([])
watch(data, () => {
users.value = [...users.value, ...(data.value || [])]
})
function onOpen() {
if (!users.value?.length) {
execute()
}
}
const selectMenu = useTemplateRef('selectMenu')
onMounted(() => {
useInfiniteScroll(
() => selectMenu.value?.viewportRef,
() => {
skip.value += 10
},
{
canLoadMore: () => {
return status.value !== 'pending'
}
}
)
})
</script>
<template>
<USelectMenu ref="selectMenu" placeholder="Select user" :items="users" @update:open="onOpen" />
</template><script setup lang="ts">
import { ref, watch, onMounted, useTemplateRef } from 'vue'
import { useInfiniteScroll } from '@vueuse/core'
type User = {
firstName: string
}
type UserResponse = {
users: User[]
total: number
skip: number
limit: number
}
const skip = ref(0)
const { data, status, execute } = await useLazyFetch(
'https://dummyjson.com/users?limit=10&select=firstName',
{
key: 'select-menu-users-infinite-scroll',
params: { skip },
transform: (data?: UserResponse) => {
return data?.users.map((user) => user.firstName)
},
immediate: false
}
)
const users = ref<string[]>([])
watch(data, () => {
users.value = [...users.value, ...(data.value || [])]
})
function onOpen() {
if (!users.value?.length) {
execute()
}
}
const selectMenu = useTemplateRef('selectMenu')
onMounted(() => {
useInfiniteScroll(
() => selectMenu.value?.viewportRef,
() => {
skip.value += 10
},
{
canLoadMore: () => {
return status.value !== 'pending'
}
}
)
})
</script>
<template>
<USelectMenu ref="selectMenu" placeholder="Select user" :items="users" @update:open="onOpen" />
</template>useLazyFetch avec immediate: false, de sorte que les données ne sont chargées que lorsque l'utilisateur fait défiler.Avec pleine largeur de contenu
Vous pouvez étendre le contenu à toute la largeur de ses éléments en ajoutant la classe min-w-fit sur l'emplacement ui.content.
<script setup lang="ts">
const { data: users, 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
})
function onOpen() {
if (!users.value?.length) {
execute()
}
}
</script>
<template>
<USelectMenu
:items="users"
icon="i-lucide-user"
placeholder="Select user"
:ui="{ content: 'min-w-fit' }"
class="w-48"
@update:open="onOpen"
>
<template #item-label="{ item }">
{{ item.label }}
<span class="text-muted">
{{ item.email }}
</span>
</template>
</USelectMenu>
</template>
app.config.ts:export default defineAppConfig({
ui: {
selectMenu: {
slots: {
content: 'min-w-fit'
}
}
}
})
As sélecteur de pays
Vous pouvez utiliser le SelectMenu comme sélecteur de pays avec un chargement paresseux. Les pays ne sont récupérés que lorsque le menu est ouvert pour la première fois.
<script setup lang="ts">
const { data: countries, status, execute } = await useLazyFetch<{
name: string
code: string
emoji: string
}[]>('/api/countries.json', {
key: 'api-countries',
immediate: false
})
function onOpen() {
if (!countries.value?.length) {
execute()
}
}
</script>
<template>
<USelectMenu
:items="countries"
:loading="status === 'pending'"
label-key="name"
:search-input="{ icon: 'i-lucide-search' }"
placeholder="Select country"
class="w-48"
@update:open="onOpen"
>
<template #leading="{ modelValue, ui }">
<span v-if="modelValue" class="size-5 text-center">
{{ modelValue?.emoji }}
</span>
<UIcon v-else name="i-lucide-earth" :class="ui.leadingIcon()" />
</template>
<template #item-leading="{ item }">
<span class="size-5 text-center">
{{ item.emoji }}
</span>
</template>
</USelectMenu>
</template>
useLazyFetch avec immediate: false pour charger uniquement les pays lorsque le menu est ouvert pour la première fois.API
Props équipement
| Prop | Default | Type |
|---|---|---|
id | string | |
placeholder | stringThe placeholder text when the select is empty. | |
searchInput | true | boolean | Omit<InputProps<AcceptableValue, ModelModifiers>, "modelValue" | "defaultValue"> Whether to display the search input or not.
Can be an object to pass additional props to the input.
|
color | 'primary' | "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral" |
variant | 'outline' | "outline" | "soft" | "subtle" | "ghost" | "none" |
size | 'md' | "sm" | "md" | "xs" | "lg" | "xl" |
required | boolean | |
trailingIcon | appConfig.ui.icons.chevronDown | anyThe icon displayed to open the menu. |
selectedIcon | appConfig.ui.icons.check | anyThe icon displayed when an item is selected. |
clear | false | C & false | C & true | C & Partial<Omit<ButtonProps, LinkPropsKeys>>Display a clear button to reset the model value. Can be an object to pass additional props to the Button. |
clearIcon | appConfig.ui.icons.close | anyThe icon displayed in the clear button. |
content | { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' } | Omit<ComboboxContentProps, "asChild" | "as" | "forceMount"> & Partial<EmitsToProps<DismissableLayerEmits>>The content of the menu. |
arrow | false | boolean | Omit<ComboboxArrowProps, "asChild" | "as"> Display an arrow alongside the menu.
|
portal | true | string | false | true | HTMLElementRender the menu in a portal. |
virtualize | false | boolean | { overscan?: number ; estimateSize?: number | ((index: number) => number) | undefined; } | undefinedEnable virtualization for large lists. Note: when enabled, all groups are flattened into a single list due to a limitation of Reka UI (https://github.com/unovue/reka-ui/issues/1885). |
valueKey | undefined | VKWhen |
labelKey | 'label' | keyof Extract<NestedItem<T>, object> & string | DotPathKeys<Extract<NestedItem<T>, object>>When |
descriptionKey | 'description' | keyof Extract<NestedItem<T>, object> & string | DotPathKeys<Extract<NestedItem<T>, object>>When |
items | T | |
defaultValue | _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod> | IsClearUsed<M, C>The value of the SelectMenu when initially rendered. Use when you do not need to control the state of the SelectMenu. | |
modelValue | _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod> | IsClearUsed<M, C>The controlled value of the SelectMenu. Can be binded-with with | |
modelModifiers | Mod | |
multiple | MWhether multiple options can be selected or not. | |
highlight | boolean Highlight the ring color like a focus state. | |
fixed | boolean Keep the mobile text size on all breakpoints. | |
createItem | false | boolean | "always" | { position?: "top" | "bottom" ; when?: "always" | "empty" | undefined; } | undefinedDetermines if custom user input that does not exist in options can be added. |
filterFields | [labelKey] | string[]Fields to filter items by. |
ignoreFilter | false | boolean When |
autofocus | boolean | |
autofocusDelay | 0 | number |
disabled | boolean When | |
open | boolean The controlled open state of the Combobox. Can be binded with | |
defaultOpen | boolean The open state of the combobox when it is initially rendered. | |
name | stringThe name of the field. Submitted with its owning form as part of a name/value pair. | |
resetSearchTermOnBlur | true | boolean Whether to reset the searchTerm when the Combobox input blurred |
resetSearchTermOnSelect | true | boolean Whether to reset the searchTerm when the Combobox value is selected |
resetModelValueOnClear | true | boolean When |
highlightOnHover | boolean When | |
by | string | (a: T, b: T): booleanUse this to compare objects by a particular field, or pass your own comparison function for complete control over how objects are compared. | |
icon | anyDisplay an icon based on the | |
avatar | AvatarPropsDisplay an avatar on the left side. | |
leading | boolean When | |
leadingIcon | anyDisplay an icon on the left side. | |
trailing | boolean When | |
loading | boolean When | |
loadingIcon | appConfig.ui.icons.loading | anyThe icon when the |
searchTerm | '' | string |
ui | { base?: SlotClass; leading?: SlotClass; leadingIcon?: SlotClass; leadingAvatar?: SlotClass; leadingAvatarSize?: SlotClass; trailing?: SlotClass; trailingIcon?: SlotClass; value?: SlotClass; placeholder?: SlotClass; arrow?: SlotClass; content?: SlotClass; viewport?: SlotClass; group?: SlotClass; empty?: SlotClass; label?: SlotClass; separator?: SlotClass; item?: SlotClass; itemLeadingIcon?: SlotClass; itemLeadingAvatar?: SlotClass; itemLeadingAvatarSize?: SlotClass; itemLeadingChip?: SlotClass; itemLeadingChipSize?: SlotClass; itemTrailing?: SlotClass; itemTrailingIcon?: SlotClass; itemWrapper?: SlotClass; itemLabel?: SlotClass; itemDescription?: SlotClass; input?: SlotClass; focusScope?: SlotClass; trailingClear?: SlotClass; } |
Slots
| Slot | Type |
|---|---|
leading | { modelValue: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod> | IsClearUsed<M, C>; open: boolean; ui: object; } |
default | { modelValue: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod> | IsClearUsed<M, C>; open: boolean; ui: object; } |
trailing | { modelValue: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod> | IsClearUsed<M, C>; open: boolean; ui: object; } |
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; } |
content-top | {} |
content-bottom | {} |
create-item-label | { item: string; } |
Emis
| Event | Type |
|---|---|
update:open | [value: boolean] |
change | [event: Event] |
blur | [event: FocusEvent] |
focus | [event: FocusEvent] |
create | [item: string] |
clear | [] |
highlight | [payload: { ref: HTMLElement; value: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod> | IsClearUsed<M, C>; } | undefined] |
update:modelValue | [value: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod> | IsClearUsed<M, C>] |
update:searchTerm | [value: string] |
Exposer
Lorsque vous accédez au composant via une référence de modèle, vous pouvez utiliser les éléments suivants:
| nom | type |
|---|---|
triggerRefx{lang="ts-type"} | Ref<HTMLButtonElement | null>x{lang="ts-type"} |
viewportRefx{lang="ts-type"} | Ref<HTMLDivElement | null>x{lang="ts-type"} |
thème
export default defineAppConfig({
ui: {
selectMenu: {
slots: {
base: [
'relative group rounded-md inline-flex items-center disabled:cursor-not-allowed disabled:opacity-75',
'transition-colors'
],
leading: 'absolute inset-y-0 start-0 flex items-center',
leadingIcon: 'shrink-0 text-dimmed',
leadingAvatar: 'shrink-0',
leadingAvatarSize: '',
trailing: 'absolute inset-y-0 end-0 flex items-center',
trailingIcon: 'shrink-0 text-dimmed',
value: 'truncate pointer-events-none',
placeholder: 'truncate text-dimmed',
arrow: 'fill-bg stroke-default',
content: [
'max-h-[min(15rem,var(--reka-select-content-available-height,15rem))] w-(--reka-select-trigger-width) bg-default shadow-lg rounded-md ring ring-default overflow-hidden origin-(--reka-select-content-transform-origin) pointer-events-auto flex flex-col',
'max-h-[min(15rem,var(--reka-combobox-content-available-height,15rem))] origin-(--reka-combobox-content-transform-origin) w-(--reka-combobox-trigger-width)'
],
viewport: 'relative scroll-py-1 overflow-y-auto flex-1',
group: 'p-1 isolate',
empty: 'text-center text-muted',
label: 'font-semibold text-highlighted',
separator: '-mx-1 my-1 h-px bg-border',
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: '',
itemTrailing: 'ms-auto inline-flex gap-1.5 items-center',
itemTrailingIcon: 'shrink-0',
itemWrapper: 'flex-1 flex flex-col min-w-0',
itemLabel: 'truncate',
itemDescription: 'truncate text-muted',
input: 'border-b border-default',
focusScope: 'flex flex-col min-h-0',
trailingClear: 'p-0'
},
variants: {
fieldGroup: {
horizontal: 'not-only:first:rounded-e-none not-only:last:rounded-s-none not-last:not-first:rounded-none focus-visible:z-[1]',
vertical: 'not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none focus-visible:z-[1]'
},
size: {
xs: {
base: 'px-2 py-1 text-sm/4 gap-1',
leading: 'ps-2',
trailing: 'pe-2',
leadingIcon: 'size-4',
leadingAvatarSize: '3xs',
trailingIcon: 'size-4',
label: 'p-1 text-[10px]/3 gap-1',
item: 'p-1 text-xs gap-1',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemLeadingChip: 'size-4',
itemLeadingChipSize: 'sm',
itemTrailingIcon: 'size-4',
empty: 'p-2 text-xs'
},
sm: {
base: 'px-2.5 py-1.5 text-sm/4 gap-1.5',
leading: 'ps-2.5',
trailing: 'pe-2.5',
leadingIcon: 'size-4',
leadingAvatarSize: '3xs',
trailingIcon: 'size-4',
label: 'p-1.5 text-[10px]/3 gap-1.5',
item: 'p-1.5 text-xs gap-1.5',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemLeadingChip: 'size-4',
itemLeadingChipSize: 'sm',
itemTrailingIcon: 'size-4',
empty: 'p-2.5 text-xs'
},
md: {
base: 'px-2.5 py-1.5 text-base/5 gap-1.5',
leading: 'ps-2.5',
trailing: 'pe-2.5',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs',
trailingIcon: 'size-5',
label: 'p-1.5 text-xs gap-1.5',
item: 'p-1.5 text-sm gap-1.5',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemLeadingChip: 'size-5',
itemLeadingChipSize: 'md',
itemTrailingIcon: 'size-5',
empty: 'p-2.5 text-sm'
},
lg: {
base: 'px-3 py-2 text-base/5 gap-2',
leading: 'ps-3',
trailing: 'pe-3',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs',
trailingIcon: 'size-5',
label: 'p-2 text-xs gap-2',
item: 'p-2 text-sm gap-2',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemLeadingChip: 'size-5',
itemLeadingChipSize: 'md',
itemTrailingIcon: 'size-5',
empty: 'p-3 text-sm'
},
xl: {
base: 'px-3 py-2 text-base gap-2',
leading: 'ps-3',
trailing: 'pe-3',
leadingIcon: 'size-6',
leadingAvatarSize: 'xs',
trailingIcon: 'size-6',
label: 'p-2 text-sm gap-2',
item: 'p-2 text-base gap-2',
itemLeadingIcon: 'size-6',
itemLeadingAvatarSize: 'xs',
itemLeadingChip: 'size-6',
itemLeadingChipSize: 'lg',
itemTrailingIcon: 'size-6',
empty: 'p-3 text-base'
}
},
variant: {
outline: 'text-highlighted bg-default ring ring-inset ring-accented hover:bg-elevated disabled:bg-default',
soft: 'text-highlighted bg-elevated/50 hover:bg-elevated focus:bg-elevated disabled:bg-elevated/50',
subtle: 'text-highlighted bg-elevated ring ring-inset ring-accented hover:bg-accented/75 disabled:bg-elevated',
ghost: 'text-highlighted bg-transparent hover:bg-elevated focus:bg-elevated disabled:bg-transparent dark:disabled:bg-transparent',
none: 'text-highlighted bg-transparent focus:outline-none'
},
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
leading: {
true: ''
},
trailing: {
true: ''
},
loading: {
true: ''
},
highlight: {
true: ''
},
fixed: {
false: ''
},
type: {
file: 'file:me-1.5 file:font-medium file:text-muted file:outline-none'
},
position: {
popper: {
content: 'data-[state=open]:animate-[scale-in_100ms_var(--ease-out)] data-[state=closed]:animate-[scale-out_100ms_var(--ease-out)]'
},
'item-aligned': {
content: ''
}
},
multiple: {
true: ''
},
virtualize: {
true: {
viewport: 'p-1 isolate'
},
false: {
viewport: 'divide-y divide-default'
}
}
},
compoundVariants: [
{
color: 'primary',
variant: [
'outline',
'subtle'
],
class: 'outline-primary/25 focus-visible:outline-3 focus-visible:ring-primary'
},
{
color: 'primary',
variant: [
'soft',
'ghost'
],
class: 'outline-primary/25 focus-visible:outline-3'
},
{
color: 'primary',
highlight: true,
class: 'ring ring-inset ring-primary'
},
{
color: 'neutral',
variant: [
'outline',
'subtle'
],
class: 'outline-inverted/25 focus-visible:outline-3 focus-visible:ring-inverted'
},
{
color: 'neutral',
variant: [
'soft',
'ghost'
],
class: 'outline-inverted/25 focus-visible:outline-3'
},
{
color: 'neutral',
highlight: true,
class: 'ring ring-inset ring-inverted'
},
{
leading: true,
size: 'xs',
class: 'ps-7'
},
{
leading: true,
size: 'sm',
class: 'ps-8'
},
{
leading: true,
size: 'md',
class: 'ps-9'
},
{
leading: true,
size: 'lg',
class: 'ps-10'
},
{
leading: true,
size: 'xl',
class: 'ps-11'
},
{
trailing: true,
size: 'xs',
class: 'pe-7'
},
{
trailing: true,
size: 'sm',
class: 'pe-8'
},
{
trailing: true,
size: 'md',
class: 'pe-9'
},
{
trailing: true,
size: 'lg',
class: 'pe-10'
},
{
trailing: true,
size: 'xl',
class: 'pe-11'
},
{
loading: true,
leading: true,
class: {
leadingIcon: 'animate-spin'
}
},
{
loading: true,
leading: false,
trailing: true,
class: {
trailingIcon: 'animate-spin'
}
},
{
fixed: false,
size: 'xs',
class: 'md:text-xs'
},
{
fixed: false,
size: 'sm',
class: 'md:text-xs'
},
{
fixed: false,
size: 'md',
class: 'md:text-sm'
},
{
fixed: false,
size: 'lg',
class: 'md:text-sm'
}
],
defaultVariants: {
size: 'md',
color: 'primary',
variant: 'outline',
position: 'popper'
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
selectMenu: {
slots: {
base: [
'relative group rounded-md inline-flex items-center disabled:cursor-not-allowed disabled:opacity-75',
'transition-colors'
],
leading: 'absolute inset-y-0 start-0 flex items-center',
leadingIcon: 'shrink-0 text-dimmed',
leadingAvatar: 'shrink-0',
leadingAvatarSize: '',
trailing: 'absolute inset-y-0 end-0 flex items-center',
trailingIcon: 'shrink-0 text-dimmed',
value: 'truncate pointer-events-none',
placeholder: 'truncate text-dimmed',
arrow: 'fill-bg stroke-default',
content: [
'max-h-[min(15rem,var(--reka-select-content-available-height,15rem))] w-(--reka-select-trigger-width) bg-default shadow-lg rounded-md ring ring-default overflow-hidden origin-(--reka-select-content-transform-origin) pointer-events-auto flex flex-col',
'max-h-[min(15rem,var(--reka-combobox-content-available-height,15rem))] origin-(--reka-combobox-content-transform-origin) w-(--reka-combobox-trigger-width)'
],
viewport: 'relative scroll-py-1 overflow-y-auto flex-1',
group: 'p-1 isolate',
empty: 'text-center text-muted',
label: 'font-semibold text-highlighted',
separator: '-mx-1 my-1 h-px bg-border',
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: '',
itemTrailing: 'ms-auto inline-flex gap-1.5 items-center',
itemTrailingIcon: 'shrink-0',
itemWrapper: 'flex-1 flex flex-col min-w-0',
itemLabel: 'truncate',
itemDescription: 'truncate text-muted',
input: 'border-b border-default',
focusScope: 'flex flex-col min-h-0',
trailingClear: 'p-0'
},
variants: {
fieldGroup: {
horizontal: 'not-only:first:rounded-e-none not-only:last:rounded-s-none not-last:not-first:rounded-none focus-visible:z-[1]',
vertical: 'not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none focus-visible:z-[1]'
},
size: {
xs: {
base: 'px-2 py-1 text-sm/4 gap-1',
leading: 'ps-2',
trailing: 'pe-2',
leadingIcon: 'size-4',
leadingAvatarSize: '3xs',
trailingIcon: 'size-4',
label: 'p-1 text-[10px]/3 gap-1',
item: 'p-1 text-xs gap-1',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemLeadingChip: 'size-4',
itemLeadingChipSize: 'sm',
itemTrailingIcon: 'size-4',
empty: 'p-2 text-xs'
},
sm: {
base: 'px-2.5 py-1.5 text-sm/4 gap-1.5',
leading: 'ps-2.5',
trailing: 'pe-2.5',
leadingIcon: 'size-4',
leadingAvatarSize: '3xs',
trailingIcon: 'size-4',
label: 'p-1.5 text-[10px]/3 gap-1.5',
item: 'p-1.5 text-xs gap-1.5',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemLeadingChip: 'size-4',
itemLeadingChipSize: 'sm',
itemTrailingIcon: 'size-4',
empty: 'p-2.5 text-xs'
},
md: {
base: 'px-2.5 py-1.5 text-base/5 gap-1.5',
leading: 'ps-2.5',
trailing: 'pe-2.5',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs',
trailingIcon: 'size-5',
label: 'p-1.5 text-xs gap-1.5',
item: 'p-1.5 text-sm gap-1.5',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemLeadingChip: 'size-5',
itemLeadingChipSize: 'md',
itemTrailingIcon: 'size-5',
empty: 'p-2.5 text-sm'
},
lg: {
base: 'px-3 py-2 text-base/5 gap-2',
leading: 'ps-3',
trailing: 'pe-3',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs',
trailingIcon: 'size-5',
label: 'p-2 text-xs gap-2',
item: 'p-2 text-sm gap-2',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemLeadingChip: 'size-5',
itemLeadingChipSize: 'md',
itemTrailingIcon: 'size-5',
empty: 'p-3 text-sm'
},
xl: {
base: 'px-3 py-2 text-base gap-2',
leading: 'ps-3',
trailing: 'pe-3',
leadingIcon: 'size-6',
leadingAvatarSize: 'xs',
trailingIcon: 'size-6',
label: 'p-2 text-sm gap-2',
item: 'p-2 text-base gap-2',
itemLeadingIcon: 'size-6',
itemLeadingAvatarSize: 'xs',
itemLeadingChip: 'size-6',
itemLeadingChipSize: 'lg',
itemTrailingIcon: 'size-6',
empty: 'p-3 text-base'
}
},
variant: {
outline: 'text-highlighted bg-default ring ring-inset ring-accented hover:bg-elevated disabled:bg-default',
soft: 'text-highlighted bg-elevated/50 hover:bg-elevated focus:bg-elevated disabled:bg-elevated/50',
subtle: 'text-highlighted bg-elevated ring ring-inset ring-accented hover:bg-accented/75 disabled:bg-elevated',
ghost: 'text-highlighted bg-transparent hover:bg-elevated focus:bg-elevated disabled:bg-transparent dark:disabled:bg-transparent',
none: 'text-highlighted bg-transparent focus:outline-none'
},
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
leading: {
true: ''
},
trailing: {
true: ''
},
loading: {
true: ''
},
highlight: {
true: ''
},
fixed: {
false: ''
},
type: {
file: 'file:me-1.5 file:font-medium file:text-muted file:outline-none'
},
position: {
popper: {
content: 'data-[state=open]:animate-[scale-in_100ms_var(--ease-out)] data-[state=closed]:animate-[scale-out_100ms_var(--ease-out)]'
},
'item-aligned': {
content: ''
}
},
multiple: {
true: ''
},
virtualize: {
true: {
viewport: 'p-1 isolate'
},
false: {
viewport: 'divide-y divide-default'
}
}
},
compoundVariants: [
{
color: 'primary',
variant: [
'outline',
'subtle'
],
class: 'outline-primary/25 focus-visible:outline-3 focus-visible:ring-primary'
},
{
color: 'primary',
variant: [
'soft',
'ghost'
],
class: 'outline-primary/25 focus-visible:outline-3'
},
{
color: 'primary',
highlight: true,
class: 'ring ring-inset ring-primary'
},
{
color: 'neutral',
variant: [
'outline',
'subtle'
],
class: 'outline-inverted/25 focus-visible:outline-3 focus-visible:ring-inverted'
},
{
color: 'neutral',
variant: [
'soft',
'ghost'
],
class: 'outline-inverted/25 focus-visible:outline-3'
},
{
color: 'neutral',
highlight: true,
class: 'ring ring-inset ring-inverted'
},
{
leading: true,
size: 'xs',
class: 'ps-7'
},
{
leading: true,
size: 'sm',
class: 'ps-8'
},
{
leading: true,
size: 'md',
class: 'ps-9'
},
{
leading: true,
size: 'lg',
class: 'ps-10'
},
{
leading: true,
size: 'xl',
class: 'ps-11'
},
{
trailing: true,
size: 'xs',
class: 'pe-7'
},
{
trailing: true,
size: 'sm',
class: 'pe-8'
},
{
trailing: true,
size: 'md',
class: 'pe-9'
},
{
trailing: true,
size: 'lg',
class: 'pe-10'
},
{
trailing: true,
size: 'xl',
class: 'pe-11'
},
{
loading: true,
leading: true,
class: {
leadingIcon: 'animate-spin'
}
},
{
loading: true,
leading: false,
trailing: true,
class: {
trailingIcon: 'animate-spin'
}
},
{
fixed: false,
size: 'xs',
class: 'md:text-xs'
},
{
fixed: false,
size: 'sm',
class: 'md:text-xs'
},
{
fixed: false,
size: 'md',
class: 'md:text-sm'
},
{
fixed: false,
size: 'lg',
class: 'md:text-sm'
}
],
defaultVariants: {
size: 'md',
color: 'primary',
variant: 'outline',
position: 'popper'
}
}
}
})
]
})