aboutsummaryrefslogtreecommitdiff
path: root/src/lib/Tooltip
diff options
context:
space:
mode:
authorFuwn <[email protected]>2024-10-09 00:41:20 -0700
committerFuwn <[email protected]>2024-10-09 00:41:43 -0700
commit998b63a35256ac985a5a2714dd1ca451af4dfd8a (patch)
tree50796121a9d5ab0330fdc5d7e098bda2860d9726 /src/lib/Tooltip
parentfeat(graphql): add badgeCount field (diff)
downloaddue.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.tar.xz
due.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.zip
chore(prettier): use spaces instead of tabs
Diffstat (limited to 'src/lib/Tooltip')
-rw-r--r--src/lib/Tooltip/LinkedTooltip.svelte464
-rw-r--r--src/lib/Tooltip/tooltip.ts316
2 files changed, 390 insertions, 390 deletions
diff --git a/src/lib/Tooltip/LinkedTooltip.svelte b/src/lib/Tooltip/LinkedTooltip.svelte
index 096e4291..6e468cd6 100644
--- a/src/lib/Tooltip/LinkedTooltip.svelte
+++ b/src/lib/Tooltip/LinkedTooltip.svelte
@@ -1,244 +1,244 @@
<script lang="ts">
- import tooltipPosition from '$stores/tooltipPosition';
- import { fade } from 'svelte/transition';
-
- export let id: string | undefined = undefined;
- export let pin: string | undefined = undefined;
- export let content: string;
- export let disable: boolean = false;
- export let pinPosition: 'top' | 'bottom' | 'left' | 'right' = 'top';
- export let offset = 10;
- export let tooltipTransitionTime = 200;
- export let tooltipHideDelay = 10;
- export let debounceDelay = 100;
- export let tooltipOpacityTransitionTime = 200;
- export let relative = false;
- export let ignoreAnchorStyling = false;
-
- let tooltipDiv: HTMLDivElement | null = null;
- let hideTimeout: number | null = null;
- let debounceTimer: number | null = null;
- let opacity = 0;
-
- const createTooltip = () => {
- if (!tooltipDiv) {
- tooltipDiv = document.createElement('div');
- tooltipDiv.style.position = 'absolute';
- tooltipDiv.style.zIndex = '1000';
- opacity = 0;
- tooltipDiv.style.pointerEvents = 'none';
- tooltipDiv.style.whiteSpace = 'nowrap';
-
- tooltipDiv.classList.add('card');
- tooltipDiv.classList.add('card-small');
- document.body.appendChild(tooltipDiv);
- }
- };
-
- const updateTooltipPosition = (x: number, y: number) => {
- if (tooltipDiv) {
- if (pin) {
- const pinnedElement = document.getElementById(pin);
-
- if (pinnedElement) {
- const rectangle = pinnedElement.getBoundingClientRect();
- const parentRectangle = pinnedElement.offsetParent?.getBoundingClientRect();
- const tooltipWidth = tooltipDiv.offsetWidth;
- const tooltipHeight = tooltipDiv.offsetHeight;
- let top = 0;
- let left = 0;
-
- switch (pinPosition) {
- case 'top':
- if (relative && parentRectangle) {
- top = rectangle.top - tooltipHeight - offset - parentRectangle.top;
- left =
- rectangle.left + rectangle.width / 2 - tooltipWidth / 2 - parentRectangle.left;
- } else {
- top = rectangle.top - tooltipHeight - offset;
- left = rectangle.left + rectangle.width / 2 - tooltipWidth / 2;
- }
-
- break;
- case 'bottom':
- if (relative && parentRectangle) {
- top = rectangle.top + rectangle.height + offset - parentRectangle.top;
- left =
- rectangle.left + rectangle.width / 2 - tooltipWidth / 2 - parentRectangle.left;
- } else {
- top = rectangle.top + rectangle.height + offset;
- left = rectangle.left + rectangle.width / 2 - tooltipWidth / 2;
- }
-
- break;
- case 'left':
- if (relative && parentRectangle) {
- top =
- rectangle.top + rectangle.height / 2 - tooltipHeight / 2 - parentRectangle.top;
- left = rectangle.left - tooltipWidth - offset - parentRectangle.left;
- } else {
- top = rectangle.top + rectangle.height / 2 - tooltipHeight / 2;
- left = rectangle.left - tooltipWidth - offset;
- }
-
- break;
- case 'right':
- if (relative && parentRectangle) {
- top =
- rectangle.top + rectangle.height / 2 - tooltipHeight / 2 - parentRectangle.top;
- left = rectangle.left + rectangle.width + offset - parentRectangle.left;
- } else {
- top = rectangle.top + rectangle.height / 2 - tooltipHeight / 2;
- left = rectangle.left + rectangle.width + offset;
- }
-
- break;
- }
-
- if (relative && parentRectangle) {
- if (left + parentRectangle.left < 0) left = offset - parentRectangle.left;
- if (left + tooltipWidth + parentRectangle.left > window.innerWidth)
- left = window.innerWidth - tooltipWidth - offset - parentRectangle.left;
- if (top + parentRectangle.top < 0)
- top = rectangle.top + rectangle.height + offset - parentRectangle.top;
- if (top + tooltipHeight + parentRectangle.top > window.innerHeight)
- top = window.innerHeight - tooltipHeight - offset - parentRectangle.top;
- } else {
- if (left < 0) left = offset;
- if (left + tooltipWidth > window.innerWidth)
- left = window.innerWidth - tooltipWidth - offset;
- if (top < 0) top = rectangle.top + rectangle.height + offset;
- if (top + tooltipHeight > window.innerHeight)
- top = window.innerHeight - tooltipHeight - offset;
- }
-
- tooltipPosition.set({
- x: left,
- y: top + (relative ? 0 : window.scrollY)
- });
-
- return;
- }
- }
-
- 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;
-
- tooltipPosition.set({
- x: left,
- y: top
- });
- }
- };
-
- const showTooltip = (content: string, x: number, y: number) => {
- if (hideTimeout !== null) {
- clearTimeout(hideTimeout);
-
- hideTimeout = null;
- }
-
- createTooltip();
-
- if (tooltipDiv) {
- tooltipDiv.innerHTML = content.replace(/\n/g, '<br>');
- tooltipDiv.style.opacity = '0';
-
- updateTooltipPosition(x, y);
- setTimeout(() => {
- if (tooltipDiv) {
- opacity = 1;
- }
- }, 10);
- }
- };
-
- const hideTooltip = () => {
- setTimeout(() => {
- if (tooltipDiv) {
- opacity = 0;
-
- hideTimeout = window.setTimeout(() => {
- if (tooltipDiv) {
- document.body.removeChild(tooltipDiv);
-
- tooltipDiv = null;
- }
- }, tooltipTransitionTime);
- }
- }, tooltipHideDelay);
- };
-
- const handleMouseEnter = (event: MouseEvent) => {
- if (disable) return;
-
- if (hideTimeout !== null) {
- clearTimeout(hideTimeout);
-
- hideTimeout = null;
- }
-
- if (!tooltipDiv) showTooltip(content, event.pageX, event.pageY);
- };
-
- const handleMouseMove = (event: MouseEvent) => {
- if (debounceTimer !== null) clearTimeout(debounceTimer);
-
- debounceTimer = window.setTimeout(() => {
- if (tooltipDiv && opacity === 1) updateTooltipPosition(event.pageX, event.pageY);
- }, debounceDelay);
- };
-
- const handleMouseLeave = () => {
- hideTooltip();
- };
+ import tooltipPosition from '$stores/tooltipPosition';
+ import { fade } from 'svelte/transition';
+
+ export let id: string | undefined = undefined;
+ export let pin: string | undefined = undefined;
+ export let content: string;
+ export let disable: boolean = false;
+ export let pinPosition: 'top' | 'bottom' | 'left' | 'right' = 'top';
+ export let offset = 10;
+ export let tooltipTransitionTime = 200;
+ export let tooltipHideDelay = 10;
+ export let debounceDelay = 100;
+ export let tooltipOpacityTransitionTime = 200;
+ export let relative = false;
+ export let ignoreAnchorStyling = false;
+
+ let tooltipDiv: HTMLDivElement | null = null;
+ let hideTimeout: number | null = null;
+ let debounceTimer: number | null = null;
+ let opacity = 0;
+
+ const createTooltip = () => {
+ if (!tooltipDiv) {
+ tooltipDiv = document.createElement('div');
+ tooltipDiv.style.position = 'absolute';
+ tooltipDiv.style.zIndex = '1000';
+ opacity = 0;
+ tooltipDiv.style.pointerEvents = 'none';
+ tooltipDiv.style.whiteSpace = 'nowrap';
+
+ tooltipDiv.classList.add('card');
+ tooltipDiv.classList.add('card-small');
+ document.body.appendChild(tooltipDiv);
+ }
+ };
+
+ const updateTooltipPosition = (x: number, y: number) => {
+ if (tooltipDiv) {
+ if (pin) {
+ const pinnedElement = document.getElementById(pin);
+
+ if (pinnedElement) {
+ const rectangle = pinnedElement.getBoundingClientRect();
+ const parentRectangle = pinnedElement.offsetParent?.getBoundingClientRect();
+ const tooltipWidth = tooltipDiv.offsetWidth;
+ const tooltipHeight = tooltipDiv.offsetHeight;
+ let top = 0;
+ let left = 0;
+
+ switch (pinPosition) {
+ case 'top':
+ if (relative && parentRectangle) {
+ top = rectangle.top - tooltipHeight - offset - parentRectangle.top;
+ left =
+ rectangle.left + rectangle.width / 2 - tooltipWidth / 2 - parentRectangle.left;
+ } else {
+ top = rectangle.top - tooltipHeight - offset;
+ left = rectangle.left + rectangle.width / 2 - tooltipWidth / 2;
+ }
+
+ break;
+ case 'bottom':
+ if (relative && parentRectangle) {
+ top = rectangle.top + rectangle.height + offset - parentRectangle.top;
+ left =
+ rectangle.left + rectangle.width / 2 - tooltipWidth / 2 - parentRectangle.left;
+ } else {
+ top = rectangle.top + rectangle.height + offset;
+ left = rectangle.left + rectangle.width / 2 - tooltipWidth / 2;
+ }
+
+ break;
+ case 'left':
+ if (relative && parentRectangle) {
+ top =
+ rectangle.top + rectangle.height / 2 - tooltipHeight / 2 - parentRectangle.top;
+ left = rectangle.left - tooltipWidth - offset - parentRectangle.left;
+ } else {
+ top = rectangle.top + rectangle.height / 2 - tooltipHeight / 2;
+ left = rectangle.left - tooltipWidth - offset;
+ }
+
+ break;
+ case 'right':
+ if (relative && parentRectangle) {
+ top =
+ rectangle.top + rectangle.height / 2 - tooltipHeight / 2 - parentRectangle.top;
+ left = rectangle.left + rectangle.width + offset - parentRectangle.left;
+ } else {
+ top = rectangle.top + rectangle.height / 2 - tooltipHeight / 2;
+ left = rectangle.left + rectangle.width + offset;
+ }
+
+ break;
+ }
+
+ if (relative && parentRectangle) {
+ if (left + parentRectangle.left < 0) left = offset - parentRectangle.left;
+ if (left + tooltipWidth + parentRectangle.left > window.innerWidth)
+ left = window.innerWidth - tooltipWidth - offset - parentRectangle.left;
+ if (top + parentRectangle.top < 0)
+ top = rectangle.top + rectangle.height + offset - parentRectangle.top;
+ if (top + tooltipHeight + parentRectangle.top > window.innerHeight)
+ top = window.innerHeight - tooltipHeight - offset - parentRectangle.top;
+ } else {
+ if (left < 0) left = offset;
+ if (left + tooltipWidth > window.innerWidth)
+ left = window.innerWidth - tooltipWidth - offset;
+ if (top < 0) top = rectangle.top + rectangle.height + offset;
+ if (top + tooltipHeight > window.innerHeight)
+ top = window.innerHeight - tooltipHeight - offset;
+ }
+
+ tooltipPosition.set({
+ x: left,
+ y: top + (relative ? 0 : window.scrollY)
+ });
+
+ return;
+ }
+ }
+
+ 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;
+
+ tooltipPosition.set({
+ x: left,
+ y: top
+ });
+ }
+ };
+
+ const showTooltip = (content: string, x: number, y: number) => {
+ if (hideTimeout !== null) {
+ clearTimeout(hideTimeout);
+
+ hideTimeout = null;
+ }
+
+ createTooltip();
+
+ if (tooltipDiv) {
+ tooltipDiv.innerHTML = content.replace(/\n/g, '<br>');
+ tooltipDiv.style.opacity = '0';
+
+ updateTooltipPosition(x, y);
+ setTimeout(() => {
+ if (tooltipDiv) {
+ opacity = 1;
+ }
+ }, 10);
+ }
+ };
+
+ const hideTooltip = () => {
+ setTimeout(() => {
+ if (tooltipDiv) {
+ opacity = 0;
+
+ hideTimeout = window.setTimeout(() => {
+ if (tooltipDiv) {
+ document.body.removeChild(tooltipDiv);
+
+ tooltipDiv = null;
+ }
+ }, tooltipTransitionTime);
+ }
+ }, tooltipHideDelay);
+ };
+
+ const handleMouseEnter = (event: MouseEvent) => {
+ if (disable) return;
+
+ if (hideTimeout !== null) {
+ clearTimeout(hideTimeout);
+
+ hideTimeout = null;
+ }
+
+ if (!tooltipDiv) showTooltip(content, event.pageX, event.pageY);
+ };
+
+ const handleMouseMove = (event: MouseEvent) => {
+ if (debounceTimer !== null) clearTimeout(debounceTimer);
+
+ debounceTimer = window.setTimeout(() => {
+ if (tooltipDiv && opacity === 1) updateTooltipPosition(event.pageX, event.pageY);
+ }, debounceDelay);
+ };
+
+ const handleMouseLeave = () => {
+ hideTooltip();
+ };
</script>
<span
- {id}
- on:mouseenter={handleMouseEnter}
- on:mousemove={handleMouseMove}
- on:mouseleave={handleMouseLeave}
- role="tooltip"
+ {id}
+ on:mouseenter={handleMouseEnter}
+ on:mousemove={handleMouseMove}
+ on:mouseleave={handleMouseLeave}
+ role="tooltip"
>
- <slot />
+ <slot />
</span>
{#if tooltipDiv}
- <div
- class="tooltip card card-small"
- style={`left: ${$tooltipPosition.x}px; top: ${$tooltipPosition.y}px; opacity: ${opacity}; --tooltip-opacity-transition-time: ${tooltipOpacityTransitionTime}ms;`}
- >
- {#key content}
- <span
- in:fade={{ duration: tooltipTransitionTime }}
- out:fade={{ duration: tooltipTransitionTime }}
- class:ignore-anchor-styling={ignoreAnchorStyling}
- >
- {@html content.replace(/\n/g, '<br>')}
- </span>
- {/key}
- </div>
+ <div
+ class="tooltip card card-small"
+ style={`left: ${$tooltipPosition.x}px; top: ${$tooltipPosition.y}px; opacity: ${opacity}; --tooltip-opacity-transition-time: ${tooltipOpacityTransitionTime}ms;`}
+ >
+ {#key content}
+ <span
+ in:fade={{ duration: tooltipTransitionTime }}
+ out:fade={{ duration: tooltipTransitionTime }}
+ class:ignore-anchor-styling={ignoreAnchorStyling}
+ >
+ {@html content.replace(/\n/g, '<br>')}
+ </span>
+ {/key}
+ </div>
{/if}
<style>
- .tooltip {
- position: absolute;
- z-index: 1000;
- transition: opacity var(--tooltip-opacity-transition-time) ease-in-out;
- pointer-events: none;
- white-space: nowrap;
- }
-
- /* fix line break slot */
-
- .ignore-anchor-styling {
- color: var(--fg);
- }
+ .tooltip {
+ position: absolute;
+ z-index: 1000;
+ transition: opacity var(--tooltip-opacity-transition-time) ease-in-out;
+ pointer-events: none;
+ white-space: nowrap;
+ }
+
+ /* fix line break slot */
+
+ .ignore-anchor-styling {
+ color: var(--fg);
+ }
</style>
diff --git a/src/lib/Tooltip/tooltip.ts b/src/lib/Tooltip/tooltip.ts
index 348689b1..024ca0fd 100644
--- a/src/lib/Tooltip/tooltip.ts
+++ b/src/lib/Tooltip/tooltip.ts
@@ -1,162 +1,162 @@
const tooltip = (element: HTMLElement) => {
- let tooltipDiv: HTMLDivElement | null = null;
- const offset = 10;
- const tooltipTransitionTime = 200;
- const tooltipHideDelay = 10;
- const debounceDelay = 100;
- let hideTimeout: number | null = null;
- let debounceTimer: number | null = null;
-
- if (element.dataset.tooltipDisable === 'true') return;
-
- 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, top 0.3s ease, left 0.3s ease`;
- tooltipDiv.style.pointerEvents = 'none';
- tooltipDiv.style.whiteSpace = 'nowrap';
- tooltipDiv.style.zIndex = '1000';
-
- tooltipDiv.classList.add('card');
- tooltipDiv.classList.add('card-small');
- document.body.appendChild(tooltipDiv);
- }
- };
-
- const updateTooltipPosition = (x: number, y: number) => {
- if (tooltipDiv) {
- const tooltipPin = element.dataset.tooltippin;
-
- if (tooltipPin) {
- const pinnedElement = document.getElementById(tooltipPin);
-
- if (pinnedElement) {
- const rect = pinnedElement.getBoundingClientRect();
- const tooltipWidth = tooltipDiv.offsetWidth;
- const tooltipHeight = tooltipDiv.offsetHeight;
- let top = rect.top - tooltipHeight - offset;
- let left = rect.left + rect.width / 2 - tooltipWidth / 2;
-
- if (left < 0) left = offset;
- if (left + tooltipWidth > window.innerWidth)
- left = window.innerWidth - tooltipWidth - offset;
- if (top < 0) top = rect.top + rect.height + offset;
-
- tooltipDiv.style.left = `${left}px`;
- tooltipDiv.style.top = `${top + window.scrollY}px`;
-
- return;
- }
- }
-
- 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 showTooltip = (content: string, x: number, y: number) => {
- if (hideTimeout !== null) {
- clearTimeout(hideTimeout);
-
- hideTimeout = null;
- }
-
- createTooltip();
-
- if (tooltipDiv) {
- tooltipDiv.innerHTML = content.replace(/\n/g, '<br>');
-
- updateTooltipPosition(x, y);
- setTimeout(() => {
- if (tooltipDiv) {
- tooltipDiv.style.opacity = '1';
- }
- }, 10);
- }
- };
-
- const hideTooltip = () => {
- setTimeout(() => {
- if (tooltipDiv) {
- tooltipDiv.style.opacity = '0';
-
- hideTimeout = window.setTimeout(() => {
- if (tooltipDiv) {
- document.body.removeChild(tooltipDiv);
- tooltipDiv = null;
- }
- }, tooltipTransitionTime);
- }
- }, tooltipHideDelay);
- };
-
- const handleMouseEnter = (event: MouseEvent) => {
- const title = element.getAttribute('title');
-
- if (title) {
- element.removeAttribute('title');
-
- if (hideTimeout !== null) {
- clearTimeout(hideTimeout);
- hideTimeout = null;
- }
-
- if (!tooltipDiv) {
- showTooltip(title, event.pageX, event.pageY);
- }
- }
- };
-
- const handleMouseMove = (event: MouseEvent) => {
- if (debounceTimer !== null) clearTimeout(debounceTimer);
-
- debounceTimer = window.setTimeout(() => {
- if (tooltipDiv && tooltipDiv.style.opacity === '1')
- updateTooltipPosition(event.pageX, event.pageY);
- }, debounceDelay);
- };
-
- const handleMouseLeave = () => {
- element.setAttribute(
- 'title',
- tooltipDiv ? tooltipDiv.innerHTML?.replace(/<br>/g, '\n') || '' : ''
- );
- hideTooltip();
- };
-
- element.addEventListener('mouseenter', handleMouseEnter);
- element.addEventListener('mousemove', handleMouseMove);
- element.addEventListener('mouseleave', handleMouseLeave);
-
- return {
- destroy() {
- element.removeEventListener('mouseenter', handleMouseEnter);
- element.removeEventListener('mousemove', handleMouseMove);
- element.removeEventListener('mouseleave', handleMouseLeave);
-
- if (hideTimeout !== null) clearTimeout(hideTimeout);
- if (debounceTimer !== null) clearTimeout(debounceTimer);
-
- if (tooltipDiv && document.body.contains(tooltipDiv)) {
- document.body.removeChild(tooltipDiv);
-
- tooltipDiv = null;
- }
- }
- };
+ let tooltipDiv: HTMLDivElement | null = null;
+ const offset = 10;
+ const tooltipTransitionTime = 200;
+ const tooltipHideDelay = 10;
+ const debounceDelay = 100;
+ let hideTimeout: number | null = null;
+ let debounceTimer: number | null = null;
+
+ if (element.dataset.tooltipDisable === 'true') return;
+
+ 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, top 0.3s ease, left 0.3s ease`;
+ tooltipDiv.style.pointerEvents = 'none';
+ tooltipDiv.style.whiteSpace = 'nowrap';
+ tooltipDiv.style.zIndex = '1000';
+
+ tooltipDiv.classList.add('card');
+ tooltipDiv.classList.add('card-small');
+ document.body.appendChild(tooltipDiv);
+ }
+ };
+
+ const updateTooltipPosition = (x: number, y: number) => {
+ if (tooltipDiv) {
+ const tooltipPin = element.dataset.tooltippin;
+
+ if (tooltipPin) {
+ const pinnedElement = document.getElementById(tooltipPin);
+
+ if (pinnedElement) {
+ const rect = pinnedElement.getBoundingClientRect();
+ const tooltipWidth = tooltipDiv.offsetWidth;
+ const tooltipHeight = tooltipDiv.offsetHeight;
+ let top = rect.top - tooltipHeight - offset;
+ let left = rect.left + rect.width / 2 - tooltipWidth / 2;
+
+ if (left < 0) left = offset;
+ if (left + tooltipWidth > window.innerWidth)
+ left = window.innerWidth - tooltipWidth - offset;
+ if (top < 0) top = rect.top + rect.height + offset;
+
+ tooltipDiv.style.left = `${left}px`;
+ tooltipDiv.style.top = `${top + window.scrollY}px`;
+
+ return;
+ }
+ }
+
+ 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 showTooltip = (content: string, x: number, y: number) => {
+ if (hideTimeout !== null) {
+ clearTimeout(hideTimeout);
+
+ hideTimeout = null;
+ }
+
+ createTooltip();
+
+ if (tooltipDiv) {
+ tooltipDiv.innerHTML = content.replace(/\n/g, '<br>');
+
+ updateTooltipPosition(x, y);
+ setTimeout(() => {
+ if (tooltipDiv) {
+ tooltipDiv.style.opacity = '1';
+ }
+ }, 10);
+ }
+ };
+
+ const hideTooltip = () => {
+ setTimeout(() => {
+ if (tooltipDiv) {
+ tooltipDiv.style.opacity = '0';
+
+ hideTimeout = window.setTimeout(() => {
+ if (tooltipDiv) {
+ document.body.removeChild(tooltipDiv);
+ tooltipDiv = null;
+ }
+ }, tooltipTransitionTime);
+ }
+ }, tooltipHideDelay);
+ };
+
+ const handleMouseEnter = (event: MouseEvent) => {
+ const title = element.getAttribute('title');
+
+ if (title) {
+ element.removeAttribute('title');
+
+ if (hideTimeout !== null) {
+ clearTimeout(hideTimeout);
+ hideTimeout = null;
+ }
+
+ if (!tooltipDiv) {
+ showTooltip(title, event.pageX, event.pageY);
+ }
+ }
+ };
+
+ const handleMouseMove = (event: MouseEvent) => {
+ if (debounceTimer !== null) clearTimeout(debounceTimer);
+
+ debounceTimer = window.setTimeout(() => {
+ if (tooltipDiv && tooltipDiv.style.opacity === '1')
+ updateTooltipPosition(event.pageX, event.pageY);
+ }, debounceDelay);
+ };
+
+ const handleMouseLeave = () => {
+ element.setAttribute(
+ 'title',
+ tooltipDiv ? tooltipDiv.innerHTML?.replace(/<br>/g, '\n') || '' : ''
+ );
+ hideTooltip();
+ };
+
+ element.addEventListener('mouseenter', handleMouseEnter);
+ element.addEventListener('mousemove', handleMouseMove);
+ element.addEventListener('mouseleave', handleMouseLeave);
+
+ return {
+ destroy() {
+ element.removeEventListener('mouseenter', handleMouseEnter);
+ element.removeEventListener('mousemove', handleMouseMove);
+ element.removeEventListener('mouseleave', handleMouseLeave);
+
+ if (hideTimeout !== null) clearTimeout(hideTimeout);
+ if (debounceTimer !== null) clearTimeout(debounceTimer);
+
+ if (tooltipDiv && document.body.contains(tooltipDiv)) {
+ document.body.removeChild(tooltipDiv);
+
+ tooltipDiv = null;
+ }
+ }
+ };
};
export default tooltip;