Alla fine ho utilizzato un componente Theme personalizzato per questo scopo:
// A-Z richiede l'intera categoria: il core non può ordinare un elenco di topic per titolo
// (TopicQuery::SORTABLE_MAPPING non contiene "title"), quindi esauriamo l'elenco paginato
// prima di ordinare. È limitato — le categorie dei documenti sono piccole; per qualsiasi cosa
// più grande si ricorre all'ordinamento di ciò che è stato caricato, mantenuto aggiornato dal
// MutationObserver sottostante.
// ponytail: elimina questo intero ciclo se il core implementerà mai l'ordinamento del titolo lato server.
const MAX_DOC_PAGES = 12; // circa 360 topic a 30 per pagina
let docLoadRun = 0;
async function loadAllDocTopics(api) {
const run = ++docLoadRun;
// `controller:discovery/topics` è deprecato nell'attuale Discourse
// (id deprecazione: discourse.discovery-topics-controller). Discovery ora utilizza un
// controller dedicato per ogni filtro; le categorie dei documenti sono sempre l'elenco
// dei topic più recenti, quindi cerchiamo direttamente quel controller. Un errore qui
// significa semplicemente che ordiniamo la prima pagina, quindi ogni passaggio è
// opzionale-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();
// Navigato altrove durante il caricamento, o l'elenco ha smesso di crescere — in entrambi i casi, fermati.
if (run !== docLoadRun || (list.topics?.length ?? 0) === before) {
return;
}
}
}
// Ordina l'elenco dei topic di Docs da A a Z, usando la collazione della lingua della pagina <html lang>.
let docSortObservers = new Map();
function docSortLocale() {
const lang = document.documentElement.lang;
if (!lang) {
return undefined;
}
try {
Intl.getCanonicalLocales(lang); // genera RangeError per un tag malformato
return lang;
} catch {
return undefined; // ricade sulla locale predefinita del browser
}
}
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; // già ordinato — impedisce all'observer di auto-alimentarsi
}
// Inserisci prima di ciò che segue le righe, mantenendo l'eventuale sentinella di caricamento più in fondo.
const anchor = rows[rows.length - 1].nextSibling;
// Fissa la prima riga visibile in modo che gli inserimenti sopra di essa non facciano saltare lo scroll.
const pinned = rows.find((row) => row.getBoundingClientRect().bottom > 0);
const pinnedTop = pinned?.getBoundingClientRect().top;
// Observer in pausa in modo che i nostri movimenti non riescano in questa funzione.
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();
// Non è un contenitore flex/grid, quindi il riordino deve spostare i nodi DOM — CSS `order` è ignorato.
document.querySelectorAll(".topic-list.doc-simple-mode .topic-list-body").forEach((body) => {
// try/finally in modo che l'elenco venga sempre rivelato anche se l'ordinamento genera un errore.
try {
sortDocTopicList(body, locale);
} finally {
body.classList.add("docs-sorted");
}
// Ordina solo quando il caricamento si è stabilizzato — il riordino durante il caricamento bloccava il loader.
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);
});
}
Penso che il codice possa probabilmente essere migliorato, accetto con piacere suggerimenti e/o PR.