Tag - DsfrTag
🌟 Introduction
Le tag catégorise/classe/organise les contenus à l'aide de mots-clés. Il aide les utilisateurs à rechercher et à trouver facilement une information.
Le tag peut être utilisé dans deux contextes :
Dans le contenu (carte, en-tête, liste) : il catégorise le contenu auquel il est apposé. Il peut être cliquable ou non cliquable ;
En tant que filtre (dans une page de résultats de recherche par exemple). Dans ce cas il peut-être :
- activable comme filtre en place à sélectionner/désélectionner ;
- supprimable, il sert de rappel à un filtre qui a été coché dans une sidebar ou une liste déroulante.
🏅 La documentation sur le tag sur le DSFR
La story sur le tag sur le storybook de VueDsfr📐 Structure
Il se compose des éléments suivants :
- un libellé obligatoire : soit en utilisant la prop
label
soit en utilisant le slot par défaut ; - une icône optionnelle : avec la prop
icon
qui peut être soit le nom d’une classe correspondant à une icône du DSFR (elle commence par'fr-icon-'
), soit le nom d’une icône de@iconify/vue
, cf. la documentation sur les icônes) et celle du composant VIcon.
🛠️ Props
Nom | Type | Défaut | Obligatoire | Description |
---|---|---|---|---|
label | string | undefined | Le texte affiché sur l'étiquette. | |
link | string | undefined | URL pour un lien externe. Détermine aussi le type de balise (a ou RouterLink). | |
tagName | string | 'p' | Nom de la balise utilisée pour l'étiquette (devrait être 'p' ou 'button' ). | |
icon | string | undefined | Nom de l'icône (@iconify/vue ) à afficher sur l'étiquette. | |
disabled | boolean | undefined | Désactive l'étiquette si elle est un bouton. | |
small | boolean | undefined | Réduit la taille de l'étiquette. | |
iconOnly | boolean | undefined | Affiche uniquement l'icône, sans texte. | |
selectable | boolean | false | Rend le tag sélectionnable. | |
selected | boolean (si selectable) | false | Indique si le tag est sélectionné. | |
value | T (si selectable) | undefined | Valeur associée au tag (utile dans une liste de tags sélectionnables). |
📡 Évenements
Pas d'événements personnalisés pour ce composant. Il se repose sur les événements natifs de ses balises sous-jacentes.
🧩 Slots
Nom | Description |
---|---|
default | Slot par défaut pour ajouter du contenu supplémentaire à côté du label ou de l'icône. |
📝 Exemples
vue
<script lang="ts" setup>
import { ref } from 'vue'
import DsfrTag from '@/components/DsfrTag/DsfrTag.vue'
const loneTag = ref(false)
const filters = ref(['individualTags', 'tagSets'])
</script>
<template>
<div
v-if="filters.includes('individualTags')"
class="flex gap-2 fr-m-2v flex-wrap"
>
<DsfrTag
label="Vue Power"
/>
<DsfrTag
label="DSFR"
icon="fr-icon-success-line"
/>
<DsfrTag
label="Iconify Power"
icon="ri-bell-line"
/>
<DsfrTag
label="Tag inactif (disabled)"
icon="ri-stop-line"
title="désactivé"
disabled
/>
<DsfrTag
label="Icône seule"
title="Icône seule"
icon="ri-play-line"
icon-only
/>
<DsfrTag
label="Petit tag"
title="Petite"
small
/>
<DsfrTag
label="Petit tag avec icone DSFR"
title="Petite"
small
icon="fr-icon-success-line"
/>
<DsfrTag
label="Petit tag avec icone Iconify"
title="Petite"
small
icon="ri-checkbox-circle-line"
/>
<DsfrTag
label="Tag tout cliquable seul !"
selectable
:selected="loneTag"
@select="loneTag = !loneTag"
/>
<DsfrTag
label="Tag cliquable tout seul mais désactivé !"
selectable
disabled
:selected="loneTag"
@select="loneTag = !loneTag"
/>
</div>
</template>
⚙️ Code source du composant
vue
<script lang="ts" setup generic="T = string">
import { computed } from 'vue'
import VIcon from '../VIcon/VIcon.vue'
import type { DsfrTagProps } from './DsfrTags.types'
const props = withDefaults(defineProps<DsfrTagProps<T>>(), {
label: undefined,
link: undefined,
tagName: 'p',
icon: undefined,
disabled: undefined,
})
defineEmits<{
select: [[unknown, boolean]]
}>()
const isExternalLink = computed(() => typeof props.link === 'string' && props.link.startsWith('http'))
const is = computed(() => {
return props.link
? (isExternalLink.value ? 'a' : 'RouterLink')
: (((props.disabled && props.tagName === 'p') || props.selectable) ? 'button' : props.tagName)
})
const linkProps = computed(() => {
return { [isExternalLink.value ? 'href' : 'to']: props.link }
})
const dsfrIcon = computed(() => typeof props.icon === 'string' && props.icon.startsWith('fr-icon-'))
const defaultScale = props.small ? 0.65 : 0.9
const iconProps = computed(() => dsfrIcon.value ? undefined : typeof props.icon === 'string' ? { name: props.icon, scale: defaultScale } : { scale: defaultScale, ...(props.icon ?? {}) })
</script>
<template>
<component
:is="is"
class="fr-tag"
:disabled="disabled"
:class="{
'fr-tag--sm': small,
[icon as string]: dsfrIcon,
'fr-tag--icon-left': dsfrIcon,
}"
:aria-pressed="selectable ? selected : undefined"
v-bind="{ ...linkProps, ...$attrs }"
@click="!disabled && $emit('select', [value, selected])"
>
<VIcon
v-if="props.icon && !dsfrIcon"
:label="iconOnly ? label : undefined"
:class="{ 'fr-mr-1v': !iconOnly }"
v-bind="iconProps"
/>
<template v-if="!iconOnly">
{{ label }}
</template>
<!-- @slot Slot par défaut pour le contenu du tag -->
<slot />
</component>
</template>
<style scoped>
.ov-icon {
margin-top: 0.1rem;
}
.fr-tag {
align-items: center;
}
.success {
color: var(--success);
background-color: var(--bg-success);
}
.error {
color: var(--error);
background-color: var(--bg-error);
}
.warning {
color: var(--warning);
background-color: var(--bg-warning);
}
.info {
color: var(--info);
background-color: var(--bg-info);
}
</style>
ts
export type DsfrTagProps<T = string> = {
label?: string
link?: string
tagName?: string
icon?: string
disabled?: boolean
small?: boolean
iconOnly?: boolean
} & ({
selectable: true
selected?: boolean
value?: T
} | {
selectable?: false
})
export type DsfrTagsProps<T = string> = {
tags: DsfrTagProps<T>[]
modelValue?: T[]
}