aboutsummaryrefslogtreecommitdiff
path: root/src/lib/Tooltip
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-03-01 16:20:51 -0800
committerFuwn <[email protected]>2026-03-01 16:21:02 -0800
commiteae5d24d9e79e59a19d4721caaeaa0ca650ecb33 (patch)
tree1b685bb248e051dfa26d2bfdebe6689402dd93c5 /src/lib/Tooltip
parentchore(tooling): remove legacy eslint and prettier (diff)
downloaddue.moe-eae5d24d9e79e59a19d4721caaeaa0ca650ecb33.tar.xz
due.moe-eae5d24d9e79e59a19d4721caaeaa0ca650ecb33.zip
chore(biome): drop formatter style overrides
Diffstat (limited to 'src/lib/Tooltip')
-rw-r--r--src/lib/Tooltip/LinkedTooltip.svelte328
-rw-r--r--src/lib/Tooltip/tooltip.ts317
2 files changed, 338 insertions, 307 deletions
diff --git a/src/lib/Tooltip/LinkedTooltip.svelte b/src/lib/Tooltip/LinkedTooltip.svelte
index 4c90d9af..82147536 100644
--- a/src/lib/Tooltip/LinkedTooltip.svelte
+++ b/src/lib/Tooltip/LinkedTooltip.svelte
@@ -1,12 +1,12 @@
<script lang="ts">
-import tooltipPosition from '$stores/tooltipPosition';
-import { fade } from 'svelte/transition';
+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 = false;
-export let pinPosition: 'top' | 'bottom' | 'left' | 'right' = 'top';
+export let pinPosition: "top" | "bottom" | "left" | "right" = "top";
export let offset = 10;
export let tooltipTransitionTime = 200;
export let tooltipHideDelay = 10;
@@ -21,178 +21,208 @@ 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);
- }
+ 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
- });
- }
+ 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);
+ if (hideTimeout !== null) {
+ clearTimeout(hideTimeout);
- hideTimeout = null;
- }
+ hideTimeout = null;
+ }
- createTooltip();
+ createTooltip();
- if (tooltipDiv) {
- tooltipDiv.innerHTML = content.replace(/\n/g, '<br>');
- tooltipDiv.style.opacity = '0';
+ if (tooltipDiv) {
+ tooltipDiv.innerHTML = content.replace(/\n/g, "<br>");
+ tooltipDiv.style.opacity = "0";
- updateTooltipPosition(x, y);
- setTimeout(() => {
- if (tooltipDiv) {
- opacity = 1;
- }
- }, 10);
- }
+ 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);
+ 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 (disable) return;
- if (hideTimeout !== null) {
- clearTimeout(hideTimeout);
+ if (hideTimeout !== null) {
+ clearTimeout(hideTimeout);
- hideTimeout = null;
- }
+ hideTimeout = null;
+ }
- if (!tooltipDiv) showTooltip(content, event.pageX, event.pageY);
+ if (!tooltipDiv) showTooltip(content, event.pageX, event.pageY);
};
const handleMouseMove = (event: MouseEvent) => {
- if (debounceTimer !== null) clearTimeout(debounceTimer);
+ if (debounceTimer !== null) clearTimeout(debounceTimer);
- debounceTimer = window.setTimeout(() => {
- if (tooltipDiv && opacity === 1) updateTooltipPosition(event.pageX, event.pageY);
- }, debounceDelay);
+ debounceTimer = window.setTimeout(() => {
+ if (tooltipDiv && opacity === 1)
+ updateTooltipPosition(event.pageX, event.pageY);
+ }, debounceDelay);
};
const handleMouseLeave = () => {
- hideTooltip();
+ hideTooltip();
};
</script>
diff --git a/src/lib/Tooltip/tooltip.ts b/src/lib/Tooltip/tooltip.ts
index 024ca0fd..5772c33f 100644
--- a/src/lib/Tooltip/tooltip.ts
+++ b/src/lib/Tooltip/tooltip.ts
@@ -1,162 +1,163 @@
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;