🌟 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 :
Prop | Type | Défaut | Description |
---|---|---|---|
button | object | undefined | undefined | Configuration du bouton d'action. Si défini, le bouton s'affichera sous le texte principal. |
title | string | undefined | undefined | Titre de l'encadré, affiché dans un élément HTML déterminé par titleTag . |
titleTag | string | 'h3' | Balise HTML utilisée pour le titre (h3 par défaut). |
icon | string | object | undefined | undefined | Icô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. |
content | string | Obligatoire | Texte 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.
<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>
<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>