Archived
Refactor UI assets and harden quality checks
This commit is contained in:
1 parent
23ab466c52
commit
4ecc8eb874
62 files changed
+3314
-3206
No files matched your search
@@ -0,0 +1,87 @@
|
||||
const legacyCopy = (value) => {
|
||||
const textarea = document.createElement("textarea");
|
||||
textarea.value = value;
|
||||
textarea.setAttribute("readonly", "");
|
||||
textarea.style.position = "fixed";
|
||||
textarea.style.top = "-9999px";
|
||||
textarea.style.left = "-9999px";
|
||||
document.body.append(textarea);
|
||||
textarea.select();
|
||||
textarea.setSelectionRange(0, textarea.value.length);
|
||||
try {
|
||||
return document.execCommand("copy");
|
||||
} finally {
|
||||
textarea.remove();
|
||||
}
|
||||
};
|
||||
|
||||
const copyText = async (value) => {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
await navigator.clipboard.writeText(value);
|
||||
return true;
|
||||
}
|
||||
return legacyCopy(value);
|
||||
};
|
||||
|
||||
const showCopyStatus = (button, text, className, duration = 1200) => {
|
||||
const label = button.querySelector("[data-copy-label]");
|
||||
const oldLabel = label ? label.textContent : "";
|
||||
button.classList.add(className);
|
||||
if (label) {
|
||||
label.setAttribute("aria-live", "polite");
|
||||
label.textContent = text;
|
||||
}
|
||||
setTimeout(() => {
|
||||
button.classList.remove(className);
|
||||
if (label) label.textContent = oldLabel;
|
||||
}, duration);
|
||||
};
|
||||
|
||||
document.addEventListener("click", async (event) => {
|
||||
document.querySelectorAll("details.copy-menu[open]").forEach((menu) => {
|
||||
if (!menu.contains(event.target)) menu.removeAttribute("open");
|
||||
});
|
||||
|
||||
const toggle = event.target.closest("[data-secret-toggle]");
|
||||
if (toggle) {
|
||||
const group = toggle.closest(".copy-field");
|
||||
const field = group ? group.querySelector("[data-secret-input], [data-secret-value]") : null;
|
||||
if (!field) return;
|
||||
if (field.matches("[data-secret-input]")) {
|
||||
const masked = field.type === "password";
|
||||
field.type = masked ? "text" : "password";
|
||||
toggle.textContent = masked ? "Hide" : "View";
|
||||
toggle.setAttribute("aria-label", masked ? "Hide secret value" : "Show secret value");
|
||||
} else {
|
||||
const masked = field.dataset.secretMasked !== "false";
|
||||
field.dataset.secretMasked = masked ? "false" : "true";
|
||||
field.textContent = masked ? (field.dataset.secretValue || "") : "********";
|
||||
toggle.textContent = masked ? "Hide" : "View";
|
||||
toggle.setAttribute("aria-label", masked ? "Hide secret value" : "Show secret value");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const button = event.target.closest("[data-copy]");
|
||||
if (!button) return;
|
||||
const menu = button.closest("details.copy-menu");
|
||||
try {
|
||||
const copied = await copyText(button.dataset.copy || "");
|
||||
if (!copied) throw new Error("copy failed");
|
||||
showCopyStatus(button, "Copied", "is-copied");
|
||||
if (menu) {
|
||||
setTimeout(() => {
|
||||
menu.removeAttribute("open");
|
||||
}, 250);
|
||||
}
|
||||
} catch (_) {
|
||||
showCopyStatus(button, "Copy failed", "is-copy-error", 1800);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key !== "Escape") return;
|
||||
document.querySelectorAll("details.copy-menu[open]").forEach((menu) => {
|
||||
menu.removeAttribute("open");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user