Ensemble de Tags - DsfrTags
🌟 Introduction
Le composant DsfrTags
permet d'afficher un groupe de tags interactifs et personnalisables. Il est particulièrement utile pour gérer des listes de filtres ou des catégories sélectionnables. Il s'appuie sur le composant DsfrTag
et offre la possibilité de suivre l'état des tags sélectionnés via une liaison avec v-model
.
🏅 La documentation sur le tag sur le DSFR
La story sur le tag sur le storybook de VueDsfr📐 Structure
Ce composant affiche une liste de tags sous forme de <ul>
et permet d'associer un modèle réactif (v-model
) pour suivre les sélections des tags interactifs.
🛠️ Props
Nom | Type | Par défaut | Description |
---|---|---|---|
tags | DsfrTagProps<T>[] | [] | Liste des tags à afficher. |
modelValue | T[] | undefined | Liste des valeurs des tags sélectionnés (si les tags sont sélectionnables). |
📡 Événements
Nom | Paramètres | Description |
---|---|---|
update:modelValue | T[] | Émis lorsqu'un tag sélectionnable est (dé)sélectionné, mettant à jour la liste des valeurs sélectionnées. |
🧩 Slots
(Aucun slot spécifique, chaque tag étant généré automatiquement en fonction de la liste fournie en props.)
📝 Exemples
vue
<script lang="ts" setup>
import { computed, ref } from 'vue'
import type { DsfrTagProps } from '@/components/DsfrTag/DsfrTags.types.ts'
import DsfrTags from '@/components/DsfrTag/DsfrTags.vue'
const tagSet: (DsfrTagProps)[] = [
{
label: 'Les fruits',
selectable: true,
selected: true,
value: 'fruit',
},
{
label: 'Les légumes',
selectable: true,
value: 'legume',
},
]
type FruitOrVegetable = 'fruit' | 'legume'
const items: { name: string, type: FruitOrVegetable }[] = [
{
name: 'Banane',
type: 'fruit',
},
{
name: 'Pomme',
type: 'fruit',
},
{
name: 'Poire',
type: 'fruit',
},
{
name: 'Courgette',
type: 'legume',
},
{
name: 'Poivron',
type: 'legume',
},
{
name: 'Navet',
type: 'legume',
},
]
const filters = ref<FruitOrVegetable[]>(['fruit', 'legume'])
const filteredItems = computed(() =>
items // Get all items
.filter(item => filters.value.includes(item.type)) // Filter according to filters
.sort((a, b) => a.name > b.name ? 1 : -1), // Sort alphabetically
)
</script>
<template>
<div class="max-w-90">
<div class="fr-mt-2w">
<DsfrTags
v-model="filters"
:tags="tagSet"
/>
</div>
<ul>
<li
v-for="item of filteredItems"
:key="item.name"
>
{{ item.name }}
</li>
</ul>
</div>
</template>
<style scoped>
.max-w-90 {
max-width: 90%;
}
</style>
⚙️ 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[]
}