44 lines
1.4 KiB
TypeScript
44 lines
1.4 KiB
TypeScript
import { useMemo, Fragment } from 'react'
|
||
|
||
interface HighlighterProps {
|
||
/** Vurgulanacak kelimeler; boş/whitespace olanlar yok sayılır. */
|
||
searchWords: string[]
|
||
textToHighlight: string
|
||
highlightClassName?: string
|
||
}
|
||
|
||
const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||
|
||
/**
|
||
* react-highlight-words yerine kullanılan yerel bileşen.
|
||
* Kullanılan tek senaryo: arama kelimelerini büyük/küçük harf duyarsız
|
||
* eşleyip <mark> ile işaretlemek (paketteki `autoEscape` davranışı varsayılan).
|
||
*/
|
||
const Highlighter = ({ searchWords, textToHighlight, highlightClassName }: HighlighterProps) => {
|
||
const parts = useMemo(() => {
|
||
const words = searchWords.map((word) => word?.trim()).filter(Boolean)
|
||
if (words.length === 0 || !textToHighlight) return null
|
||
|
||
const pattern = new RegExp(`(${words.map(escapeRegExp).join('|')})`, 'gi')
|
||
return textToHighlight.split(pattern)
|
||
}, [searchWords, textToHighlight])
|
||
|
||
if (!parts) return <>{textToHighlight}</>
|
||
|
||
// split içindeki tek sayılı indeksler yakalanan gruplardır.
|
||
return (
|
||
<>
|
||
{parts.map((part, index) =>
|
||
index % 2 === 1 ? (
|
||
<mark key={index} className={highlightClassName}>
|
||
{part}
|
||
</mark>
|
||
) : (
|
||
<Fragment key={index}>{part}</Fragment>
|
||
),
|
||
)}
|
||
</>
|
||
)
|
||
}
|
||
|
||
export default Highlighter
|