iter(g14): 演示站目录支持关键字过滤

This commit is contained in:
HIsheR
2026-10-26 10:29:03 +08:00
parent 4ffc6545ef
commit b3c3bc70a4
2 changed files with 26 additions and 2 deletions
+4
View File
@@ -49,6 +49,10 @@ body{margin:0;background:var(--page-bg);color:var(--page-text);font:14px/1.7 -ap
.page{max-width:900px;margin:0;padding:0}
.toc{position:sticky;top:24px;align-self:start;max-height:calc(100vh - 48px);overflow:auto;font-size:13px}
.toc-title{color:var(--page-muted);font-size:11px;letter-spacing:.08em;margin-bottom:8px}
.toc-search{width:100%;box-sizing:border-box;margin-bottom:8px;padding:4px 8px;border:1px solid var(--ua-color-border);border-radius:6px;background:var(--ua-color-bg);color:var(--page-text);font-size:12px}
.toc-search:focus{outline:none;border-color:var(--ua-color-accent);box-shadow:0 0 0 2px color-mix(in srgb,var(--ua-color-accent) 18%,transparent)}
.toc-link[hidden]{display:none}
.toc-empty{color:var(--page-muted);font-size:12px;padding:4px 8px;margin:0}
.toc-link{display:block;padding:3px 8px;border-left:2px solid transparent;border-radius:0 4px 4px 0;color:var(--page-muted);text-decoration:none;transition:color .15s,background .15s,border-color .15s}
.toc-link:hover{color:var(--page-text);background:rgba(127,127,127,.08)}
.toc-link.is-active{color:var(--ua-color-accent);border-left-color:var(--ua-color-accent);background:color-mix(in srgb,var(--ua-color-accent) 10%,transparent)}
+22 -2
View File
@@ -51,14 +51,34 @@ import {
} from '@ui-arch/ui';
import type { RowKey } from '@ui-arch/ui';
/*** 侧边目录:挂载后从 .sec 反推锚点,IntersectionObserver 高亮视口内区块(清单改动自动跟随) ***/
/*** 侧边目录:挂载后从 .sec 反推锚点,IntersectionObserver 高亮视口内区块(清单改动自动跟随);顶部搜索框按标题过滤 ***/
function buildToc(toc: HTMLElement, content: HTMLElement): void {
const secs = Array.from(content.querySelectorAll<HTMLElement>('section.sec'));
if (!secs.length) return; // 卫语句:无区块不建目录
const links = secs.map((sec) =>
h('a', { class: 'toc-link', href: `#${sec.id}`, 'data-for': sec.id }, sec.querySelector('h2')?.textContent ?? sec.id),
);
toc.append(...links);
const search = h('input', { class: 'toc-search', type: 'search', placeholder: '过滤组件…', 'aria-label': '过滤组件目录' }) as HTMLInputElement;
const empty = h('p', { class: 'toc-empty' }, '无匹配项'); // 过滤后一条不剩时的提示
empty.hidden = true;
/*** 按标题子串过滤目录项;空关键字恢复全量 ***/
const apply = (): void => {
const q = search.value.trim().toLowerCase();
let hit = 0;
for (const link of links) {
const on = !q || (link.textContent ?? '').toLowerCase().includes(q);
link.hidden = !on; // 隐藏项同时退出 Tab 序(hidden 自带)
if (on) hit++;
}
empty.hidden = hit > 0;
};
search.addEventListener('input', apply);
search.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key !== 'Escape' || !search.value) return; // 卫语句:无内容时 Esc 交给默认行为
search.value = '';
apply();
});
toc.append(search, ...links, empty);
const io = new IntersectionObserver(
(entries) => {
for (const en of entries) {