Navigation principale - DsfrNavigation
🌟 Introduction
Le système de navigation principal permet d'orienter les utilisateurs à travers l'application. Il constitue l'épine dorsale de la navigation d'un site, offrant une structure claire et accessible pour explorer les différentes sections et fonctionnalités.
Le composant DsfrNavigation est le système central de navigation au sein d'un site. Il permet d'orienter aisément l'usager à travers l'application avec une structure hiérarchique claire et des menus déroulants.
🏅 La documentation sur la navigation sur le DSFR
La story sur la navigation sur le storybook de VueDsfr📐 Structure
La navigation principale est composée des éléments suivants :
- un conteneur principal avec un identifiant unique (prop
id) - un label d'accessibilité (prop
label) - une liste de liens et sous-menus (prop
navItems) organisée hiérarchiquement - des menus déroulants qui s'ouvrent/ferment au clic
- une gestion des événements clavier pour l'accessibilité (touches Échap, flèches)
🛠️ Props
| nom | type | défaut | obligatoire | description |
|---|---|---|---|---|
id | string | () => useRandomId(...) | Identifiant unique pour la navigation | |
label | string | 'Menu principal' | Nom associé à la navigation pour l'accessibilité | |
navItems | array | () => [] | ✅ | Tableau contenant les liens ou sous-menus de la navigation |
📡 Événements
DsfrNavigation déclenche les événements suivants :
| nom | donnée (payload) | description |
|---|---|---|
click | aucune | Émis au clic qui déclenche l'ouverture ou la fermeture d'un menu |
keydown | aucune | Émis en appuyant sur Échap qui déclenche la fermeture d'un menu ouvert |
🧩 Slots
DsfrNavigation possède un slot par défaut pour le contenu personnalisé de la navigation.
| nom | description |
|---|---|
default | Slot par défaut pour le contenu personnalisé de la navigation |
📝 Exemples
Exemple simple d'utilisation de DsfrNavigation :
<script lang="ts" setup>
import DsfrNavigation from '../DsfrNavigation.vue'
const navItemsDemo = [
{
to: '/accueil',
text: 'Accueil',
},
{
to: '/tableau-de-bord',
text: 'Tableau de bord',
},
{
to: '/historique',
text: 'Historique',
},
]
</script>
<template>
<DsfrNavigation
label="Menu principal démo"
:nav-items="navItemsDemo"
/>
</template>⚙️ Code source du composant
<script lang="ts" setup>
import type {
DsfrNavigationMegaMenuProps,
DsfrNavigationMenuLinkProps,
DsfrNavigationMenuLinks,
DsfrNavigationMenuProps,
DsfrNavigationProps,
} from './DsfrNavigation.types'
import { onMounted, onUnmounted, ref } from 'vue'
import { useRandomId } from '../../utils/random-utils'
import DsfrNavigationItem from './DsfrNavigationItem.vue'
import DsfrNavigationMegaMenu from './DsfrNavigationMegaMenu.vue'
import DsfrNavigationMenu from './DsfrNavigationMenu.vue'
import DsfrNavigationMenuLink from './DsfrNavigationMenuLink.vue'
export type { DsfrNavigationMenuLinks, DsfrNavigationProps }
const props = withDefaults(defineProps<DsfrNavigationProps>(), {
id: () => useRandomId('nav'),
label: 'Menu principal',
navItems: () => [],
})
const expandedMenuId = ref<string | undefined>(undefined)
const toggle = (id: string | undefined) => {
if (id === expandedMenuId.value) {
expandedMenuId.value = undefined
return
}
expandedMenuId.value = id
}
const handleElementClick = (el: HTMLElement) => {
if (el === document.getElementById(props.id)) {
return
}
if (!el?.parentNode) {
toggle(expandedMenuId.value)
return
}
handleElementClick(el.parentNode as HTMLElement)
}
const onDocumentClick = (e: MouseEvent) => {
handleElementClick(e.target as HTMLElement)
}
const onKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
toggle(expandedMenuId.value)
}
}
onMounted(() => {
document.addEventListener('click', onDocumentClick)
document.addEventListener('keydown', onKeyDown)
})
onUnmounted(() => {
document.removeEventListener('click', onDocumentClick)
document.removeEventListener('keydown', onKeyDown)
})
</script>
<template>
<nav
:id="id"
class="fr-nav"
role="navigation"
:aria-label="label"
>
<ul class="fr-nav__list">
<!-- @slot Slot par défaut pour le contenu de la liste. Sera dans `<ul class="fr-nav__list">` -->
<slot />
<DsfrNavigationItem
v-for="(navItem, idx) of navItems"
:id="navItem.id"
:key="idx"
>
<DsfrNavigationMenuLink
v-if="(navItem as DsfrNavigationMenuLinkProps).to && (navItem as DsfrNavigationMenuLinkProps).text"
v-bind="navItem"
:expanded-id="expandedMenuId"
@toggle-id="toggle($event)"
/>
<!-- @vue-ignore -->
<DsfrNavigationMenu
v-else-if="(navItem as DsfrNavigationMenuProps).title && (navItem as DsfrNavigationMenuProps).links"
v-bind="(navItem as DsfrNavigationMenuProps)"
:expanded-id="expandedMenuId"
@toggle-id="toggle($event)"
/>
<!-- @vue-ignore -->
<DsfrNavigationMegaMenu
v-else-if="(navItem as DsfrNavigationMegaMenuProps).title && (navItem as DsfrNavigationMegaMenuProps).menus"
v-bind="(navItem as DsfrNavigationMegaMenuProps)"
:expanded-id="expandedMenuId"
@toggle-id="toggle($event)"
/>
</DsfrNavigationItem>
</ul>
</nav>
</template>
<style>
.fr-nav__list {
position: relative;
}
</style>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
)[]
}