iter: d08 演示页侧边目录 + 滚动高亮
This commit is contained in:
+10
-1
@@ -45,7 +45,16 @@ ${css}
|
||||
@media (prefers-color-scheme:dark){:root{--page-bg:#101216;--page-text:#e7e9ec;--page-muted:#888780;--page-border:rgba(255,255,255,.15)}}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;background:var(--page-bg);color:var(--page-text);font:14px/1.7 -apple-system,"Segoe UI","Microsoft YaHei",sans-serif}
|
||||
.page{max-width:880px;margin:0 auto;padding:36px 20px 80px}
|
||||
.shell{max-width:1160px;margin:0 auto;padding:36px 20px 80px;display:grid;grid-template-columns:190px minmax(0,1fr);gap:36px}
|
||||
.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-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)}
|
||||
@media (max-width:820px){.shell{grid-template-columns:1fr;gap:0}.toc{display:none}}
|
||||
html{scroll-behavior:smooth}
|
||||
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
|
||||
header{margin-bottom:30px}
|
||||
h1{font-size:20px;font-weight:500;margin:0}
|
||||
.sub{color:var(--page-muted);font-size:13px;margin:8px 0 0}
|
||||
|
||||
+26
-1
@@ -27,6 +27,27 @@ import {
|
||||
tooltip,
|
||||
} from '@ui-arch/ui';
|
||||
|
||||
/*** 侧边目录:挂载后从 .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 io = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const en of entries) {
|
||||
if (!en.isIntersecting) continue; // 只处理刚进入视口的区块
|
||||
const id = (en.target as HTMLElement).id;
|
||||
for (const link of links) link.classList.toggle('is-active', link.getAttribute('data-for') === id);
|
||||
}
|
||||
},
|
||||
{ rootMargin: '-10% 0px -80% 0px' }, // 顶部窄条判定,滚动时高亮更跟手
|
||||
);
|
||||
for (const sec of secs) io.observe(sec);
|
||||
}
|
||||
|
||||
/*** 生成演示区块;id 供文档站 iframe 锚点定位 ***/
|
||||
function section(id: string, title: string, desc: string, ...body: unknown[]): HTMLElement {
|
||||
return h(
|
||||
@@ -84,7 +105,7 @@ const menuPick = signal('');
|
||||
// —— 禁用态演示:开关信号同时绑定 switch 的 checked 与 checkbox 的 disabled ——
|
||||
const locked = signal(true);
|
||||
|
||||
const app = h(
|
||||
const content = h(
|
||||
'div',
|
||||
{ class: 'page' },
|
||||
h(
|
||||
@@ -322,4 +343,8 @@ const app = h(
|
||||
),
|
||||
);
|
||||
|
||||
const toc = h('aside', { class: 'toc' }, h('div', { class: 'toc-title' }, '目录')); // 目录项由 buildToc 填充
|
||||
const app = h('div', { class: 'shell' }, toc, content);
|
||||
|
||||
mount('#app', () => app);
|
||||
buildToc(toc, content);
|
||||
|
||||
Reference in New Issue
Block a user