mirror of
https://github.com/cupcakearmy/svelte-hint.git
synced 2025-09-06 16:20:40 +00:00
94 lines
2.4 KiB
Svelte
94 lines
2.4 KiB
Svelte
<script lang="ts" context="module">
|
|
import type { DOMDetectOverflowOptions, Placement } from '@floating-ui/dom'
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
import { computePosition, flip, shift, offset as offsetMiddleware, autoPlacement } from '@floating-ui/dom'
|
|
import { onMount } from 'svelte'
|
|
|
|
export let placement: Placement | undefined = undefined
|
|
export let text: string | null = null
|
|
export let boundary: DOMDetectOverflowOptions['boundary'] = 'clippingParents'
|
|
export let offset: Parameters<typeof offsetMiddleware>[0] = 4
|
|
export let auto: boolean | 'start' | 'end' = false
|
|
|
|
let trigger: HTMLSpanElement | null = null
|
|
let hint: HTMLSpanElement | null = null
|
|
let dataShow = false
|
|
let id = ''
|
|
|
|
async function update() {
|
|
if (!hint || !trigger) return
|
|
|
|
const options = {
|
|
placement: placement as Placement,
|
|
middleware: auto
|
|
? [offsetMiddleware(offset), autoPlacement({ boundary, alignment: typeof auto === 'string' ? auto : null })]
|
|
: [offsetMiddleware(offset), flip({ boundary }), shift({ boundary, padding: 4 })],
|
|
}
|
|
const { x, y } = await computePosition(trigger, hint, options)
|
|
Object.assign(hint!.style, {
|
|
left: `${x}px`,
|
|
top: `${y}px`,
|
|
})
|
|
}
|
|
|
|
async function show() {
|
|
await update()
|
|
dataShow = true
|
|
}
|
|
|
|
function hide() {
|
|
dataShow = false
|
|
}
|
|
|
|
onMount(() => {
|
|
update()
|
|
id = Math.random().toString(16).slice(2) // Random id for the tooltip
|
|
|
|
// Bind events
|
|
const events: [keyof HTMLElementEventMap, EventListener][] = [
|
|
['mouseenter', show],
|
|
['focus', show],
|
|
['mouseleave', hide],
|
|
['blur', hide],
|
|
]
|
|
events.forEach(([event, handler]) => trigger?.addEventListener(event, handler))
|
|
return () => {
|
|
events.forEach(([event, handler]) => trigger?.removeEventListener(event, handler))
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<div bind:this={trigger} aria-describedby={id}>
|
|
<slot />
|
|
</div>
|
|
<div bind:this={hint} {id} data-show={dataShow} role="tooltip" class="svelte-hint-tooltip">
|
|
{#if text}
|
|
<span>{text}</span>
|
|
{:else}
|
|
<slot name="hint" />
|
|
{/if}
|
|
</div>
|
|
|
|
<style>
|
|
.svelte-hint-tooltip {
|
|
position: absolute;
|
|
visibility: hidden;
|
|
}
|
|
|
|
.svelte-hint-tooltip[data-show='true'] {
|
|
visibility: visible;
|
|
}
|
|
|
|
span {
|
|
display: inline-block;
|
|
background-color: #111;
|
|
color: #fff;
|
|
padding: 0.125rem 0.25rem;
|
|
font-size: 0.8rem;
|
|
width: max-content;
|
|
border-radius: 0.25rem;
|
|
}
|
|
</style>
|