Lien d’en-tête - DsfrHeaderMenuLink
🌟 Introduction
Le composant DsfrHeaderMenuLink
offre une flexibilité pour des liens internes, externes, des boutons, et même des liens mailto, tout en intégrant la possibilité d'ajouter des icônes du DSFR ou grâce à Iconify
(cf. la documentation sur les icônes VueDsfr).
📐 Structure
<DsfrHeaderMenuLink>
: Le composant principal.- Utilise
<component :is="...">
pour rendre dynamiquement le bon type d'élément (lien, bouton, ou RouterLink). - Intègre des icônes avec
<VIcon>
si nécessaire.
- Utilise
🛠️ Props
Propriété | Type | Description | Valeur par défaut |
---|---|---|---|
button | boolean | Si vrai, le composant se comporte comme un bouton. | undefined |
icon | string | VIcon['$props'] | Nom de l'icône ou les props de l'icône à utiliser. | undefined |
iconAttrs | VIcon['$props'] & import('vue').HTMLAttributes | Attributs supplémentaires pour l'icône. | {} |
iconRight | boolean | Si vrai, positionne l'icône à droite du label. | false |
label | string | Texte du lien ou du bouton. | '' |
target | string | Cible du lien, par exemple _blank pour ouvrir dans un nouvel onglet. | '_self' |
onClick | Function | Fonction à exécuter lors du clic sur le composant. | () => undefined |
to | import('vue-router').RouteLocationRaw | Destination pour le routeur Vue, si utilisé comme lien de navigation interne. | undefined |
href | string | URL pour les liens externes. | undefined |
📡 Événements
click
: Événement déclenché lorsque l'utilisateur clique sur le composant.
🧩 Slots
Pas de slots spécifiques pour ce composant. Il utilise le label
pour afficher le texte du lien ou du bouton.
📝 Exemples
Ce composant est utilisé en interne dans DsfrHeader. Il n’y a pas de raison de l’utiliser en dehors.
⚙️ Code source du composant
vue
<script lang="ts" setup>
import { computed } from 'vue'
import VIcon from '../VIcon/VIcon.vue'
import type { DsfrHeaderMenuLinkProps } from './DsfrHeader.types'
export type { DsfrHeaderMenuLinkProps }
const props = withDefaults(defineProps<DsfrHeaderMenuLinkProps>(), {
icon: undefined,
iconAttrs: () => ({}),
onClick: () => undefined,
target: '_self',
label: '',
href: undefined,
to: undefined,
path: '',
})
const isPathString = computed(() => {
return typeof props.path === 'string'
})
const isExternalLink = computed(() => {
return props.href?.startsWith('http') ||
(isPathString.value && (props.path as string).startsWith('http')) ||
(typeof props.to === 'string' && (props.to as string).startsWith('http'))
})
const isMailto = computed(() => {
return props.href?.startsWith('mailto') ||
(isPathString.value && (props.path as string).startsWith('mailto')) ||
(typeof props.to === 'string' && (props.to as string).startsWith('mailto'))
})
const is = computed(() => {
if (props.button) {
return 'button'
}
return isExternalLink.value || isMailto.value ? 'a' : 'RouterLink'
})
const actualHref = computed(() => {
if (!isExternalLink.value && !isMailto.value) {
return undefined
}
return props.to ?? props.href ?? props.path
})
const actualTo = computed(() => {
if (isExternalLink.value || isMailto.value) {
return undefined
}
return props.to ?? props.path
})
const linkData = computed(() => {
return actualTo.value ? { to: actualTo.value } : { href: actualHref.value }
})
const dsfrIcon = computed(() => typeof props.icon === 'string' && props.icon.startsWith('fr-icon-'))
const defaultScale = 1
const iconProps = computed(() => typeof props.icon === 'string'
? { name: props.icon, scale: defaultScale, ...(props.iconAttrs ?? {}) }
: { scale: defaultScale, ...(props.icon ?? {}), ...(props.iconAttrs ?? {}) },
)
</script>
<template>
<component
:is="is"
class="fr-btn"
:class="{
'fr-btn--icon-right': dsfrIcon && iconRight,
'fr-btn--icon-left': dsfrIcon && !iconRight,
[String(icon)]: dsfrIcon,
}"
v-bind="linkData"
:target="target"
@click.stop="onClick($event)"
>
<template
v-if="!dsfrIcon && (icon || iconAttrs?.name) && !iconRight"
>
<VIcon
class="fr-mr-1w"
v-bind="iconProps"
/>
</template>
{{ label }}
<template
v-if="!dsfrIcon && (icon || iconAttrs?.name) && iconRight"
>
<VIcon
class="fr-ml-1w"
v-bind="iconProps"
/>
</template>
</component>
</template>
ts
import type { HTMLAttributes, StyleValue } from 'vue'
import type { RouteLocationRaw } from 'vue-router'
import type { DsfrLanguageSelectorProps } from '../DsfrLanguageSelector/DsfrLanguageSelector.types'
import type VIcon from '../VIcon/VIcon.vue'
export type DsfrHeaderMenuLinkProps = {
button?: boolean
icon?: string | InstanceType<typeof VIcon>['$props']
iconAttrs?: InstanceType<typeof VIcon>['$props'] & HTMLAttributes
iconRight?: boolean
label?: string
target?: string
onClick?: ($event: MouseEvent) => void
to?: RouteLocationRaw
/**
* @deprecated Use the prop `to` instead
*/
href?: string
/**
* @deprecated Use the prop `to` instead
*/
path?: string
}
export type DsfrHeaderProps = {
searchbarId?: string
serviceTitle?: string
serviceDescription?: string
homeTo?: string
logoText?: string | string[]
modelValue?: string
operatorImgAlt?: string
operatorImgSrc?: string
operatorImgStyle?: StyleValue
placeholder?: string
quickLinks?: (DsfrHeaderMenuLinkProps & HTMLAttributes)[]
languageSelector?: DsfrLanguageSelectorProps
searchLabel?: string
quickLinksAriaLabel?: string
showSearch?: boolean
showSearchLabel?: string
showBeta?: boolean
menuLabel?: string
menuModalLabel?: string
closeMenuModalLabel?: string
homeLabel?: string
}