Anpassung Tagsuche, Fix Fehler bei längeren Ladezeiten
This commit is contained in:
137
ui/classify.js
137
ui/classify.js
@@ -14,6 +14,11 @@ const $ = (id) => document.getElementById(id);
|
||||
let dokumentId = null;
|
||||
let alleTags = [];
|
||||
|
||||
// Gewählte Tags als Menge. Bewusst getrennt vom angezeigten Zustand der
|
||||
// Kontrollkästchen: Der Filter blendet Zeilen aus, die Auswahl darf davon
|
||||
// nicht berührt werden.
|
||||
const gewaehlt = new Set();
|
||||
|
||||
document.addEventListener("DOMContentLoaded", start);
|
||||
|
||||
async function start() {
|
||||
@@ -66,30 +71,128 @@ function nameVon(liste, id) {
|
||||
}
|
||||
|
||||
function zeigeTags(gesetzt) {
|
||||
const ziel = $("tags");
|
||||
ziel.replaceChildren();
|
||||
for (const tag of alleTags) {
|
||||
const zeile = document.createElement("div");
|
||||
zeile.className = "wahl";
|
||||
const box = document.createElement("input");
|
||||
box.type = "checkbox";
|
||||
box.id = `t${tag.id}`;
|
||||
box.value = String(tag.id);
|
||||
box.checked = gesetzt.includes(tag.id);
|
||||
const bez = document.createElement("label");
|
||||
bez.htmlFor = box.id;
|
||||
bez.textContent = tag.name;
|
||||
zeile.append(box, bez);
|
||||
ziel.append(zeile);
|
||||
for (const id of gesetzt) gewaehlt.add(Number(id));
|
||||
const feld = $("tagSuche");
|
||||
if (feld) {
|
||||
feld.addEventListener("input", zeichneTags);
|
||||
// Eingabetaste soll den Dialog nicht abschicken, sondern beim
|
||||
// einzigen Treffer diesen umschalten - das ist beim Tippen der
|
||||
// erwartete Ablauf.
|
||||
feld.addEventListener("keydown", (e) => {
|
||||
if (e.key !== "Enter") return;
|
||||
e.preventDefault();
|
||||
const treffer = gefilterteTags();
|
||||
if (treffer.length === 1) {
|
||||
umschalten(treffer[0].id);
|
||||
feld.value = "";
|
||||
zeichneTags();
|
||||
}
|
||||
});
|
||||
}
|
||||
zeichneTags();
|
||||
}
|
||||
|
||||
/** Tags, die zum aktuellen Filter passen und noch nicht gewählt sind. */
|
||||
function gefilterteTags() {
|
||||
const filter = ($("tagSuche")?.value || "").trim().toLowerCase();
|
||||
return alleTags.filter((t) => {
|
||||
if (gewaehlt.has(Number(t.id))) return false;
|
||||
if (!filter) return true;
|
||||
return String(t.name).toLowerCase().includes(filter);
|
||||
});
|
||||
}
|
||||
|
||||
function umschalten(id) {
|
||||
const n = Number(id);
|
||||
if (gewaehlt.has(n)) gewaehlt.delete(n);
|
||||
else gewaehlt.add(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tagliste zeichnen.
|
||||
*
|
||||
* Zwei Bereiche: oben die gewählten, darunter die übrigen. Die gewählten
|
||||
* bleiben immer sichtbar, auch wenn der Filter sie ausschliesst - sonst
|
||||
* verliert man beim Suchen den Überblick, was bereits angehakt ist.
|
||||
*/
|
||||
function zeichneTags() {
|
||||
const filter = ($("tagSuche")?.value || "").trim().toLowerCase();
|
||||
|
||||
const oben = $("tagsGewaehlt");
|
||||
oben.replaceChildren();
|
||||
const gewaehlteListe = alleTags
|
||||
.filter((t) => gewaehlt.has(Number(t.id)))
|
||||
.sort((a, b) => String(a.name).localeCompare(String(b.name), "de"));
|
||||
for (const tag of gewaehlteListe) {
|
||||
oben.append(tagZeile(tag, true, ""));
|
||||
}
|
||||
|
||||
const unten = $("tags");
|
||||
unten.replaceChildren();
|
||||
const rest = gefilterteTags()
|
||||
.sort((a, b) => String(a.name).localeCompare(String(b.name), "de"));
|
||||
for (const tag of rest) {
|
||||
unten.append(tagZeile(tag, false, filter));
|
||||
}
|
||||
|
||||
const leer = $("tagsLeer");
|
||||
if (leer) leer.style.display = (rest.length === 0 && filter) ? "" : "none";
|
||||
}
|
||||
|
||||
function tagZeile(tag, istGewaehlt, filter) {
|
||||
const zeile = document.createElement("div");
|
||||
zeile.className = "wahl";
|
||||
|
||||
const box = document.createElement("input");
|
||||
box.type = "checkbox";
|
||||
box.id = `t${tag.id}`;
|
||||
box.value = String(tag.id);
|
||||
box.checked = istGewaehlt;
|
||||
box.addEventListener("change", () => {
|
||||
umschalten(tag.id);
|
||||
zeichneTags();
|
||||
});
|
||||
|
||||
const bez = document.createElement("label");
|
||||
bez.htmlFor = box.id;
|
||||
beschriftungSetzen(bez, String(tag.name), filter);
|
||||
|
||||
zeile.append(box, bez);
|
||||
return zeile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Beschriftung mit hervorgehobenem Treffer.
|
||||
*
|
||||
* Aufbau über createElement und textContent, nicht über innerHTML - ein
|
||||
* Tagname stammt aus Paperless und damit mittelbar aus fremden Quellen.
|
||||
*/
|
||||
function beschriftungSetzen(el, name, filter) {
|
||||
el.replaceChildren();
|
||||
if (!filter) {
|
||||
el.textContent = name;
|
||||
return;
|
||||
}
|
||||
const pos = name.toLowerCase().indexOf(filter);
|
||||
if (pos < 0) {
|
||||
el.textContent = name;
|
||||
return;
|
||||
}
|
||||
el.append(document.createTextNode(name.slice(0, pos)));
|
||||
const treffer = document.createElement("span");
|
||||
treffer.className = "trefferHervor";
|
||||
treffer.textContent = name.slice(pos, pos + filter.length);
|
||||
el.append(treffer);
|
||||
el.append(document.createTextNode(name.slice(pos + filter.length)));
|
||||
}
|
||||
|
||||
async function speichern() {
|
||||
$("speichern").disabled = true;
|
||||
meldungAnzeigen(browser.i18n.getMessage("msgSaving") || "Wird gespeichert …");
|
||||
try {
|
||||
const tags = [...$("tags").querySelectorAll("input:checked")]
|
||||
.map((b) => Number(b.value));
|
||||
// Aus der Menge lesen, nicht aus den Kästchen: Gefilterte Zeilen
|
||||
// sind gar nicht im Dokument, ihre Auswahl ginge sonst verloren.
|
||||
const tags = [...gewaehlt];
|
||||
const payload = { title: $("titel").value.trim(), tags };
|
||||
const erstellt = $("erstellt").value.trim();
|
||||
if (erstellt) payload.created = erstellt;
|
||||
|
||||
Reference in New Issue
Block a user