gilles
27.Август.2026 09:05:32
1
Я замечаю, что по умолчанию невозможно отсортировать категорию по алфавиту по названиям тем. Мне, конечно, скажут, что достаточно использовать фильтры для настройки, но это порождает две серьёзные проблемы:
Слишком много действий, что не под силу каждому.
Теряется привычная визуальная подача плиток.
Возможно ли добавить параметр для каждой категории, позволяющий задать алфавитную сортировку тем по умолчанию? Не знаю, предусмотрено ли это в дорожной карте.
Повторяю, это просто обратная связь из практики, но мне кажется, что обидно упускать этот вариант, который является стандартным в Discourse.
2 лайка
chapoi
27.Август.2026 10:32:12
2
Зачем вам нужен алфавитный порядок? Какой сценарий использования вы имеете в виду?
gilles
27.Август.2026 10:35:54
3
На самом деле, в моих категориях хранятся карточки настольных игр, и я хотел, чтобы при входе в эти конкретные категории темы сортировались в алфавитном порядке. Я попытался написать небольшой код, но у меня не получилось.
2 лайка
RGJ
(Richard - Communiteq)
27.Август.2026 10:51:40
4
Это полезно для курсов и подобных материалов, где темы озаглавлены как «Урок 1», «Урок 2» и так далее.
Кроме того, одна из моих главных претензий к плагину doc-categories заключается в том, что он по-прежнему отображает список тем, особенно на мобильных устройствах. Возможность сортировать их по алфавиту хотя бы частично смягчила бы эту проблему.
4 лайка
Мы принудительно применяем локальную сортировку от А до Я, чтобы сохранить чистоту глоссария. В некоторых случаях это действительно необходимо.
Moin
27.Август.2026 11:35:17
6
gilles:
Вы теряете обычную сетку плиток.
Я не уверен, что понимаю этот момент. Какие именно плитки теряются, если посмотреть на https://meta.discourse.org/filter?q=order%3Atitle-asc ?
1 лайк
gilles
27.Август.2026 12:10:31
7
Может быть, я не совсем понятно выразился в письме Я думал, что мы сохраним тот же визуальный стиль плиток
категория без фильтра
категория с фильтром
1 лайк
Могу подтвердить, что этот фильтр бесполезен для тех из нас, кто хочет оставить вид по умолчанию без изменений, особенно если он применяется к категориям, выполняющим роль справочных разделов (глоссарий, CMS).
Это хорошая отправная точка, но она не покрывает простую потребность — возможно, не с технической точки зрения, но точно с точки зрения наших пользователей — отображать темы в алфавитном порядке.
Я считаю, что фильтр всесторонне покрывает другие потребности.
Просто делюсь решением, которое я использовал несколько недель:
satonotdead:
В итоге я использовал для этого кастомный компонент темы :
// Для сортировки А-Я нужна вся категория: ядро не может отсортировать список тем по заголовку
// (в 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). Теперь для открытия используется
// отдельный контроллер для каждого фильтра; категории документации всегда представляют собой список
// последних тем, поэтому мы напрямую ищем этот контроллер. Если здесь будет ошибка, мы просто отсортируем
// первую страницу, поэтому каждый шаг использует опциональное связывание.
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;
}
}
}
// Сортирует список тем документации по алфавиту (А-Я), используя локаль страницы из <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.
1 лайк
gilles
14.Сентябрь.2026 06:25:00
10
Прекрасная инициатива, спасибо @satonotdead
1 лайк