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:
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);
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toc-list .toc-h1 a {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
.toc-list .toc-h3 a {
|
.toc-list .toc-h3 a {
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
}
|
}
|
||||||
@@ -372,6 +376,30 @@ html.dark .article-end-separator span {
|
|||||||
padding-left: 24px;
|
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 {
|
.toc-float-btn {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 18px;
|
right: 18px;
|
||||||
|
|||||||
+59
-17
@@ -19,9 +19,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initPostToc() {
|
function initPostToc() {
|
||||||
var content = document.querySelector('.wp-posts-content[data-nav="posts"][data-aksrt-toc="1"]');
|
// Accept either explicit data-aksrt-toc="1" or any [data-nav] article body.
|
||||||
var tocWidget = document.querySelector('.posts-nav-box.toc-widget');
|
var content =
|
||||||
var tocList = tocWidget ? tocWidget.querySelector('.toc-list') : null;
|
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 mobileToggle = document.querySelector('.toc-float-btn');
|
||||||
var drawer = document.querySelector('.toc-drawer');
|
var drawer = document.querySelector('.toc-drawer');
|
||||||
var overlay = document.querySelector('.toc-drawer-overlay');
|
var overlay = document.querySelector('.toc-drawer-overlay');
|
||||||
@@ -29,16 +51,25 @@
|
|||||||
var closeBtn = drawer ? drawer.querySelector('.toc-drawer__close') : null;
|
var closeBtn = drawer ? drawer.querySelector('.toc-drawer__close') : null;
|
||||||
|
|
||||||
if (!content || content.dataset.aksrtTocBound === '1') return;
|
if (!content || content.dataset.aksrtTocBound === '1') return;
|
||||||
if (!tocList && !drawerList && !mobileToggle) return;
|
if (!tocLists.length && !drawerList && !mobileToggle) return;
|
||||||
|
|
||||||
content.dataset.aksrtTocBound = '1';
|
content.dataset.aksrtTocBound = '1';
|
||||||
|
|
||||||
var maxDepth = parseInt(content.dataset.aksrtTocDepth || '3', 10) || 3;
|
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() {
|
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;
|
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;
|
if (!container) return false;
|
||||||
|
|
||||||
var headings = getHeadings();
|
var headings = getHeadings();
|
||||||
if (!headings.length) return false;
|
if (headings.length < MIN_HEADINGS) return false;
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
headings.forEach(function (heading) {
|
headings.forEach(function (heading) {
|
||||||
@@ -55,7 +86,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var levelClass = '';
|
var levelClass = '';
|
||||||
if (heading.tagName === 'H3') {
|
if (heading.tagName === 'H1') {
|
||||||
|
levelClass = 'toc-h1';
|
||||||
|
} else if (heading.tagName === 'H3') {
|
||||||
levelClass = 'toc-h3';
|
levelClass = 'toc-h3';
|
||||||
} else if (heading.tagName === 'H4') {
|
} else if (heading.tagName === 'H4') {
|
||||||
levelClass = 'toc-h4';
|
levelClass = 'toc-h4';
|
||||||
@@ -68,13 +101,24 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasToc = buildToc(tocList);
|
var hasToc = false;
|
||||||
if (drawerList && drawerList !== tocList) {
|
tocLists.forEach(function (list) {
|
||||||
buildToc(drawerList);
|
if (buildToc(list)) hasToc = true;
|
||||||
|
});
|
||||||
|
if (drawerList && tocLists.indexOf(drawerList) === -1) {
|
||||||
|
if (buildToc(drawerList)) hasToc = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasToc) {
|
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 (mobileToggle) mobileToggle.remove();
|
||||||
if (drawer) drawer.remove();
|
if (drawer) drawer.remove();
|
||||||
if (overlay) overlay.remove();
|
if (overlay) overlay.remove();
|
||||||
@@ -94,7 +138,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function trackActiveHeading() {
|
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;
|
if (!headings.length) return;
|
||||||
|
|
||||||
var activeId = '';
|
var activeId = '';
|
||||||
@@ -117,10 +161,8 @@
|
|||||||
if (!target) return;
|
if (!target) return;
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
target.scrollIntoView({
|
var top = target.getBoundingClientRect().top + window.pageYOffset - 105;
|
||||||
behavior: 'smooth',
|
window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
|
||||||
block: 'start'
|
|
||||||
});
|
|
||||||
closeDrawer();
|
closeDrawer();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
/**
|
/**
|
||||||
* In-article / topic TOC shell (zibll widget_ui_posts_navs parity).
|
* In-article / topic TOC shell (zibll widget_ui_posts_navs parity).
|
||||||
*
|
*
|
||||||
* Frontend JS fills `.posts-nav-box` from heading outline when present.
|
* Shell matches the built-in single sidebar TOC so page-template.js can fill
|
||||||
|
* `.posts-nav-box.toc-widget > .toc-list` from `[data-nav]` headings.
|
||||||
*
|
*
|
||||||
* @package AKSRT
|
* @package AKSRT
|
||||||
*/
|
*/
|
||||||
@@ -18,8 +19,8 @@ class WidgetPostsNavs extends \WP_Widget {
|
|||||||
'aksrt_posts_navs',
|
'aksrt_posts_navs',
|
||||||
esc_html__('AKSRT 文章目录树', 'aksrt'),
|
esc_html__('AKSRT 文章目录树', 'aksrt'),
|
||||||
[
|
[
|
||||||
'description' => esc_html__('显示当前文章/帖子标题目录,非详情页不输出。标题超过 3 个才显示。', 'aksrt'),
|
'description' => esc_html__('显示当前文章/帖子标题目录,非详情页不输出。标题超过 2 个才填充。', 'aksrt'),
|
||||||
'classname' => 'widget_aksrt_posts_navs',
|
'classname' => 'widget_aksrt_posts_navs',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -35,16 +36,28 @@ class WidgetPostsNavs extends \WP_Widget {
|
|||||||
|
|
||||||
$data_title = $title;
|
$data_title = $title;
|
||||||
if ($mini_title !== '') {
|
if ($mini_title !== '') {
|
||||||
$data_title .= ' · ' . $mini_title;
|
$data_title .= ($data_title !== '' ? ' · ' : '') . $mini_title;
|
||||||
|
}
|
||||||
|
if ($data_title === '') {
|
||||||
|
$data_title = __('文章目录', 'aksrt');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirror aksrt-single-render built-in TOC shell so page-template.js binds.
|
||||||
|
$classes = 'zib-widget toc-widget posts-nav-box';
|
||||||
|
if ($in_affix) {
|
||||||
|
$classes .= ' is-affix';
|
||||||
}
|
}
|
||||||
|
|
||||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||||
echo '<div class="widget-container">';
|
|
||||||
printf(
|
printf(
|
||||||
'<div class="posts-nav-box"%s%s></div>',
|
'<div class="%s"%s data-title="%s" aria-label="%s">',
|
||||||
|
esc_attr($classes),
|
||||||
$in_affix ? ' data-affix="true"' : '',
|
$in_affix ? ' data-affix="true"' : '',
|
||||||
$data_title !== '' ? ' data-title="' . esc_attr($data_title) . '"' : ''
|
esc_attr($data_title),
|
||||||
|
esc_attr($data_title)
|
||||||
);
|
);
|
||||||
|
echo '<h3 class="widget-title toc-widget__title">' . esc_html($data_title) . '</h3>';
|
||||||
|
echo '<ol class="toc-list"></ol>';
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||||
}
|
}
|
||||||
@@ -68,7 +81,7 @@ class WidgetPostsNavs extends \WP_Widget {
|
|||||||
<?php esc_html_e('侧栏随动(仅侧边栏)', 'aksrt'); ?>
|
<?php esc_html_e('侧栏随动(仅侧边栏)', 'aksrt'); ?>
|
||||||
</label>
|
</label>
|
||||||
</p>
|
</p>
|
||||||
<p class="description"><?php esc_html_e('请添加到文章/帖子侧栏。正文 H2–H4 超过 3 个时前端才会填充目录。', 'aksrt'); ?></p>
|
<p class="description"><?php esc_html_e('请添加到文章/帖子侧栏。正文标题 ≥3 个时由前端填充目录;无标题则自动移除。', 'aksrt'); ?></p>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user