/* =================================================================== Auswahlfelder, die man gestalten kann. WARUM ES DAS GIBT: Ein aufgeklapptes bleibt im Dokument und behaelt seinen Wert. Es wird nur unsichtbar gemacht; darueber liegt ein eigener Knopf mit eigener Liste. Damit funktioniert alles weiter, was vorher schon da war -- jedes `feld.value`, jedes `change`-Ereignis, jedes Formular. Faellt dieses Skript aus, ist wieder das gewohnte Systemmenue da. Es erfasst auch Felder, die erst spaeter entstehen (MutationObserver) und Eintraege, die erst nachgeladen werden. Deshalb musste ich keine dreizehn Seiten einzeln umbauen -- und die naechste ist automatisch dabei. Die Liste haengt an `position: fixed`, nicht am Elternelement: In einer Karte mit `overflow: hidden` waere sie sonst abgeschnitten. --------------------------------------------------------------------- SUCHEN UND SELBST EINTRAGEN (02.09.2026) Wunsch: "ich will da auch sachen selber noch eintragen können, also aussuchen und selbst eintragen." Zwei Dinge kamen dafuer dazu, und beide sitzen HIER und nicht in einem zweiten Bauteil daneben. Der erste Anlauf war genau das -- eine eigene Personenwahl neben dieser hier. Sie hat sich prompt mit ihr gebissen: Zwei Bauteile haben denselben ist leer -- entweder eine Person ODER ein Name, nie beides. Die Seiten schicken beides getrennt zum Server. Ohne Erlaubnis bleibt alles beim Alten: filtern ja, erfinden nein. =================================================================== */ (() => { 'use strict'; const OFFEN = new WeakMap(); let aktiv = null; // gerade offene Liste /* Ab wann ein Suchfeld erscheint. Bei drei Eintraegen waere es nur im Weg, bei siebzehn ist Scrollen die falsche Bedienung. */ const SUCHE_AB = 8; const EXTERN_MAX = 80; const el = (tag, klasse, text) => { const k = document.createElement(tag); if (klasse) k.className = klasse; if (text !== undefined) k.textContent = text; return k; }; /* ---------- Ein Feld aufwerten ----------------------------------------- */ function werteAuf(feld) { if (feld.multiple || feld.dataset.wahl === 'aus' || OFFEN.has(feld)) return; const huelle = el('div', 'wahl2'); const knopf = el('button', 'wahl2__knopf'); knopf.type = 'button'; knopf.setAttribute('aria-haspopup', 'listbox'); knopf.setAttribute('aria-expanded', 'false'); const text = el('span', 'wahl2__text'); const pfeil = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); pfeil.setAttribute('viewBox', '0 0 24 24'); pfeil.setAttribute('class', 'wahl2__pfeil'); pfeil.setAttribute('aria-hidden', 'true'); pfeil.innerHTML = ''; knopf.append(text, pfeil); /* Beschriftung uebernehmen, damit der Knopf denselben Namen traegt wie vorher das Feld. */ const schild = feld.id && document.querySelector(`label[for="${feld.id}"]`); if (schild) knopf.setAttribute('aria-label', schild.textContent.trim()); else if (feld.getAttribute('aria-label')) knopf.setAttribute('aria-label', feld.getAttribute('aria-label')); feld.parentNode.insertBefore(huelle, feld); huelle.append(feld, knopf); feld.classList.add('wahl2__echt'); const zustand = { feld, knopf, text, liste: null }; OFFEN.set(feld, zustand); knopf.addEventListener('click', () => (aktiv === zustand ? schliessen() : oeffnen(zustand))); knopf.addEventListener('keydown', (e) => { if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); oeffnen(zustand); } }); /* Aendert die Seite den Wert selbst, muss der Knopf mitziehen. */ feld.addEventListener('change', () => zeige(zustand)); /* Eintraege werden oft erst nachgeladen. Nach jeder Aenderung neu zeichnen -- im naechsten Bild, damit ein direkt danach gesetztes `feld.value` noch mitgenommen wird. */ new MutationObserver(() => requestAnimationFrame(() => zeige(zustand))) .observe(feld, { childList: true, subtree: true }); zeige(zustand); } function zeige(z) { const extern = z.feld.dataset.extern || ''; const gewaehlt = z.feld.selectedOptions[0]; /* Ein frei eingetragener Name schlaegt die Auswahl -- das , kennt aber den frei eingetragenen Namen nicht -- ohne das hier stuende er nach dem Abbrechen noch da und behauptete eine Zuordnung, die es nicht mehr gibt. */ document.addEventListener('reset', (e) => { const felder = e.target?.querySelectorAll?.('select[data-extern]'); if (!felder) return; setTimeout(() => { for (const f of felder) { f.dataset.extern = ''; const z = OFFEN.get(f); if (z) zeige(z); } }, 0); }); /* ---------- Aufwerten, jetzt und spaeter --------------------------------- */ const alle = () => document.querySelectorAll('select:not(.wahl2__echt)'); const laufen = () => { for (const f of alle()) werteAuf(f); }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', laufen); } else { laufen(); } new MutationObserver(() => laufen()).observe(document.documentElement, { childList: true, subtree: true, }); /* ---------- Freie Namen erlauben ----------------------------------------- Die Seiten rufen das auf, nachdem sie ihre Personen geladen haben: window.personenwahl?.('f-creator', { hinweis: '…' }); Danach nimmt genau dieses Feld auch einen getippten Namen an. Der Hinweis steht unter dem Feld, sobald wirklich einer eingetragen ist. Als Attribute am Feld und nicht als Zustand hier drinnen: So ueberlebt die Einstellung, wenn die Seite das Feld spaeter neu aufbaut -- und man sieht ihr im Browser-Werkzeug an, dass sie gesetzt ist. */ window.personenwahl = function (feld, optionen = {}) { const f = typeof feld === 'string' ? document.getElementById(feld) : feld; if (!f) return null; f.dataset.frei = optionen.frei === false ? 'nein' : 'ja'; if (optionen.hinweis) f.dataset.freiHinweis = optionen.hinweis; if (!f.dataset.extern) f.dataset.extern = ''; /* Falls das Feld schon aufgewertet ist, den Knopf neu beschriften -- "disabled" haengt jetzt an einer anderen Bedingung. */ const z = OFFEN.get(f); if (z) zeige(z); return { wert: () => f.value, extern: () => f.dataset.extern || '', zuruecksetzen() { f.value = ''; f.dataset.extern = ''; const zz = OFFEN.get(f); if (zz) zeige(zz); }, }; }; })();