Mixare i filtri (da A a Z) con la visualizzazione dei documenti?

Mi chiedo se possiamo usare i filtri per visualizzare una categoria utilizzando la visualizzazione docs e A-Z (in ordine alfabetico).

Abbiamo un glossario che dovrebbe visualizzare la vista per argomento ordinata :slight_smile:

Attualmente la vista del filtro è separata dai documenti:

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.

Hmm - purtroppo ricevo un errore 404 quando seguo il link nel tuo post: https://github.com/somos-criptonautas/discourse-horizon-mod-nautas/blob/main/javascripts/api-initializers/init.gjs

Oh, scusa per questo. Ho aggiornato l’URL, è interno e sto ancora lavorando su di esso, quindi consideralo sperimentale.

Resto attento a qualsiasi feedback, suggerimento o errore che potresti trovare se dai un’occhiata al codice o lo provi! So che non è la soluzione più elegante, ma funziona per il nostro caso d’uso.