Files
brass-and-sigil/server/wwwroot/modules/modal.js
T
Matt Sijbers a1331212cb Initial commit: Brass & Sigil monorepo
Self-hosted Minecraft modpack distribution + administration system.

- launcher/  Avalonia 12 desktop client; single-file win-x64 publish.
             Microsoft auth via XboxAuthNet, manifest+SHA-1 mod sync,
             portable install path, sidecar settings.
- server/    brass-sigil-server daemon (.NET 8, linux-x64). Wraps the
             MC subprocess, embedded Kestrel admin panel with cookie
             auth + rate limiting, RCON bridge, scheduled backups,
             BlueMap CLI integration with player markers + skin proxy,
             friend-side whitelist request flow, world wipe with seed
             selection (keep current / random / custom).
- pack/      pack.lock.json (Modrinth + manual CurseForge entries),
             data-only tweak source under tweaks/, build outputs in
             overrides/ (gitignored).
- scripts/   Build-Pack / Build-Tweaks / Update-Pack / Check-Updates
             plus Deploy-Brass.ps1 unified one-shot deploy with
             version-bump pre-flight and daemon-state detection.
2026-05-05 00:19:05 +01:00

58 lines
1.8 KiB
JavaScript

// Tiny modal helper. Registers a single document-level Esc + backdrop-click
// handler so individual modals don't have to. Public API: openModal(id) /
// closeModal(id) / closeAllModals().
"use strict";
let bound = false;
function bindGlobal() {
if (bound) return;
bound = true;
document.addEventListener("keydown", e => {
if (e.key === "Escape") closeAllModals();
});
document.addEventListener("click", e => {
// Backdrop click closes the topmost open modal.
const backdrop = e.target.closest(".modal-backdrop");
if (backdrop) closeModal(backdrop.parentElement.id);
const closeBtn = e.target.closest(".modal-close");
if (closeBtn) closeModal(closeBtn.closest(".modal").id);
});
}
export function openModal(id) {
bindGlobal();
const m = document.getElementById(id);
if (!m) return;
m.hidden = false;
document.body.classList.add("modal-open");
// Focus first input/button for keyboard users.
setTimeout(() => {
const focusable = m.querySelector("input, button:not(.modal-close), select, textarea");
focusable?.focus();
}, 50);
}
export function closeModal(id) {
const m = document.getElementById(id);
if (!m) return;
m.hidden = true;
if (!document.querySelector(".modal:not([hidden])")) {
document.body.classList.remove("modal-open");
}
}
export function closeAllModals() {
document.querySelectorAll(".modal:not([hidden])").forEach(m => m.hidden = true);
document.body.classList.remove("modal-open");
}
/// Wires `data-open-modal="someId"` on any element to opening the modal.
export function setupModalTriggers() {
bindGlobal();
document.addEventListener("click", e => {
const trigger = e.target.closest("[data-open-modal]");
if (trigger) openModal(trigger.getAttribute("data-open-modal"));
});
}