SelectMenu 선택
Usage
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를 사용합니다.Items 파일
items 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" 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>또한 다음 속성을 사용하여 객체 배열을 전달할 수 있습니다.
label?: string의 발음을 -label?: stringtype?: "label" | "separator" | "item"icon?: stringavatar?: AvatarPropschip?: ChipPropsdisabled?: booleanonSelect?: (e: Event) => void(-onSelect?: (e: Event) => void)class?: anyui?: { label?: ClassNameValue, separator?: ClassNameValue, item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLabel?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingIcon?: ClassNameValue }
<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 prop.기본값은 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 prop을 사용합니다.다중
multiple prop을 사용하여 여러 개의 선택을 허용하면 선택된 항목은 트리거에서 쉼표로 구분됩니다.
<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 prop 또는 v-model 디렉티브로 전달해야 합니다.placeholder 위치 표시자
placeholder prop을 사용하여 자리 표시자 텍스트를 설정합니다.
<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 입력
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 prop 을 false 로 설정하여 검색 입력을 숨길 수 있습니다.:search-input="{ autofocus: false }"를 사용하여 메뉴가 열릴 때 검색 입력이 집중되지 않도록 하고, 예를 들어, 터치 장치에서 가상 키보드를 열지 않도록 한다.Content 파일
content prop을 사용하여 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 색상
SelectMenu에 초점이 맞춰질 때 color Prop을 사용하여 링 색상을 변경합니다.
<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 prop은 초점 상태를 표시하기 위해 사용되며, 유효성 검사 오류가 발생할 때 내부적으로 사용됩니다.변형
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 prop을 사용하여 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 prop을 사용하여 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 소품을 사용하여 후행 Icon를 사용자 정의합니다. 기본값은 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>Clear : 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를 사용자 정의합니다. 기본값은 i-lucide-x입니다.
<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 prop를 사용하여 SelectMenu 내부에 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 중
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 아이콘
loading-icon prop을 사용하여 로드 아이콘을 사용자 정의합니다. 기본값은 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을 사용하여 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>examples 예제
With 아이템 타입
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 항목을 그룹 제목으로 사용할 때 배열 배열을 전달하여 레이블이 그룹과 함께 필터링되도록 합니다.With 아이콘 in items
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 슬롯을 사용하여 선택된 아바타를 표시할 수도 있습니다.With chip in items 항목 포함
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 슬롯이 선택한 칩을 표시하는 데 사용됩니다.Control 오픈 상태
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를 사용하여 다음 키를 눌러 SelectMenu를 토글할 수 있습니다.Control 검색 용어
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 항목 만들기
create-item prop을 사용하여 사용자가 미리 정의된 옵션에 없는 사용자 정의 값을 추가할 수 있습니다.
<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에서 항목을 가져오고 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와 immediate: false를 사용하여 메뉴가 열릴 때만 데이터를 가져오므로 페이지 로드 시 불필요한 API 호출을 방지합니다.ignore 필터 포함
ignore-filter prop을 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를 사용하여 지연되므로 메뉴가 열릴 때까지 요청이 수행되지 않습니다.With 필터 필드 포함
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를 사용하여 사용자가 스크롤할 때만 데이터가 로드됩니다.With full content width 전체 내용 너비
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'
}
}
}
})
As a country 선택기
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; } |
슬롯
| 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] |
Exposure (### 노출)
템플릿 참조를 통해 컴포넌트에 액세스하는 경우 다음을 사용할 수 있습니다.
| 이름 (Name) | 유형 (Type) |
|---|---|
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'
}
}
}
})
]
})