130 lines
3.0 KiB
Vue

<script setup lang="ts">
import type { CSSProperties } from 'vue'
import { onClickOutside, useMediaQuery, useToggle } from '@vueuse/core'
import { nextTick, ref, useTemplateRef, watch } from 'vue'
import '@vuepress/helper/transition/fade-in.css'
const [show, toggle] = useToggle(false)
const el = useTemplateRef<HTMLSpanElement>('el')
const tooltip = useTemplateRef<HTMLSpanElement>('tooltip')
const styles = ref<CSSProperties>()
const isMobile = useMediaQuery('(max-width: 768px)')
const showTooltip = () => toggle(true)
const hiddenTooltip = () => toggle(false)
onClickOutside(el, () => {
if (isMobile.value)
hiddenTooltip()
}, {
ignore: [tooltip],
})
watch(show, () => nextTick(() => {
if (__VUEPRESS_SSR__)
return
if (show.value && tooltip.value) {
const { x, width } = tooltip.value.getBoundingClientRect()
const innerWidth = window.innerWidth
const space = 16
let translate = 0
if (x - space < 0)
translate = Math.abs(x) + space
else if (x + width + space > innerWidth)
translate = innerWidth - x - width - space
if (translate !== 0) {
styles.value = {
'--vp-abbr-transform': `translateX(${translate}px) translateX(-50%)`,
'--vp-abbr-space-transform': `translateX(${-translate}px) translateX(-50%)`,
}
}
}
}))
</script>
<template>
<span
ref="el"
class="vp-abbr"
role="tooltip"
tabindex="0"
v-bind="isMobile ? {
onClick: showTooltip,
} : {
onMouseenter: showTooltip,
onMouseleave: hiddenTooltip,
onFocus: showTooltip,
onBlur: hiddenTooltip,
}"
>
<slot />
<ClientOnly>
<Transition name="fade-in">
<span v-show="show" ref="tooltip" class="vp-abbr-tooltip ignore-header" :style="styles" aria-hidden="true">
<slot name="tooltip" />
</span>
</Transition>
</ClientOnly>
</span>
</template>
<style>
:root {
--vp-abbr-bg: var(--vp-c-bg);
--vp-abbr-text: var(--vp-c-text-1);
--vp-abbr-border: var(--vp-c-divider);
--vp-abbr-transform: translateX(-50%);
--vp-abbr-space-transform: translateX(-50%);
}
.vp-abbr {
position: relative;
text-decoration: underline dotted currentcolor;
text-underline-offset: 4px;
cursor: help;
}
.vp-abbr .vp-abbr-tooltip {
position: absolute;
top: calc(100% + 12px);
left: 50%;
z-index: 3;
width: max-content;
max-width: min(calc(100vw - 32px), 360px);
padding: 8px 14px;
font-size: 14px;
line-height: 1.7;
color: var(--vp-abbr-text);
cursor: auto;
background-color: var(--vp-abbr-bg);
border: solid 1px var(--vp-abbr-border);
border-radius: 4px;
box-shadow: var(--vp-shadow-2);
transform: var(--vp-abbr-transform);
}
.vp-abbr .vp-abbr-tooltip::before,
.vp-abbr .vp-abbr-tooltip::after {
position: absolute;
top: -16px;
left: 50%;
display: block;
width: 0;
height: 0;
content: "";
border: 8px solid transparent;
border-bottom-color: var(--vp-abbr-bg);
transform: var(--vp-abbr-space-transform);
}
.vp-abbr .vp-abbr-tooltip::before {
top: -17px;
border-bottom-color: var(--vp-abbr-border);
}
</style>