mirror of
https://github.com/koel/koel
synced 2024-12-28 21:43:07 +00:00
85 lines
2.3 KiB
TypeScript
85 lines
2.3 KiB
TypeScript
import { arrow, autoUpdate, offset, Placement } from '@floating-ui/dom'
|
|
import { Directive, DirectiveBinding } from 'vue'
|
|
import { updateFloatingUi } from '@/utils'
|
|
|
|
type ElementWithTooltip = HTMLElement & {
|
|
$tooltip?: HTMLDivElement,
|
|
$cleanup?: Closure
|
|
}
|
|
|
|
const getOrCreateTooltip = (el: ElementWithTooltip): HTMLElement => {
|
|
if (el.$tooltip) return el.$tooltip
|
|
|
|
el.$tooltip = document.createElement('div')
|
|
el.$tooltip.classList.add('tooltip')
|
|
|
|
const arrow = document.createElement('div')
|
|
arrow.classList.add('tooltip-arrow')
|
|
|
|
const content = document.createElement('div')
|
|
content.classList.add('tooltip-content')
|
|
|
|
el.$tooltip.appendChild(content)
|
|
el.$tooltip.appendChild(arrow)
|
|
|
|
document.body.appendChild(el.$tooltip)
|
|
|
|
return el.$tooltip
|
|
}
|
|
|
|
const init = (el: ElementWithTooltip, binding: DirectiveBinding) => {
|
|
const $tooltip = getOrCreateTooltip(el)
|
|
|
|
// make sure the actual title is removed from the element, but keep a backup for the updated() hook calls
|
|
$tooltip.querySelector<HTMLDivElement>('.tooltip-content')!.innerText = binding.value
|
|
|| el.title
|
|
|| el.getAttribute('data-title')
|
|
|| el.innerText
|
|
|
|
if (el.title && !el.getAttribute('data-title')) {
|
|
el.setAttribute('data-title', el.title)
|
|
el.removeAttribute('title')
|
|
}
|
|
|
|
const $arrow = $tooltip.querySelector<HTMLDivElement>('.tooltip-arrow')!
|
|
|
|
let placement: Placement = 'bottom'
|
|
|
|
;(['left', 'right', 'top', 'bottom'] as Placement[]).forEach(p => {
|
|
if (binding.modifiers[p]) {
|
|
placement = p
|
|
}
|
|
})
|
|
|
|
const update = async () => await updateFloatingUi(el, $tooltip, {
|
|
placement,
|
|
middleware: [
|
|
arrow({ element: $arrow }),
|
|
offset(8)
|
|
]
|
|
}, $arrow)
|
|
|
|
el.$cleanup = el.$cleanup || autoUpdate(el, $tooltip, update)
|
|
|
|
const showTooltip = async () => {
|
|
$tooltip.classList.add('show')
|
|
await update()
|
|
}
|
|
|
|
const hideTooltip = () => $tooltip.classList.remove('show')
|
|
|
|
el.addEventListener('mouseenter', showTooltip)
|
|
el.addEventListener('focus', showTooltip)
|
|
el.addEventListener('mouseleave', hideTooltip)
|
|
el.addEventListener('blur', hideTooltip)
|
|
}
|
|
|
|
export const tooltip: Directive = {
|
|
mounted: init,
|
|
updated: init,
|
|
|
|
beforeUnmount: (el: ElementWithTooltip) => {
|
|
el.$cleanup && el.$cleanup()
|
|
el.$tooltip && document.body.removeChild(el.$tooltip)
|
|
}
|
|
}
|