Menu de navigation - DsfrNavigationMenu
🌟 Introduction
Le menu de navigation est un composant qui affiche un menu déroulant simple avec une liste de liens. Il offre une navigation hiérarchique avec animations d'ouverture/fermeture.
Le composant DsfrNavigationMenu
crée un menu déroulant traditionnel avec un bouton déclencheur et une liste de liens organisés verticalement.
Important
Ce composant NE devrait PAS être utilisé directement, il est utilisé en interne par son parent DsfrNavigation
📐 Structure
Le menu de navigation est composé des éléments suivants :
- un bouton déclencheur avec le titre (prop
title
) - un conteneur expansible avec animations de collapse
- une liste non-ordonnée (
<ul>
) de liens de navigation - chaque lien utilise les composants
DsfrNavigationMenuItem
etDsfrNavigationMenuLink
- un slot par défaut pour contenu personnalisé
🛠️ Props
nom | type | défaut | obligatoire | description |
---|---|---|---|---|
title | string | ✅ | Titre du menu affiché dans le bouton | |
id | string | () => useRandomId(...) | Identifiant unique pour le menu | |
links | DsfrNavigationMenuLinkProps[] | [] | Tableau des liens à afficher dans ce menu | |
expandedId | string | '' | ID du menu actuellement expansé | |
active | boolean | false | Indique si ce menu est actuellement actif |
📡 Événements
DsfrNavigationMenu
déclenche l'événement suivant :
nom | donnée (payload) | description |
---|---|---|
toggleId | string | Émis lors du clic sur le bouton pour ouvrir/fermer le menu |
🧩 Slots
DsfrNavigationMenu
possède un slot par défaut pour personnaliser le contenu.
nom | description |
---|---|
default | Slot par défaut pour le contenu personnalisé du menu |
📝 Exemples
Exemple d'utilisation de DsfrNavigationMenu
dans une navigation :
vue
<template>
<DsfrNavigation :nav-items="navItems">
<DsfrNavigationMenu
title="Administration"
:links="[
{ to: '/impots', text: 'Impôts' },
{ to: '/securite-sociale', text: 'Sécurité sociale' },
{ to: '/permis', text: 'Permis de conduire' },
]"
:expanded-id="expandedMenuId"
@toggle-id="handleToggle"
/>
</DsfrNavigation>
</template>
⚙️ Code source du composant
vue
<script lang="ts" setup>
import type { DsfrNavigationMenuProps } from './DsfrNavigation.types'
import { computed, onMounted, watch } from 'vue'
import { useCollapsable } from '../../composables'
import { useRandomId } from '../../utils/random-utils'
import DsfrNavigationMenuItem from './DsfrNavigationMenuItem.vue'
import DsfrNavigationMenuLink from './DsfrNavigationMenuLink.vue'
export type { DsfrNavigationMenuProps }
const props = withDefaults(defineProps<DsfrNavigationMenuProps>(), {
id: () => useRandomId('menu'),
links: () => [],
expandedId: '',
})
defineEmits<{ (event: 'toggleId', id: string): void }>()
const {
collapse,
collapsing,
cssExpanded,
doExpand,
onTransitionEnd,
} = useCollapsable()
const expanded = computed(() => props.id === props.expandedId)
watch(expanded, (newValue, oldValue) => {
// @see https://github.com/GouvernementFR/dsfr/blob/main/src/core/script/collapse/collapse.js
if (newValue !== oldValue) {
doExpand(newValue)
}
})
onMounted(() => {
// NavigationMenu can be expanded by default
// We need to trigger the expand animation at mounted
if (expanded.value) {
doExpand(true)
}
})
</script>
<template>
<button
class="fr-nav__btn"
:aria-expanded="expanded"
:aria-current="active || undefined"
:aria-controls="id"
@click="$emit('toggleId', id)"
>
<span>{{ title }}</span>
</button>
<div
:id="id"
ref="collapse"
class="fr-collapse fr-menu"
data-testid="navigation-menu"
:class="{ 'fr-collapse--expanded': cssExpanded, 'fr-collapsing': collapsing }"
@transitionend="onTransitionEnd(expanded)"
>
<ul
class="fr-menu__list"
>
<!-- @slot Slot par défaut pour le contenu de l’item de liste. Sera dans `<ul class="fr-menu__list">` -->
<slot />
<DsfrNavigationMenuItem
v-for="(link, idx) of links"
:key="idx"
>
<DsfrNavigationMenuLink
v-bind="link"
@toggle-id="$emit('toggleId', expandedId)"
/>
</DsfrNavigationMenuItem>
</ul>
</div>
</template>
ts
import type { RouteLocationRaw } from 'vue-router'
export type DsfrNavigationMenuLinkProps = {
id?: string
to?: string | RouteLocationRaw
text?: string
icon?: string
onClick?: ($event: MouseEvent) => void
}
export type DsfrNavigationMenuItemProps = {
id?: string
active?: boolean
}
export type DsfrNavigationMenuProps = {
id?: string
title: string
links?: DsfrNavigationMenuLinkProps[]
expandedId?: string
active?: boolean
}
export type DsfrNavigationItemProps = {
id?: string
active?: boolean
}
export type DsfrNavigationMegaMenuCategoryProps = {
title: string
active?: boolean
links: DsfrNavigationMenuLinkProps[]
}
export type DsfrNavigationMegaMenuProps = {
id?: string
title: string
description?: string
link?: { to: RouteLocationRaw, text: string }
menus?: DsfrNavigationMegaMenuCategoryProps[]
expandedId?: string
active?: boolean
}
export type DsfrNavigationMenuLinks = (DsfrNavigationMenuLinkProps | DsfrNavigationMegaMenuProps | DsfrNavigationMenuProps)[]
export type DsfrNavigationProps = {
id?: string
label?: string
navItems: (
DsfrNavigationMenuLinkProps
| DsfrNavigationMenuProps
| DsfrNavigationMegaMenuProps
)[]
}