import { effect, h, type Signal } from '@ui-arch/frame'; import type { HighlightProps } from './types.js'; /*** 作用:读信号或原始值(string | Signal 统一取值) ***/ const read = (v: string | Signal): 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; }