From b3c3bc70a4ba5b436ebc42b7be565dd9c9f8d6d8 Mon Sep 17 00:00:00 2001 From: HIsheR Date: Mon, 26 Oct 2026 10:29:03 +0800 Subject: [PATCH] =?UTF-8?q?iter(g14):=20=E6=BC=94=E7=A4=BA=E7=AB=99?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E6=94=AF=E6=8C=81=E5=85=B3=E9=94=AE=E5=AD=97?= =?UTF-8?q?=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/demo/build.mjs | 4 ++++ apps/demo/main.ts | 24 ++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/demo/build.mjs b/apps/demo/build.mjs index 9f07ffe..7c89c68 100644 --- a/apps/demo/build.mjs +++ b/apps/demo/build.mjs @@ -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)} diff --git a/apps/demo/main.ts b/apps/demo/main.ts index cc9d84e..f40dd08 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -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('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) {