用法
使用v-model指令控制SelectMenu的值,或使用default-value prop在不需要控制其状态时设置初始值。
<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>InputMenu,但它使用的是Select(选择)而不是Input(输入),并在菜单内进行搜索。项目
将items属性用作字符串、数字或布尔值的数组:
<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>您也可以传递具有下列属性的物件数组:
060 x的字符串 066 x年061月063日062月067日064日065日066 x年066月067日064日065月066日 073 x年07月07日星期一 080x-081x-080x 087 x年082月084日083日088 x年085 x年086 x年087 x年087 x年087 x月087 x日087 x年087 x月087 x日087 x年087 x月087 x日087 x年0877 x月087 x日087 x年087 x月087 x月087 x日087 x月087 x月087 x日087 x月087 x日087 x月087 x日 090 {lang="ts-type"}的字符串 094 x年12月15日 097 x年12月15日星期一 100 x个字符
<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>您也可以将数组的数组传递给items prop,以显示分开的项目群组。
<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>值键
您可以选择使用value-key属性来系结物件的单一属性,而非整个物件。预设值为undefined。
<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>model-value是对象时,使用by属性按字段而不是按引用来比较对象。多重
使用multiple道具可进行多项选择,所选项目将在触发器中以逗号分隔。
<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属性或v-model指令。占位符
使用placeholder属性设置占位符文本。
<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-input prop自定义或隐藏搜索输入(使用false值)。
您可以从Input组件传递任何属性来对其进行自定义。
<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属性设置为false以隐藏搜索输入。:search-input="{ autofocus: false }"可防止在菜单打开时聚焦搜索输入,例如避免在触摸设备上打开虚拟键盘。内容
使用content属性来控制SelectMenu内容的呈现方式,例如align或side。
<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>Arrow
使用arrow prop在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>Color
使用color道具来改变选择菜单聚焦时的环颜色。
<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属性来显示焦点状态。当发生验证错误时,在内部使用它。Variant
使用variant prop更改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
使用size属性更改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>Icon
使用icon道具在SelectMenu中显示Icon。
<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>拖尾图标
使用trailing-icon prop将尾随的Icon.xml自定义为i-lucide-chevron-down。
<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>选定图标
使用selected-icon道具自定义选中项目时的图标。将其转换为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>清除:badge{label="4.4+" class="align-text-top"}
使用clear prop在选择值时显示清除按钮。
<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>清除图标:badge{label="4.4+" class="align-text-top"}
使用clear-icon道具自定义清除按钮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>Avatar
使用avatar道具在选择菜单中显示Avatar。
<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 prop在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>加载图标
使用loading-icon道具自定义加载图标. 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>禁用
使用disabled prop禁用选择菜单。
<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>示例
带项目类型
可以将type属性与separator一起使用,以显示项之间的分隔符,或将label一起使用,以显示标签。
<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项目作为组标题时,传递一个数组数组,以便在搜索时将标签与其组一起过滤掉。项目中带有图标
可以使用icon属性在项目中显示Icon。
<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插槽来显示选定的图标。物品中有头像
您可以使用avatar属性在项目中显示Avatar。
<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插槽来显示选定的头像。带芯片的物品
您可以使用chip属性在项目中显示Chip。
<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插槽用于显示所选筹码。控制打开状态
您可以使用default-open prop或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,您可以通过按:kbd{value="O"}切换选择菜单。控件搜索词
使用v-model:search-term指令控制搜索词。
<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>
带旋转图标
下面是一个带有旋转图标的示例,该图标指示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>
带创建项
使用create-item属性可以让用户添加预定义选项中没有的自定义值。
<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,即使存在类似的值也会显示。使用获取的项目
您可以从API获取项目并在选择菜单中使用它们。
<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和immediate: false,仅在菜单打开时获取数据,避免了页面加载时不必要的API调用。带忽略过滤器
将ignore-filter属性设置为true以禁用内部搜索并使用您自己的搜索逻辑。
<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对API调用进行反跳。immediate: false延迟提取,因此在菜单打开之前不会发出请求。带过滤器字段
使用filter-fields prop和一个字段数组来过滤. [labelKey]。
<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和immediate: false,仅在菜单打开时获取数据,避免了页面加载时不必要的API调用。虚拟化:badge{label="4.1+" class="align-text-top"}
使用virtualize prop将大型列表虚拟化为布尔值或带有{ 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>无限滚动:badge{label="4.4+" class="align-text-top"}
您可以使用useInfiniteScroll组合文件在用户滚动时加载更多数据。
<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和immediate: false,因此数据仅在用户滚动时加载。具有完整内容宽度
您可以通过在ui.content插槽上添加min-w-fit类来将内容扩展到其项目的全宽。
<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'
}
}
}
})
作为国家选择器
您可以使用SelectMenu作为国家/地区选择器,并进行延迟加载。只有在首次打开菜单时才会提取国家/地区。
<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和immediate: false,仅在菜单首次打开时加载国家/地区。API
Props
| 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; } |
Emits
| 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] |
曝光
通过模板引用访问组件时,可以使用以下命令:
| 名称 | 类型 |
|---|---|
triggerRef | Ref<HTMLButtonElement | null> |
viewportRef | Ref<HTMLDivElement | null> |
Theme
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'
}
}
}
})
]
})