أتساءل عما إذا كان بإمكاننا استخدام عوامل التصفية لعرض فئة باستخدام عرض المستندات و A-Z (أبجديًا).
لدينا مسرد يجب أن يعرض عرض الموضوع مرتبًا ![]()
حاليًا، عرض التصفية منفصل عن المستندات:
في النهاية، استخدمت مكوّن Theme مخصص لهذا الغرض:
// يتطلب 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
// متحكمًا مخصصًا لكل مرشّح؛ فئات المستندات هي دائمًا قائمة أحدث المواضيع، لذا
// نبحث عن ذلك المتحكم مباشرة. الفشل هنا يعني ببساطة أننا سنفرز الصفحة الأولى،
// لذلك كل خطوة اختيارية السلسلة.
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 — يتم تجاهل `order` في CSS.
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);
});
}
أعتقد أن الكود يمكن على الأرجح تحسينه، وأنا أقبل باحترام كبير الاقتراحات و/أو طلبات الدمج (PRs).