aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorFuwn <[email protected]>2024-02-02 23:48:51 -0800
committerFuwn <[email protected]>2024-02-02 23:48:51 -0800
commit92251db4d85e707f74ee78b82f3a137a4ccad6c3 (patch)
treef1d5a125ada6480f3bc7fa00ec21f475c5df6dae /src/lib
parentfeat(anime): better hover bounds (diff)
downloaddue.moe-92251db4d85e707f74ee78b82f3a137a4ccad6c3.tar.xz
due.moe-92251db4d85e707f74ee78b82f3a137a4ccad6c3.zip
feat(tooltip): tooltip animation
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/Tooltip/tooltip.ts135
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;
+ }
}
};
};