-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ✨ Nouveau composant Bulle d'aide
- Loading branch information
1 parent
f09021f
commit 64d1c11
Showing
7 changed files
with
338 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Information contextuelle et Infobulle - DsfrTooltip | ||
|
||
## 🌟 Introduction | ||
|
||
Le `DsfrTooltip` est un composant Vue versatile, conçu pour fournir des infobulles contextuelles. Il supporte le déclenchement au survol ou au clic, et s'adapte automatiquement à la position de l'élément source pour une visibilité optimale. Ce composant est idéal pour ajouter des explications ou des informations supplémentaires sans encombrer l'interface utilisateur. | ||
|
||
## 🛠️ Les props | ||
|
||
| Nom | Type | Défaut | Obligatoire | Description | | ||
|------------|-----------|----------------------|:-----------:|-------------------------------------------------------------| | ||
| `content` | `string` | | ✅ | Le texte à afficher dans l'infobulle. | | ||
| `onHover` | `boolean` | `false` | | Si `true`, l'infobulle s'affiche au survol. | | ||
| `id` | `string` | `getRandomId('tooltip')` | | Identifiant unique pour l'infobulle. Utilisé pour l'accessibilité. | | ||
|
||
## 📡 Événements | ||
|
||
- Aucun événement personnalisé n'est émis par ce composant. | ||
|
||
## 🧩 Les slots | ||
|
||
- `default` : Contenu personnalisé pour l'élément déclencheur de l'infobulle (peut être un lien ou un bouton selon `onHover`). | ||
|
||
## 📝 Exemples | ||
|
||
```vue | ||
<DsfrTooltip content="Voici une infobulle"> | ||
Survolez-moi | ||
</DsfrTooltip> | ||
``` | ||
|
||
## 📝 Toutes les variantes 🌈 d’info-bulles | ||
|
||
::: code-group | ||
|
||
<Story data-title="Démo" min-h="300px"> | ||
<DsfrTooltipExample /> | ||
</Story> | ||
|
||
<<< docs-demo/DsfrTooltipExample.vue [Code de la démo] | ||
|
||
<<< DsfrTooltip.vue | ||
::: | ||
|
||
Avec DsfrTooltip, révélez des informations cachées comme un magicien sort un lapin de son chapeau ! 🎩🐇✨ | ||
|
||
<script setup lang="ts"> | ||
import DsfrTooltipExample from './docs-demo/DsfrTooltipExample.vue' | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import DsfrTooltip from './DsfrTooltip.vue' | ||
export default { | ||
component: DsfrTooltip, | ||
title: 'Composants/DsfrTooltip', | ||
argTypes: { | ||
id: { | ||
control: 'text', | ||
description: '(optionnel) Valeur de l’attribut `id` du tooltip. Par défaut, un id pseudo-aléatoire sera donné.', | ||
}, | ||
content: { | ||
control: 'text', | ||
description: 'Contenu de votre bulle d’aide : il s’agit d’un texte sans mise en forme.', | ||
}, | ||
onHover: { | ||
control: 'boolean', | ||
description: 'Permet de définir si l’infobulle doit s’afficher au survol de l’élément (`true`) ou au clic (`false`, défaut).', | ||
}, | ||
}, | ||
} | ||
|
||
export const Infobulle = (args) => ({ | ||
components: { | ||
DsfrTooltip, | ||
}, | ||
|
||
data () { | ||
return { | ||
...args, | ||
} | ||
}, | ||
|
||
template: ` | ||
<DsfrTooltip | ||
:content="content" | ||
:on-hover="onHover" | ||
> | ||
Un élément intriguant | ||
</DsfrTooltip> | ||
`, | ||
|
||
}) | ||
Infobulle.args = { | ||
content: 'Un élément assez intriguant', | ||
onHover: false, | ||
} | ||
|
||
export const InfobulleParDefaut = (args) => ({ | ||
components: { | ||
DsfrTooltip, | ||
}, | ||
|
||
data () { | ||
return { | ||
...args, | ||
} | ||
}, | ||
|
||
template: ` | ||
<DsfrTooltip | ||
:content="content" | ||
> | ||
Un contenu qui n’apparaîtra que si hover est à \`true\` | ||
</DsfrTooltip> | ||
`, | ||
|
||
}) | ||
InfobulleParDefaut.args = { | ||
content: 'Un élément assez intriguant', | ||
} | ||
|
||
export const InfobulleAuSurvol = (args) => ({ | ||
components: { | ||
DsfrTooltip, | ||
}, | ||
|
||
data () { | ||
return { | ||
...args, | ||
} | ||
}, | ||
|
||
template: ` | ||
Du texte | ||
<DsfrTooltip | ||
:content="content" | ||
onHover | ||
> | ||
avec une précision à donner ici | ||
</DsfrTooltip> | ||
`, | ||
|
||
}) | ||
InfobulleAuSurvol.args = { | ||
content: 'Texte précisant pourquoi ce texte est là', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
<script setup lang="ts"> | ||
import { computed, defineProps, ref, watch, onUnmounted, onMounted } from 'vue' | ||
import { getRandomId } from '../../utils/random-utils' | ||
const props = withDefaults(defineProps<{ | ||
content: string | ||
onHover?: boolean | ||
id?: string, | ||
}>(), { | ||
id: () => getRandomId('tooltip'), | ||
}) | ||
const show = ref(false) | ||
const source = ref<HTMLElement | null>(null) | ||
const tooltip = ref<HTMLElement | null>(null) | ||
const translateX = ref('0px') | ||
const translateY = ref('0px') | ||
const arrowX = ref('0px') | ||
const top = ref(false) | ||
const opacity = ref(0) | ||
watch(show, async (value) => { | ||
if (typeof document === 'undefined') { | ||
return | ||
} | ||
if (!value) { | ||
return | ||
} | ||
opacity.value = 0 | ||
await new Promise(resolve => setTimeout(resolve, 100)) | ||
const sourceTop = source.value?.offsetTop | ||
const sourceHeight = source.value?.offsetHeight | ||
const sourceWidth = source.value?.offsetWidth | ||
const sourceLeft = source.value?.offsetLeft | ||
const tooltipHeight = tooltip.value?.offsetHeight | ||
const tooltipWidth = tooltip.value?.offsetWidth | ||
const isSourceAtTop = (sourceTop - tooltipHeight) < 0 | ||
const isSourceAtBottom = !isSourceAtTop && (sourceTop + sourceHeight + tooltipHeight) >= document.documentElement.offsetHeight | ||
top.value = isSourceAtBottom | ||
const isSourceOnRightSide = (sourceLeft + sourceWidth) >= document.documentElement.offsetWidth | ||
const isSourceOnLeftSide = (sourceLeft + (sourceWidth / 2) - (tooltipWidth / 2)) <= 0 | ||
translateY.value = isSourceAtBottom | ||
? `${sourceTop - tooltipHeight + 8}px` | ||
: `${sourceTop + sourceHeight - 8}px` | ||
opacity.value = 1 | ||
translateX.value = isSourceOnRightSide | ||
? `${sourceLeft + sourceWidth - tooltipWidth - 4}px` | ||
: isSourceOnLeftSide | ||
? `${sourceLeft + 4}px` | ||
: `${sourceLeft + (sourceWidth / 2) - (tooltipWidth / 2)}px` | ||
arrowX.value = isSourceOnRightSide | ||
? `${(tooltipWidth / 2) - (sourceWidth / 2) + 4}px` | ||
: isSourceOnLeftSide | ||
? `${-(tooltipWidth / 2) + (sourceWidth / 2) - 4}px` | ||
: '0px' | ||
}) | ||
const tooltipStyle = computed(() => (`transform: translate(${translateX.value}, ${translateY.value}); --arrow-x: ${arrowX.value}; opacity: ${opacity.value};'`)) | ||
const tooltipClass = computed(() => ({ | ||
'fr-tooltip--shown': show.value, | ||
'fr-placement--top': top.value, | ||
'fr-placement--bottom': !top.value, | ||
})) | ||
const clickListener = (event: MouseEvent) => { | ||
if (!show.value) { | ||
return | ||
} | ||
if (event.target === source.value || source.value?.contains(event.target as Node)) { | ||
return | ||
} | ||
if (event.target === tooltip.value || tooltip.value?.contains(event.target as Node)) { | ||
return | ||
} | ||
show.value = false | ||
} | ||
onMounted(() => { | ||
document.documentElement.addEventListener('click', clickListener) | ||
}) | ||
onUnmounted(() => { | ||
document.documentElement.removeEventListener('click', clickListener) | ||
}) | ||
const onMouseEnter = () => { | ||
if (props.onHover) { | ||
show.value = true | ||
} | ||
} | ||
const onMouseLeave = () => { | ||
if (props.onHover) { | ||
show.value = false | ||
} | ||
} | ||
const onClick = () => { | ||
if (!props.onHover) { | ||
show.value = !show.value | ||
} | ||
} | ||
</script> | ||
|
||
<template> | ||
<component | ||
:is="onHover ? 'a' : 'button'" | ||
:id="'link-' + id" | ||
ref="source" | ||
:class="onHover ? 'fr-link' : 'fr-btn fr-btn--tooltip'" | ||
:aria-describedby="id" | ||
:href="onHover ? '#' : undefined" | ||
@click="onClick()" | ||
@mouseenter="onMouseEnter()" | ||
@mouseleave="onMouseLeave()" | ||
> | ||
<slot /> | ||
</component> | ||
<span | ||
:id="id" | ||
ref="tooltip" | ||
class="fr-tooltip fr-placement" | ||
:class="tooltipClass" | ||
:style="tooltipStyle" | ||
role="tooltip" | ||
aria-hidden="true" | ||
> | ||
{{ content }} | ||
</span> | ||
</template> | ||
|
||
<style scoped> | ||
.fr-tooltip { | ||
transition: opacity 0.3s ease-in-out; | ||
} | ||
</style> |
37 changes: 37 additions & 0 deletions
37
src/components/DsfrTooltip/docs-demo/DsfrTooltipExample.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<script lang="ts" setup> | ||
import DsfrTooltip from '../DsfrTooltip.vue' | ||
</script> | ||
|
||
<template> | ||
<div | ||
class="flex flex-col justify-between w-full" | ||
style="height: 300px" | ||
> | ||
<div class="flex justify-between w-full"> | ||
<DsfrTooltip | ||
on-hover | ||
content="Texte de l’info-bulle en haut à gauche qui peut être très très long" | ||
> | ||
Avec du texte ici | ||
</DsfrTooltip> | ||
<DsfrTooltip | ||
content="Texte de l’info-bulle en haut à droite qui peut être très très long" | ||
/> | ||
</div> | ||
|
||
<div class="flex justify-center w-full"> | ||
<DsfrTooltip | ||
content="Texte de l’info-bulle au centre qui peut être très très long" | ||
/> | ||
</div> | ||
|
||
<div class="flex justify-between w-full"> | ||
<DsfrTooltip | ||
content="Texte de l’info-bulle en bas à gauche qui peut être très très long" | ||
/> | ||
<DsfrTooltip | ||
content="Texte de l’info-bulle en bas à droite qui peut être très très long" | ||
/> | ||
</div> | ||
</div> | ||
</template> |