This commit is contained in:
Daniel Ledda
2024-11-01 15:42:09 +01:00
parent f60e975765
commit 7539e6ed48
50 changed files with 2004 additions and 750 deletions

View File

@@ -0,0 +1,130 @@
interface Point {
x: number;
y: number;
}
interface Size {
height: string;
width: string;
}
class SexyTooltip {
private static readonly carrierStyle: Partial<CSSStyleDeclaration> = {
opacity: "0",
display: "block",
pointerEvents: "none",
backgroundColor: "black",
border: "white solid 1px",
color: "white",
padding: "10px",
position: "absolute",
zIndex: "1",
overflow: "hidden",
height: "0",
width: "0",
transition: "opacity 200ms, height 200ms, width 200ms",
};
private static readonly textCarrierStyle: Partial<CSSStyleDeclaration> = {
width: "350px",
display: "block",
overflow: "hidden",
};
private static readonly defaultWidth = 350;
private readonly carrier: HTMLDivElement;
private readonly textCarrier: HTMLSpanElement;
private readonly elementsWithTooltips: Element[] = [];
private active: boolean = false;
constructor(carrier: HTMLDivElement) {
if (carrier.childNodes.length > 0) {
throw new Error("Incorrect setup for tooltip! Remove the child nodes from the tooltip div.");
}
this.carrier = carrier;
this.textCarrier = document.createElement("span");
this.carrier.appendChild(this.textCarrier);
Object.assign(this.carrier.style, SexyTooltip.carrierStyle);
Object.assign(this.textCarrier.style, SexyTooltip.textCarrierStyle);
document.querySelectorAll(".tooltip").forEach((element) => {
if (element.nodeName === "SPAN") {
console.log(element.previousElementSibling);
this.elementsWithTooltips.push(element.previousElementSibling);
}
});
document.addEventListener("mousemove", (event) => this.rerenderTooltip({ x: event.pageX, y: event.pageY }));
}
rerenderTooltip(mousePos: Point) {
const newText = this.getTooltipTextForHoveredElement(mousePos);
if (newText !== this.textCarrier.innerHTML) {
this.transitionTooltipSize(newText);
const tooltipHasText = newText !== "";
if (tooltipHasText !== this.active) {
this.toggleActive();
}
}
if (this.isStillVisible()) {
this.updatePosition(mousePos);
}
}
isStillVisible() {
return getComputedStyle(this.carrier).opacity !== "0";
}
updatePosition(pos: Point) {
if (pos.x + 15 + this.carrier.clientWidth <= document.body.scrollWidth) {
this.carrier.style.left = (pos.x + 15) + "px";
} else {
this.carrier.style.left = (document.body.scrollWidth - this.carrier.clientWidth - 5) + "px";
}
if (pos.y + this.carrier.clientHeight <= document.body.scrollHeight) {
this.carrier.style.top = pos.y + "px";
} else {
this.carrier.style.top = (document.body.scrollHeight - this.carrier.clientHeight - 5) + "px";
}
}
toggleActive() {
if (this.active) {
this.active = false;
this.carrier.style.opacity = "0";
this.carrier.style.padding = "0";
} else {
this.active = true;
this.carrier.style.opacity = "1";
this.carrier.style.padding = SexyTooltip.carrierStyle.padding;
}
}
transitionTooltipSize(text: string) {
const testDiv = document.createElement("div");
testDiv.style.height = "auto";
testDiv.style.width = SexyTooltip.defaultWidth + "px";
testDiv.innerHTML = text;
document.body.appendChild(testDiv);
const calculatedHeight = testDiv.scrollHeight;
testDiv.style.display = "inline";
testDiv.style.width = "auto";
document.body.removeChild(testDiv);
const size = { height: calculatedHeight + "px", width: "350px" };
this.carrier.style.height = size.height;
this.carrier.style.width = text === "" ? "0" : size.width;
this.textCarrier.innerHTML = text;
}
getTooltipTextForHoveredElement(mousePos: Point): string {
for (const elem of this.elementsWithTooltips) {
const boundingRect = elem.getBoundingClientRect();
const inYRange = mousePos.y >= boundingRect.top + window.pageYOffset - 1 &&
mousePos.y <= boundingRect.bottom + window.pageYOffset + 1;
const inXRange = mousePos.x >= boundingRect.left + window.pageXOffset - 1 &&
mousePos.x <= boundingRect.right + window.pageXOffset + 1;
if (inYRange && inXRange) {
return (elem.nextElementSibling as HTMLSpanElement)?.innerText ?? "";
}
}
return "";
}
}
export default SexyTooltip;

47
public/home/js/main.ts Normal file
View File

@@ -0,0 +1,47 @@
import SexyTooltip from "./SexyTooltip.js";
const tooltipDiv = document.getElementById("tooltipCarrier") as HTMLDivElement;
const tooltip = new SexyTooltip(tooltipDiv);
const dudes = document.querySelectorAll(".dude") as NodeListOf<HTMLImageElement>;
const shakers = document.querySelectorAll(".shakeable") as NodeListOf<HTMLElement>;
const emailLink = document.getElementById("emailLink") as HTMLAnchorElement;
let numDudesDroppingSickBeats: number = 0;
dudes.forEach((dude) => dude.addEventListener("mouseup", () => toggleDude(dude)));
function toggleDude(dude: HTMLImageElement) {
if (dude.classList.contains("spinMe")) {
numDudesDroppingSickBeats -= 1;
dude.addEventListener("animationiteration", function listener() {
dude.classList.remove("spinMe");
dude.removeEventListener("animationiteration", listener as EventListenerOrEventListenerObject);
});
} else {
numDudesDroppingSickBeats += 1;
dude.classList.add("spinMe");
}
updateShakers();
}
function updateShakers() {
shakers.forEach((shaker) => {
if (numDudesDroppingSickBeats === 0) {
shaker.classList.remove("shakeMe");
} else if (!shaker.classList.contains("shakeMe")) {
shaker.classList.add("shakeMe");
}
});
}
emailLink.addEventListener("click", (event) => {
const myDomain = "gmail";
const myTld = "com";
const myName = "danjledda";
const dot = ".";
const at = "@";
let link = "mailto:" + myName + myDomain + myTld;
link = link.slice(0, 10) + dot + link.slice(10, 11) + dot + link.slice(11, 16) + at + link.slice(16, 21) + dot +
link.slice(21);
window.location.href = link;
});