diff options
| author | Fuwn <[email protected]> | 2024-02-02 23:48:51 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2024-02-02 23:48:51 -0800 |
| commit | 92251db4d85e707f74ee78b82f3a137a4ccad6c3 (patch) | |
| tree | f1d5a125ada6480f3bc7fa00ec21f475c5df6dae /src/lib | |
| parent | feat(anime): better hover bounds (diff) | |
| download | due.moe-92251db4d85e707f74ee78b82f3a137a4ccad6c3.tar.xz due.moe-92251db4d85e707f74ee78b82f3a137a4ccad6c3.zip | |
feat(tooltip): tooltip animation
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/Tooltip/tooltip.ts | 135 |
1 files changed, 86 insertions, 49 deletions
diff --git a/src/lib/Tooltip/tooltip.ts b/src/lib/Tooltip/tooltip.ts index bba4be11..48449352 100644 --- a/src/lib/Tooltip/tooltip.ts +++ b/src/lib/Tooltip/tooltip.ts @@ -1,66 +1,103 @@ const tooltip = (element: HTMLElement) => { - let div: HTMLDivElement; - let title: string | null; + let tooltipDiv: HTMLDivElement | null = null; const offset = 10; - const above = element.getAttribute('data-tooltip-above') !== null; - const disable = element.getAttribute('data-tooltip-disable'); + const tooltipTransitionTime = 200; + const tooltipHideDelay = 10; + let hideTimeout: number | undefined = undefined; - if (disable && disable === 'false') - return { - destroy() { - return; - } - }; - - const mouseEnter = (event: MouseEvent) => { - title = element.getAttribute('title'); - - element.removeAttribute('title'); - - div = document.createElement('div'); - div.textContent = title; - - div.setAttribute( - 'style', - `position: absolute; - top: -${event.pageX - div.offsetWidth / 2}px; - left: -${event.pageY - div.offsetHeight - offset / 2}px; - z-index: 999999999999;` - ); - div.classList.add('card'); - div.classList.add('card-small'); - document.body.appendChild(div); + const createTooltip = () => { + if (!tooltipDiv) { + tooltipDiv = document.createElement('div'); + + tooltipDiv.style.position = 'absolute'; + tooltipDiv.style.zIndex = '1000'; + tooltipDiv.style.opacity = '0'; + tooltipDiv.style.transition = `opacity ${tooltipTransitionTime}ms ease-in-out`; + tooltipDiv.style.pointerEvents = 'none'; + tooltipDiv.style.whiteSpace = 'nowrap'; + + tooltipDiv.classList.add('card'); + tooltipDiv.classList.add('card-small'); + document.body.appendChild(tooltipDiv); + } }; - const mouseMove = (event: MouseEvent) => { - if (div) { - div.style.left = `${ - above || - event.pageX - div.offsetWidth / 2 > 0 || - event.pageX + div.offsetWidth / 2 > window.innerWidth - ? event.pageX - div.offsetWidth / 2 - : event.pageX + offset - }px`; - div.style.top = `${event.pageY - div.offsetHeight - offset / 2}px`; + const updateTooltipPosition = (x: number, y: number) => { + if (tooltipDiv) { + const tooltipWidth = tooltipDiv.offsetWidth; + const tooltipHeight = tooltipDiv.offsetHeight; + let top = y - tooltipHeight - offset; + let left = x - tooltipWidth / 2; + + if (left < 0) left = offset; + if (left + tooltipWidth > window.innerWidth) left = window.innerWidth - tooltipWidth - offset; + if (top < 0) top = y + offset; + + tooltipDiv.style.left = `${left}px`; + tooltipDiv.style.top = `${top}px`; } }; - const mouseLeave = () => { - if (div) { - document.body.removeChild(div); - element.setAttribute('title', title as string); + const showTooltip = (content: string, x: number, y: number) => { + clearTimeout(hideTimeout); + createTooltip(); + + if (tooltipDiv) { + tooltipDiv.innerHTML = content; + + updateTooltipPosition(x, y); + setTimeout(() => { + if (tooltipDiv) tooltipDiv.style.opacity = '1'; + }, 10); } }; - element.addEventListener('mouseenter', mouseEnter); - element.addEventListener('mouseleave', mouseLeave); - element.addEventListener('mousemove', mouseMove); + const hideTooltip = () => { + hideTimeout = window.setTimeout(() => { + if (tooltipDiv) { + tooltipDiv.style.opacity = '0'; + + setTimeout(() => { + if (tooltipDiv) { + document.body.removeChild(tooltipDiv); + tooltipDiv = null; + } + }, tooltipTransitionTime); + } + }, tooltipHideDelay); + }; + + element.addEventListener('mouseenter', (event) => { + const title = element.getAttribute('title'); + + if (title) { + element.removeAttribute('title'); + showTooltip(title, event.pageX, event.pageY); + } + }); + + element.addEventListener('mousemove', (event) => { + if (tooltipDiv && tooltipDiv.style.opacity === '1') + updateTooltipPosition(event.pageX, event.pageY); + }); + + element.addEventListener('mouseleave', () => { + element.setAttribute('title', tooltipDiv ? tooltipDiv.textContent || '' : ''); + hideTooltip(); + }); return { destroy() { - element.removeEventListener('mouseenter', mouseEnter); - element.removeEventListener('mouseleave', mouseLeave); - element.removeEventListener('mousemove', mouseMove); + element.removeEventListener('mouseenter', showTooltip as unknown as EventListener); + element.removeEventListener('mousemove', updateTooltipPosition as unknown as EventListener); + element.removeEventListener('mouseleave', hideTooltip as EventListener); + + if (hideTimeout) clearTimeout(hideTimeout); + if (tooltipDiv && document.body.contains(tooltipDiv)) { + document.body.removeChild(tooltipDiv); + + tooltipDiv = null; + } } }; }; |