[READ-ONLY] a fast, modern browser for the npm registry
at main 71 lines 1.8 kB view raw
1<script setup lang="ts"> 2const props = defineProps<{ 3 /** Tooltip text (optional when using content slot) */ 4 text?: string 5 /** Position: 'top' | 'bottom' | 'left' | 'right' */ 6 position?: 'top' | 'bottom' | 'left' | 'right' 7 /** Enable interactive tooltip (pointer events + hide delay for clickable content) */ 8 interactive?: boolean 9 /** Teleport target for the tooltip content (defaults to 'body') */ 10 to?: string | HTMLElement 11 /** Whether to defer teleport rendering until after the component is mounted */ 12 defer?: boolean 13 /** Offset distance in pixels (default: 4) */ 14 offset?: number 15}>() 16 17const isVisible = shallowRef(false) 18const tooltipId = useId() 19const hideTimeout = shallowRef<ReturnType<typeof setTimeout> | null>(null) 20 21function show() { 22 if (hideTimeout.value) { 23 clearTimeout(hideTimeout.value) 24 hideTimeout.value = null 25 } 26 isVisible.value = true 27} 28 29function hide() { 30 if (props.interactive) { 31 // Delay hide so cursor can travel from trigger to tooltip 32 hideTimeout.value = setTimeout(() => { 33 isVisible.value = false 34 }, 150) 35 } else { 36 isVisible.value = false 37 } 38} 39 40const tooltipAttrs = computed(() => { 41 const attrs: Record<string, unknown> = { role: 'tooltip', id: tooltipId } 42 if (props.interactive) { 43 attrs.onMouseenter = show 44 attrs.onMouseleave = hide 45 } 46 return attrs 47}) 48</script> 49 50<template> 51 <TooltipBase 52 :text 53 :isVisible 54 :position 55 :interactive 56 :to 57 :defer 58 :offset 59 :tooltip-attr="tooltipAttrs" 60 @mouseenter="show" 61 @mouseleave="hide" 62 @focusin="show" 63 @focusout="hide" 64 :aria-describedby="isVisible ? tooltipId : undefined" 65 > 66 <slot /> 67 <template v-if="$slots.content" #content> 68 <slot name="content" /> 69 </template> 70 </TooltipBase> 71</template>