Popover (fenêtre contextuelle)
Couche flottante à contenu riche : elle transporte plus d'informations qu'un Tooltip (titre, corps, boutons d'action). Le déclencheur est enveloppé dans le slot par défaut, tandis que le contenu flottant est transmis via le slot content. Lors d'un déclenchement par clic, la couche flottante a role="dialog" et piège le focus ; lors d'un déclenchement par survol, elle se contente d'afficher le contenu sans interrompre le flux clavier.
Utilisation de base
<script setup lang="ts">
</script>
<template>
<LhPopover title="Astuce encre de Chine" placement="top">
<LhButton>Afficher au clic</LhButton>
<template #content>
<p>Ceci est un contenu riche, dans lequel vous pouvez placer n'importe quel markup.</p>
</template>
</LhPopover>
</template><script>
export default {}
</script>
<template>
<LhPopover title="Astuce encre de Chine" placement="top">
<LhButton>Afficher au clic</LhButton>
<template #content>
<p>Ceci est un contenu riche, dans lequel vous pouvez placer n'importe quel markup.</p>
</template>
</LhPopover>
</template>Modes de déclenchement
<template>
<!-- Déclenchement au survol -->
<LhPopover trigger="hover" title="Déclenchement au survol" :show-after="0">
<LhButton>Survolez-moi</LhButton>
<template #content><p>Déplacer le pointeur à l'intérieur de la couche flottante ne la ferme pas.</p></template>
</LhPopover>
<!-- Déclenchement au focus -->
<LhPopover trigger="focus" title="Déclenchement au focus" :show-after="0">
<LhInput placeholder="Champ de saisie" />
<template #content><p>Affiché lorsque le champ reçoit le focus.</p></template>
</LhPopover>
<!-- Contrôle manuel -->
<LhPopover v-model:visible="manualVisible" trigger="manual" title="Contrôle manuel">
<LhButton @click="manualVisible = !manualVisible">Commutateur externe</LhButton>
<template #content><p>L'affichage est entièrement contrôlé par le v-model externe.</p></template>
</LhPopover>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const manualVisible = ref(false)
</script><template>
<!-- Déclenchement au survol -->
<LhPopover trigger="hover" title="Déclenchement au survol" :show-after="0">
<LhButton>Survolez-moi</LhButton>
<template #content><p>Déplacer le pointeur à l'intérieur de la couche flottante ne la ferme pas.</p></template>
</LhPopover>
<!-- Déclenchement au focus -->
<LhPopover trigger="focus" title="Déclenchement au focus" :show-after="0">
<LhInput placeholder="Champ de saisie" />
<template #content><p>Affiché lorsque le champ reçoit le focus.</p></template>
</LhPopover>
<!-- Contrôle manuel -->
<LhPopover v-model:visible="manualVisible" trigger="manual" title="Contrôle manuel">
<LhButton @click="manualVisible = !manualVisible">Commutateur externe</LhButton>
<template #content><p>L'affichage est entièrement contrôlé par le v-model externe.</p></template>
</LhPopover>
</template>
<script>
export default {
data() {
return { manualVisible: false }
},
}
</script>Douze orientations
<template>
<LhPopover
v-for="p in placements"
:key="p"
:placement="p"
:title="p"
trigger="hover"
:show-after="0"
>
<LhButton size="small">{{ p }}</LhButton>
<template #content><p>Orientation : {{ p }}</p></template>
</LhPopover>
</template>
<script setup lang="ts">
const placements = [
'top', 'top-start', 'top-end',
'bottom', 'bottom-start', 'bottom-end',
'left', 'left-start', 'left-end',
'right', 'right-start', 'right-end',
] as const
</script><template>
<LhPopover
v-for="p in placements"
:key="p"
:placement="p"
:title="p"
trigger="hover"
:show-after="0"
>
<LhButton size="small">{{ p }}</LhButton>
<template #content><p>Orientation : {{ p }}</p></template>
</LhPopover>
</template>
<script>
export default {
data() {
return {
placements: [
'top', 'top-start', 'top-end',
'bottom', 'bottom-start', 'bottom-end',
'left', 'left-start', 'left-end',
'right', 'right-start', 'right-end',
],
}
},
}
</script>En cas d'espace insuffisant dans la fenêtre, la couche flottante retourne automatiquement (flip) et se recentre sur l'axe secondaire (shift) ; la position de la flèche s'adapte en conséquence.
Contenu interactif imbriqué
<template>
<LhPopover placement="right" title="Note rapide">
<LhButton>Ajouter une note</LhButton>
<template #content>
<div class="demo-popover-form">
<LhInput v-model="note" placeholder="Saisir le contenu de la note" />
<LhButton type="primary" @click="saveNote">Enregistrer</LhButton>
</div>
</template>
</LhPopover>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const note = ref('')
const saved = ref('')
function saveNote() {
saved.value = note.value
note.value = ''
}
</script><template>
<LhPopover placement="right" title="Note rapide">
<LhButton>Ajouter une note</LhButton>
<template #content>
<div class="demo-popover-form">
<LhInput v-model="note" placeholder="Saisir le contenu de la note" />
<LhButton type="primary" @click="saveNote">Enregistrer</LhButton>
</div>
</template>
</LhPopover>
</template>
<script>
export default {
data() {
return { note: '', saved: '' }
},
methods: {
saveNote() {
this.saved = this.note
this.note = ''
},
},
}
</script>Autres propriétés
<script setup lang="ts">
</script>
<template>
<!-- Désactivé -->
<LhPopover disabled title="Désactivé">
<LhButton disabled>Déclencheur désactivé</LhButton>
<template #content><p>Lorsque disabled est actif, le déclencheur et la couche flottante ne réagissent plus.</p></template>
</LhPopover>
<!-- Sans flèche -->
<LhPopover title="Sans flèche" :arrow="false" :show-after="0">
<LhButton>Sans flèche</LhButton>
<template #content><p>Lorsque arrow vaut false, la flèche directionnelle n'est pas rendue.</p></template>
</LhPopover>
<!-- Largeur fixe -->
<LhPopover title="Largeur fixe" width="240" :show-after="0">
<LhButton>Largeur fixe</LhButton>
<template #content><p>La largeur de la couche flottante est définie via width, ici 240px.</p></template>
</LhPopover>
<!-- Masquage différé -->
<LhPopover title="Masquage différé" :hide-after="800" :show-after="0">
<LhButton>Masquage différé</LhButton>
<template #content><p>Le repli n'intervient que 800 ms après la sortie du déclencheur.</p></template>
</LhPopover>
<!-- Classe personnalisée -->
<LhPopover title="Classe personnalisée" popper-class="demo-popper-accent" :show-after="0">
<LhButton>Classe personnalisée</LhButton>
<template #content><p>popperClass est ajouté à l'élément racine de la couche flottante, afin de personnaliser les styles.</p></template>
</LhPopover>
<!-- Cible de téléportation -->
<LhPopover title="Cible de téléportation" teleport-target="#demo-popover-host" :show-after="0">
<LhButton>Conteneur Teleport</LhButton>
<template #content><p>La couche flottante est téléportée dans le conteneur ci-dessous et non dans body.</p></template>
</LhPopover>
</template><script>
export default {}
</script>
<template>
<!-- Désactivé -->
<LhPopover disabled title="Désactivé">
<LhButton disabled>Déclencheur désactivé</LhButton>
<template #content><p>Lorsque disabled est actif, le déclencheur et la couche flottante ne réagissent plus.</p></template>
</LhPopover>
<!-- Sans flèche -->
<LhPopover title="Sans flèche" :arrow="false" :show-after="0">
<LhButton>Sans flèche</LhButton>
<template #content><p>Lorsque arrow vaut false, la flèche directionnelle n'est pas rendue.</p></template>
</LhPopover>
<!-- Largeur fixe -->
<LhPopover title="Largeur fixe" width="240" :show-after="0">
<LhButton>Largeur fixe</LhButton>
<template #content><p>La largeur de la couche flottante est définie via width, ici 240px.</p></template>
</LhPopover>
<!-- Masquage différé -->
<LhPopover title="Masquage différé" :hide-after="800" :show-after="0">
<LhButton>Masquage différé</LhButton>
<template #content><p>Le repli n'intervient que 800 ms après la sortie du déclencheur.</p></template>
</LhPopover>
<!-- Classe personnalisée -->
<LhPopover title="Classe personnalisée" popper-class="demo-popper-accent" :show-after="0">
<LhButton>Classe personnalisée</LhButton>
<template #content><p>popperClass est ajouté à l'élément racine de la couche flottante, afin de personnaliser les styles.</p></template>
</LhPopover>
<!-- Cible de téléportation -->
<LhPopover title="Cible de téléportation" teleport-target="#demo-popover-host" :show-after="0">
<LhButton>Conteneur Teleport</LhButton>
<template #content><p>La couche flottante est téléportée dans le conteneur ci-dessous et non dans body.</p></template>
</LhPopover>
</template>Affichage contrôlé
v-model:visible contrôle manuellement l'affichage de la couche flottante.
<script setup lang="ts">
import { ref } from 'vue'
const popVisible = ref(false)
</script>
<template>
<LhPopover v-model:visible="popVisible" title="Couche flottante contrôlée" trigger="manual">
<LhButton>{{ popVisible ? 'Replier' : 'Déplier' }}</LhButton>
</LhPopover>
</template><script>
export default {
data() {
return { popVisible: false }
},
}
</script>
<template>
<LhPopover v-model:visible="popVisible" title="Couche flottante contrôlée" trigger="manual">
<LhButton>{{ popVisible ? 'Replier' : 'Déplier' }}</LhButton>
</LhPopover>
</template>API Popover
Props
| Prop | Description | Type | Valeur par défaut |
|---|---|---|---|
| placement | Position d'affichage | "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "left-start" | "left-end" | "right" | "right-start" | "right-end" | 'top' |
| trigger | Mode de déclenchement | "click" | "hover" | "focus" | "manual" | 'click' |
| title | Titre (le slot title fonctionne aussi) | string | — |
| visible | État d'affichage contrôlé manuellement (v-model:visible) | boolean | — |
| disabled | Désactivé | boolean | — |
| arrow | Afficher la flèche | boolean | true |
| width | Largeur de la couche flottante | string | number | — |
| popperClass | Nom de classe personnalisé de la couche flottante | string | — |
| teleportTarget | Cible du Teleport (sélecteur ou élément) | string | HTMLElement | 'body' |
| showAfter | Délai d'affichage (ms) | number | — |
| hideAfter | Délai de masquage (ms) | number | — |
| themeVars | Variables de thème au niveau du composant, prime sur la configuration de ConfigProvider | { [x: string]: string; } | — |
| bgPattern | Motif d'arrière-plan au niveau du composant, prime sur la configuration de ConfigProvider | string | — |
| sx | Props de mise en page sx (appliquées à l'élément racine du déclencheur) | LhSxProps | — |
Events
| Événement | Description | Paramètre |
|---|---|---|
| update:visible | Changement d'affichage | (value: boolean) |
| show | Émis après l'affichage | — |
| hide | Émis après le masquage | — |
Slots
| Slot | Description |
|---|---|
| default | Contenu du déclencheur |
| title | Titre personnalisé |
| content | Contenu flottant |
Methods
| Méthode | Description |
|---|---|
| show | Affichage programmatique |
| hide | Masquage programmatique |
| toggle | Basculement programmatique |
Accessibilité
- Lors d'un déclenchement par clic, la couche flottante a
role="dialog"+aria-modal="true"; le déclencheur portearia-haspopup/aria-expanded - Le déclenchement par clic déplace le focus vers le premier élément focalisable de la couche flottante, Tab boucle à l'intérieur de celle-ci et, à la fermeture, le focus est rendu au déclencheur
- Lors d'un déclenchement par survol / focus, le focus n'est pas déplacé, afin de ne pas interrompre la navigation clavier
- Escape ferme la couche, ainsi qu'un clic à l'extérieur ; en présence d'un titre, celui-ci est lié via
aria-labelledby - Le moteur de positionnement est développé en interne sous forme de fonctions pures (flip/shift anti-débordement), sans aucune dépendance runtime tierce
Repli lorsque le déclencheur sort de la fenêtre : la couche flottante est positionnée en
fixed; une fois le déclencheur hors de la fenêtre, elle ne le suit plus et reste suspendue au-dessus du contenu de la page avec un z-index élevé. C'est pourquoi, quel que soit le mode de déclenchement (y compris le mode contrôlémanual), lorsque le déclencheur sort entièrement de la fenêtre, le panneau se replie et émetupdate:visiblepour synchroniser l'état externe.
Définitions de types
Ce composant exporte les structures de types TypeScript suivantes (extraites automatiquement de packages/components/popover/src/types.ts et des sources @lh-ui/share/core/* ; cliquez sur chaque entrée pour déplier la définition complète ; la description des props se trouve dans le tableau Props ci-dessus) :
LhPopoverProps
/**
* Props du composant Popover
*/
export interface LhPopoverProps {
/** Position d'affichage */
placement?: LhPlacement
/** Mode de déclenchement */
trigger?: LhPopoverTrigger
/** Titre */
title?: string
/** État d'affichage contrôlé manuellement (v-model:visible) */
visible?: boolean
/** Désactivé */
disabled?: boolean
/** Afficher la flèche */
arrow?: boolean
/** Largeur de la couche flottante */
width?: string | number
/** Nom de classe personnalisé de la couche flottante */
popperClass?: string
/** Cible du Teleport (sélecteur ou élément, body par défaut) */
teleportTarget?: string | HTMLElement
/** Délai d'affichage (ms) */
showAfter?: number
/** Délai de masquage (ms) */
hideAfter?: number
/** Variables de thème au niveau du composant, prime sur la configuration de ConfigProvider */
themeVars?: Record<string, string>
/** Motif d'arrière-plan au niveau du composant, prime sur la configuration de ConfigProvider */
bgPattern?: string
/** Props de mise en page sx (appliquées à l'élément racine du déclencheur) */
sx?: LhSxProps
}LhPopoverPlacement (provenant de @lh-ui/share/core/shared/popper)
/**
* Moteur de positionnement Popper (fonctions pures, sans dépendance framework)
*
* Calculs géométriques partagés par les couches flottantes
* (Tooltip / Popover / Popconfirm / Dropdown, etc.) :
* positionnement selon 12 placements, retournement sur l'axe principal en cas de
* débordement (flip), recentrage sur l'axe secondaire (shift).
* Tous les paramètres sont des littéraux rect, sans toucher au DOM,
* directement testables par tests unitaires.
*/
/** Orientation de la couche flottante (direction + alignement) */
export type LhPlacement =
| 'top'
| 'top-start'
| 'top-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'left'
| 'left-start'
| 'left-end'
| 'right'
| 'right-start'
| 'right-end'LhPopoverTrigger (provenant de @lh-ui/share/core/popover (logic.ts))
/** Mode de déclenchement */
export type LhPopoverTrigger = 'click' | 'hover' | 'focus' | 'manual'