Ich frage mich, ob wir die Filter verwenden können, um eine Kategorie mit der Dokumentenansicht und A-Z (alphabetisch) anzuzeigen.\n\nWir haben ein Glossar, das die Themenansicht geordnet anzeigen soll
\n\nDerzeit ist die Filteransicht getrennt von den Dokumenten:\n\n
Ich habe am Ende eine eigene Theme-Komponente dafür verwendet:
// A-Z benötigt die gesamte Kategorie: core kann eine Themenliste nicht nach Titel sortieren
// (TopicQuery::SORTABLE_MAPPING enthält kein "title"), daher wird die paginierte Liste
// vollständig geladen, bevor sortiert wird. Begrenzt — Dokumentationskategorien sind klein;
// bei größeren wird auf das Sortieren der geladenen Einträge zurückgefallen, was der
// MutationObserver unten aktuell hält.
// ponytail: Diese gesamte Schleife entfernen, falls core jemals serverseitige Titelsortierung bereitstellt.
const MAX_DOC_PAGES = 12; // ~360 Themen bei 30 pro Seite
let docLoadRun = 0;
async function loadAllDocTopics(api) {
const run = ++docLoadRun;
// `controller:discovery/topics` ist im aktuellen Discourse veraltet
// (Deprecation-ID: discourse.discovery-topics-controller). Discovery verwendet nun einen
// dedizierten Controller pro Filter; Dokumentationskategorien sind immer die neueste Themenliste,
// daher wird dieser Controller direkt abgefragt. Ein Fehler hier bedeutet nur, dass die erste
// Seite sortiert wird, daher sind alle Schritte optional-chained.
const list = api.container.lookup("controller:discovery/latest")?.model;
if (!list?.loadMore) {
return;
}
for (let page = 0; page < MAX_DOC_PAGES; page++) {
if (!list.more_topics_url) {
return;
}
const before = list.topics?.length ?? 0;
await list.loadMore();
// Während des Ladens navigiert oder die Liste wächst nicht mehr — in beiden Fällen stoppen.
if (run !== docLoadRun || (list.topics?.length ?? 0) === before) {
return;
}
}
}
// Sortiert die Dokumentations-Themenliste A-Z, kollationiert mit der eigenen <html lang> der Seite.
let docSortObservers = new Map();
function docSortLocale() {
const lang = document.documentElement.lang;
if (!lang) {
return undefined;
}
try {
Intl.getCanonicalLocales(lang); // wirft RangeError bei fehlerhaftem Tag
return lang;
} catch {
return undefined; // fällt auf die Standard-Locale des Browsers zurück
}
}
function sortDocTopicList(body, locale) {
const rows = [...body.querySelectorAll(":scope > .topic-list-item")];
if (rows.length < 2) {
return;
}
const sorted = [...rows].sort((a, b) => {
const titleA = a.querySelector(".title")?.textContent.trim() ?? "";
const titleB = b.querySelector(".title")?.textContent.trim() ?? "";
return titleA.localeCompare(titleB, locale);
});
if (sorted.every((row, i) => rows[i] === row)) {
return; // bereits sortiert — verhindert, dass der Observer sich selbst füttert
}
// Vor dem Element nach den Zeilen einfügen, damit ein mögliches load-more-Sentinel zuletzt bleibt.
const anchor = rows[rows.length - 1].nextSibling;
// Die erste sichtbare Zeile fixieren, damit Einfügungen darüber das Scrollen nicht verschieben.
const pinned = rows.find((row) => row.getBoundingClientRect().bottom > 0);
const pinnedTop = pinned?.getBoundingClientRect().top;
// Observer pausiert, damit unsere eigenen Bewegungen diese Funktion nicht erneut aufrufen.
const observer = docSortObservers.get(body);
observer?.disconnect();
sorted.forEach((row) => body.insertBefore(row, anchor));
observer?.observe(body, { childList: true });
if (pinned) {
window.scrollBy(0, pinned.getBoundingClientRect().top - pinnedTop);
}
}
function sortDocCategoryTopicLists() {
docSortObservers.forEach((observer) => observer.disconnect());
docSortObservers = new Map();
const locale = docSortLocale();
// Kein Flex/Grid-Container, daher müssen DOM-Knoten verschoben werden — CSS `order` wird ignoriert.
document.querySelectorAll(".topic-list.doc-simple-mode .topic-list-body").forEach((body) => {
// try/finally, damit die Liste immer angezeigt wird, selbst wenn beim Sortieren ein Fehler auftritt.
try {
sortDocTopicList(body, locale);
} finally {
body.classList.add("docs-sorted");
}
// Nur sortieren, wenn das Laden abgeschlossen ist — Reordering während des Ladens hat den Lader blockiert.
let settleTimer = null;
const observer = new MutationObserver(() => {
window.clearTimeout(settleTimer);
settleTimer = window.setTimeout(() => {
sortDocTopicList(body, locale);
}, 400);
});
observer.observe(body, { childList: true });
docSortObservers.set(body, observer);
});
}
Ich denke, der Code lässt sich sehr wahrscheinlich verbessern. Ich nehme Vorschläge und/oder PRs gerne an.
Hmm – leider erhalte ich beim Folgen des Links in deinem Beitrag einen 404-Fehler: https://github.com/somos-criptonautas/discourse-horizon-mod-nautas/blob/main/javascripts/api-initializers/init.gjs
Oh, sorry for that. I updated the url, it is internal and I am still working on it so please take it as experimental.
I remain attentive to any feedback, suggestion, or error you find if you look the code or try it! I know that is not the most elegant solution but it works for our use-case.
