Select

オプションのリストから選択するselect要素。

使用法

Selectプロパティの値を制御するにはv-modelディレクティブを使用して、状態を制御する必要がない場合はdefault-valueプロパティの初期値を設定します。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect 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>
  <USelect v-model="value" :items="items" />
</template>

アイテム

itemsプロパティを文字列、数値、ブール値の配列として使用します。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect 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>
  <USelect v-model="value" :items="items" class="w-48" />
</template>

次のプロパティを持つオブジェクトの配列を渡すこともできます:

  • label?: string
  • value?: string
  • type?: "label" | "separator" | "item"
  • icon?: string
  • avatar?: AvatarProps
  • chip?: ChipProps
  • disabled?: boolean
  • class?: any
  • ui?: { 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 { SelectItem } from '@nuxt/ui'

const items = ref<SelectItem[]>([
  {
    label: 'Backlog',
    value: 'backlog'
  },
  {
    label: 'Todo',
    value: 'todo'
  },
  {
    label: 'In Progress',
    value: 'in_progress'
  },
  {
    label: 'Done',
    value: 'done'
  }
])
const value = ref('backlog')
</script>

<template>
  <USelect v-model="value" :items="items" class="w-48" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectItem } from '@nuxt/ui'

const items = ref<SelectItem[]>([
  {
    label: 'Backlog',
    value: 'backlog'
  },
  {
    label: 'Todo',
    value: 'todo'
  },
  {
    label: 'In Progress',
    value: 'in_progress'
  },
  {
    label: 'Done',
    value: 'done'
  }
])
const value = ref('backlog')
</script>

<template>
  <USelect v-model="value" :items="items" class="w-48" />
</template>
オブジェクトを使用する場合は、v-modelディレクティブまたはdefault-valueプロパティでオブジェクトのvalueプロパティを参照する必要があります。

itemsプロパティに配列の配列を渡して、項目の分離グループを表示することもできます。

<script setup lang="ts">
const items = ref([
  ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple'],
  ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
])
const value = ref('Apple')
</script>

<template>
  <USelect 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>
  <USelect v-model="value" :items="items" class="w-48" />
</template>

Valueキー

値を設定するために使用するプロパティは、value-keyプロパティを使用して変更できます。デフォルトはvalueです。

<script setup lang="ts">
import type { SelectItem } from '@nuxt/ui'

const items = ref<SelectItem[]>([
  {
    label: 'Backlog',
    id: 'backlog'
  },
  {
    label: 'Todo',
    id: 'todo'
  },
  {
    label: 'In Progress',
    id: 'in_progress'
  },
  {
    label: 'Done',
    id: 'done'
  }
])
const value = ref('backlog')
</script>

<template>
  <USelect v-model="value" value-key="id" :items="items" class="w-48" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectItem } from '@nuxt/ui'

const items = ref<SelectItem[]>([
  {
    label: 'Backlog',
    id: 'backlog'
  },
  {
    label: 'Todo',
    id: 'todo'
  },
  {
    label: 'In Progress',
    id: 'in_progress'
  },
  {
    label: 'Done',
    id: 'done'
  }
])
const value = ref('backlog')
</script>

<template>
  <USelect v-model="value" value-key="id" :items="items" class="w-48" />
</template>

複数

複数選択を許可するにはmultipleプロパティを使用します。選択された項目はトリガー内でコンマで区切られます。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref(['Backlog', 'Todo'])
</script>

<template>
  <USelect 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>
  <USelect v-model="value" multiple :items="items" class="w-48" />
</template>
default-valueプロパティまたはv-modelディレクティブに配列を渡してください。

Placeholder

placeholderプロパティを使用してプレースホルダーテキストを設定します。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
</script>

<template>
  <USelect 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>
  <USelect placeholder="Select status" :items="items" class="w-48" />
</template>

コンテンツ

contentプロパティを使用して、alignやsideなど、Selectコンテンツのレンダリング方法を制御します。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect
    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>
  <USelect
    v-model="value"
    :content="{
      align: 'center',
      side: 'bottom',
      sideOffset: 8
    }"
    :items="items"
    class="w-48"
  />
</template>
これらのオプションはcontent.positionがpopper(デフォルト)の場合にのみ適用されます。

ポジションbadge{label="4.7+" class="align-text-top"}

content.positionプロパティを使用して、Selectコンテンツがトリガーに対してどのように配置されるかを制御します。デフォルトはpopperで、他のポップオーバーと同様にコンテンツを配置します。コンテンツを選択したアイテムに合わせるには、item-alignedに設定します(macOSネイティブメニューと同様)。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Todo')
</script>

<template>
  <USelect
    v-model="value"
    :content="{
      position: 'item-aligned'
    }"
    :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('Todo')
</script>

<template>
  <USelect
    v-model="value"
    :content="{
      position: 'item-aligned'
    }"
    :items="items"
    class="w-48"
  />
</template>

Arrow

arrowプロパティを使用して、Selectに矢印を表示します。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect 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>
  <USelect v-model="value" arrow :items="items" class="w-48" />
</template>

Color

Selectフォーカス時にリングの色を変更するには、colorプロパティを使用します。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect 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>
  <USelect v-model="value" color="neutral" highlight :items="items" class="w-48" />
</template>
highlightプロパティはフォーカスの状態を表示するために使用されます。バリデーションエラーが発生したときに内部で使用されます。

Variant

variantプロパティを使用してSelectのバリアントを変更します。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect 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>
  <USelect v-model="value" color="neutral" variant="subtle" :items="items" class="w-48" />
</template>

サイズ

sizeプロパティを使用してSelectのサイズを変更します。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect 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>
  <USelect v-model="value" size="xl" :items="items" class="w-48" />
</template>

Icon

iconプロパティを使用して、Select内にIconを表示します。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect 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>
  <USelect v-model="value" icon="i-lucide-search" size="md" :items="items" class="w-48" />
</template>

Trailingアイコン

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>
  <USelect
    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>
  <USelect
    v-model="value"
    trailing-icon="i-lucide-arrow-down"
    size="md"
    :items="items"
    class="w-48"
  />
</template>
このアイコンはapp.config.tsのui.icons.chevronDownキーでグローバルにカスタマイズできます。
このアイコンはvite.config.tsのui.icons.chevronDownキーでグローバルにカスタマイズできます。

選択したアイコン

selected-iconプロパティを使用して、アイテムが選択されたときにアイコンをカスタマイズします。デフォルトはi-lucide-checkです。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect 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>
  <USelect v-model="value" selected-icon="i-lucide-flame" size="md" :items="items" class="w-48" />
</template>
このアイコンはapp.config.tsのui.icons.checkキーでグローバルにカスタマイズできます。
このアイコンはvite.config.tsのui.icons.checkキーでグローバルにカスタマイズできます。

アバター

avatarプロパティを使用して、Select内にAvatarを表示します。

<script setup lang="ts">
const items = ref(['Nuxt', 'NuxtHub', 'NuxtLabs', 'Nuxt Modules', 'Nuxt Community'])
const value = ref('Nuxt')
</script>

<template>
  <USelect
    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>
  <USelect
    v-model="value"
    :avatar="{
      src: 'https://github.com/nuxt.png',
      loading: 'lazy'
    }"
    :items="items"
    class="w-48"
  />
</template>

Loading

loadingプロパティを使用して、Selectにロードアイコンを表示します。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect 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>
  <USelect v-model="value" loading :items="items" class="w-48" />
</template>

Loading Icon

loading-iconプロパティを使用してロードアイコンをカスタマイズします。デフォルトはi-lucide-loader-circleです。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect 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>
  <USelect v-model="value" loading loading-icon="i-lucide-loader" :items="items" class="w-48" />
</template>
このアイコンはapp.config.tsのui.icons.loadingキーでグローバルにカスタマイズできます。
このアイコンはvite.config.tsのui.icons.loadingキーでグローバルにカスタマイズできます。

無効

disabledプロパティを使用してSelectを無効にします。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
</script>

<template>
  <USelect 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>
  <USelect disabled placeholder="Select status" :items="items" class="w-48" />
</template>

サンプル

With items type

typeプロパティをseparatorとともに使用してアイテム間の区切り文字を表示したり、labelを使用してラベルを表示したりできます。

<script setup lang="ts">
import type { SelectItem } from '@nuxt/ui'

const items = ref<SelectItem[]>([
  {
    type: 'label',
    label: 'Fruits'
  },
  'Apple',
  'Banana',
  'Blueberry',
  'Grapes',
  'Pineapple',
  {
    type: 'separator'
  },
  {
    type: 'label',
    label: 'Vegetables'
  },
  'Aubergine',
  'Broccoli',
  'Carrot',
  'Courgette',
  'Leek'
])
const value = ref('Apple')
</script>

<template>
  <USelect v-model="value" :items="items" class="w-48" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectItem } from '@nuxt/ui'

const items = ref<SelectItem[]>([
  {
    type: 'label',
    label: 'Fruits'
  },
  'Apple',
  'Banana',
  'Blueberry',
  'Grapes',
  'Pineapple',
  {
    type: 'separator'
  },
  {
    type: 'label',
    label: 'Vegetables'
  },
  'Aubergine',
  'Broccoli',
  'Carrot',
  'Courgette',
  'Leek'
])
const value = ref('Apple')
</script>

<template>
  <USelect v-model="value" :items="items" class="w-48" />
</template>

アイテムにアイコン付き

iconプロパティを使用して、アイテム内にIconを表示できます。

<script setup lang="ts">
import type { SelectItem } 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 SelectItem[])

const value = ref(items.value[0]?.value)

const icon = computed(() => items.value.find(item => item.value === value.value)?.icon)
</script>

<template>
  <USelect v-model="value" :items="items" value-key="value" :icon="icon" class="w-48" />
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { SelectItem } 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 SelectItem[])

const value = ref(items.value[0]?.value)

const icon = computed(() => items.value.find(item => item.value === value.value)?.icon)
</script>

<template>
  <USelect v-model="value" :items="items" value-key="value" :icon="icon" class="w-48" />
</template>
この例では、選択されたアイテムのvalueプロパティからアイコンが計算されます。
#leadingスロットを使用して、選択したアイコンを表示することもできます。

アイテムにアバター付き

avatarプロパティを使用して、アイテム内にAvatarを表示できます。

<script setup lang="ts">
import type { SelectItem } 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 SelectItem[])

const value = ref(items.value[0]?.value)

const avatar = computed(() => items.value.find(item => item.value === value.value)?.avatar)
</script>

<template>
  <USelect v-model="value" :items="items" value-key="value" :avatar="avatar" class="w-48" />
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { SelectItem } 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 SelectItem[])

const value = ref(items.value[0]?.value)

const avatar = computed(() => items.value.find(item => item.value === value.value)?.avatar)
</script>

<template>
  <USelect v-model="value" :items="items" value-key="value" :avatar="avatar" class="w-48" />
</template>
この例では、選択されたアイテムのvalueプロパティからアバターが計算されます。
#leadingスロットを使用して、選択したアバターを表示することもできます。

Withチップinアイテム

chipプロパティを使用して、アイテム内にChipを表示できます。

<script setup lang="ts">
import type { SelectItem, 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 SelectItem[])

const value = ref(items.value[0]?.value)

function getChip(value: string) {
  return items.value.find(item => item.value === value)?.chip
}
</script>

<template>
  <USelect v-model="value" :items="items" value-key="value" class="w-48">
    <template #leading="{ modelValue, ui }">
      <UChip
        v-if="modelValue"
        v-bind="getChip(modelValue)"
        inset
        standalone
        :size="(ui.itemLeadingChipSize() as ChipProps['size'])"
        :class="ui.itemLeadingChip()"
      />
    </template>
  </USelect>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectItem, 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 SelectItem[])

const value = ref(items.value[0]?.value)

function getChip(value: string) {
  return items.value.find(item => item.value === value)?.chip
}
</script>

<template>
  <USelect v-model="value" :items="items" value-key="value" class="w-48">
    <template #leading="{ modelValue, ui }">
      <UChip
        v-if="modelValue"
        v-bind="getChip(modelValue)"
        inset
        standalone
        :size="(ui.itemLeadingChipSize() as ChipProps['size'])"
        :class="ui.itemLeadingChip()"
      />
    </template>
  </USelect>
</template>
この例では、#leadingスロットを使用して選択したチップを表示します。

Controlオープンステート

オープン状態はdefault-openプロパティまたは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>
  <USelect 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>
  <USelect v-model="value" v-model:open="open" :items="items" class="w-48" />
</template>
この例では、defineShortcutsを活用して、kbd{value="O"}を押して選択を切り替えることができます。

回転アイコン付き

選択の開いた状態を示す回転アイコンの例を以下に示します。

<script setup lang="ts">
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const value = ref('Backlog')
</script>

<template>
  <USelect
    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>
  <USelect
    v-model="value"
    :items="items"
    :ui="{
      trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
    }"
    class="w-48"
  />
</template>

フェッチされたアイテム

APIから項目を取得し、Selectで使用できます。

<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 getUserAvatar(value: string) {
  return users.value?.find(user => user.value === value)?.avatar || {}
}

function onOpen() {
  if (!users.value?.length) {
    execute()
  }
}
</script>

<template>
  <USelect
    :items="users"
    :loading="status === 'pending'"
    icon="i-lucide-user"
    placeholder="Select user"
    value-key="value"
    class="w-48"
    @update:open="onOpen"
  >
    <template #leading="{ modelValue, ui }">
      <UAvatar
        v-if="modelValue"
        v-bind="getUserAvatar(modelValue)"
        :size="(ui.leadingAvatarSize() as AvatarProps['size'])"
        :class="ui.leadingAvatar()"
      />
    </template>
  </USelect>
</template>
この例では、useLazyFetchとimmediate: falseを使用して、メニューが開いたときにのみデータをフェッチします。

無限スクロール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-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 select = useTemplateRef('select')

onMounted(() => {
  useInfiniteScroll(
    () => select.value?.viewportRef,
    () => {
      skip.value += 10
    },
    {
      canLoadMore: () => {
        return status.value !== 'pending'
      }
    }
  )
})
</script>

<template>
  <USelect ref="select" 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-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 select = useTemplateRef('select')

onMounted(() => {
  useInfiniteScroll(
    () => select.value?.viewportRef,
    () => {
      skip.value += 10
    },
    {
      canLoadMore: () => {
        return status.value !== 'pending'
      }
    }
  )
})
</script>

<template>
  <USelect ref="select" placeholder="Select user" :items="users" @update:open="onOpen" />
</template>
この例ではuseLazyFetchとimmediate: falseを使用しています。

全コンテンツ幅

ui.contentスロットにmin-w-fitクラスを追加することで、コンテンツをアイテムの幅いっぱいに展開できます。

<script setup lang="ts">
const value = ref<string>()

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>
  <USelect
    v-model="value"
    :items="users"
    placeholder="Select user"
    value-key="value"
    :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>
  </USelect>
</template>
<script setup lang="ts">
import { ref } from 'vue'

const value = ref<string>()

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>
  <USelect
    v-model="value"
    :items="users"
    placeholder="Select user"
    value-key="value"
    :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>
  </USelect>
</template>
app.config.tsでコンテンツ幅をグローバルに変更することもできます:
export default defineAppConfig({
  ui: {
    select: {
      slots: {
        content: 'min-w-fit'
      }
    }
  }
})

API

Props

Prop Default Type
id
string
placeholder
string

The placeholder text when the select is empty.

color
'primary'
"primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral"
variant
'outline'
"outline" | "soft" | "subtle" | "ghost" | "none"
size
'md'
"sm" | "md" | "xs" | "lg" | "xl"
trailingIcon
appConfig.ui.icons.chevronDown
any

The icon displayed to open the menu.

selectedIcon
appConfig.ui.icons.check
any

The icon displayed when an item is selected.

content
{ side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }
SelectContentProps & Partial<EmitsToProps<SelectContentImplEmits>>

The content of the menu.

arrow
false
boolean | SelectArrowProps

Display an arrow alongside the menu. { rounded: true }

portal
true
string | false | true | HTMLElement

Render the menu in a portal.

valueKey
'value'
VK

When items is an array of objects, select the field to use as the value.

labelKey
'label'
keyof Extract<NestedItem<T>, object> & string | DotPathKeys<Extract<NestedItem<T>, object>>

When items is an array of objects, select the field to use as the label.

descriptionKey
'description'
keyof Extract<NestedItem<T>, object> & string | DotPathKeys<Extract<NestedItem<T>, object>>

When items is an array of objects, select the field to use as the description.

items
T
defaultValue
_Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod>

The value of the Select when initially rendered. Use when you do not need to control the state of the Select.

modelValue
_Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod>

The controlled value of the Select. Can be bind as v-model.

modelModifiers
Mod
multiple
M

Whether 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.

autofocus
boolean
autofocusDelay
0
number
disabled
boolean

When true, prevents the user from interacting with Select

open
boolean

The controlled open state of the Select. Can be bind as v-model:open.

defaultOpen
boolean

The open state of the select when it is initially rendered. Use when you do not need to control its open state.

nullableValue
string

The value of the hidden native select option when the model value is nullish.

autocomplete
string

Native html input autocomplete attribute.

name
string

The name of the field. Submitted with its owning form as part of a name/value pair.

required
boolean

When true, indicates that the user must set the value before the owning form can be submitted.

icon
any

Display an icon based on the leading and trailing props.

avatar
AvatarProps

Display an avatar on the left side.

leading
boolean

When true, the icon will be displayed on the left side.

leadingIcon
any

Display an icon on the left side.

trailing
boolean

When true, the icon will be displayed on the right side.

loading
boolean

When true, the loading icon will be displayed.

loadingIcon
appConfig.ui.icons.loading
any

The icon when the loading prop is true.

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; }
このコンポーネントはすべてのネイティブ<button> HTML属性もサポートします。

スロット

Slot Type
leading
{ modelValue: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod>; open: boolean; ui: object; }
default
{ modelValue: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod>; open: boolean; ui: object; }
trailing
{ modelValue: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod>; open: boolean; ui: object; }
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
{}

Emits

Event Type
update:modelValue
[value: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod>]
update:open
[value: boolean]
change
[event: Event]
blur
[event: FocusEvent]
focus
[event: FocusEvent]

Expose

テンプレート参照を介してコンポーネントにアクセスする場合、以下を使用できます:

名前タイプ
triggerRefRef<HTMLButtonElement | null>
viewportRefRef<HTMLDivElement | null>

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    select: {
      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',
        viewport: 'relative divide-y divide-default 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'
      },
      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: ''
        }
      },
      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'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        select: {
          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',
            viewport: 'relative divide-y divide-default 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'
          },
          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: ''
            }
          },
          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'
          }
        }
      }
    })
  ]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitHub.

Changelog

No recent changes