Vitral 0.2
Data

Listbox

A list to choose one or several options from, always open: the sibling of Select.

Import

main.ts
import { Listbox } from '@vitral/vue';

Single

Selection follows the arrows, as in a native list box. Évora is disabled.

  • São Paulo
  • Rio de Janeiro
  • Lisboa
  • Porto
  • Évora
  • Montréal
  • Bogotá
  • Kraków
  • Reykjavík
  • Zürich
  • Tōkyō
  • Málaga
Value: LIS
<script setup lang="ts">import { Label, Listbox, StackPanel } from '@vitral/vue';import { ref } from 'vue'; const cities = [    { name: 'São Paulo', code: 'SAO' },    { name: 'Rio de Janeiro', code: 'RIO' },    { name: 'Lisboa', code: 'LIS' },    { name: 'Porto', code: 'OPO' },    { name: 'Évora', code: 'EVO', off: true },    { name: 'Montréal', code: 'YUL' },    { name: 'Bogotá', code: 'BOG' },    { name: 'Kraków', code: 'KRK' },    { name: 'Reykjavík', code: 'REK' },    { name: 'Zürich', code: 'ZRH' },    { name: 'Tōkyō', code: 'TYO' },    { name: 'Málaga', code: 'AGP' }];const city = ref<string | null>('LIS');</script> <template>    <StackPanel spacing="0.375rem" style="min-width: 16rem">        <Label id="lb-city-label">Airport</Label>        <Listbox v-model="city" :options="cities" option-label="name" option-value="code" option-disabled="off" checkmark aria-labelledby="lb-city-label" scroll-height="14rem" />        <small style="color: var(--vt-text-muted-color)">Value: {{ city ?? 'null' }}</small>    </StackPanel></template>

Multiple, with a filter

Search ignores accents and case: “ingles” is not needed, “espanol” finds Español.

  • Português
  • English
  • Español
  • Français
  • Deutsch
  • Íslenska
  • Polski
  • 日本語 (Japanese)
  • Wolof
  • Euskara
Value: pt, en
<script setup lang="ts">import { Label, Listbox, StackPanel } from '@vitral/vue';import { ref } from 'vue'; const languages = [    { name: 'Português', code: 'pt' },    { name: 'English', code: 'en' },    { name: 'Español', code: 'es' },    { name: 'Français', code: 'fr' },    { name: 'Deutsch', code: 'de' },    { name: 'Íslenska', code: 'is' },    { name: 'Polski', code: 'pl' },    { name: '日本語 (Japanese)', code: 'ja' },    { name: 'Wolof', code: 'wo' },    { name: 'Euskara', code: 'eu' }];const spoken = ref<string[]>(['pt', 'en']);</script> <template>    <StackPanel spacing="0.375rem" style="min-width: 16rem">        <Label id="lb-lang-label">Languages spoken</Label>        <Listbox v-model="spoken" :options="languages" option-label="name" option-value="code" multiple filter checkmark aria-labelledby="lb-lang-label" scroll-height="12rem" />        <small style="color: var(--vt-text-muted-color)">Value: {{ spoken.join(', ') || 'none' }}</small>    </StackPanel></template>

Groups and custom options

    • São Paulo
    • Recife
    • Florianópolis
    • Lisboa
    • Coimbra
    • Montréal
    • Québec
  • Online
  • Away
  • Busy
  • Offline
<script setup lang="ts">import { Label, Listbox, StackPanel } from '@vitral/vue';import { ref } from 'vue'; const byCountry = [    { country: 'Brasil', cities: [{ name: 'São Paulo' }, { name: 'Recife' }, { name: 'Florianópolis' }] },    { country: 'Portugal', cities: [{ name: 'Lisboa' }, { name: 'Coimbra' }] },    { country: 'Canada', cities: [{ name: 'Montréal' }, { name: 'Québec' }] }]; const statuses = [    { label: 'Online', value: 'online', tone: 'success' },    { label: 'Away', value: 'away', tone: 'warn' },    { label: 'Busy', value: 'busy', tone: 'danger' },    { label: 'Offline', value: 'offline', tone: 'muted' }]; const grouped = ref<unknown>(null);const status = ref('online');</script> <template>    <StackPanel orientation="horizontal" spacing="0.75rem" align="start" wrap>        <StackPanel spacing="0.375rem" style="min-width: 16rem">            <Label id="lb-group-label">City by country</Label>            <Listbox v-model="grouped" :options="byCountry" option-group-label="country" option-group-children="cities" option-label="name" aria-labelledby="lb-group-label" scroll-height="14rem" />        </StackPanel>        <StackPanel spacing="0.375rem" style="min-width: 16rem">            <Label id="lb-status-label">Status</Label>            <Listbox v-model="status" :options="statuses" option-label="label" option-value="value" aria-labelledby="lb-status-label">                <template #option="{ option }">                    <span :class="['demo-dot', `demo-dot-${(option as { tone: string }).tone}`]" aria-hidden="true" />                    <span>{{ (option as { label: string }).label }}</span>                </template>            </Listbox>        </StackPanel>    </StackPanel></template> <style scoped>.demo-dot {    width: 0.5rem;    height: 0.5rem;    flex-shrink: 0;    border-radius: 999px;    background: var(--vt-text-muted-color);} .demo-dot-success {    background: var(--vt-success-color);} .demo-dot-warn {    background: var(--vt-warn-color);} .demo-dot-danger {    background: var(--vt-danger-color);}</style>

States

  • São Paulo
  • Rio de Janeiro
  • Lisboa
  • Porto
  • São Paulo
  • Rio de Janeiro
  • Lisboa
  • Porto
<script setup lang="ts">import { Listbox } from '@vitral/vue'; const cities = [    { name: 'São Paulo', code: 'SAO' },    { name: 'Rio de Janeiro', code: 'RIO' },    { name: 'Lisboa', code: 'LIS' },    { name: 'Porto', code: 'OPO' }];</script> <template>    <Listbox :options="cities" option-label="name" invalid aria-label="Invalid" />    <Listbox :model-value="cities[2]" :options="cities" option-label="name" disabled aria-label="Disabled" /></template>

API

Read from packages/vue/src/components/Listbox/types.ts, so it says what the component actually accepts.

Props

NameTypeDescription
optionsany[]—
optionLabelstringField (dotted path) holding an option's text. Without it, options are shown as they are.
optionValuestringField holding the value v-model receives. Without it, the whole option is the value.
optionDisabledstring—
optionGroupLabelstringTurns `options` into groups: this field is each group's label…
optionGroupChildrenstring…and this one its options. Defaults to `items`.
dataKeystringCompare object values by this field instead of structurally.
multiplebooleanv-model becomes an array, and the list is multi-selectable.
checkmarkbooleanMarks the selected options with a check, as well as with the highlight.
filterbooleanAdds a search box above the list.
filterPlaceholderstring—
filterFieldsstring[]Fields to search instead of the label.
filterMatchModestringAny registered match mode; accent- and case-insensitive `contains` by default.
scrollHeightstringThe list's maximum height before it scrolls.
emptyMessagestring—
emptyFilterMessagestring—
selectOnFocusbooleanIn single mode, moving with the arrows also selects, the way a native list box does. Off, the arrows only move and Enter or Space selects.
invalidboolean—
disabledboolean—
fluidboolean—

Plus pt, dt and unstyled from BaseProps, see pass-through and unstyled mode.

Emits

EventPayloadDescription
changeevent: ListboxChangeEvent—
filterquery: string—
focusevent: FocusEvent—
blurevent: FocusEvent—

Slots

NameSlot propsDescription
option(props: { option: unknown; index: number; selected: boolean; focused: boolean })—
optiongroup(props: { group: unknown })—
header——
footer——
empty——