120 lines
3.1 KiB
JavaScript
120 lines
3.1 KiB
JavaScript
// Emoji zoom-in functionality.
|
|
|
|
class EmojiTooltip extends HTMLElement {
|
|
constructor(emoji, { onClosed }) {
|
|
super();
|
|
|
|
this.emoji = emoji;
|
|
this.onClosed = onClosed;
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.role = "tooltip";
|
|
|
|
this.image = new Image();
|
|
this.image.src = this.emoji.src;
|
|
|
|
this.description = document.createElement("p");
|
|
this.description.textContent = `${this.emoji.emojiName}`;
|
|
|
|
let emojiBoundingBox = this.emoji.getBoundingClientRect();
|
|
this.style.left = `${emojiBoundingBox.left + emojiBoundingBox.width / 2}px`;
|
|
this.style.top = `calc(${emojiBoundingBox.top}px + 1.5em)`;
|
|
|
|
this.fullyOpaque = false;
|
|
this.addEventListener("transitionend", event => {
|
|
if (event.propertyName == "opacity") {
|
|
this.fullyOpaque = !this.fullyOpaque;
|
|
if (!this.fullyOpaque) {
|
|
this.onClosed();
|
|
}
|
|
}
|
|
});
|
|
// Timeout is zero because we just want to execute this later, to be definitely sure
|
|
// the transition plays out.
|
|
setTimeout(() => this.classList.add("transitioned-in"), 0);
|
|
|
|
this.appendChild(this.image);
|
|
this.appendChild(this.description);
|
|
}
|
|
|
|
close() {
|
|
this.classList.remove("transitioned-in");
|
|
}
|
|
}
|
|
|
|
customElements.define("th-emoji-tooltip", EmojiTooltip);
|
|
|
|
let emojiTooltips = null;
|
|
addEventListener("wheel", event => emojiTooltips.closeTooltips(event));
|
|
|
|
class EmojiTooltips extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.tooltips = new Set();
|
|
this.abortController = new AbortController();
|
|
|
|
}
|
|
|
|
connectedCallback() {
|
|
emojiTooltips = this;
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
this.abortController.abort();
|
|
}
|
|
|
|
openTooltip(emoji) {
|
|
let tooltip = new EmojiTooltip(emoji, {
|
|
onClosed: () => {
|
|
this.removeChild(tooltip);
|
|
this.tooltips.delete(tooltip);
|
|
},
|
|
});
|
|
|
|
this.appendChild(tooltip);
|
|
this.tooltips.add(tooltip);
|
|
|
|
return tooltip;
|
|
}
|
|
|
|
closeTooltip(tooltip) {
|
|
tooltip.close();
|
|
}
|
|
|
|
closeTooltips() {
|
|
for (let tooltip of this.tooltips) {
|
|
console.log("close", this);
|
|
|
|
tooltip.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
customElements.define("th-emoji-tooltips", EmojiTooltips);
|
|
|
|
class Emoji extends HTMLImageElement {
|
|
connectedCallback() {
|
|
this.emojiName = this.title;
|
|
|
|
// title makes the browser add a tooltip. We replace browser tooltips with our own,
|
|
// so remove the title.
|
|
this.title = "";
|
|
|
|
this.addEventListener("mouseenter", () => this.openTooltip());
|
|
this.addEventListener("mouseleave", () => this.closeTooltip());
|
|
this.addEventListener("scroll", () => this.closeTooltip());
|
|
}
|
|
|
|
openTooltip() {
|
|
this.tooltip = emojiTooltips.openTooltip(this);
|
|
}
|
|
|
|
closeTooltip() {
|
|
emojiTooltips.closeTooltip(this.tooltip);
|
|
this.tooltip = null;
|
|
}
|
|
}
|
|
|
|
customElements.define("th-emoji", Emoji, { extends: "img" });
|