Skip to content

🌟 Introduction

Le composant DsfrCallout est un composant Vue.js qui permet de créer des encadrés de mise en avant avec un titre, un contenu, une icône optionnelle, et un bouton configurable. Il est conçu pour s'intégrer harmonieusement dans les projets utilisant le Design System Français (DSFR), tout en offrant une grande flexibilité grâce à la personnalisation des éléments visuels.

🏅 La documentation sur la mise en avant sur le DSFR

La story sur la mise en avant sur le storybook de VueDsfr

📐 Structure

Le composant DsfrCallout s'utilise pour afficher un message ou une information importante dans un encadré visuellement distinct, avec un titre, une icône, du contenu textuel, et éventuellement un bouton d'action. Chaque partie de l'encadré peut être personnalisée via les propriétés du composant.

🛠️ Props

Voici les différentes propriétés que vous pouvez utiliser avec ce composant :

PropTypeDéfautDescription
buttonobject | undefinedundefinedConfiguration du bouton d'action. Si défini, le bouton s'affichera sous le texte principal.
titlestring | undefinedundefinedTitre de l'encadré, affiché dans un élément HTML déterminé par titleTag.
titleTagstring'h3'Balise HTML utilisée pour le titre (h3 par défaut).
iconstring | object | undefinedundefinedIcône affichée à gauche du titre. Peut être une chaîne pour une icône DSFR, un objet pour un composant VIcon, ou undefined si aucune icône n'est nécessaire.
contentstringObligatoireTexte principal de l'encadré, généralement une description ou un message important.

📡 Événements

Ce composant ne déclenche pas d'événements personnalisés.

🧩 Slots

  • default : Contenu additionnel à afficher à l'intérieur de l'encadré. Ce slot est intégré dans la structure principale du composant et s'affiche sous le texte principal.
vue
<script lang="ts" setup>
import { ref } from 'vue'

import DsfrButton from '../../DsfrButton/DsfrButton.vue'
import DsfrCallout from '../DsfrCallout.vue'

import type { VIconProps } from '@/components/VIcon/VIcon.types'

const title = 'Titre de la mise en avant'
const button = undefined
const icon = 'ri:notification-3-line'
const content = 'Lorem ipsum dolor sit amet, consectetur adipiscing, incididunt, ut labore et dol'
const titleTag = undefined

const animateTitle = 'Titre de la mise en avant stylée'
const animatedIcon: VIconProps = { name: 'bi:bell', animation: 'ring' }
const animatedtitleTag = 'h4'
const buttonIcon = ref<VIconProps>({ name: 'ri-refresh-line', animation: 'spin' })
const possibleAnimations = ['spin', 'wrench', 'pulse', 'spin-pulse', 'flash', 'float', 'ring'] as const
const getRandomNb = (max = 1000, min = 0) => Math.floor(min + Math.random() * (max + 1 - min))
function getRandomEl<T> (x: readonly T[]): T | undefined {
  if (x.length === 0) {
    return undefined
  }
  return x.at(getRandomNb(x.length - 1))
}
const getRandomAnimation = () => getRandomEl<(typeof possibleAnimations)[number]>(possibleAnimations)
</script>

<template>
  <DsfrCallout
    :title="`${title} (${titleTag ?? 'h3'})`"
    :content="content"
    :button="button"
    :icon="icon"
    :title-tag="titleTag"
  />
  <DsfrCallout
    :title="`${animateTitle} (${animatedtitleTag ?? 'h3'})`"
    :button="button"
    :icon="animatedIcon"
    :title-tag="animatedtitleTag"
  >
    Contenu <em>élaboré</em> avec d’autres composants
    <DsfrButton
      type="button"
      :label="`(${buttonIcon.animation}) Cliquez-moi pour changer l’animation de l’icône !`"
      :icon="buttonIcon"
      @click="buttonIcon.animation = getRandomAnimation()"
    />
  </DsfrCallout>
</template>
vue
<script lang="ts" setup>
import { computed } from 'vue'

import DsfrButton from '../DsfrButton/DsfrButton.vue'
import VIcon from '../VIcon/VIcon.vue'

import type { DsfrCalloutProps } from './DsfrCallout.types'

export type { DsfrCalloutProps }

const props = withDefaults(defineProps<DsfrCalloutProps>(), {
  // @ts-expect-error this is really undefined
  button: () => undefined,
  titleTag: 'h3',
  icon: undefined,
})

const dsfrIcon = computed(() => typeof props.icon === 'string' && props.icon.startsWith('fr-icon-'))
const iconProps = computed(() => dsfrIcon.value ? undefined : typeof props.icon === 'string' ? { name: props.icon } : { ...(props.icon ?? {}) })
</script>

<template>
  <div
    class="fr-callout"
    :class="{ [String(icon)]: dsfrIcon }"
  >
    <VIcon
      v-if="icon && iconProps"
      v-bind="iconProps"
    />
    <component
      :is="titleTag"
      v-if="title"
      class="fr-callout__title"
    >
      {{ title }}
    </component>

    <p class="fr-callout__text">
      {{ content }}
    </p>

    <DsfrButton
      v-if="button"
      v-bind="button"
    />

    <!-- @slot Slot par défaut pour le contenu de la mise en avant. Sera dans `<div class="fr-callout">` -->
    <slot />
  </div>
</template>

<style scoped>
.fr-callout__text {
  color: var(--text-default-grey);
}
</style>

⚙️ Code source du composant