Add loading indicator and improve non-clipboard interactions

This commit is contained in:
Bad Manners 2024-09-23 11:26:24 -03:00
parent 66f6f52773
commit aa5759d6f5
4 changed files with 73 additions and 20 deletions

14
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "badmanners.xyz",
"version": "2.1.14",
"version": "2.1.15",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "badmanners.xyz",
"version": "2.1.14",
"version": "2.1.15",
"hasInstallScript": true,
"dependencies": {
"@astrojs/check": "^0.9.3",
@ -14,6 +14,7 @@
"@astrojs/tailwind": "^5.1.0",
"astro": "^4.15.5",
"astro-htaccess": "^0.2.0",
"astro-loading-indicator": "^0.6.0",
"date-fns": "^3.6.0",
"tailwindcss": "^3.4.11",
"tippy.js": "^6.3.7",
@ -2394,6 +2395,15 @@
"astro": "^4.0.0"
}
},
"node_modules/astro-loading-indicator": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/astro-loading-indicator/-/astro-loading-indicator-0.6.0.tgz",
"integrity": "sha512-0/gJ94ygnZ+qnLeP+dapOwAmj74hRP6t4o784+W41TicCp3pPm3d+WoH7XHlSQlokghtVynD9RdFjRd0ZWs9bg==",
"license": "MIT",
"peerDependencies": {
"astro": "^4.0.0"
}
},
"node_modules/autoprefixer": {
"version": "10.4.18",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz",

View file

@ -1,7 +1,7 @@
{
"name": "badmanners.xyz",
"type": "module",
"version": "2.1.14",
"version": "2.1.15",
"scripts": {
"postinstall": "astro sync",
"dev": "astro dev",
@ -20,6 +20,7 @@
"@astrojs/tailwind": "^5.1.0",
"astro": "^4.15.5",
"astro-htaccess": "^0.2.0",
"astro-loading-indicator": "^0.6.0",
"date-fns": "^3.6.0",
"tailwindcss": "^3.4.11",
"tippy.js": "^6.3.7",

View file

@ -1,5 +1,6 @@
---
import { ViewTransitions } from "astro:transitions";
import LoadingIndicator from "astro-loading-indicator/component";
import DarkModeScript from "@components/DarkModeScript.astro";
import NavHeader from "@components/NavHeader.astro";
import { IconSun, IconMoon } from "@components/icons";
@ -28,11 +29,13 @@ const title = pageTitle ? `${pageTitle} | Bad Manners` : "Bad Manners";
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<link rel="me" href="https://meow.social/@BadManners" />
<meta property="og:title" content={title} />
<meta property="og:url" content={Astro.url} />
<meta content="/logo.webp" property="og:image" />
<slot name="head" />
<ViewTransitions />
<LoadingIndicator color="#3b82f6" height="0.25rem" threshold={false} />
</head>
<body>
<div class="flex min-h-screen flex-col">

View file

@ -1,7 +1,15 @@
---
import { readFile } from "node:fs/promises";
import BaseLayout from "@layouts/BaseLayout.astro";
import { IconEnvelope, IconBriefcase, IconKey, IconLink, IconCommentDots, IconSSH } from "@components/icons";
import {
IconEnvelope,
IconBriefcase,
IconKey,
IconLink,
IconCommentDots,
IconSSH,
IconSquareRSS,
} from "@components/icons";
import {
IconBluesky,
IconCodeberg,
@ -104,6 +112,21 @@ 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>
<a
id="gallery-feed"
class="u-url contact-link group"
type="application/rss+xml"
href="https://gallery.badmanners.xyz/feed.xml"
rel="alternate"
aria-label="Gallery feed"
title="Gallery feed"
data-tooltip
>
<IconSquareRSS height="1.75rem" width="1.75rem" class="contact-icon" />
<p class="sr-only select-none">Gallery feed</p>
</a>
</li>
<li>
<a
id="pronouns"
@ -270,6 +293,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
id="gpg"
class="u-key contact-link group"
href="/gpg.pub"
type="text/plain"
aria-label="GPG public key"
title="GPG public key"
data-tooltip
@ -439,6 +463,7 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
id="ssh"
class="u-key contact-link group"
href="/ssh.pub"
type="text/plain"
aria-label="SSH public key"
title="SSH public key"
data-tooltip
@ -556,12 +581,6 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
}
// Validate links
const customClipboardItems = indexLinks.querySelectorAll<HTMLElementTagNameMap["a" | "button"]>(
"li > :is(a, button)[data-clipboard]",
);
if (!customClipboardItems.length) {
console.warn("Missing custom clipboard elements in #links list.");
}
indexLinks.querySelectorAll("li > :not(a, button)").forEach((el) => {
console.warn("Element with unknown type found in #links list:", el);
});
@ -580,6 +599,12 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
// Add functionality to custom clipboard items
if ("clipboard" in navigator) {
const customClipboardItems = indexLinks.querySelectorAll<HTMLElementTagNameMap["a" | "button"]>(
"li > :is(a, button)[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;
@ -587,7 +612,6 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
console.warn(`Missing "data-clipboard" field for custom clipboard element, ignoring...`, element);
return;
}
// const disabled = element.getAttribute("disabled");
element.removeAttribute("disabled");
const elementTooltip = tippy(element, {
content: label ? `${label} copied to clipboard!` : "Copied to clipboard!",
@ -603,20 +627,35 @@ const sshKey = await readFile("./public/ssh.pub", { encoding: "utf-8" });
.then(() => elementTooltip.show())
.catch((e) => {
console.error("Unable to copy custom content to clipboard.", clipboard, e);
// Clean up clipboard logic, and revert element to original state
// element.removeEventListener("click", onClickElement);
// elementTooltip.destroy();
// if (disabled !== null) {
// element.setAttribute("disabled", disabled);
// } else {
// // Invoke default click
// element.click();
// }
});
});
};
element.addEventListener("click", onClickElement);
});
} else {
const customClipboardButtons = indexLinks.querySelectorAll<HTMLElementTagNameMap["button"]>(
"li > button[data-clipboard][disabled]",
);
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);
});
}
};