import { IconFile, IconHeader, IconJump, IconTitle, Link, SvgWrapper, } from '@rspress/core/theme'; import { useRef } from 'react'; import './SuggestItem.scss'; import type { DefaultMatchResultItem, HighlightInfo } from './logic/types'; import { getSlicedStrByByteLength } from './logic/util'; export function SuggestItem({ suggestion, closeSearch, isCurrent, setCurrentSuggestionIndex, scrollTo, onMouseMove, }: { suggestion: DefaultMatchResultItem; closeSearch: () => void; isCurrent: boolean; setCurrentSuggestionIndex: ( event: React.MouseEvent, ) => void; onMouseMove: (event: React.MouseEvent) => void; inCurrentDocIndex: boolean; scrollTo: (top: number, height: number) => void; }) { const ICON_MAP = { title: IconTitle, header: IconHeader, content: IconFile, }; const HitIcon = ICON_MAP[suggestion.type]; const selfRef = useRef(null); if (isCurrent && selfRef.current?.offsetTop) { scrollTo(selfRef.current?.offsetTop, selfRef.current?.offsetHeight); } const getHighlightedFragments = ( rawText: string, highlights: HighlightInfo[], ) => { // Split raw text into several parts, and add styles.mark className to the parts that need to be highlighted. // highlightInfoList is an array of objects, each object contains the start index and the length of the part that needs to be highlighted. // For example, if the statement is "This is a statement", and the query is "is", then highlightInfoList is [{start: 2, length: 2}, {start: 5, length: 2}]. const fragmentList = []; let lastIndex = 0; for (const highlightInfo of highlights) { const { start, length } = highlightInfo; const prefix = rawText.slice(lastIndex, start); const queryStr = getSlicedStrByByteLength(rawText, start, length); fragmentList.push(prefix); fragmentList.push( {queryStr} , ); lastIndex = start + queryStr.length; } if (lastIndex < rawText.length) { fragmentList.push(rawText.slice(lastIndex)); } return fragmentList; }; const renderHeaderMatch = () => { if (suggestion.type === 'header' || suggestion.type === 'title') { const { header, highlightInfoList } = suggestion; return (
{getHighlightedFragments(header, highlightInfoList)}
); } return
{suggestion.header}
; }; const renderStatementMatch = () => { if (suggestion.type !== 'content') { return
; } const { statement, highlightInfoList } = suggestion; return (
{getHighlightedFragments(statement, highlightInfoList)}
); }; let hitContent = null; switch (suggestion.type) { case 'title': case 'header': hitContent = renderHeaderMatch(); break; case 'content': hitContent = ( <> {renderStatementMatch()}

{suggestion.title}

); break; default: break; } return (
  • { closeSearch(); e.stopPropagation(); }} >
    {hitContent}
  • ); }