Интересно, можно ли использовать фильтры для отображения категории в виде документации и в алфавитном порядке.
У нас есть глоссарий, который должен отображать тематический вид в порядке ![]()
В настоящее время вид фильтров отделен от документации:
В итоге я использовал для этого кастомный компонент темы:
// Для сортировки A-Z нужна вся категория: ядро не может отсортировать список тем по названию
// (в TopicQuery::SORTABLE_MAPPING нет "title"), поэтому мы выгружаем весь пагинированный список
// перед сортировкой. Ограничено — категории документации небольшие; для больших списков
// выполняется сортировка только загруженных элементов, а MutationObserver ниже поддерживает их актуальность.
// ponytail: уберите этот цикл целиком, если ядро когда-нибудь добавит серверную сортировку по названию.
const MAX_DOC_PAGES = 12; // ~360 тем при 30 на страницу
let docLoadRun = 0;
async function loadAllDocTopics(api) {
const run = ++docLoadRun;
// `controller:discovery/topics` устарел в текущем Discourse
// (идентификатор устаревания: discourse.discovery-topics-controller). Теперь Discovery использует
// отдельный контроллер для каждого фильтра; категории документации всегда представляют собой список
// последних тем, поэтому мы напрямую ищем этот контроллер. Сбой здесь просто означает сортировку
// первой страницы, поэтому каждый шаг использует 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();
// Переход на другую страницу во время загрузки или прекращение роста списка — в любом случае останавливаемся.
if (run !== docLoadRun || (list.topics?.length ?? 0) === before) {
return;
}
}
}
// Сортирует список тем документации A-Z, используя локаль страницы из <html lang>.
let docSortObservers = new Map();
function docSortLocale() {
const lang = document.documentElement.lang;
if (!lang) {
return undefined;
}
try {
Intl.getCanonicalLocales(lang); // выбрасывает RangeError для некорректного тега
return lang;
} catch {
return undefined; // откат к локали по умолчанию браузера
}
}
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; // уже отсортировано — предотвращает повторный вызов наблюдателем
}
// Вставляем перед тем, что следует за строками, сохраняя маркер «загрузить ещё» в конце.
const anchor = rows[rows.length - 1].nextSibling;
// Фиксируем первую видимую строку, чтобы вставки выше неё не сбрасывали прокрутку.
const pinned = rows.find((row) => row.getBoundingClientRect().bottom > 0);
const pinnedTop = pinned?.getBoundingClientRect().top;
// Наблюдатель приостановлен, чтобы наши собственные перемещения не вызывали повторный вход в функцию.
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();
// Это не flex/grid-контейнер, поэтому для изменения порядка нужно перемещать DOM-узлы — CSS `order` игнорируется.
document.querySelectorAll(".topic-list.doc-simple-mode .topic-list-body").forEach((body) => {
// try/finally, чтобы список всегда отображался, даже если сортировка выбросит ошибку.
try {
sortDocTopicList(body, locale);
} finally {
body.classList.add("docs-sorted");
}
// Сортируем только после завершения загрузки — перестановка во время загрузки останавливала загрузчик.
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);
});
}
Я думаю, что код, скорее всего, можно улучшить, и я с удовольствием приму предложения и/или PR.
Хм — к сожалению, при переходе по ссылке в вашем сообщении: https://github.com/somos-criptonautas/discourse-horizon-mod-nautas/blob/main/javascripts/api-initializers/init.gjs, я получаю ошибку 404.
Ой, извините за это. Я обновил URL, он внутренний, и я всё ещё работаю над ним, поэтому относитесь к нему как к экспериментальному.
Я внимательно отнесусь к любой обратной связи, предложению или ошибке, которые вы обнаружите, если посмотрите на код или попробуете его! Я знаю, что это не самое элегантное решение, но оно работает для нашего случая.