Raccourci clavier : Ctrl + K
Commencer

Notification

Des notifications levées depuis n'importe où dans le code en appelant toast(), et affichées par un seul VToaster monté une fois. Plusieurs peuvent s'empiler, chacune avec son propre compte à rebours.

Utilisation

vue
<script setup lang="ts">
import { VButton, toast } from 'vectis-ui'
</script>

<template>
  <!-- `toast()` can be called from anywhere: a component, a store, the handler that
       just saved something. What shows it is a single <VToaster /> mounted once at the
       root of the application, and only one, since the queue behind these calls has a
       single renderer. -->
  <VButton
    variant="outline"
    tone="neutral"
    @click="toast({ tone: 'success', message: 'Changes saved.' })"
  >
    Save
  </VButton>
</template>

Exemples

Variantes et tonalités

tone dit ce que signifie la notification, en cinq valeurs, et décide de l'icône qu'elle prend quand aucune n'est donnée. variant est l'intensité avec laquelle ce ton est peint, teinté ou plein.

vue

soft, the default

solid

<script setup lang="ts">
import { VButton, toast, type ToastTone } from 'vectis-ui'

const tones: ToastTone[] = ['neutral', 'accent', 'success', 'warning', 'danger']
</script>

<template>
  <!-- Raised into the <VToaster /> mounted once at the root of the application. -->
  <div class="demo">
    <!-- The tone says what the notification means, and it also decides which icon it
         takes when none is given. Five of them, because a notification reports a state
         and success and warning are states. -->
    <div class="row">
      <p class="caption">soft, the default</p>
      <div class="buttons">
        <VButton
          v-for="tone in tones"
          :key="tone"
          variant="outline"
          tone="neutral"
          @click="toast({ tone, message: `A ${tone} notification.` })"
        >
          {{ tone }}
        </VButton>
      </div>
    </div>

    <!-- The variant is how strongly it is painted: a tinted background with a border,
         or the full colour. -->
    <div class="row">
      <p class="caption">solid</p>
      <div class="buttons">
        <VButton
          v-for="tone in tones"
          :key="tone"
          variant="outline"
          tone="neutral"
          @click="toast({ tone, variant: 'solid', message: `A solid ${tone} notification.` })"
        >
          {{ tone }}
        </VButton>
      </div>
    </div>
  </div>
</template>

<style scoped>
.demo {
  display: grid;
  gap: var(--vectis-space-5);
}
.row {
  display: grid;
  gap: var(--vectis-space-2);
}
.buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-3);
}
.caption {
  margin: 0;
  color: var(--vectis-color-text-muted);
  font-size: var(--vectis-text-caption-size);
}
</style>

Titre et message

message porte la notification, et title l'encadre en quelques mots quand le message seul ne dirait pas de quoi il retourne.

vue
<script setup lang="ts">
import { VButton, toast } from 'vectis-ui'
</script>

<template>
  <!-- Raised into the <VToaster /> mounted once at the root of the application. -->
  <div class="demo">
    <!-- The message is the whole of it, and one sentence is usually enough. -->
    <VButton
      variant="outline"
      tone="neutral"
      @click="toast({ tone: 'success', message: 'Changes saved.' })"
    >
      Message only
    </VButton>

    <!-- A title frames the message when the message alone would not say what it is
         about. Keep it to a few words: it is a heading, not a first sentence. -->
    <VButton
      variant="outline"
      tone="neutral"
      @click="
        toast({
          tone: 'danger',
          title: 'Upload failed',
          message: 'invoice-2481.pdf is larger than the 10 MB limit.',
        })
      "
    >
      Title and message
    </VButton>
  </div>
</template>

<style scoped>
.demo {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-3);
}
</style>

Icônes

Sans elle, icon vient du ton. En nommer une la remplace, et passer false la retire tout à fait.

vue
<script setup lang="ts">
import { VButton, toast } from 'vectis-ui'
import { cloud_upload as cloudUpload } from 'vectis-ui/icons'
</script>

<template>
  <!-- Raised into the <VToaster /> mounted once at the root of the application. -->
  <div class="demo">
    <!-- Left out, the icon comes from the tone: a notification arrives unannounced, so
         it is scanned before it is read and the glyph is what carries the meaning at a
         glance. -->
    <VButton
      variant="outline"
      tone="neutral"
      @click="toast({ tone: 'warning', message: 'Your session expires in 5 minutes.' })"
    >
      From the tone
    </VButton>

    <!-- Name one and it replaces the tone's, for a notification whose subject is more
         specific than its meaning. -->
    <VButton
      variant="outline"
      tone="neutral"
      @click="toast({ tone: 'success', icon: cloudUpload, message: 'Backup finished.' })"
    >
      A custom icon
    </VButton>

    <!-- `false` removes it altogether, which is not the same as leaving it out: the
         card then carries no glyph at all and the text runs to the edge. -->
    <VButton
      variant="outline"
      tone="neutral"
      @click="toast({ tone: 'accent', icon: false, message: 'Three new comments.' })"
    >
      No icon
    </VButton>
  </div>
</template>

<style scoped>
.demo {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-3);
}
</style>

Largeur

width accepte n'importe quelle longueur CSS et remplace le plancher et le plafond de la carte. Elle ne dépasse jamais la largeur de la fenêtre.

vue
<script setup lang="ts">
import { VButton, toast } from 'vectis-ui'

const LONG =
  'The export finished with 3 warnings. Rows 12, 48 and 91 were skipped because their reference column was empty.'
</script>

<template>
  <!-- Raised into the <VToaster /> mounted once at the root of the application. -->
  <div class="demo">
    <!-- Left alone the card sits between a floor and a ceiling of its own, so a short
         message is not a sliver and a long one does not stretch across the page. -->
    <VButton
      variant="outline"
      tone="neutral"
      @click="toast({ tone: 'warning', title: 'Export finished', message: LONG })"
    >
      Default width
    </VButton>

    <!-- Any CSS length replaces both. It is never allowed past the width of the
         viewport, margins included, so a value too large for a phone is simply
         ignored there rather than pushing the card off the screen. -->
    <VButton
      variant="outline"
      tone="neutral"
      @click="toast({ tone: 'warning', title: 'Export finished', message: LONG, width: '32rem' })"
    >
      32rem
    </VButton>

    <VButton
      variant="outline"
      tone="neutral"
      @click="toast({ tone: 'accent', message: 'Saved.', width: '14rem' })"
    >
      14rem
    </VButton>
  </div>
</template>

<style scoped>
.demo {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-3);
}
</style>

Placements

placement pose la notification dans l'un des six coins, chacun ayant sa propre pile. Posé sur le VToaster, il est la valeur par défaut de toutes les notifications ; passé au moment d'en lever une, il n'appartient qu'à elle.

vue
<script setup lang="ts">
import { VButton, toast, type ToastPlacement } from 'vectis-ui'

const placements: ToastPlacement[] = [
  'top-left',
  'top-center',
  'top-right',
  'bottom-left',
  'bottom-center',
  'bottom-right',
]
</script>

<template>
  <!-- Raised into the <VToaster /> mounted once at the root of the application. -->
  <div class="demo">
    <!-- Six corners, each with a stack of its own, so notifications aimed at different
         ones never queue behind each other. Set on the VToaster the placement is the
         default for every notification; passed here it is this one's alone. -->
    <VButton
      v-for="placement in placements"
      :key="placement"
      variant="outline"
      tone="neutral"
      @click="toast({ placement, message: `Raised at ${placement}.` })"
    >
      {{ placement }}
    </VButton>
  </div>
</template>

<style scoped>
.demo {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-3);
}
</style>

Empilement

Les notifications s'empilent au lieu de se remplacer, chacune gardant son propre compte à rebours, si bien qu'elles partent au rythme de leur propre horloge.

vue
<script setup lang="ts">
import { VButton, toast } from 'vectis-ui'

let n = 0

function raise() {
  n += 1
  toast({ tone: 'accent', message: `Notification ${n}.` })
}

function raiseThree() {
  toast({ tone: 'success', message: 'Backup finished.' })
  toast({ tone: 'warning', message: 'Two files were skipped.' })
  toast({ tone: 'danger', message: 'The report could not be sent.' })
}
</script>

<template>
  <!-- Raised into the <VToaster /> mounted once at the root of the application. -->
  <div class="demo">
    <!-- Notifications stack rather than replace one another, because two states can be
         true at the same time: a finished backup and a failed send are both worth
         reading. Each keeps a countdown of its own, so they go away in the order their
         own clocks run out and not in the order they arrived. -->
    <VButton variant="outline" tone="neutral" @click="raise">Raise one more</VButton>
    <VButton variant="outline" tone="neutral" @click="raiseThree">Raise three at once</VButton>
  </div>
</template>

<style scoped>
.demo {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-3);
}
</style>

Combien de temps elle reste

duration est la durée d'affichage d'une notification, cinq secondes par défaut, et chacune peut demander la sienne. Le compte à rebours se suspend tant que le pointeur repose quelque part sur la pile et tant que le clavier y est, et ne repart que lorsque les deux sont partis.

vue
<script setup lang="ts">
import { VButton, toast } from 'vectis-ui'
</script>

<template>
  <!-- Raised into the <VToaster /> mounted once at the root of the application. -->
  <div class="demo">
    <!-- Five seconds by default, and every notification may ask for its own. The
         countdown holds while the pointer rests anywhere on the stack, so something
         that disappears on a clock can be read to the end. -->
    <VButton
      variant="outline"
      tone="neutral"
      @click="toast({ tone: 'accent', message: 'Gone in 2 seconds.', duration: 2000 })"
    >
      2 seconds
    </VButton>

    <VButton
      variant="outline"
      tone="neutral"
      @click="
        toast({
          tone: 'warning',
          title: 'Take your time',
          message: 'Gone in 10 seconds, unless the pointer rests on it.',
          duration: 10000,
        })
      "
    >
      10 seconds
    </VButton>
  </div>
</template>

<style scoped>
.demo {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-3);
}
</style>

Notifications persistantes

Une duration de 0 désarme le compte à rebours et la notification reste jusqu'à ce qu'on la retire. Laissez la croix de fermeture pour qu'il y ait une sortie.

vue
<script setup lang="ts">
import { VButton, toast } from 'vectis-ui'
</script>

<template>
  <!-- Raised into the <VToaster /> mounted once at the root of the application. -->
  <!-- A duration of 0 disarms the countdown: the notification then stays until it is
       dismissed. Reserve it for something the reader has to see, a failure they can
       act on, and leave the close cross on so there is a way out. -->
  <VButton
    variant="outline"
    tone="neutral"
    @click="
      toast({
        tone: 'danger',
        title: 'Connection lost',
        message: 'Your changes are kept locally until the connection comes back.',
        duration: 0,
      })
    "
  >
    Stays until dismissed
  </VButton>
</template>

Renvoyer une notification

hideClose retire la croix de fermeture. toast rend un identifiant et dismissToast retire cette notification, ou toutes d'un coup lorsqu'il est appelé sans argument.

vue
<script setup lang="ts">
import { ref } from 'vue'
import { VButton, dismissToast, toast } from 'vectis-ui'

const id = ref<number | null>(null)

function raise() {
  // `toast` hands back an id, which is what lets this notification be taken away from
  // code: the request it was about came back, or the state it reported is over.
  id.value = toast({ tone: 'accent', message: 'Uploading…', duration: 0 })
}

function dismissOne() {
  if (id.value !== null) dismissToast(id.value)
  id.value = null
}
</script>

<template>
  <!-- Raised into the <VToaster /> mounted once at the root of the application. -->
  <div class="demo">
    <!-- The close cross is on by default, and turning it off only makes sense on a
         notification that goes away on its own: with no countdown and no cross,
         nothing but code can remove it. -->
    <VButton
      variant="outline"
      tone="neutral"
      @click="toast({ tone: 'success', message: 'Saved.', hideClose: true })"
    >
      No cross
    </VButton>

    <VButton variant="outline" tone="neutral" @click="raise">Raise one to dismiss</VButton>
    <VButton variant="outline" tone="neutral" @click="dismissOne">Dismiss that one</VButton>

    <!-- With no argument it clears every notification at once. -->
    <VButton variant="outline" tone="neutral" @click="dismissToast()">Dismiss all</VButton>
  </div>
</template>

<style scoped>
.demo {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-3);
}
</style>

API

Props

PropTypeDéfaut
placementToastPlacement'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right''bottom-right'
Dans quel coin les notifications apparaissent, sauf si l'une d'elles en demande un autre.
durationnumber5000
Combien de temps une notification reste, en millisecondes, sauf si elle demande autre chose. Une notification à qui l'on donne 0 reste jusqu'à ce qu'elle soit fermée.
closeLabelstringaucune
Ce que fait la croix de fermeture, en mots. Elle retombe sur le dictionnaire du design system.
labelstringaucune
Ce que les lecteurs d'écran annoncent pour les zones de notification elles-mêmes, qui sont des points de repère de la page. Il retombe sur le dictionnaire du design system.

Variables CSS

TokenValeur
--vectis-control-size-toast-width22rem