Listbox

ListboxGitLab
Uma lista selecionável de itens com busca, virtualização e renderização rica de itens.

Uso

Use a diretiva v-model para controlar o valor do Listbox ou a prop default-value para definir o valor inicial quando você não precisa controlar o estado.

France
Germany
Italy
Spain
Netherlands
Poland
Belgium
Portugal
Austria
Sweden
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  },
  {
    label: 'Netherlands',
    icon: 'i-lucide-map-pin',
    value: 'NL'
  },
  {
    label: 'Poland',
    icon: 'i-lucide-map-pin',
    value: 'PL'
  },
  {
    label: 'Belgium',
    icon: 'i-lucide-map-pin',
    value: 'BE'
  },
  {
    label: 'Portugal',
    icon: 'i-lucide-map-pin',
    value: 'PT'
  },
  {
    label: 'Austria',
    icon: 'i-lucide-map-pin',
    value: 'AT'
  },
  {
    label: 'Sweden',
    icon: 'i-lucide-map-pin',
    value: 'SE'
  }
])
const value = ref({
  label: 'France',
  icon: 'i-lucide-map-pin',
  value: 'FR'
})
</script>

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

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  },
  {
    label: 'Netherlands',
    icon: 'i-lucide-map-pin',
    value: 'NL'
  },
  {
    label: 'Poland',
    icon: 'i-lucide-map-pin',
    value: 'PL'
  },
  {
    label: 'Belgium',
    icon: 'i-lucide-map-pin',
    value: 'BE'
  },
  {
    label: 'Portugal',
    icon: 'i-lucide-map-pin',
    value: 'PT'
  },
  {
    label: 'Austria',
    icon: 'i-lucide-map-pin',
    value: 'AT'
  },
  {
    label: 'Sweden',
    icon: 'i-lucide-map-pin',
    value: 'SE'
  }
])
const value = ref({
  label: 'France',
  icon: 'i-lucide-map-pin',
  value: 'FR'
})
</script>

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

Itens

Use a prop items como um array de objetos com as seguintes propriedades:

FranceThe Hexagon
GermanyThe Federal Republic
ItalyThe Boot
SpainThe Bull Skin
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    description: 'The Hexagon',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    description: 'The Federal Republic',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    description: 'The Boot',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    description: 'The Bull Skin',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
</script>

<template>
  <NListbox :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    description: 'The Hexagon',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    description: 'The Federal Republic',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    description: 'The Boot',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    description: 'The Bull Skin',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
</script>

<template>
  <NListbox :items="items" />
</template>

Você também pode passar um array de arrays para a prop items para exibir grupos separados de itens.

France
Germany
Italy
Brazil
Argentina
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[][]>([
  [
    {
      label: 'France',
      icon: 'i-lucide-map-pin',
      value: 'FR'
    },
    {
      label: 'Germany',
      icon: 'i-lucide-map-pin',
      value: 'DE'
    },
    {
      label: 'Italy',
      icon: 'i-lucide-map-pin',
      value: 'IT'
    }
  ],
  [
    {
      label: 'Brazil',
      icon: 'i-lucide-map-pin',
      value: 'BR'
    },
    {
      label: 'Argentina',
      icon: 'i-lucide-map-pin',
      value: 'AR'
    }
  ]
])
</script>

<template>
  <NListbox :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[][]>([
  [
    {
      label: 'France',
      icon: 'i-lucide-map-pin',
      value: 'FR'
    },
    {
      label: 'Germany',
      icon: 'i-lucide-map-pin',
      value: 'DE'
    },
    {
      label: 'Italy',
      icon: 'i-lucide-map-pin',
      value: 'IT'
    }
  ],
  [
    {
      label: 'Brazil',
      icon: 'i-lucide-map-pin',
      value: 'BR'
    },
    {
      label: 'Argentina',
      icon: 'i-lucide-map-pin',
      value: 'AR'
    }
  ]
])
</script>

<template>
  <NListbox :items="items" />
</template>

Múltiplo

Use a prop multiple para permitir a seleção de vários itens. Quando habilitada, o v-model será um array.

France
Germany
Italy
Spain
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
</script>

<template>
  <NListbox multiple :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
</script>

<template>
  <NListbox multiple :items="items" />
</template>

Chave de valor

Você pode optar por vincular uma única propriedade do objeto em vez do objeto inteiro usando a prop value-key. O padrão é undefined.

France
Germany
Italy
Spain
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
const value = ref('FR')
</script>

<template>
  <NListbox v-model="value" value-key="value" :items="items" class="w-full" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
const value = ref('FR')
</script>

<template>
  <NListbox v-model="value" value-key="value" :items="items" class="w-full" />
</template>

Filtro

Use a prop filter para exibir um campo de filtro ou passe um objeto para personalizar o componente Input. O padrão é false.

France
Germany
Italy
Spain
Netherlands
Poland
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  },
  {
    label: 'Netherlands',
    icon: 'i-lucide-map-pin',
    value: 'NL'
  },
  {
    label: 'Poland',
    icon: 'i-lucide-map-pin',
    value: 'PL'
  }
])
</script>

<template>
  <NListbox :filter="{
  placeholder: 'Filter...',
  icon: 'i-lucide-search'
}" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  },
  {
    label: 'Netherlands',
    icon: 'i-lucide-map-pin',
    value: 'NL'
  },
  {
    label: 'Poland',
    icon: 'i-lucide-map-pin',
    value: 'PL'
  }
])
</script>

<template>
  <NListbox :filter="{
  placeholder: 'Filter...',
  icon: 'i-lucide-search'
}" :items="items" />
</template>

Ícone de selecionado

Use a prop selected-icon para personalizar o ícone quando um item é selecionado. O padrão é i-lucide-check.

France
Germany
Italy
Spain
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
const value = ref('FR')
</script>

<template>
  <NListbox v-model="value" selected-icon="i-lucide-flame" value-key="value" :items="items" class="w-full" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
const value = ref('FR')
</script>

<template>
  <NListbox v-model="value" selected-icon="i-lucide-flame" value-key="value" :items="items" class="w-full" />
</template>

Tamanho

Use a prop size para alterar o tamanho do Listbox.

France
Germany
Italy
Spain
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
</script>

<template>
  <NListbox size="xl" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
</script>

<template>
  <NListbox size="xl" :items="items" />
</template>

Carregando

Use a prop loading para exibir um indicador de carregamento. Use a prop loading-icon para personalizar o ícone.

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

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  }
])
</script>

<template>
  <NListbox loading :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  }
])
</script>

<template>
  <NListbox loading :items="items" />
</template>

Desabilitado

Use a prop disabled para impedir qualquer interação do usuário com o Listbox.

France
Germany
Italy
Spain
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
</script>

<template>
  <NListbox disabled :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
</script>

<template>
  <NListbox disabled :items="items" />
</template>

Exemplos

Com tipo de itens

Você pode usar a propriedade type com separator para exibir um separador entre os itens ou label para exibir um rótulo.

Fruits
Apple
Banana
Blueberry
Grapes
Pineapple
Vegetables
Aubergine
Broccoli
Carrot
Courgette
Leek
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

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

<template>
  <NListbox :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

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

<template>
  <NListbox :items="items" />
</template>
Ao usar itens label como cabeçalhos de grupo, passe um array de arrays para que um rótulo seja filtrado junto com o grupo dele durante a busca.

Com ícone nos itens

Você pode usar a propriedade icon para exibir um Icon dentro dos itens.

Backlog
Todo
In Progress
Done
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'Backlog',
    icon: 'i-lucide-circle-help',
    value: 'backlog'
  },
  {
    label: 'Todo',
    icon: 'i-lucide-circle-plus',
    value: 'todo'
  },
  {
    label: 'In Progress',
    icon: 'i-lucide-circle-arrow-up',
    value: 'in_progress'
  },
  {
    label: 'Done',
    icon: 'i-lucide-circle-check',
    value: 'done'
  }
])
</script>

<template>
  <NListbox :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'Backlog',
    icon: 'i-lucide-circle-help',
    value: 'backlog'
  },
  {
    label: 'Todo',
    icon: 'i-lucide-circle-plus',
    value: 'todo'
  },
  {
    label: 'In Progress',
    icon: 'i-lucide-circle-arrow-up',
    value: 'in_progress'
  },
  {
    label: 'Done',
    icon: 'i-lucide-circle-check',
    value: 'done'
  }
])
</script>

<template>
  <NListbox :items="items" />
</template>

Com avatar nos itens

Você pode usar a propriedade avatar para exibir um Avatar dentro dos itens.

benjamincanac
romhml
atinux
HugoRCD
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'benjamincanac',
    avatar: {
      src: 'https://github.com/benjamincanac.png'
    }
  },
  {
    label: 'romhml',
    avatar: {
      src: 'https://github.com/romhml.png'
    }
  },
  {
    label: 'atinux',
    avatar: {
      src: 'https://github.com/atinux.png'
    }
  },
  {
    label: 'HugoRCD',
    avatar: {
      src: 'https://github.com/HugoRCD.png'
    }
  }
])
</script>

<template>
  <NListbox :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'benjamincanac',
    avatar: {
      src: 'https://github.com/benjamincanac.png'
    }
  },
  {
    label: 'romhml',
    avatar: {
      src: 'https://github.com/romhml.png'
    }
  },
  {
    label: 'atinux',
    avatar: {
      src: 'https://github.com/atinux.png'
    }
  },
  {
    label: 'HugoRCD',
    avatar: {
      src: 'https://github.com/HugoRCD.png'
    }
  }
])
</script>

<template>
  <NListbox :items="items" />
</template>

Com chip nos itens

Você pode usar a propriedade chip para exibir um Chip dentro dos itens.

bug
feature
enhancement
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'bug',
    chip: {
      color: 'error'
    }
  },
  {
    label: 'feature',
    chip: {
      color: 'success'
    }
  },
  {
    label: 'enhancement',
    chip: {
      color: 'info'
    }
  }
])
</script>

<template>
  <NListbox :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'bug',
    chip: {
      color: 'error'
    }
  },
  {
    label: 'feature',
    chip: {
      color: 'success'
    }
  },
  {
    label: 'enhancement',
    chip: {
      color: 'info'
    }
  }
])
</script>

<template>
  <NListbox :items="items" />
</template>

Com descrição nos itens

Você pode usar a propriedade description para exibir um texto adicional abaixo do rótulo.

FranceThe Hexagon
GermanyThe Federal Republic
ItalyThe Boot
SpainThe Bull Skin
<script setup lang="ts">
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    description: 'The Hexagon',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    description: 'The Federal Republic',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    description: 'The Boot',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    description: 'The Bull Skin',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
</script>

<template>
  <NListbox :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListboxItem } from '@nitro/ui'

const items = ref<ListboxItem[]>([
  {
    label: 'France',
    description: 'The Hexagon',
    icon: 'i-lucide-map-pin',
    value: 'FR'
  },
  {
    label: 'Germany',
    description: 'The Federal Republic',
    icon: 'i-lucide-map-pin',
    value: 'DE'
  },
  {
    label: 'Italy',
    description: 'The Boot',
    icon: 'i-lucide-map-pin',
    value: 'IT'
  },
  {
    label: 'Spain',
    description: 'The Bull Skin',
    icon: 'i-lucide-map-pin',
    value: 'ES'
  }
])
</script>

<template>
  <NListbox :items="items" />
</template>

Controlar os itens selecionados

Você pode controlar o item selecionado usando a prop default-value ou a diretiva v-model.

França
Alemanha
Itália
Espanha
Holanda
Polônia

Controlar o termo de busca

Use a diretiva v-model:search-term para controlar o termo de busca.

França

Com filtro ignorado

Defina a prop ignore-filter como true para desabilitar a busca interna e usar sua própria lógica de busca.

Nenhum dado
Este exemplo usa refDebounced para aplicar debounce nas chamadas de API.

Com campos de filtro

Use a prop filter-fields com um array de campos para filtrar. O padrão é [labelKey].

Nenhum dado

Com virtualização

Use a prop virtualize para habilitar a virtualização em listas grandes, como um booleano ou um objeto com opções como { estimateSize: 32, overscan: 12 }.

Como lista de transferência

Você pode compor dois componentes Listbox com controles de Button para construir um padrão de lista de transferência.

Available
França
Alemanha
Itália
Espanha
Holanda
Polônia
Bélgica
Portugal
Selected
Nenhum dado

API

Props

Prop Default Type
as'div'any

The element or component this component should render as.

id string
color'primary' "primary" | "secondary" | "accent" | "success" | "info" | "warning" | "error" | "neutral"
size'md' "sm" | "md" | "xs" | "lg" | "xl"
items T

The items to display in the list.

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

The controlled value of the Listbox. Can be bound with v-model.

modelModifiers Mod
defaultValue _Number<_Optional<_Nullable<GetModelValue<T, VK, M, undefined>, Mod>, Mod>, Mod>

The default value when not controlled.

multiplefalse M

Whether multiple items can be selected.

valueKeyundefined 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>>

The key used to get the label from the item.

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

The key used to get the description from the item.

loadingboolean

Whether the list is in a loading state.

loadingIconappConfig.ui.icons.loadingany

The icon displayed when loading.

filterfalseboolean | Omit<InputProps<AcceptableValue, ModelModifiers>, "modelValue" | "defaultValue">

Whether to display a filter input or not. Can be an object to pass additional props to the input. { placeholder: 'Search...', variant: 'none' }

filterFields[labelKey] string[]

The fields to filter by.

ignoreFilterfalseboolean

When true, disable the default filters, useful for custom filtering (useAsyncData, useFetch, etc.).

selectedIconappConfig.ui.icons.checkany

The icon displayed when an item is selected.

virtualizefalseboolean | { overscan?: number ; estimateSize?: number | ((index: number) => number) | undefined; } | undefined

Enable virtualization for large lists.

highlightboolean

Highlight the ring color like a focus state.

autofocusboolean
autofocusDelay0 number
by string | (a: AcceptableValue, b: AcceptableValue): boolean

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

disabledboolean

When true, prevents the user from interacting with listbox

highlightOnHovertrueboolean

When true, hover over item will trigger highlight

name string

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

orientation "vertical" | "horizontal"

The orientation of the listbox.
Mainly so arrow navigation is done accordingly (left & right vs. up & down)

requiredboolean

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

selectionBehavior'toggle' "replace" | "toggle"

How multiple selection should behave in the collection.

searchTerm'' string
ui { root?: SlotClass; input?: SlotClass; content?: SlotClass; group?: SlotClass; label?: SlotClass; separator?: SlotClass; empty?: SlotClass; loading?: SlotClass; loadingIcon?: SlotClass; item?: SlotClass; itemLeadingIcon?: SlotClass; itemLeadingAvatar?: SlotClass; itemLeadingAvatarSize?: SlotClass; itemLeadingChip?: SlotClass; itemLeadingChipSize?: SlotClass; itemWrapper?: SlotClass; itemLabel?: SlotClass; itemDescription?: SlotClass; itemTrailing?: SlotClass; itemTrailingIcon?: SlotClass; }

Slots

Slot Type
loading{}
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; }

Emits

Event Type
entryFocus[event: CustomEvent<any>]
highlight[payload: { ref: HTMLElement; value: AcceptableValue; } | undefined]
leave[event: Event]
change[event: Event]
update:modelValue[value: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, undefined>, Mod>, Mod>, Mod>]
update:searchTerm[value: string]

Tema

app.config.ts
export default defineAppConfig({
  ui: {
    listbox: {
      slots: {
        root: 'flex flex-col min-h-0 min-w-0 ring ring-inset ring-default rounded-lg overflow-hidden',
        input: 'border-b border-default',
        content: 'relative overflow-y-auto flex-1 max-h-60 scroll-py-1 focus:outline-none',
        group: 'p-1 isolate',
        label: 'font-semibold text-highlighted',
        separator: '-mx-1 my-1 h-px bg-border',
        empty: 'text-center text-muted',
        loading: 'flex items-center justify-center text-muted',
        loadingIcon: 'animate-spin shrink-0',
        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: '',
        itemWrapper: 'flex-1 flex flex-col min-w-0',
        itemLabel: 'truncate',
        itemDescription: 'truncate text-muted',
        itemTrailing: 'ms-auto inline-flex gap-1.5 items-center',
        itemTrailingIcon: 'shrink-0'
      },
      variants: {
        size: {
          xs: {
            label: 'p-1 text-[10px]/3 gap-1',
            empty: 'py-3 text-xs',
            loading: 'py-3',
            loadingIcon: 'size-4',
            item: 'p-1 text-xs gap-1',
            itemLeadingIcon: 'size-4',
            itemLeadingAvatarSize: '3xs',
            itemLeadingChip: 'size-4',
            itemLeadingChipSize: 'sm',
            itemTrailingIcon: 'size-4'
          },
          sm: {
            label: 'p-1.5 text-[10px]/3 gap-1.5',
            empty: 'py-4 text-xs',
            loading: 'py-4',
            loadingIcon: 'size-4',
            item: 'p-1.5 text-xs gap-1.5',
            itemLeadingIcon: 'size-4',
            itemLeadingAvatarSize: '3xs',
            itemLeadingChip: 'size-4',
            itemLeadingChipSize: 'sm',
            itemTrailingIcon: 'size-4'
          },
          md: {
            label: 'p-1.5 text-xs gap-1.5',
            empty: 'py-6 text-sm',
            loading: 'py-6',
            loadingIcon: 'size-5',
            item: 'p-1.5 text-sm gap-1.5',
            itemLeadingIcon: 'size-5',
            itemLeadingAvatarSize: '2xs',
            itemLeadingChip: 'size-5',
            itemLeadingChipSize: 'md',
            itemTrailingIcon: 'size-5'
          },
          lg: {
            label: 'p-2 text-xs gap-2',
            empty: 'py-7 text-sm',
            loading: 'py-7',
            loadingIcon: 'size-5',
            item: 'p-2 text-sm gap-2',
            itemLeadingIcon: 'size-5',
            itemLeadingAvatarSize: '2xs',
            itemLeadingChip: 'size-5',
            itemLeadingChipSize: 'md',
            itemTrailingIcon: 'size-5'
          },
          xl: {
            label: 'p-2 text-sm gap-2',
            empty: 'py-8 text-base',
            loading: 'py-8',
            loadingIcon: 'size-6',
            item: 'p-2 text-base gap-2',
            itemLeadingIcon: 'size-6',
            itemLeadingAvatarSize: 'xs',
            itemLeadingChip: 'size-6',
            itemLeadingChipSize: 'lg',
            itemTrailingIcon: 'size-6',
            itemDescription: 'text-sm'
          }
        },
        color: {
          primary: {
            root: 'outline-primary/25 has-focus-visible:outline-3 has-focus-visible:ring-primary'
          },
          secondary: {
            root: 'outline-secondary/25 has-focus-visible:outline-3 has-focus-visible:ring-secondary'
          },
          accent: {
            root: 'outline-accent/25 has-focus-visible:outline-3 has-focus-visible:ring-accent'
          },
          success: {
            root: 'outline-success/25 has-focus-visible:outline-3 has-focus-visible:ring-success'
          },
          info: {
            root: 'outline-info/25 has-focus-visible:outline-3 has-focus-visible:ring-info'
          },
          warning: {
            root: 'outline-warning/25 has-focus-visible:outline-3 has-focus-visible:ring-warning'
          },
          error: {
            root: 'outline-error/25 has-focus-visible:outline-3 has-focus-visible:ring-error'
          },
          neutral: {
            root: 'outline-inverted/25 has-focus-visible:outline-3 has-focus-visible:ring-inverted'
          }
        },
        virtualize: {
          true: {
            content: 'p-1 isolate'
          },
          false: {
            content: 'divide-y divide-default'
          }
        },
        disabled: {
          true: {
            root: 'opacity-75 cursor-not-allowed'
          }
        },
        highlight: {
          true: ''
        }
      },
      compoundVariants: [
        {
          color: 'primary',
          highlight: true,
          class: {
            root: 'ring ring-inset ring-primary'
          }
        },
        {
          color: 'neutral',
          highlight: true,
          class: {
            root: 'ring ring-inset ring-inverted'
          }
        }
      ],
      defaultVariants: {
        color: 'primary',
        size: 'md'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nitro/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        listbox: {
          slots: {
            root: 'flex flex-col min-h-0 min-w-0 ring ring-inset ring-default rounded-lg overflow-hidden',
            input: 'border-b border-default',
            content: 'relative overflow-y-auto flex-1 max-h-60 scroll-py-1 focus:outline-none',
            group: 'p-1 isolate',
            label: 'font-semibold text-highlighted',
            separator: '-mx-1 my-1 h-px bg-border',
            empty: 'text-center text-muted',
            loading: 'flex items-center justify-center text-muted',
            loadingIcon: 'animate-spin shrink-0',
            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: '',
            itemWrapper: 'flex-1 flex flex-col min-w-0',
            itemLabel: 'truncate',
            itemDescription: 'truncate text-muted',
            itemTrailing: 'ms-auto inline-flex gap-1.5 items-center',
            itemTrailingIcon: 'shrink-0'
          },
          variants: {
            size: {
              xs: {
                label: 'p-1 text-[10px]/3 gap-1',
                empty: 'py-3 text-xs',
                loading: 'py-3',
                loadingIcon: 'size-4',
                item: 'p-1 text-xs gap-1',
                itemLeadingIcon: 'size-4',
                itemLeadingAvatarSize: '3xs',
                itemLeadingChip: 'size-4',
                itemLeadingChipSize: 'sm',
                itemTrailingIcon: 'size-4'
              },
              sm: {
                label: 'p-1.5 text-[10px]/3 gap-1.5',
                empty: 'py-4 text-xs',
                loading: 'py-4',
                loadingIcon: 'size-4',
                item: 'p-1.5 text-xs gap-1.5',
                itemLeadingIcon: 'size-4',
                itemLeadingAvatarSize: '3xs',
                itemLeadingChip: 'size-4',
                itemLeadingChipSize: 'sm',
                itemTrailingIcon: 'size-4'
              },
              md: {
                label: 'p-1.5 text-xs gap-1.5',
                empty: 'py-6 text-sm',
                loading: 'py-6',
                loadingIcon: 'size-5',
                item: 'p-1.5 text-sm gap-1.5',
                itemLeadingIcon: 'size-5',
                itemLeadingAvatarSize: '2xs',
                itemLeadingChip: 'size-5',
                itemLeadingChipSize: 'md',
                itemTrailingIcon: 'size-5'
              },
              lg: {
                label: 'p-2 text-xs gap-2',
                empty: 'py-7 text-sm',
                loading: 'py-7',
                loadingIcon: 'size-5',
                item: 'p-2 text-sm gap-2',
                itemLeadingIcon: 'size-5',
                itemLeadingAvatarSize: '2xs',
                itemLeadingChip: 'size-5',
                itemLeadingChipSize: 'md',
                itemTrailingIcon: 'size-5'
              },
              xl: {
                label: 'p-2 text-sm gap-2',
                empty: 'py-8 text-base',
                loading: 'py-8',
                loadingIcon: 'size-6',
                item: 'p-2 text-base gap-2',
                itemLeadingIcon: 'size-6',
                itemLeadingAvatarSize: 'xs',
                itemLeadingChip: 'size-6',
                itemLeadingChipSize: 'lg',
                itemTrailingIcon: 'size-6',
                itemDescription: 'text-sm'
              }
            },
            color: {
              primary: {
                root: 'outline-primary/25 has-focus-visible:outline-3 has-focus-visible:ring-primary'
              },
              secondary: {
                root: 'outline-secondary/25 has-focus-visible:outline-3 has-focus-visible:ring-secondary'
              },
              accent: {
                root: 'outline-accent/25 has-focus-visible:outline-3 has-focus-visible:ring-accent'
              },
              success: {
                root: 'outline-success/25 has-focus-visible:outline-3 has-focus-visible:ring-success'
              },
              info: {
                root: 'outline-info/25 has-focus-visible:outline-3 has-focus-visible:ring-info'
              },
              warning: {
                root: 'outline-warning/25 has-focus-visible:outline-3 has-focus-visible:ring-warning'
              },
              error: {
                root: 'outline-error/25 has-focus-visible:outline-3 has-focus-visible:ring-error'
              },
              neutral: {
                root: 'outline-inverted/25 has-focus-visible:outline-3 has-focus-visible:ring-inverted'
              }
            },
            virtualize: {
              true: {
                content: 'p-1 isolate'
              },
              false: {
                content: 'divide-y divide-default'
              }
            },
            disabled: {
              true: {
                root: 'opacity-75 cursor-not-allowed'
              }
            },
            highlight: {
              true: ''
            }
          },
          compoundVariants: [
            {
              color: 'primary',
              highlight: true,
              class: {
                root: 'ring ring-inset ring-primary'
              }
            },
            {
              color: 'neutral',
              highlight: true,
              class: {
                root: 'ring ring-inset ring-inverted'
              }
            }
          ],
          defaultVariants: {
            color: 'primary',
            size: 'md'
          }
        }
      }
    })
  ]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitLab.

Changelog

No recent changes