Improve tooltips with Alpine

This commit is contained in:
Bad Manners 2024-09-25 16:56:05 -03:00
parent 4d3ba2776c
commit 6c834dd58a
6 changed files with 101 additions and 154 deletions

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "badmanners.xyz",
"version": "2.1.16",
"version": "2.1.17",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "badmanners.xyz",
"version": "2.1.16",
"version": "2.1.17",
"hasInstallScript": true,
"dependencies": {
"@astrojs/alpinejs": "^0.4.0",

View file

@ -1,7 +1,7 @@
{
"name": "badmanners.xyz",
"type": "module",
"version": "2.1.16",
"version": "2.1.17",
"scripts": {
"postinstall": "astro sync",
"dev": "astro dev",

View file

@ -0,0 +1,21 @@
---
---
<script>
import tippy from "tippy.js";
import "tippy.js/dist/tippy.css";
const initTooltips = () => {
const tooltipItems = document.querySelectorAll<HTMLElement>("[title][data-tooltip]");
tooltipItems.forEach((el) => el.setAttribute("data-tooltip", el.title));
tippy(tooltipItems, {
content: (el) => (el as HTMLElement).dataset.tooltip!,
theme: "bm",
});
tooltipItems.forEach((el) => el.removeAttribute("title"));
};
document.addEventListener("astro:page-load", initTooltips);
initTooltips();
</script>

View file

@ -3,6 +3,7 @@ import { Image } from "astro:assets";
import BaseLayout from "@layouts/BaseLayout.astro";
import { IconArrowUpRightFromSquare } from "@components/icons";
import { ImageSamStickerJuicebox } from "@assets/images";
import EnhancedTooltips from "@components/EnhancedTooltips.astro";
---
<BaseLayout pageTitle="About me">
@ -65,20 +66,4 @@ import { ImageSamStickerJuicebox } from "@assets/images";
</article>
</BaseLayout>
<script>
import tippy from "tippy.js";
import "tippy.js/dist/tippy.css";
const initTooltips = () => {
const tooltipItems = document.querySelectorAll<HTMLElement>("[title][data-tooltip]");
tooltipItems.forEach((el) => el.setAttribute("data-tooltip", el.title));
tippy(tooltipItems, {
content: (el) => (el as HTMLElement).dataset.tooltip!,
theme: "bm",
});
tooltipItems.forEach((el) => el.removeAttribute("title"));
};
document.addEventListener("astro:page-load", initTooltips);
initTooltips();
</script>
<EnhancedTooltips />

View file

@ -38,9 +38,12 @@ import {
IconWeasyl,
IconYouTube,
} from "@components/icons/brands";
import EnhancedTooltips from "@components/EnhancedTooltips.astro";
const gpgKey = await readFile("./public/gpg.pub", { encoding: "utf-8" });
const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
const [gpgKey, sshKey] = await Promise.all([
readFile("./public/gpg.pub", { encoding: "utf-8" }),
readFile("./public/ssh.pub", { encoding: "utf-8" }),
]);
---
<BaseLayout>
@ -80,7 +83,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
>
{
Astro.site ? (
<li data-link>
<li>
<a
id="permalink"
class="u-url contact-link group"
@ -88,8 +91,8 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
aria-label="Permalink"
title="Permalink"
data-tooltip
data-clipboard={Astro.site}
data-noun="URL"
x-data={`{ clipboard: ${JSON.stringify(Astro.site.toString())}, tooltip: 'URL copied to clipboard!' }`}
@click.prevent="navigator.clipboard.writeText(clipboard).catch(()=>{}); $dispatch('tippyTooltip', { target: $el, content: tooltip })"
>
<IconLink height="1.75rem" width="1.75rem" class="contact-icon" />
<p class="sr-only select-none">https://badmanners.xyz</p>
@ -97,7 +100,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
</li>
) : null
}
<li data-link>
<li>
<a
id="gallery"
class="u-url contact-link group"
@ -112,7 +115,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="sr-only select-none">Gallery on https://gallery.badmanners.xyz</p>
</a>
</li>
<li data-link>
<li>
<a
id="gallery-feed"
class="u-url contact-link group"
@ -127,7 +130,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="sr-only select-none">Gallery feed</p>
</a>
</li>
<li data-link>
<li>
<a
id="pronouns"
class="u-url contact-link group"
@ -143,7 +146,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-pronoun hidden">they/them/their/theirs/themself</p>
</a>
</li>
<li data-link>
<li>
<a
id="e-mail"
class="u-email contact-link group"
@ -152,14 +155,14 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
aria-label="E-mail"
title="E-mail"
data-tooltip
data-clipboard="me@badmanners.xyz"
data-noun="E-mail address"
x-data="{ clipboard: 'me@badmanners.xyz', tooltip: 'E-mail address copied to clipboard!' }"
@click.prevent="navigator.clipboard.writeText(clipboard).catch(()=>{}); $dispatch('tippyTooltip', { target: $el, content: tooltip })"
>
<IconEnvelope height="1.75rem" width="1.75rem" class="contact-icon" />
<p class="sr-only select-none">me@badmanners.xyz</p>
</a>
</li>
<li data-link>
<li>
<a
id="bluesky"
class="u-url contact-link group"
@ -173,7 +176,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">@badmanners.xyz on Bluesky</p>
</a>
</li>
<li data-link>
<li>
<a
id="codeberg"
class="u-url contact-link group"
@ -187,22 +190,21 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">BadManners on Codeberg</p>
</a>
</li>
<li data-link>
<li>
<button
id="discord"
class="text-link group block w-full py-2 transition-colors motion-reduce:transition-none"
aria-label="Discord"
title="Discord"
data-tooltip
data-clipboard="badmanners"
data-noun="Discord username"
disabled
x-data="{ clipboard: 'badmanners', tooltip: 'Discord username copied to clipboard!' }"
@click.prevent="navigator.clipboard.writeText(clipboard).catch(()=>{}); $dispatch('tippyTooltip', { target: $el, content: tooltip })"
>
<IconDiscord height="1.75rem" width="1.75rem" class="contact-icon" />
<p class="p-nickname sr-only select-none">badmanners on Discord</p>
</button>
</li>
<li data-link>
<li>
<a
id="eka-s-portal"
class="u-url contact-link group"
@ -216,7 +218,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">BadManners on Eka's Portal</p>
</a>
</li>
<li data-link>
<li>
<a
id="fur-affinity"
class="u-url contact-link group"
@ -230,7 +232,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">BadManners on Fur Affinity</p>
</a>
</li>
<li data-link>
<li>
<a
id="gitgud"
class="u-url contact-link group"
@ -244,7 +246,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">BadMannersXYZ on GitGud</p>
</a>
</li>
<li data-link>
<li>
<a
id="github"
class="u-url contact-link group"
@ -258,7 +260,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">BadMannersXYZ on GitHub</p>
</a>
</li>
<li data-link>
<li>
<a
id="gitlab"
class="u-url contact-link group"
@ -272,7 +274,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">Bad_Manners on GitLab</p>
</a>
</li>
<li data-link>
<li>
<a
id="google"
class="u-email contact-link group"
@ -281,14 +283,14 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
aria-label="Google"
title="Google"
data-tooltip
data-clipboard="google@badmanners.xyz"
data-noun="Google address"
x-data="{ clipboard: 'google@badmanners.xyz', tooltip: 'Google address copied to clipboard!' }"
@click.prevent="navigator.clipboard.writeText(clipboard).catch(()=>{}); $dispatch('tippyTooltip', { target: $el, content: tooltip })"
>
<IconGoogle height="1.75rem" width="1.75rem" class="contact-icon" />
<p class="sr-only select-none">google@badmanners.xyz</p>
</a>
</li>
<li data-link>
<li>
<a
id="gpg"
class="u-key contact-link group"
@ -297,14 +299,14 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
aria-label="GPG public key"
title="GPG public key"
data-tooltip
data-clipboard={gpgKey}
data-noun="GPG key"
x-data={`{ clipboard: ${JSON.stringify(gpgKey)}, tooltip: 'GPG key copied to clipboard!' }`}
@click.prevent="navigator.clipboard.writeText(clipboard).catch(()=>{}); $dispatch('tippyTooltip', { target: $el, content: tooltip })"
>
<IconKey height="1.75rem" width="1.75rem" class="contact-icon" />
<p class="sr-only select-none">GPG public key</p>
</a>
</li>
<li data-link>
<li>
<a
id="inkbunny"
class="u-url contact-link group"
@ -318,7 +320,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">BadManners on Inkbunny</p>
</a>
</li>
<li data-link>
<li>
<a
id="itaku"
class="u-url contact-link group"
@ -332,7 +334,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">badmanners on Itaku</p>
</a>
</li>
<li data-link>
<li>
<a
id="itch"
class="u-url contact-link group"
@ -346,7 +348,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">Bad Manners on Itch.io</p>
</a>
</li>
<li data-link>
<li>
<a
id="keybase"
class="u-url contact-link group"
@ -360,7 +362,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">badmanners on Keybase</p>
</a>
</li>
<li data-link>
<li>
<a
id="keyoxide"
class="u-url contact-link group"
@ -374,7 +376,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-uid sr-only select-none">aspe:keyoxide.org:UWYBVFCBFXTVUF2U6FS6AYJHLU</p>
</a>
</li>
<li data-link>
<li>
<a
id="ko-fi"
class="u-url contact-link group"
@ -388,7 +390,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">badmanners on Ko-fi</p>
</a>
</li>
<li data-link>
<li>
<a
id="mastodon"
class="u-url contact-link group"
@ -402,7 +404,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">@BadManners@meow.social on Mastodon</p>
</a>
</li>
<li data-link>
<li>
<a
id="neocities"
class="u-url contact-link group"
@ -416,7 +418,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">badmanners.neocities.org on Neocities</p>
</a>
</li>
<li data-link>
<li>
<a
id="picarto"
class="u-url contact-link group"
@ -430,7 +432,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">BadManners on Picarto</p>
</a>
</li>
<li data-link>
<li>
<a
id="signal"
class="u-url contact-link group"
@ -444,7 +446,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">badmanners.10 on Signal</p>
</a>
</li>
<li data-link>
<li>
<a
id="sofurry"
class="u-url contact-link group"
@ -458,7 +460,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">Bad Manners on SoFurry</p>
</a>
</li>
<li data-link>
<li>
<a
id="ssh"
class="u-key contact-link group"
@ -467,14 +469,14 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
aria-label="SSH public key"
title="SSH public key"
data-tooltip
data-clipboard={sshKey}
data-noun="SSH key"
x-data={`{ clipboard: ${JSON.stringify(sshKey)}, tooltip: 'SSH key address copied to clipboard!' }`}
@click.prevent="navigator.clipboard.writeText(clipboard).catch(()=>{}); $dispatch('tippyTooltip', { target: $el, content: tooltip })"
>
<IconSSH height="1.75rem" width="1.75rem" class="contact-icon" />
<p class="sr-only select-none">SSH public key</p>
</a>
</li>
<li data-link>
<li>
<a
id="steam"
class="u-url contact-link group"
@ -488,7 +490,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">badmanners_ on Steam</p>
</a>
</li>
<li data-link>
<li>
<a
id="subscribestar"
class="u-url contact-link group"
@ -502,7 +504,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">Bad Manners on SubscribeStar</p>
</a>
</li>
<li data-link>
<li>
<a
id="telegram"
class="u-url contact-link group"
@ -516,7 +518,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">@bad_manners on Telegram</p>
</a>
</li>
<li data-link>
<li>
<a
id="twitch"
class="u-url contact-link group"
@ -530,7 +532,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">bad__manners on Twitch</p>
</a>
</li>
<li data-link>
<li>
<a
id="weasyl"
class="u-url contact-link group"
@ -544,7 +546,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
<p class="p-nickname sr-only select-none">BadManners on Weasyl</p>
</a>
</li>
<li data-link>
<li>
<a
id="youtube"
class="u-url contact-link group"
@ -570,85 +572,39 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
}
</style>
<EnhancedTooltips />
<script>
import tippy, { hideAll } from "tippy.js";
import "tippy.js/dist/tippy.css";
const initIndexIcons = () => {
const handleTooltipEvent = () => {
const indexLinks = document.querySelector<HTMLElementTagNameMap["ul"]>("ul#links");
if (!indexLinks) {
return;
}
// Instantiate hover tooltips
const tooltipItems = document.querySelectorAll<HTMLElement>("[title][data-tooltip]");
tooltipItems.forEach((el) => el.setAttribute("data-tooltip", el.title));
tippy(tooltipItems, {
content: (el) => (el as HTMLElement).dataset.tooltip!,
theme: "bm",
});
tooltipItems.forEach((el) => el.removeAttribute("title"));
// Add functionality to custom clipboard items
if ("clipboard" in navigator) {
const customClipboardItems = document.querySelectorAll<HTMLElement>("ul#links li[data-link] [data-clipboard]");
if (!customClipboardItems.length) {
console.warn("Missing custom clipboard elements in #links list.");
}
customClipboardItems.forEach((element) => {
const label = element.dataset.noun ?? element.getAttribute("aria-label");
const clipboard = element.dataset.clipboard;
if (!clipboard) {
console.warn(`Missing "data-clipboard" field for custom clipboard element, ignoring...`, element);
return;
}
element.removeAttribute("disabled");
const elementTooltip = tippy(element, {
content: label ? `${label} copied to clipboard!` : "Copied to clipboard!",
trigger: "manual",
theme: "bm",
});
const onClickElement = async (ev: Event) => {
ev.preventDefault();
hideAll();
requestAnimationFrame(() => {
navigator.clipboard
.writeText(clipboard)
.then(() => elementTooltip.show())
.catch((e) => {
console.error("Unable to copy custom content to clipboard.", clipboard, e);
});
});
};
element.addEventListener("click", onClickElement);
});
} else {
const customClipboardButtons = document.querySelectorAll<HTMLElementTagNameMap["button"]>(
"ul#links li[data-link] button[data-clipboard]",
);
customClipboardButtons.forEach((element) => {
element.removeAttribute("disabled");
const clipboard = element.dataset.clipboard;
if (!clipboard) {
console.warn(`Missing "data-clipboard" field for custom clipboard element, ignoring...`, element);
return;
}
element.removeAttribute("disabled");
const elementTooltip = tippy(element, {
content: clipboard,
trigger: "manual",
theme: "bm",
});
const onClickElement = async (ev: Event) => {
ev.preventDefault();
hideAll();
elementTooltip.show();
};
element.addEventListener("click", onClickElement);
});
interface TippyTooltipEvent extends Event {
detail: {
target: HTMLElement;
content: string;
};
}
document.addEventListener("tippyTooltip", (e: Event) => {
const { target, content } = (e as TippyTooltipEvent).detail;
hideAll();
const tooltip = tippy(target, {
content,
theme: "bm",
onHidden(instance) {
instance.destroy();
},
});
tooltip.show();
});
};
document.addEventListener("astro:page-load", initIndexIcons);
initIndexIcons();
document.addEventListener("astro:page-load", handleTooltipEvent);
handleTooltipEvent();
</script>

View file

@ -3,6 +3,7 @@ import { Image } from "astro:assets";
import BaseLayout from "@layouts/BaseLayout.astro";
import { IconArrowUpRightFromSquare } from "@components/icons";
import { ImageSamAllStickers, ImageSamRefsheet } from "@assets/images";
import EnhancedTooltips from "@components/EnhancedTooltips.astro";
---
<BaseLayout pageTitle="Sam Brendan">
@ -195,20 +196,4 @@ import { ImageSamAllStickers, ImageSamRefsheet } from "@assets/images";
</article>
</BaseLayout>
<script>
import tippy from "tippy.js";
import "tippy.js/dist/tippy.css";
const initTooltips = () => {
const tooltipItems = document.querySelectorAll<HTMLElement>("[title][data-tooltip]");
tooltipItems.forEach((el) => el.setAttribute("data-tooltip", el.title));
tippy(tooltipItems, {
content: (el) => (el as HTMLElement).dataset.tooltip!,
theme: "bm",
});
tooltipItems.forEach((el) => el.removeAttribute("title"));
};
document.addEventListener("astro:page-load", initTooltips);
initTooltips();
</script>
<EnhancedTooltips />