Raccourci clavier : Ctrl + K
Commencer

Liste déroulante

Un champ qui cherche dans une liste et retient ce qui est choisi, une valeur ou plusieurs. Les options peuvent être à plat, groupées ou séparées, et elles peuvent arriver d'un serveur à mesure que le lecteur saisit.

Utilisation

vue
<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox } from 'vectis-ui'

const country = ref('')

const options = [
  { value: 'be', label: 'Belgium' },
  { value: 'ca', label: 'Canada' },
  { value: 'fr', label: 'France' },
  { value: 'ch', label: 'Switzerland' },
]
</script>

<template>
  <VCombobox
    v-model="country"
    :options="options"
    aria-label="Country"
    placeholder="Choose a country"
  />
</template>

Exemples

Label et texte d'aide

label affiche un texte descriptif au-dessus du champ, et hint affiche un texte d'aide en dessous.

vue

Type to narrow the list down. Accents are ignored, so reunion finds Réunion.

<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox } from 'vectis-ui'

const country = ref('fr')

const countries = [
  { value: 'be', label: 'Belgium' },
  { value: 'ca', label: 'Canada' },
  { value: 'ci', label: "Côte d'Ivoire" },
  { value: 'fr', label: 'France' },
  { value: 'lu', label: 'Luxembourg' },
  { value: 're', label: 'Réunion' },
  { value: 'ch', label: 'Switzerland' },
]
</script>

<template>
  <div class="column">
    <VCombobox
      v-model="country"
      :options="countries"
      label="Country"
      hint="Type to narrow the list down. Accents are ignored, so reunion finds Réunion."
      placeholder="Choose a country"
      clearable
    />
  </div>
</template>

<style scoped>
.column {
  max-inline-size: 26rem;
}
</style>

Tailles

Définit la hauteur du champ à 32, 40 ou 48 pixels. La prop compact réduit cette hauteur de 4px.

vue
France
France
France
France
France
France
<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox } from 'vectis-ui'

const countries = [
  { value: 'be', label: 'Belgium' },
  { value: 'ca', label: 'Canada' },
  { value: 'fr', label: 'France' },
  { value: 'ch', label: 'Switzerland' },
]

/* Multiple mode on every row: the chips are what shows that the panel and the field are
   not the only things following the step. */
const rows = ref(
  (['sm', 'md', 'lg'] as const).flatMap((size) => [
    { key: size, size, compact: false, label: size, selected: ['fr'] },
    { key: `${size}-compact`, size, compact: true, label: `${size}, compact`, selected: ['fr'] },
  ]),
)
</script>

<template>
  <div class="column">
    <VCombobox
      v-for="row in rows"
      :key="row.key"
      v-model="row.selected"
      :options="countries"
      :size="row.size"
      :compact="row.compact"
      :label="row.label"
      multiple
    />
  </div>
</template>

<style scoped>
.column {
  display: flex;
  flex-direction: column;
  gap: var(--vectis-space-5);
  max-inline-size: 26rem;
}
</style>

États

disabled rend le champ inutilisable. readonly empêche la modification tout en gardant le champ focalisable. invalid marque le champ en erreur. loading affiche un indicateur de chargement. emptyText définit le message affiché quand il n'y a aucune option. clearable ajoute une icône pour vider la sélection.

vue

The cross empties the selection and the search at once

For a rule the browser cannot check by itself

Set by your subscription

<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox } from 'vectis-ui'

const chosen = ref('fr')

const countries = [
  { value: 'be', label: 'Belgium' },
  { value: 'ca', label: 'Canada' },
  { value: 'fr', label: 'France' },
  { value: 'ch', label: 'Switzerland' },
]
</script>

<template>
  <div class="column">
    <VCombobox
      v-model="chosen"
      :options="countries"
      clearable
      label="Clearable"
      hint="The cross empties the selection and the search at once"
    />

    <VCombobox
      :options="countries"
      model-value="fr"
      invalid
      label="Invalid"
      hint="For a rule the browser cannot check by itself"
    />

    <VCombobox :options="countries" model-value="fr" disabled label="Disabled" />

    <!-- Frozen rather than out of reach: it still takes the focus and can be copied from. -->
    <VCombobox
      :options="countries"
      model-value="fr"
      readonly
      clearable
      label="Read-only"
      hint="Set by your subscription"
    />

    <VCombobox :options="[]" loading label="Loading" placeholder="Fetching the list" />

    <!-- No option and nothing loading: the panel says so rather than opening empty. -->
    <VCombobox
      :options="[]"
      empty-text="No country matches"
      label="Nothing to show"
      placeholder="Open me"
    />
  </div>
</template>

<style scoped>
.column {
  display: flex;
  flex-direction: column;
  gap: var(--vectis-space-5);
  max-inline-size: 26rem;
}
</style>

Positionnement

Définit la direction d'ouverture préférée (au-dessus ou en dessous du champ) pour le panneau de la liste d'options.

vue
<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox } from 'vectis-ui'

/* Labels wider than the field: the panel is at least as wide as what it is anchored to,
   so it takes more than that here and the two alignments become visible. */
const departments = [
  { value: 'ops', label: 'Operations and logistics' },
  { value: 'fin', label: 'Finance and accounting' },
  { value: 'eng', label: 'Engineering and platform' },
  { value: 'hr', label: 'People and culture' },
]

const placements = ['bottom-start', 'bottom-end', 'top-start', 'top-end'] as const

const chosen = ref<Record<string, string>>({
  'bottom-start': 'ops',
  'bottom-end': 'fin',
  'top-start': 'eng',
  'top-end': 'hr',
})
</script>

<template>
  <div class="grid">
    <VCombobox
      v-for="placement in placements"
      :key="placement"
      v-model="chosen[placement]"
      :options="departments"
      :placement="placement"
      :label="placement"
    />
  </div>
</template>

<style scoped>
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--vectis-space-5);
  max-inline-size: 26rem;
}
</style>

Groupes et séparateurs

La prop options accepte une liste simple, ou peut être structurée avec des groupes nommés et des séparateurs.

vue
Europe
Africa
America
<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox, type ComboboxItem } from 'vectis-ui'

const country = ref('fr')

/* An entry is an option, a named block, or a separator, and the three mix freely. A group
   the search empties disappears with its name, and a stranded separator is dropped. */
const items: ComboboxItem[] = [
  {
    label: 'Europe',
    options: [
      { value: 'fr', label: 'France' },
      { value: 'be', label: 'Belgium' },
      { value: 'ch', label: 'Switzerland' },
      { value: 'lu', label: 'Luxembourg' },
    ],
  },
  { separator: true },
  {
    label: 'Africa',
    options: [
      { value: 're', label: 'Réunion' },
      { value: 'ci', label: "Côte d'Ivoire" },
      { value: 'ma', label: 'Morocco' },
    ],
  },
  {
    label: 'America',
    options: [
      { value: 'ca', label: 'Canada' },
      { value: 'us', label: 'United States' },
      { value: 'br', label: 'Brazil' },
    ],
  },
  { separator: true },
  { value: 'other', label: 'Other, not listed' },
]
</script>

<template>
  <div class="column">
    <VCombobox v-model="country" :options="items" label="Country" placeholder="Choose a country" />
  </div>
</template>

<style scoped>
.column {
  max-inline-size: 26rem;
}
</style>

Sélection multiple

multiple permet de sélectionner plusieurs valeurs, qui s'affichent sous forme de puces (chips) supprimables à l'intérieur du champ.

vue
FranceBelgium

Click elsewhere: the search field folds away and only the chips remain

fr, be
<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox } from 'vectis-ui'

const served = ref(['fr', 'be'])

const countries = [
  { value: 'be', label: 'Belgium' },
  { value: 'ca', label: 'Canada' },
  { value: 'ci', label: "Côte d'Ivoire" },
  { value: 'fr', label: 'France' },
  { value: 'lu', label: 'Luxembourg' },
  { value: 'mc', label: 'Monaco', disabled: true },
  { value: 're', label: 'Réunion' },
  { value: 'ch', label: 'Switzerland' },
]
</script>

<template>
  <div class="column">
    <VCombobox
      v-model="served"
      :options="countries"
      multiple
      clearable
      label="Served countries"
      hint="Click elsewhere: the search field folds away and only the chips remain"
      placeholder="Add a country"
    />
    <output class="value" aria-label="Chosen values">{{ served.join(', ') || 'none' }}</output>
  </div>
</template>

<style scoped>
.column {
  display: grid;
  gap: var(--vectis-space-3);
  max-inline-size: 26rem;
}
.value {
  font-family: var(--vectis-text-family-code);
  font-size: var(--vectis-text-body-sm-size);
  color: var(--vectis-color-text-muted);
}
</style>

Valeurs en texte

display="text" affiche les valeurs choisies sous forme de libellés séparés par des virgules, sur une seule ligne tronquée par des points de suspension. Le champ garde la hauteur d'un contrôle ordinaire, et sous le focus la ligne laisse au moins la moitié de la place à la recherche. On retire une valeur en la décochant dans la liste, avec Retour arrière sur une recherche vide, ou avec la croix de clearable.

vue
France, Belgium, Switzerland, Luxembourg

One line of labels, cut short when it runs out of room

<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox } from 'vectis-ui'

const served = ref(['fr', 'be', 'ch', 'lu'])

const countries = [
  { value: 'be', label: 'Belgium' },
  { value: 'ca', label: 'Canada' },
  { value: 'ci', label: "Côte d'Ivoire" },
  { value: 'fr', label: 'France' },
  { value: 'lu', label: 'Luxembourg' },
  { value: 'mc', label: 'Monaco', disabled: true },
  { value: 're', label: 'Réunion' },
  { value: 'ch', label: 'Switzerland' },
]
</script>

<template>
  <div class="column">
    <VCombobox
      v-model="served"
      :options="countries"
      multiple
      display="text"
      clearable
      label="Served countries"
      hint="One line of labels, cut short when it runs out of room"
      placeholder="Add a country"
    />
  </div>
</template>

<style scoped>
.column {
  max-inline-size: 20rem;
}
</style>

Valeurs montrées champ replié

max garde en vue les premières valeurs choisies et résume les autres en « +X », en puces comme en texte. Cela vaut tant que le champ n'a pas le focus : sous le focus, toutes les valeurs reviennent pour être vues et retirées. overflowText reformule le compte, et le slot #overflow le remplace.

vue
FranceBelgium+3

Two chips, the rest counted until the field is focused

France, Belgium+3 countries
<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox } from 'vectis-ui'

const chips = ref(['fr', 'be', 'ch', 'lu', 're'])
const text = ref(['fr', 'be', 'ch', 'lu', 're'])

const countries = [
  { value: 'be', label: 'Belgium' },
  { value: 'ca', label: 'Canada' },
  { value: 'ci', label: "Côte d'Ivoire" },
  { value: 'fr', label: 'France' },
  { value: 'lu', label: 'Luxembourg' },
  { value: 're', label: 'Réunion' },
  { value: 'ch', label: 'Switzerland' },
]
</script>

<template>
  <div class="column">
    <VCombobox
      v-model="chips"
      :options="countries"
      multiple
      :max="2"
      label="Served countries"
      hint="Two chips, the rest counted until the field is focused"
    />
    <VCombobox
      v-model="text"
      :options="countries"
      multiple
      display="text"
      :max="2"
      :overflow-text="(count) => `+${count} countries`"
      label="Served countries, as text"
    />
  </div>
</template>

<style scoped>
.column {
  display: grid;
  gap: 1rem;
  max-inline-size: 20rem;
}
</style>

Icône du champ

iconStart affiche une icône au début du champ. iconStartLabel fournit un label accessible si l'icône est rendue interactive au clic.

vue
DesignAccessibility

Sorted A to Z.

<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox } from 'vectis-ui'
import { search, swap_vert as swapVert } from 'vectis-ui/icons'

const topic = ref('')
const tags = ref(['design', 'a11y'])
const order = ref<'A to Z' | 'Z to A'>('A to Z')

const options = [
  { value: 'design', label: 'Design' },
  { value: 'a11y', label: 'Accessibility' },
  { value: 'perf', label: 'Performance' },
  { value: 'docs', label: 'Documentation' },
]

function switchOrder() {
  order.value = order.value === 'A to Z' ? 'Z to A' : 'A to Z'
}
</script>

<template>
  <div class="fields">
    <VCombobox
      v-model="topic"
      :options="options"
      :icon-start="search"
      label="Decorative icon"
      placeholder="Search a topic"
    />

    <!-- The icon is rendered before the chips, so a multiple field keeps both. -->
    <VCombobox
      v-model="tags"
      :options="options"
      multiple
      :icon-start="search"
      label="Beside the chips"
    />

    <VCombobox
      v-model="topic"
      :options="options"
      :icon-start="swapVert"
      icon-start-label="Switch the order"
      label="Clickable icon"
      :hint="`Sorted ${order}.`"
      placeholder="Search a topic"
      @click:icon-start="switchOrder"
    />
  </div>
</template>

<style scoped>
.fields {
  display: flex;
  flex-direction: column;
  gap: var(--vectis-space-5);
  max-inline-size: 26rem;
}
</style>

Icônes des options

La propriété icon d'une option permet d'afficher une icône à côté de son libellé dans la liste déroulante.

vue
<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox, type ComboboxOption } from 'vectis-ui'
import {
  description,
  folder_zip as folderZip,
  image,
  picture_as_pdf as pictureAsPdf,
  table_chart as tableChart,
  video_file as videoFile,
} from 'vectis-ui/icons'

const type = ref('img')

/* `icon` takes what every icon prop in the library takes. A row without one starts
   straight at its label rather than reserving a blank column. */
const types: ComboboxOption[] = [
  { value: 'doc', label: 'Document', icon: description },
  { value: 'img', label: 'Image', icon: image },
  { value: 'vid', label: 'Video', icon: videoFile },
  { value: 'pdf', label: 'PDF', icon: pictureAsPdf },
  { value: 'zip', label: 'Archive', icon: folderZip },
  { value: 'raw', label: 'Anything else' },
  { value: 'xls', label: 'Spreadsheet', icon: tableChart, disabled: true },
]
</script>

<template>
  <div class="column">
    <VCombobox v-model="type" :options="types" label="File type" placeholder="Choose a type" />
  </div>
</template>

<style scoped>
.column {
  max-inline-size: 26rem;
}
</style>

Désactiver filter affiche les options exactement telles que fournies par la source. searchDebounce définit le délai en millisecondes avant d'émettre la recherche.

vue

The list is narrowed by the source, so local filtering is turned off

<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox, type ComboboxOption } from 'vectis-ui'

const CATALOGUE: ComboboxOption[] = Array.from({ length: 120 }, (_, i) => ({
  value: `ref-${i + 1}`,
  label: `Reference ${String(i + 1).padStart(3, '0')}`,
}))

/* Stands in for a server: latency, and the filtering done on its side. */
function fetchReferences(query: string): Promise<ComboboxOption[]> {
  const found = CATALOGUE.filter((option) =>
    option.label.toLowerCase().includes(query.toLowerCase()),
  )
  return new Promise((resolve) => setTimeout(() => resolve(found.slice(0, 20)), 400))
}

const reference = ref('')
const options = ref<ComboboxOption[]>([])
const loading = ref(false)

/* A token, so a slow answer to an old keystroke cannot overwrite a fresh one. */
let latest = 0

async function onSearch(query: string) {
  const current = ++latest
  loading.value = true
  const found = await fetchReferences(query)
  if (current !== latest) return
  options.value = found
  loading.value = false
}
</script>

<template>
  <div class="column">
    <VCombobox
      v-model="reference"
      :options="options"
      :loading="loading"
      :filter="false"
      label="Reference"
      hint="The list is narrowed by the source, so local filtering is turned off"
      placeholder="Search for a reference"
      empty-text="No reference matches"
      clearable
      @search="onSearch"
    />
  </div>
</template>

<style scoped>
.column {
  max-inline-size: 26rem;
}
</style>

Défilement infini

hasMore indique que d'autres pages sont disponibles, déclenchant un événement load-more lorsque la fin de la liste devient visible à l'écran.

vue

Scroll to the foot of the list: the next page is asked for as the end comes into view

0 loaded of 0
<script setup lang="ts">
import { computed, ref } from 'vue'
import { VCombobox, type ComboboxOption } from 'vectis-ui'

const PAGE_SIZE = 20

const CATALOGUE: ComboboxOption[] = Array.from({ length: 120 }, (_, i) => ({
  value: `ref-${i + 1}`,
  label: `Reference ${String(i + 1).padStart(3, '0')}`,
}))

function fetchPage(query: string, page: number) {
  const found = CATALOGUE.filter((option) =>
    option.label.toLowerCase().includes(query.toLowerCase()),
  )
  return new Promise<{ items: ComboboxOption[]; total: number }>((resolve) =>
    setTimeout(
      () =>
        resolve({
          items: found.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE),
          total: found.length,
        }),
      400,
    ),
  )
}

const reference = ref('')
const options = ref<ComboboxOption[]>([])
const loading = ref(false)
const total = ref(0)
const page = ref(0)
const query = ref('')

const hasMore = computed(() => options.value.length < total.value)

async function onSearch(term: string) {
  query.value = term
  page.value = 0
  loading.value = true
  const result = await fetchPage(term, 0)
  options.value = result.items
  total.value = result.total
  loading.value = false
}

/* The component asks once per page and waits: the next request only goes out when the
   sentinel comes back into view, which it cannot do until this one has landed. */
async function onLoadMore() {
  loading.value = true
  const result = await fetchPage(query.value, page.value + 1)
  page.value += 1
  options.value = [...options.value, ...result.items]
  loading.value = false
}
</script>

<template>
  <div class="column">
    <VCombobox
      v-model="reference"
      :options="options"
      :loading="loading"
      :has-more="hasMore"
      :filter="false"
      label="Reference"
      hint="Scroll to the foot of the list: the next page is asked for as the end comes into view"
      placeholder="Search for a reference"
      @search="onSearch"
      @load-more="onLoadMore"
    />
    <output class="count" aria-label="Options loaded"
      >{{ options.length }} loaded of {{ total }}</output
    >
  </div>
</template>

<style scoped>
.column {
  display: grid;
  gap: var(--vectis-space-3);
  max-inline-size: 26rem;
}
.count {
  font-family: var(--vectis-text-family-code);
  font-size: var(--vectis-text-body-sm-size);
  color: var(--vectis-color-text-muted);
}
</style>

Options personnalisées

Le slot #option permet de personnaliser le contenu et la mise en page d'une ligne (ex: ajout d'un badge ou d'une deuxième ligne de texte).

vue
<script setup lang="ts">
import { ref } from 'vue'
import { VCombobox } from 'vectis-ui'

const country = ref('fr')

const countries = [
  { value: 'be', label: 'Belgium' },
  { value: 'ca', label: 'Canada' },
  { value: 'fr', label: 'France' },
  { value: 'lu', label: 'Luxembourg' },
  { value: 'ch', label: 'Switzerland' },
]

const capitals: Record<string, string> = {
  be: 'Brussels',
  ca: 'Ottawa',
  fr: 'Paris',
  lu: 'Luxembourg',
  ch: 'Bern',
}
</script>

<template>
  <div class="column">
    <VCombobox v-model="country" :options="countries" label="Country">
      <template #option="{ option, selected }">
        <span class="row">
          <span>{{ option.label }}</span>
          <small class="capital"
            >{{ capitals[option.value] }}{{ selected ? ' · chosen' : '' }}</small
          >
        </span>
      </template>
    </VCombobox>
  </div>
</template>

<style scoped>
.column {
  max-inline-size: 26rem;
}
/* The slot replaces the label, so its content is laid out inside the row the panel
   already spaces and aligns. */
.row {
  display: grid;
}
.capital {
  color: var(--vectis-color-text-muted);
}
</style>

Puces personnalisées

Le slot #chip permet de personnaliser l'apparence des puces (chips) des valeurs sélectionnées.

vue
DocumentImage
<script setup lang="ts">
import { ref } from 'vue'
import { VChip, VCombobox, type ComboboxOption } from 'vectis-ui'
import {
  description,
  image,
  picture_as_pdf as pictureAsPdf,
  video_file as videoFile,
} from 'vectis-ui/icons'

const chosen = ref(['doc', 'img'])

const types: ComboboxOption[] = [
  { value: 'doc', label: 'Document', icon: description },
  { value: 'img', label: 'Image', icon: image },
  { value: 'vid', label: 'Video', icon: videoFile },
  { value: 'pdf', label: 'PDF', icon: pictureAsPdf },
]
</script>

<template>
  <div class="column">
    <VCombobox
      v-model="chosen"
      :options="types"
      multiple
      label="File types"
      placeholder="Add a type"
    >
      <!--
        `size` and `compact` are the step the field worked out for its chips, which cannot
        be guessed from out here, and `remove` is what keeps the value removable.
      -->
      <template #chip="{ option, label, remove, size, compact }">
        <VChip
          :icon-start="option?.icon"
          :size="size"
          :compact="compact"
          :dismiss-label="`Remove ${label}`"
          variant="outline"
          tone="accent"
          dismissible
          @dismiss="remove"
        >
          {{ label }}
        </VChip>
      </template>
    </VCombobox>
  </div>
</template>

<style scoped>
.column {
  max-inline-size: 26rem;
}
</style>

API

Props

PropTypeDéfaut
optionsComboboxItem[]aucune
Ce que la liste propose. Une entrée peut être une option, un bloc nommé d'options, ou un séparateur ; une simple liste d'options reste parfaitement valable.
multiplebooleanfalse
Permet de choisir plusieurs valeurs, ce qui fait de la valeur une liste et montre ce qui a été choisi dans le champ, en puces ou en texte selon display.
displayComboboxDisplay'chip' | 'text''chip'
Comment les valeurs choisies sont montrées quand on peut en choisir plusieurs : une puce supprimable chacune, ou leurs libellés séparés par des virgules sur une seule ligne, tronquée par des points de suspension. Ne change rien pour une valeur unique, qui est toujours du texte.
maxnumberaucune
Combien de valeurs choisies montrer avant de résumer les autres en « +X », en puces comme en texte. Cela vaut tant que le champ n'a pas le focus ; sous le focus, toutes les valeurs reviennent pour être vues et retirées. Omis, ou à 0, toutes les valeurs sont montrées. Ne change rien sans multiple.
overflowText(count: number) => stringaucune
Reformule le « +X » qui représente les valeurs au-delà de max, « +5 produits » par exemple. Reçoit le nombre de valeurs masquées.
labelstringaucune
Le libellé au-dessus du champ, lié à lui pour qu'un clic dessus y place le focus.
hintstringaucune
Une ligne d'aide sous le champ, lue en même temps que le libellé.
sizeComboboxSize'sm' | 'md' | 'lg''md'
La hauteur du champ : 32, 40 ou 48 pixels. Le panneau et ses lignes la suivent.
compactbooleanfalse
Retire 4px à la hauteur, comme partout ailleurs dans le design system.
placeholderstringaucune
Ce que dit le champ tant que rien n'est choisi et que rien n'a été saisi.
disabledbooleanfalse
Rend le champ inutilisable, grisé par les tokens de couleur.
readonlybooleanfalse
Montre ce qui a été choisi sans permettre de le changer : rien ne se tape, la liste ne s'ouvre jamais, les chips perdent leur croix et aucune croix de vidage n'est proposée. Le champ garde le focus et reste copiable, ce qui le distingue de disabled.
invalidbooleanfalse
Marque le champ comme invalide, pour une règle à vous.
iconStartIconSourceaucune
Une icône dans le champ, au début. Elle est rendue avant les chips et non à leur place. Décorative jusqu'à ce qu'un écouteur @click:icon-start en fasse un bouton.
iconStartLabelstringaucune
Ce que fait l'icône de début, en mots, une fois cliquable.
expandIconIconSourceexpand_more
Le chevron à la fin du champ, qui pivote à l'ouverture de la liste. Un clic dessus quand la liste est ouverte la ferme. Il reste une décoration : le champ lui-même ouvre la liste et Échap la ferme au clavier, donc le chevron est masqué aux lecteurs d'écran et ne prend pas de libellé.
clearablebooleanfalse
Propose une croix qui vide à la fois la sélection et la recherche.
clearLabelstringaucune
Ce que fait cette croix, en mots. Sa valeur par défaut vient du dictionnaire du design system.
emptyTextstringaucune
Ce que dit le panneau quand la recherche ne correspond à rien. Un lecteur d'écran l'entend même quand le slot #empty dessine autre chose : renseignez les deux ensemble.
filterComboboxFiltertrue
Comment la liste se resserre à la saisie. La couper signifie que les options arrivent déjà filtrées par leur source et sont montrées telles quelles. Une règle à vous reçoit la requête TELLE QUE SAISIE, simplement rognée, et non la forme insensible aux accents utilisée en interne.
searchDebouncenumber250
Combien de temps attendre avant de dire à la source ce qui est cherché, en millisecondes. Zéro le lui dit aussitôt, ce qui convient à une source qui n'est pas une requête réseau.
loadingbooleanfalse
Dit que quelque chose est en cours de chargement. Sans option encore, tout le panneau le dit ; avec des options déjà listées, un indicateur apparaît au pied de la liste, puisque ce qui charge est alors la page suivante. Dans les deux cas, le champ remplace son chevron par un indicateur.
loadingTextstringaucune
Ce qui est dit pendant le chargement, et ce comme quoi l'indicateur est annoncé. Un lecteur d'écran l'entend même quand le slot #loading dessine autre chose : renseignez les deux ensemble.
hasMorebooleanfalse
Dit qu'il reste des pages à venir, ce qui est ce qui pousse le composant à demander la suivante quand la fin de la liste entre dans le champ de vision.
placementComboboxPlacement'bottom' | 'bottom-start' | 'bottom-end' | 'top' | 'top-start' | 'top-end''bottom-start'
Où la liste s'ouvre par rapport au champ. Le panneau est ancré en CSS, donc cette valeur nomme une préférence : un navigateur à court de place se rabat déjà tout seul.
v-modelItemValue | ItemValue[]''
La valeur de l'option choisie, ou leur liste quand multiple est posé. Elle part sur une chaîne vide, et le tableau n'est jamais muté sur place.

Événements

ÉvénementType
search[query: string]
Ce qui est cherché, à envoyer à la source. C'est retardé de searchDebounce pendant la saisie, et émis aussitôt à l'ouverture du panneau pour qu'une première page puisse être chargée. Le même terme n'est jamais émis deux fois de suite.
click:icon-start[event: MouseEvent]
L'icône de début a été cliquée. Attacher cet écouteur est ce qui en fait un vrai bouton, qui demande alors iconStartLabel.
clear[]
La croix de vidage a vidé la sélection et la recherche.
load-more[]
La fin de la liste est entrée dans le champ de vision : envoyez la page suivante.

Slots

SlotType
start{}
Du contenu au début du champ, rendu après iconStart plutôt qu'à sa place.
value-end{}
Des contrôles à vous à l'intérieur du champ, placés avant ceux que le champ possède : la croix d'effacement et l'icône qui ouvre le panneau. Ces deux-là sont l'affordance propre du composant, ce qui explique l'absence de slot end ici.
optionComboboxOptionSlotProps
Ce qu'une ligne de la liste montre, à la place du simple libellé : un sous-titre, un avatar, un badge. On lui dit si la ligne est celle mise en évidence et si elle est déjà choisie.
chipComboboxChipSlotProps
Remplace la puce qui représente une valeur choisie. Elle reçoit remove, sans quoi la valeur ne pourrait plus être retirée, ainsi que la taille et la densité calculées pour tenir dans le champ, qui ne se devinent pas de l'extérieur. L'option elle-même peut manquer, si cette valeur n'a jamais figuré parmi les options.
overflowComboboxOverflowSlotProps
Remplace le « +X » qui représente les valeurs au-delà de max. Reçoit count, le nombre de valeurs masquées, ainsi que la taille et la densité des puces du champ, pour qu'une puce à vous s'aligne sur les autres.
emptyComboboxEmptySlotProps
Ce que le panneau montre quand rien ne correspond. Il reçoit le terme cherché.
loading{}
Ce que le panneau montre pendant le chargement de ses premières options.

Types

Les types que les tables ci-dessus nomment, écrits comme la librairie les déclare. Ceux qui portent export s'importent depuis vectis-ui pour typer votre propre code ; les autres décrivent la forme de ce qu'un slot fournit.

export interface BuiltinIcon {
  name: string
  paths: readonly [string] | readonly [string, string]
}
export type ChipSize = 'xs' | 'sm'
export interface ComboboxChipSlotProps {
  value: ItemValue
  option: ComboboxOption | undefined
  label: string
  remove: () => void
  size: ChipSize
  compact: boolean
}
export interface ComboboxEmptySlotProps {
  query: string
}
export type ComboboxFilter = boolean | ((option: ComboboxOption, query: string) => boolean)
export interface ComboboxGroup {
  label: string
  options: ComboboxOption[]
}
export type ComboboxItem = ComboboxOption | ComboboxGroup | ComboboxSeparator
export interface ComboboxOption {
  value: ItemValue
  label: string
  icon?: IconSource
  disabled?: boolean
}
export interface ComboboxOptionSlotProps {
  option: ComboboxOption
  index: number
  active: boolean
  selected: boolean
}
export interface ComboboxOverflowSlotProps {
  count: number
  size: ChipSize
  compact: boolean
}
export interface ComboboxSeparator {
  separator: true
}
export type IconRender =
  | { path: string; viewBox?: string }
  | { component: Component; props?: Record<string, unknown> }
  | { src: string }
  | { text: string; class?: string }
  | { class: string }
export type IconSource = string | BuiltinIcon | IconRender
export type ItemValue = string | number

Variables CSS

TokenValeur
--vectis-control-size-combobox-list-max-block18rem