Combinar filtros (ordem A-Z) com visão de documentos?

Gostaria de saber se podemos usar os filtros para exibir uma categoria usando a visualização de documentos e A-Z (em ordem alfabética).

Temos um glossário que deve exibir a visualização do tópico em ordem :slight_smile:

Atualmente, a visualização do filtro está separada dos documentos:

Acabei usando um Componente de Tema personalizado para isso:

// A-Z precisa da categoria inteira: o core não consegue ordenar uma lista de tópicos por título
// (TopicQuery::SORTABLE_MAPPING não tem "title"), então esgotamos a lista paginada
// antes de ordenar. Limitado — as categorias de docs são pequenas; qualquer coisa maior
// recorre para ordenar o que estiver carregado, o que o MutationObserver abaixo mantém atualizado.
// ponytail: remova este loop inteiro se o core um dia implementar ordenação por título no servidor.
const MAX_DOC_PAGES = 12; // ~360 tópicos com 30 por página
let docLoadRun = 0;

async function loadAllDocTopics(api) {
  const run = ++docLoadRun;

  // `controller:discovery/topics` está descontinuado no Discourse atual
  // (id de depreciação: discourse.discovery-topics-controller). O Discovery agora usa um
  // controlador dedicado por filtro; as categorias de docs são sempre a lista de tópicos mais recentes,
  // então buscamos esse controlador diretamente. Falhar aqui significa apenas que ordenamos
  // a primeira página, então cada passo usa optional-chaining.
  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();

    // Navegou para outra página durante o carregamento, ou a lista parou de crescer — em ambos os casos, para.
    if (run !== docLoadRun || (list.topics?.length ?? 0) === before) {
      return;
    }
  }
}

// Ordena a lista de tópicos de Docs de A a Z, colacionando com o próprio <html lang> da página.
let docSortObservers = new Map();

function docSortLocale() {
  const lang = document.documentElement.lang;
  if (!lang) {
    return undefined;
  }

  try {
    Intl.getCanonicalLocales(lang); // lança RangeError para tags malformadas
    return lang;
  } catch {
    return undefined; // recorre para o locale padrão do navegador
  }
}

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; // já está ordenado — impede que o observer se alimente a si mesmo
  }

  // Insere antes de qualquer elemento que siga as linhas, mantendo o sentinel de carregar mais por último.
  const anchor = rows[rows.length - 1].nextSibling;

  // Fixa a primeira linha visível para que inserções acima dela não pulem o scroll.
  const pinned = rows.find((row) => row.getBoundingClientRect().bottom > 0);
  const pinnedTop = pinned?.getBoundingClientRect().top;

  // Observer pausado para que nossos próprios movimentos não reentrem nesta função.
  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();

  // Não é um contêiner flex/grid, então reordenar deve mover nós do DOM — CSS `order` é ignorado.
  document.querySelectorAll(".topic-list.doc-simple-mode .topic-list-body").forEach((body) => {
    // try/finally para que a lista seja sempre revelada, mesmo se a ordenação lançar erro.
    try {
      sortDocTopicList(body, locale);
    } finally {
      body.classList.add("docs-sorted");
    }

    // Ordena apenas uma vez que o carregamento fique quieto — reordenar durante o carregamento travava o 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);
  });
}

Acho que o código provavelmente pode ser melhorado; aceito sugestões e/ou PRs com prazer.