feat(widgets): wire PostsNavs TOC shell to page-template scrollspy

Match built-in single sidebar .posts-nav-box.toc-widget/.toc-list so the
sidebar widget is filled (≥3 headings), supports multiple TOC shells, and
removes empty chrome.
This commit is contained in:
kate522 committed 2026-07-21 08:03:25 +08:00
1 parent 5ab935f015
commit c0b7cb504b
3 files changed
+108 -25

No files matched your search

@@ -364,6 +364,10 @@ html.dark .article-end-separator span {
color: var(--color-primary);
}
.toc-list .toc-h1 a {
font-weight: 700;
}
.toc-list .toc-h3 a {
padding-left: 12px;
}
@@ -372,6 +376,30 @@ html.dark .article-end-separator span {
padding-left: 24px;
}
/* WidgetPostsNavs + built-in shell share .posts-nav-box.toc-widget */
.widget .posts-nav-box.toc-widget,
.widget_aksrt_posts_navs .posts-nav-box.toc-widget {
margin: 0;
box-shadow: none;
background: transparent;
padding: 0;
border-radius: 0;
}
.widget .posts-nav-box.toc-widget > h3,
.widget .posts-nav-box.toc-widget .toc-widget__title,
.widget_aksrt_posts_navs .posts-nav-box.toc-widget > h3 {
margin: 0 0 10px;
font-size: 15px;
font-weight: 700;
}
body.single .sidebar .posts-nav-box.toc-widget.is-affix,
body.single .sidebar .posts-nav-box.toc-widget[data-affix="true"] {
position: sticky;
top: calc(var(--nav-height, 60px) + 16px);
}
.toc-float-btn {
position: fixed;
right: 18px;
+59 -17
View File
@@ -19,9 +19,31 @@
}
function initPostToc() {
var content = document.querySelector('.wp-posts-content[data-nav="posts"][data-aksrt-toc="1"]');
var tocWidget = document.querySelector('.posts-nav-box.toc-widget');
var tocList = tocWidget ? tocWidget.querySelector('.toc-list') : null;
// Accept either explicit data-aksrt-toc="1" or any [data-nav] article body.
var content =
document.querySelector('.wp-posts-content[data-nav="posts"][data-aksrt-toc="1"]') ||
document.querySelector('.wp-posts-content[data-nav]') ||
document.querySelector('[data-nav="posts"]') ||
document.querySelector('[data-nav]');
// Support multiple TOC shells (built-in single sidebar + WidgetPostsNavs).
var tocWidgets = Array.prototype.slice.call(
document.querySelectorAll('.posts-nav-box.toc-widget, .posts-nav-box[data-title]')
);
var tocLists = [];
tocWidgets.forEach(function (box) {
if (!box.classList.contains('toc-widget')) {
box.classList.add('toc-widget');
}
var list = box.querySelector('.toc-list');
if (!list) {
list = document.createElement('ol');
list.className = 'toc-list';
box.appendChild(list);
}
tocLists.push(list);
});
var mobileToggle = document.querySelector('.toc-float-btn');
var drawer = document.querySelector('.toc-drawer');
var overlay = document.querySelector('.toc-drawer-overlay');
@@ -29,16 +51,25 @@
var closeBtn = drawer ? drawer.querySelector('.toc-drawer__close') : null;
if (!content || content.dataset.aksrtTocBound === '1') return;
if (!tocList && !drawerList && !mobileToggle) return;
if (!tocLists.length && !drawerList && !mobileToggle) return;
content.dataset.aksrtTocBound = '1';
var maxDepth = parseInt(content.dataset.aksrtTocDepth || '3', 10) || 3;
// zibll section-navs: only fill when heading count > 2 (≥3).
var MIN_HEADINGS = 3;
function getHeadings() {
return Array.prototype.slice.call(content.querySelectorAll('h2, h3, h4')).filter(function (heading) {
// zibll scans h1–h4 inside [data-nav]; depth setting still caps lower levels.
return Array.prototype.slice.call(content.querySelectorAll('h1, h2, h3, h4')).filter(function (heading) {
if (heading.closest('.no-nav') || heading.classList.contains('item-heading')) {
return false;
}
var level = parseInt((heading.tagName || 'H2').replace('H', ''), 10) || 2;
return level <= maxDepth;
// toc_depth is typically 2–4 meaning deepest heading level from h2 baseline;
// allow h1 always, then h2..h(1+maxDepth) capped at h4.
if (level === 1) return true;
return level <= Math.min(4, maxDepth + 1);
});
}
@@ -46,7 +77,7 @@
if (!container) return false;
var headings = getHeadings();
if (!headings.length) return false;
if (headings.length < MIN_HEADINGS) return false;
var html = '';
headings.forEach(function (heading) {
@@ -55,7 +86,9 @@
}
var levelClass = '';
if (heading.tagName === 'H3') {
if (heading.tagName === 'H1') {
levelClass = 'toc-h1';
} else if (heading.tagName === 'H3') {
levelClass = 'toc-h3';
} else if (heading.tagName === 'H4') {
levelClass = 'toc-h4';
@@ -68,13 +101,24 @@
return true;
}
var hasToc = buildToc(tocList);
if (drawerList && drawerList !== tocList) {
buildToc(drawerList);
var hasToc = false;
tocLists.forEach(function (list) {
if (buildToc(list)) hasToc = true;
});
if (drawerList && tocLists.indexOf(drawerList) === -1) {
if (buildToc(drawerList)) hasToc = true;
}
if (!hasToc) {
if (tocWidget) tocWidget.remove();
tocWidgets.forEach(function (box) {
var host = box.closest('.widget') || box.parentElement;
// Prefer removing the whole widget chrome when empty.
if (host && host !== document.body && host.querySelectorAll('.posts-nav-box').length <= 1) {
host.remove();
} else {
box.remove();
}
});
if (mobileToggle) mobileToggle.remove();
if (drawer) drawer.remove();
if (overlay) overlay.remove();
@@ -94,7 +138,7 @@
}
function trackActiveHeading() {
var headings = content.querySelectorAll('h2[id], h3[id], h4[id]');
var headings = content.querySelectorAll('h1[id], h2[id], h3[id], h4[id]');
if (!headings.length) return;
var activeId = '';
@@ -117,10 +161,8 @@
if (!target) return;
event.preventDefault();
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
var top = target.getBoundingClientRect().top + window.pageYOffset - 105;
window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
closeDrawer();
});
});