我想知道我们是否可以使用过滤器来使用文档视图和 A-Z(按字母顺序)显示一个类别。
我们有一个应该按主题视图排序的词汇表 ![]()
目前,过滤器视图与文档是分开的:
我最终为它使用了一个自定义的 Theme Component:
// A-Z 排序需要完整的分类:核心功能无法按标题对主题列表进行排序
// (TopicQuery::SORTABLE_MAPPING 中没有 "title"),因此我们必须在排序前
// 耗尽分页列表。设有上限——文档分类较小;如果更大,则回退到
// 对已加载的内容进行排序,下方的 MutationObserver 会保持其最新状态。
// ponytail: 如果核心功能将来支持服务器端标题排序,请删除整个循环。
const MAX_DOC_PAGES = 12; // 每页 30 个主题,约 360 个主题
let docLoadRun = 0;
async function loadAllDocTopics(api) {
const run = ++docLoadRun;
// `controller:discovery/topics` 在当前 Discourse 版本中已弃用
// (弃用 ID: discourse.discovery-topics-controller)。Discovery 现在为每个过滤器
// 使用专用的控制器;文档分类始终是最新主题列表,因此我们直接查找该控制器。
// 如果此处失败,仅意味着我们对第一页进行排序,因此每一步都使用了可选链。
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。
嗯——很遗憾,我点击你帖子中的链接时遇到了 404 错误:https://github.com/somos-criptonautas/discourse-horizon-mod-nautas/blob/main/javascripts/api-initializers/init.gjs
哦,抱歉。我已经更新了 URL,它目前还是内部版本,我仍在继续开发,所以请将其视为实验性功能。
如果你查看代码或尝试运行,欢迎提供任何反馈、建议或发现的错误!我知道这可能不是最优雅的解决方案,但对于我们的使用场景来说它是有效的。