Onglet - DsfrTabItem
🌟 Introduction
Le composant DsfrTabItem
représente un onglet individuel dans un ensemble d'onglets. Il gère les interactions utilisateur telles que les clics et les commandes clavier pour naviguer entre les onglets. Ce composant offre également une intégration avec des icônes pour une expérience utilisateur enrichie et intuitive.
Ce composant doit s’utiliser dans un DsfrTabs
.
📐 Structure
<DsfrTabItem>
: Un élément de liste représentant un onglet.- Contient un bouton pour activer l'onglet.
- Gère les commandes clavier pour permettre la navigation entre les onglets.
🛠️ Props
Propriété | Type | Description | Valeur par défaut |
---|---|---|---|
panelId | string | ID unique du panneau de contenu associé à cet onglet. | obligatoire |
tabId | string | ID unique de l'onglet, utilisé pour l'accessibilité. | obligatoire |
icon | string | Nom de l'icône à afficher dans l'onglet (facultatif). | undefined |
📡 Événements
click
: Événement émis lorsque l'onglet est cliqué, envoie l’index de l’onglet (number
, entier commençant à0
).next
: Événement émis lorsque l'utilisateur appuie sur la touche "flèche droite" ou "flèche bas".previous
: Événement émis lorsque l'utilisateur appuie sur la touche "flèche gauche" ou "flèche haut".first
: Événement émis lorsque l'utilisateur appuie sur la touche "Home".last
: Événement émis lorsque l'utilisateur appuie sur la touche "End".
🧩 Slots
default
: Slot pour insérer le contenu de l'onglet.
📝 Exemples
vue
<script lang="ts" setup>
import { ref } from 'vue'
import DsfrButton from '../../DsfrButton/DsfrButton.vue'
import DsfrTabContent from '../DsfrTabContent.vue'
import DsfrTabItem from '../DsfrTabItem.vue'
import DsfrTabs from '../DsfrTabs.vue'
const tabListName = 'Liste d’onglet'
const title1 = 'Titre 1'
const tabTitles = [
{ title: title1, icon: 'ri-checkbox-circle-line', tabId: 'tab-0', panelId: 'tab-content-0' },
{ title: 'Titre 2', icon: 'ri-checkbox-circle-line', tabId: 'tab-1', panelId: 'tab-content-1' },
{ title: 'Titre 3', icon: 'ri-checkbox-circle-line', tabId: 'tab-2', panelId: 'tab-content-2' },
{ title: 'Titre 4', icon: 'ri-checkbox-circle-line', tabId: 'tab-3', panelId: 'tab-content-3' },
]
const activeTab = ref(0)
const selectPrevious = async () => {
const newIndex = activeTab.value === 0 ? tabTitles.length - 1 : activeTab.value - 1
activeTab.value = newIndex
}
const selectNext = async () => {
const newIndex = activeTab.value === tabTitles.length - 1 ? 0 : activeTab.value + 1
activeTab.value = newIndex
}
const selectFirst = async () => {
activeTab.value = 0
}
const selectLast = async () => {
activeTab.value = tabTitles.length - 1
}
</script>
<template>
<div class="fr-container fr-my-2w">
<DsfrTabs
v-model="activeTab"
:tab-list-name="tabListName"
>
<template #tab-items>
<DsfrTabItem
v-for="(tab, index) of tabTitles"
:key="tab.tabId"
:tab-id="tab.tabId"
:panel-id="tab.panelId"
:icon="tab.icon"
@click="activeTab = index"
@next="selectNext()"
@previous="selectPrevious()"
@first="selectFirst()"
@last="selectLast()"
>
{{ tab.title }}
</DsfrTabItem>
</template>
<DsfrTabContent
panel-id="tab-content-0"
tab-id="tab-0"
>
<div>Contenu 1 avec d'<em>autres composants</em></div>
</DsfrTabContent>
<DsfrTabContent
panel-id="tab-content-1"
tab-id="tab-1"
>
<div>Contenu 2 avec d'<strong>autres composants</strong></div>
</DsfrTabContent>
<DsfrTabContent
panel-id="tab-content-2"
tab-id="tab-2"
>
<div>Contenu 3 avec d'<em><strong>autres composants</strong></em></div>
</DsfrTabContent>
<DsfrTabContent
panel-id="tab-content-3"
tab-id="tab-3"
>
<div>
<p>Contenu 4 avec beaucoup de contenus</p>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Vitae fugit sit et eos a officiis adipisci nulla repellat cupiditate? Assumenda, explicabo ullam laboriosam ex sit corporis enim illum a itaque.</p>
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quasi animi quis quos consectetur alias delectus recusandae sunt quisquam incidunt provident quidem, at voluptatibus id, molestias et? Temporibus perspiciatis aut voluptates.</p>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Quibusdam obcaecati at delectus iusto possimus! Molestiae, iusto veritatis. Nostrum magni officiis autem, in ullam aliquid, mollitia, commodi architecto vitae omnis vero.</p>
</div>
</DsfrTabContent>
</DsfrTabs>
<div style="display: flex; gap: 1rem; margin-block: 1rem;">
<DsfrButton
label="Activer le 1er onglet"
:disabled="activeTab === 0"
@click="activeTab = 0"
/>
<DsfrButton
label="Activer le 2è onglet"
:disabled="activeTab === 1"
@click="activeTab = 1"
/>
<DsfrButton
label="Activer le 3è onglet"
:disabled="activeTab === 2"
@click="activeTab = 2"
/>
<DsfrButton
label="Activer le dernier onglet"
:disabled="activeTab === tabTitles.length - 1"
@click="activeTab = tabTitles.length - 1"
/>
</div>
</div>
</template>
⚙️ Code source du composant
vue
<script setup lang="ts">
import { computed, inject, toRef } from 'vue'
import { registerTabKey } from './injection-key'
export type DsfrTabContentProps = {
panelId: string
tabId: string
}
const props = defineProps<DsfrTabContentProps>()
const values = { true: '100%', false: '-100%' }
const useTab = inject(registerTabKey)!
const { isVisible, asc } = useTab(toRef(() => props.tabId))
// @ts-expect-error this will be fine
const translateValueFrom = computed(() => values[String(asc?.value)])
// @ts-expect-error this will be fine
const translateValueTo = computed(() => values[String(!asc?.value)])
</script>
<template>
<Transition
name="slide-fade"
mode="in-out"
>
<div
v-show="isVisible"
:id="panelId"
class="fr-tabs__panel"
:class="{
'fr-tabs__panel--selected': isVisible,
}"
role="tabpanel"
:aria-labelledby="tabId"
:tabindex="isVisible ? 0 : -1"
>
<!-- @slot Slot par défaut pour le contenu de l’onglet. Sera dans `<div class="fr-tabs__panel">` -->
<slot />
</div>
</Transition>
</template>
<style scoped>
.slide-fade-enter-active {
transition: all 0.3s ease-out;
}
.slide-fade-leave-active {
transition: all 0.3s ease-out;
}
.slide-fade-enter-from {
transform: translateX(v-bind(translateValueFrom));
opacity: 0;
}
.slide-fade-leave-to {
transform: translateX(v-bind(translateValueTo));
opacity: 0;
}
</style>
ts
export type DsfrTabItemProps = {
panelId: string
tabId: string
icon?: string
}
export type DsfrTabContentProps = {
panelId: string
tabId: string
}
export type DsfrTabsProps = {
modelValue: number
tabListName: string
tabTitles: (Partial<DsfrTabItemProps> & { title: string })[]
tabContents?: string[]
}
ts
import type { InjectionKey, Ref } from 'vue'
type RegisterTab = (title: Ref<string>) => {
isVisible: Ref<boolean>
asc?: Ref<boolean>
}
export const registerTabKey: InjectionKey<RegisterTab> = Symbol('tabs')
Le DsfrTabItem
utilise v-on:keydown
pour gérer les interactions clavier et permettre la navigation entre les onglets. Le composant utilise également watch
pour gérer la mise au point automatique lorsque l'onglet est sélectionné, améliorant ainsi l'accessibilité et l'expérience utilisateur.