feat: establish Stitch cockpit frontend contract

This commit is contained in:
Jens
2026-07-22 19:48:36 +02:00
parent a30ecbc553
commit 09af98193f
17 changed files with 764 additions and 941 deletions
+35 -29
View File
@@ -1,46 +1,35 @@
(() => {
"use strict";
const root = document.documentElement;
let storedTheme = null;
try {
storedTheme = localStorage.getItem("vacatureradar-theme");
} catch {
// Private or embedded browser contexts may disable persistent storage.
}
if (storedTheme === "light" || storedTheme === "dark") root.dataset.theme = storedTheme;
const themeToggle = document.querySelector("[data-theme-toggle]");
themeToggle?.addEventListener("click", () => {
const theme = root.dataset.theme === "light" ? "dark" : "light";
root.dataset.theme = theme;
try {
localStorage.setItem("vacatureradar-theme", theme);
} catch {
// The selected theme still applies for the current page.
}
themeToggle.setAttribute("aria-label", theme === "light" ? "Donker thema activeren" : "Licht thema activeren");
});
const frame = document.querySelector("[data-app-frame]");
const sidebar = document.querySelector("[data-sidebar]");
const toggle = document.querySelector("[data-nav-toggle]");
const closeButtons = document.querySelectorAll("[data-nav-close], [data-nav-scrim]");
const navToggle = document.querySelector("[data-nav-toggle]");
let returnFocus = null;
try {
const storedTheme = localStorage.getItem("vacatureradar-theme");
if (storedTheme === "light" || storedTheme === "dark") root.dataset.theme = storedTheme;
} catch { /* Persistent storage is optional. */ }
document.querySelector("[data-theme-toggle]")?.addEventListener("click", (event) => {
const next = root.dataset.theme === "light" ? "dark" : "light";
root.dataset.theme = next;
event.currentTarget.setAttribute("aria-label", next === "light" ? "Donker thema activeren" : "Licht thema activeren");
try { localStorage.setItem("vacatureradar-theme", next); } catch { /* Current page still updates. */ }
});
const closeNavigation = () => {
frame?.classList.remove("nav-open");
toggle?.setAttribute("aria-expanded", "false");
navToggle?.setAttribute("aria-expanded", "false");
if (returnFocus instanceof HTMLElement) returnFocus.focus();
};
toggle?.addEventListener("click", () => {
navToggle?.addEventListener("click", () => {
returnFocus = document.activeElement;
frame?.classList.add("nav-open");
toggle.setAttribute("aria-expanded", "true");
navToggle.setAttribute("aria-expanded", "true");
sidebar?.querySelector("a, button")?.focus();
});
closeButtons.forEach((button) => button.addEventListener("click", closeNavigation));
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") closeNavigation();
});
document.querySelectorAll("[data-nav-close], [data-nav-scrim]").forEach((control) => control.addEventListener("click", closeNavigation));
document.querySelectorAll("[data-disclosure]").forEach((button) => {
button.addEventListener("click", () => {
@@ -51,4 +40,21 @@
target.hidden = expanded;
});
});
document.querySelectorAll("[data-view-switch]").forEach((button) => {
button.addEventListener("click", () => {
const group = button.closest("[data-view-controls]");
const view = button.dataset.viewSwitch;
group?.querySelectorAll("[data-view-switch]").forEach((item) => item.setAttribute("aria-pressed", String(item === button)));
document.querySelectorAll("[data-view]").forEach((panel) => { panel.hidden = panel.dataset.view !== view; });
});
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") closeNavigation();
if (event.key === "/" && !/INPUT|TEXTAREA|SELECT/.test(document.activeElement?.tagName || "")) {
event.preventDefault();
document.getElementById("global-search")?.focus();
}
});
})();