iter(g238): 新增 highlight 关键词高亮,命中片段包 mark 并随信号实时联动(75 组件)

This commit is contained in:
HIsheR
2026-10-26 21:35:18 +08:00
parent 5f150dccaa
commit 226271986e
12 changed files with 150 additions and 10 deletions
+30
View File
@@ -0,0 +1,30 @@
import { effect, h, type Signal } from '@ui-arch/frame';
import type { HighlightProps } from './types.js';
/*** 作用:读信号或原始值(string | Signal<string> 统一取值) ***/
const read = (v: string | Signal<string>): string => (typeof v === 'function' ? v() : v);
/*** 关键词高亮:命中片段包进 mark(大小写不敏感、全部命中),text / keyword 均支持信号响应式刷新,空关键词原样展示 ***/
export function highlight(props: HighlightProps): HTMLSpanElement {
const root = h('span', { class: 'ua-highlight' }) as HTMLSpanElement;
/*** 作用:按当前 text / keyword 重画内容:命中段包 mark,未命中段原样拼接(effect 追踪两个信号,任一变化重画) ***/
const render = (): void => {
const text = read(props.text);
const kw = read(props.keyword).trim();
root.textContent = '';
if (!kw) { root.textContent = text; return; } // 空关键词不高亮
const lower = text.toLowerCase();
const k = kw.toLowerCase();
let cursor = 0;
for (let idx = lower.indexOf(k); idx !== -1; idx = lower.indexOf(k, cursor)) {
if (idx > cursor) root.append(text.slice(cursor, idx)); // 命中前缀原样
root.append(h('mark', { class: 'ua-highlight-mark' }, text.slice(idx, idx + k.length))); // 命中段包 mark
cursor = idx + k.length;
}
if (cursor < text.length) root.append(text.slice(cursor)); // 尾段原样
};
effect(render);
return root;
}