リアルタイムの提案を含むオートコンプリート入力。

使用法

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

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

<template>
  <UInputMenu 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>
  <UInputMenu v-model="value" :items="items" />
</template>
これをInput上で使用すると、オートコンプリート機能を提供するReka UIのComboboxコンポーネントを利用できます。
このコンポーネントはSelectMenuに似ていますが、Selectの代わりにInputを使用しています。

アイテム

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

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

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

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

  • label?: string
  • type?: "label" | "separator" | "item"
  • icon?: string
  • avatar?: AvatarProps
  • chip?: ChipProps
  • disabled?: boolean
  • onSelect?: (e: Event) => void
  • class?: any
  • ui?: { tagsItem?: ClassNameValue, tagsItemText?: ClassNameValue, tagsItemDelete?: ClassNameValue, tagsItemDeleteIcon?: ClassNameValue, label?: ClassNameValue, separator?: ClassNameValue, item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLabel?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingIcon?: ClassNameValue }
<script setup lang="ts">
import type { InputMenuItem } from '@nuxt/ui'

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

<template>
  <UInputMenu v-model="value" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { InputMenuItem } from '@nuxt/ui'

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

<template>
  <UInputMenu v-model="value" :items="items" />
</template>

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

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

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

Valueキー

value-key propを使用することで、オブジェクト全体ではなく、オブジェクトの単一プロパティをバインドすることができます。デフォルトはundefinedです。

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

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

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

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

<template>
  <UInputMenu v-model="value" value-key="id" :items="items" />
</template>
model-valueがオブジェクトの場合、参照の代わりにフィールドでオブジェクトを比較するには、byプロパティを使用します。

複数

multipleプロパティを使用して複数選択を許可し、選択された項目がタグとして表示されます。

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

<template>
  <UInputMenu v-model="value" multiple :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'

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

<template>
  <UInputMenu v-model="value" multiple :items="items" />
</template>
default-valueプロパティまたはv-modelディレクティブに配列を渡してください。

Deleteアイコン

multipleでは、delete-iconプロパティを使用して、タグ内の削除Iconをカスタマイズします。デフォルトはi-lucide-xです。

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

<template>
  <UInputMenu v-model="value" multiple delete-icon="i-lucide-trash" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'

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

<template>
  <UInputMenu v-model="value" multiple delete-icon="i-lucide-trash" :items="items" />
</template>
このアイコンはapp.config.tsのui.icons.closeキーでグローバルにカスタマイズできます。
このアイコンはvite.config.tsのui.icons.closeキーでグローバルにカスタマイズできます。

Placeholder

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

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

<template>
  <UInputMenu placeholder="Select status" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'

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

<template>
  <UInputMenu placeholder="Select status" :items="items" />
</template>

モードbadge{label="4.8+" class="align-text-top"}

modeプロパティをautocompleteに設定して、InputMenuを提案付きの自由形式テキスト入力に変換します。modelValueは選択された項目の代わりに入力テキストstringになります。

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

<template>
  <UInputMenu
    v-model="value"
    mode="autocomplete"
    :items="items"
    :trailing-icon="false"
    :content="{ hideWhenEmpty: true }"
    placeholder="Type a status..."
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'

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

<template>
  <UInputMenu
    v-model="value"
    mode="autocomplete"
    :items="items"
    :trailing-icon="false"
    :content="{ hideWhenEmpty: true }"
    placeholder="Type a status..."
  />
</template>
modeがautocompleteの場合、multiple、by、resetSearchTermOnSelect、resetModelValueOnClearは適用されません。
一致する提案がない場合、content.hideWhenEmptyプロパティを使用してメニューを非表示にします。

コンテンツ

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

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

<template>
  <UInputMenu
    v-model="value"
    :content="{
      align: 'center',
      side: 'bottom',
      sideOffset: 8
    }"
    :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>
  <UInputMenu
    v-model="value"
    :content="{
      align: 'center',
      side: 'bottom',
      sideOffset: 8
    }"
    :items="items"
  />
</template>

Arrow

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

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

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

Color

colorプロパティを使用して、InputMenuがフォーカスされたときにリングの色を変更します。

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

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

Variant

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

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

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

サイズ

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

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

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

Icon

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

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

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

クリアbadge{label="4.4+" class="align-text-top"}

値が選択されたときにクリアボタンを表示するには、clearプロパティを使用します。

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

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

アバター

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

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

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

読み込み中

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

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

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

Loadingアイコン

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

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

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

無効

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

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

<template>
  <UInputMenu disabled placeholder="Select status" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'

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

<template>
  <UInputMenu disabled placeholder="Select status" :items="items" />
</template>

サンプル

With items type

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

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

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

<template>
  <UInputMenu v-model="value" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { InputMenuItem } from '@nuxt/ui'

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

<template>
  <UInputMenu v-model="value" :items="items" />
</template>
labelアイテムをグループ見出しとして使用する場合は、検索時にラベルがグループとともにフィルタリングされるように配列の配列を渡します。

アイテム内のアイコン付き

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

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

const value = ref(items.value[0])
</script>

<template>
  <UInputMenu v-model="value" :icon="value?.icon" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { InputMenuItem } 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 InputMenuItem[])

const value = ref(items.value[0])
</script>

<template>
  <UInputMenu v-model="value" :icon="value?.icon" :items="items" />
</template>
#leadingスロットを使用して、選択したアイコンを表示することもできます。

アイテムにアバター付き

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

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

const value = ref(items.value[0])
</script>

<template>
  <UInputMenu v-model="value" :avatar="value?.avatar" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { InputMenuItem } 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 InputMenuItem[])

const value = ref(items.value[0])
</script>

<template>
  <UInputMenu v-model="value" :avatar="value?.avatar" :items="items" />
</template>
#leadingスロットを使用して、選択したアバターを表示することもできます。

Withチップinアイテム

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

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

const value = ref(items.value[0])
</script>

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

const value = ref(items.value[0])
</script>

<template>
  <UInputMenu v-model="value" :items="items">
    <template #leading="{ modelValue, ui }">
      <UChip
        v-if="modelValue"
        v-bind="modelValue.chip"
        inset
        standalone
        :size="(ui.itemLeadingChipSize() as ChipProps['size'])"
        :class="ui.itemLeadingChip()"
      />
    </template>
  </UInputMenu>
</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>
  <UInputMenu v-model="value" v-model:open="open" :items="items" />
</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>
  <UInputMenu v-model="value" v-model:open="open" :items="items" />
</template>
この例では、defineShortcutsを活用して、kbd{value="O"}を押してInputMenuを切り替えることができます。

フォーカス時のオープン状態を制御

open-on-focusまたはopen-on-clickプロパティを使用して、入力にフォーカスまたはクリックしたときにメニューを開くことができます。

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

<template>
  <UInputMenu
    v-model="selected"
    :items="items"
    open-on-focus
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'

const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
const selected = ref('Backlog')
</script>

<template>
  <UInputMenu
    v-model="selected"
    :items="items"
    open-on-focus
  />
</template>

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>
  <UInputMenu v-model="value" v-model:search-term="searchTerm" :items="items" />
</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>
  <UInputMenu v-model="value" v-model:search-term="searchTerm" :items="items" />
</template>

回転アイコン付き

InputMenuの開いている状態を示す回転アイコンの例を示します。

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

<template>
  <UInputMenu
    v-model="value"
    :items="items"
    :ui="{
      trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
    }"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'

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

<template>
  <UInputMenu
    v-model="value"
    :items="items"
    :ui="{
      trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
    }"
  />
</template>

With create item

create-itemプロパティを使用して、ユーザーが定義済みオプションにないカスタム値を追加できるようにします。

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

function onCreate(item: string) {
  items.value.push(item)

  value.value = item
}
</script>

<template>
  <UInputMenu
    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>
  <UInputMenu
    v-model="value"
    create-item
    :items="items"
    class="w-48"
    @create="onCreate"
  />
</template>
createオプションは、デフォルトで一致するものが見つからない場合を表示します。alwaysに設定して、類似した値が存在する場合でも表示します。
@createイベントを使用して、アイテムの作成を処理します。イベントとアイテムを引数として受け取ります。

フェッチされたアイテム

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

<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>
  <UInputMenu
    :items="users"
    :loading="status === 'pending'"
    icon="i-lucide-user"
    placeholder="Select user"
    @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>
  </UInputMenu>
</template>
この例では、useLazyFetchとimmediate: falseを使用して、メニューが開いたときにのみデータをフェッチします。

無視フィルタ付き

内部検索を無効にし、独自の検索ロジックを使用するには、ignore-filterプロパティをtrueに設定します。

<script setup lang="ts">
import { refDebounced } from '@vueuse/core'
import type { AvatarProps } from '@nuxt/ui'

const searchTerm = ref('')
const searchTermDebounced = refDebounced(searchTerm, 200)

const { data: users, status, execute } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
  key: 'input-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>
  <UInputMenu
    v-model:search-term="searchTerm"
    :items="users"
    :loading="status === 'pending'"
    ignore-filter
    icon="i-lucide-user"
    placeholder="Select user"
    @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>
  </UInputMenu>
</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: 'input-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>
  <UInputMenu
    v-model:search-term="searchTerm"
    :items="users"
    :loading="status === 'pending'"
    ignore-filter
    icon="i-lucide-user"
    placeholder="Select user"
    @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>
  </UInputMenu>
</template>
この例では、refDebouncedを使用してAPI呼び出しをデバウンスします。フェッチはimmediate: falseで延期されるため、メニューが開くまでリクエストは行われません。

フィルターフィールド付き

filter-fieldsプロパティをフィルターするフィールドの配列とともに使用します。デフォルトは[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>
  <UInputMenu
    :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>
  </UInputMenu>
</template>
この例では、useLazyFetchとimmediate: falseを使用して、メニューが開いたときにのみデータをフェッチします。

仮想化badge{label="4.1+" class="align-text-top"}

virtualizeプロパティを使用して、大きなリストをブール値または{ estimateSize: 32, overscan: 12 }のようなオプションを持つオブジェクトとして仮想化できます。

有効にすると、Reka UIの制限により、すべてのグループが1つのリストにフラット化されます。
<script setup lang="ts">
import type { InputMenuItem } from '@nuxt/ui'

const items: InputMenuItem[] = Array(1000)
  .fill(0)
  .map((_, i) => ({
    label: `item-${i}`,
    value: i
  }))
</script>

<template>
  <UInputMenu 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: 'input-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 inputMenu = useTemplateRef('inputMenu')

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

<template>
  <UInputMenu ref="inputMenu" 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: 'input-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 inputMenu = useTemplateRef('inputMenu')

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

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

全内容幅

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

<script setup lang="ts">
const { data: users, execute } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
  key: 'typicode-users-email',
  transform: (data: { id: number, name: string, email: string }[]) => {
    return data?.map(user => ({
      label: user.name,
      email: user.email,
      value: String(user.id),
      avatar: { src: `https://i.pravatar.cc/120?img=${user.id}`, loading: 'lazy' as const }
    }))
  },
  immediate: false
})

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

<template>
  <UInputMenu
    :items="users"
    icon="i-lucide-user"
    placeholder="Select user"
    :ui="{ content: 'min-w-fit' }"
    @update:open="onOpen"
  >
    <template #item-label="{ item }">
      {{ item.label }}

      <span class="text-muted">
        {{ item.email }}
      </span>
    </template>
  </UInputMenu>
</template>
app.config.tsでコンテンツ幅をグローバルに変更することもできます。
export default defineAppConfig({
  ui: {
    inputMenu: {
      slots: {
        content: 'min-w-fit'
      }
    }
  }
})

カントリーピッカーとして

InputMenuは遅延読み込みで国別ピッカーとして使用できます。国はメニューが最初に開かれたときにのみ取得されます。

<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>
  <UInputMenu
    :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>
  </UInputMenu>
</template>
この例では、useLazyFetchとimmediate: falseを使用して、メニューを最初に開いたときにのみ国をロードします。

API

Props

Prop Default Type
as
'div'
any

The element or component this component should render as.

id
string
type
'text'
"number" | "search" | "color" | "button" | "checkbox" | "date" | "datetime-local" | "email" | "file" | "hidden" | "image" | "month" | "password" | "radio" | "range" | "reset" | "submit" | "tel" | "text" | "time" | "url" | "week" | string
placeholder
string

The placeholder text when the input is empty.

color
'primary'
"primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral"
variant
'outline'
"soft" | "outline" | "subtle" | "ghost" | "none"
size
'md'
"sm" | "md" | "xs" | "lg" | "xl"
required
boolean
autofocus
boolean
autofocusDelay
0
number
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.

deleteIcon
appConfig.ui.icons.close
any

The icon displayed to delete a tag. Works only when multiple is true.

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
any

The icon displayed in the clear button.

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

The content of the menu.

arrow
false
boolean | ComboboxArrowProps

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

portal
true
string | false | true | HTMLElement

Render the menu in a portal.

virtualize
false
boolean | { overscan?: number ; estimateSize?: number | ((index: number) => number) | undefined; } | undefined

Enable 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
VK

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

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> | IsClearUsed<M, C>

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

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

The controlled value of the InputMenu. Can be binded-with with 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.

mode
'combobox'
"autocomplete" | "combobox"

The behavior of the InputMenu.

  • combobox: select one (or many) items from a list of suggestions.
  • autocomplete: free-form text input with optional suggestions. The modelValue becomes the input text (string) instead of a selected item.
createItem
false
boolean | "always" | { position?: "bottom" | "top" ; when?: "empty" | "always" | undefined; } | undefined

Determines 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 true, disable the default filters, useful for custom filtering (useAsyncData, useFetch, etc.).

disabled
boolean

When true, prevents the user from interacting with listbox

open
boolean

The controlled open state of the Combobox. Can be binded with v-model:open.

defaultOpen
boolean

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

name
string

The 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 true the modelValue will be reset to null (or [] if multiple)

highlightOnHover
boolean

When true, hover over item will trigger highlight

openOnClick
`false`
boolean

Whether to open the combobox when the input is clicked

openOnFocus
`false`
boolean

Whether to open the combobox when the input is focused

by
string | (a: T, b: T): boolean

Use this to compare objects by a particular field, or pass your own comparison function for complete control over how objects are compared.

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.

list
string
readonly
false | true | "true" | "false"
autocomplete
string | "on" | "off"
searchTerm
''
string
ui
{ root?: SlotClass; base?: SlotClass; leading?: SlotClass; leadingIcon?: SlotClass; leadingAvatar?: SlotClass; leadingAvatarSize?: SlotClass; trailing?: SlotClass; trailingIcon?: SlotClass; trailingClear?: 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; tagsItem?: SlotClass; tagsItemText?: SlotClass; tagsItemDelete?: SlotClass; tagsItemDeleteIcon?: SlotClass; tagsInput?: SlotClass; }
このコンポーネントはすべてのネイティブ<input> HTML属性もサポートします。

スロット

Slot Type
leading
{ 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; }
tags-item-text
{ item: NestedItem<T>; index: number; }
tags-item-delete
{ 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]
remove-tag
[item: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod> | IsClearUsed<M, C>]
update:modelValue
[value: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod> | IsClearUsed<M, C>]
update:searchTerm
[value: string]

Expose

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

名前タイプ
inputRefRef<HTMLInputElement | null>
viewportRefRef<HTMLDivElement | null>

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    inputMenu: {
      slots: {
        root: 'relative inline-flex items-center',
        base: [
          'rounded-md',
          'transition-colors'
        ],
        leading: 'absolute inset-y-0 start-0 flex items-center',
        leadingIcon: 'shrink-0 text-dimmed',
        leadingAvatar: 'shrink-0',
        leadingAvatarSize: '',
        trailing: 'group absolute inset-y-0 end-0 flex items-center disabled:cursor-not-allowed disabled:opacity-75 focus:outline-none',
        trailingIcon: 'shrink-0 text-dimmed',
        trailingClear: 'p-0',
        arrow: 'fill-bg stroke-default',
        content: 'max-h-[min(15rem,var(--reka-combobox-content-available-height,15rem))] w-(--reka-combobox-trigger-width) bg-default shadow-lg rounded-md ring ring-default overflow-hidden data-[state=open]:animate-[scale-in_100ms_var(--ease-out)] data-[state=closed]:animate-[scale-out_100ms_var(--ease-out)] origin-(--reka-combobox-content-transform-origin) pointer-events-auto flex flex-col',
        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 gap-1.5 p-1.5 text-sm 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',
        tagsItem: 'px-1.5 py-0.5 rounded-sm font-medium inline-flex items-center gap-0.5 ring ring-inset ring-accented bg-elevated text-default data-disabled:cursor-not-allowed data-disabled:opacity-75',
        tagsItemText: 'truncate',
        tagsItemDelete: [
          'inline-flex items-center rounded-xs text-dimmed hover:text-default hover:bg-accented/75 disabled:pointer-events-none',
          'transition-colors'
        ],
        tagsItemDeleteIcon: 'shrink-0',
        tagsInput: 'flex-1 border-0 bg-transparent placeholder:text-dimmed focus:outline-none disabled:cursor-not-allowed disabled:opacity-75'
      },
      variants: {
        fieldGroup: {
          horizontal: {
            root: 'group has-focus-visible:z-[1]'
          },
          vertical: {
            root: 'group has-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',
            tagsItem: 'text-[10px]/3',
            tagsItemDeleteIcon: 'size-3',
            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',
            tagsItem: 'text-[10px]/3',
            tagsItemDeleteIcon: 'size-3',
            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',
            tagsItem: 'text-xs',
            tagsItemDeleteIcon: 'size-3.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',
            tagsItem: 'text-xs',
            tagsItemDeleteIcon: 'size-3.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',
            tagsItem: 'text-sm',
            tagsItemDeleteIcon: 'size-4',
            empty: 'p-3 text-base'
          }
        },
        variant: {
          outline: 'text-highlighted bg-default ring ring-inset ring-accented',
          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',
          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'
        },
        virtualize: {
          true: {
            viewport: 'p-1 isolate'
          },
          false: {
            viewport: 'divide-y divide-default'
          }
        },
        multiple: {
          true: {
            root: 'flex-wrap'
          },
          false: {
            base: 'w-full border-0 placeholder:text-dimmed disabled:cursor-not-allowed disabled:opacity-75'
          }
        }
      },
      compoundVariants: [
        {
          multiple: false,
          fieldGroup: 'horizontal',
          class: {
            base: 'group-not-only:group-first:rounded-e-none group-not-only:group-last:rounded-s-none group-not-last:group-not-first:rounded-none'
          }
        },
        {
          multiple: false,
          fieldGroup: 'vertical',
          class: {
            base: 'group-not-only:group-first:rounded-b-none group-not-only:group-last:rounded-t-none group-not-last:group-not-first:rounded-none'
          }
        },
        {
          multiple: true,
          fieldGroup: 'horizontal',
          class: {
            base: 'not-only:first:rounded-e-none not-only:last:rounded-s-none not-last:not-first:rounded-none focus-visible:z-[1]'
          }
        },
        {
          multiple: true,
          fieldGroup: 'vertical',
          class: {
            base: 'not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none focus-visible:z-[1]'
          }
        },
        {
          variant: 'soft',
          multiple: true,
          class: 'has-focus:bg-elevated has-focus-visible:outline-3'
        },
        {
          variant: 'ghost',
          multiple: true,
          class: 'has-focus:bg-elevated has-focus-visible:outline-3'
        },
        {
          color: 'primary',
          multiple: true,
          variant: [
            'outline',
            'subtle'
          ],
          class: 'has-focus-visible:outline-3 has-focus-visible:ring-primary'
        },
        {
          color: 'neutral',
          multiple: true,
          variant: [
            'outline',
            'subtle'
          ],
          class: 'has-focus-visible:outline-3 has-focus-visible:ring-inverted'
        },
        {
          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'
      }
    }
  }
})
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: {
        inputMenu: {
          slots: {
            root: 'relative inline-flex items-center',
            base: [
              'rounded-md',
              'transition-colors'
            ],
            leading: 'absolute inset-y-0 start-0 flex items-center',
            leadingIcon: 'shrink-0 text-dimmed',
            leadingAvatar: 'shrink-0',
            leadingAvatarSize: '',
            trailing: 'group absolute inset-y-0 end-0 flex items-center disabled:cursor-not-allowed disabled:opacity-75 focus:outline-none',
            trailingIcon: 'shrink-0 text-dimmed',
            trailingClear: 'p-0',
            arrow: 'fill-bg stroke-default',
            content: 'max-h-[min(15rem,var(--reka-combobox-content-available-height,15rem))] w-(--reka-combobox-trigger-width) bg-default shadow-lg rounded-md ring ring-default overflow-hidden data-[state=open]:animate-[scale-in_100ms_var(--ease-out)] data-[state=closed]:animate-[scale-out_100ms_var(--ease-out)] origin-(--reka-combobox-content-transform-origin) pointer-events-auto flex flex-col',
            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 gap-1.5 p-1.5 text-sm 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',
            tagsItem: 'px-1.5 py-0.5 rounded-sm font-medium inline-flex items-center gap-0.5 ring ring-inset ring-accented bg-elevated text-default data-disabled:cursor-not-allowed data-disabled:opacity-75',
            tagsItemText: 'truncate',
            tagsItemDelete: [
              'inline-flex items-center rounded-xs text-dimmed hover:text-default hover:bg-accented/75 disabled:pointer-events-none',
              'transition-colors'
            ],
            tagsItemDeleteIcon: 'shrink-0',
            tagsInput: 'flex-1 border-0 bg-transparent placeholder:text-dimmed focus:outline-none disabled:cursor-not-allowed disabled:opacity-75'
          },
          variants: {
            fieldGroup: {
              horizontal: {
                root: 'group has-focus-visible:z-[1]'
              },
              vertical: {
                root: 'group has-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',
                tagsItem: 'text-[10px]/3',
                tagsItemDeleteIcon: 'size-3',
                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',
                tagsItem: 'text-[10px]/3',
                tagsItemDeleteIcon: 'size-3',
                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',
                tagsItem: 'text-xs',
                tagsItemDeleteIcon: 'size-3.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',
                tagsItem: 'text-xs',
                tagsItemDeleteIcon: 'size-3.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',
                tagsItem: 'text-sm',
                tagsItemDeleteIcon: 'size-4',
                empty: 'p-3 text-base'
              }
            },
            variant: {
              outline: 'text-highlighted bg-default ring ring-inset ring-accented',
              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',
              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'
            },
            virtualize: {
              true: {
                viewport: 'p-1 isolate'
              },
              false: {
                viewport: 'divide-y divide-default'
              }
            },
            multiple: {
              true: {
                root: 'flex-wrap'
              },
              false: {
                base: 'w-full border-0 placeholder:text-dimmed disabled:cursor-not-allowed disabled:opacity-75'
              }
            }
          },
          compoundVariants: [
            {
              multiple: false,
              fieldGroup: 'horizontal',
              class: {
                base: 'group-not-only:group-first:rounded-e-none group-not-only:group-last:rounded-s-none group-not-last:group-not-first:rounded-none'
              }
            },
            {
              multiple: false,
              fieldGroup: 'vertical',
              class: {
                base: 'group-not-only:group-first:rounded-b-none group-not-only:group-last:rounded-t-none group-not-last:group-not-first:rounded-none'
              }
            },
            {
              multiple: true,
              fieldGroup: 'horizontal',
              class: {
                base: 'not-only:first:rounded-e-none not-only:last:rounded-s-none not-last:not-first:rounded-none focus-visible:z-[1]'
              }
            },
            {
              multiple: true,
              fieldGroup: 'vertical',
              class: {
                base: 'not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none focus-visible:z-[1]'
              }
            },
            {
              variant: 'soft',
              multiple: true,
              class: 'has-focus:bg-elevated has-focus-visible:outline-3'
            },
            {
              variant: 'ghost',
              multiple: true,
              class: 'has-focus:bg-elevated has-focus-visible:outline-3'
            },
            {
              color: 'primary',
              multiple: true,
              variant: [
                'outline',
                'subtle'
              ],
              class: 'has-focus-visible:outline-3 has-focus-visible:ring-primary'
            },
            {
              color: 'neutral',
              multiple: true,
              variant: [
                'outline',
                'subtle'
              ],
              class: 'has-focus-visible:outline-3 has-focus-visible:ring-inverted'
            },
            {
              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'
          }
        }
      }
    })
  ]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitHub.

Changelog

No recent changes