55 lines
1.5 KiB
TypeScript
55 lines
1.5 KiB
TypeScript
|
|
import React, { useMemo } from 'react';
|
||
|
|
import { Text, TextStyle, StyleProp } from 'react-native';
|
||
|
|
|
||
|
|
interface HighlightTextProps {
|
||
|
|
text: string;
|
||
|
|
keyword: string;
|
||
|
|
style?: StyleProp<TextStyle>;
|
||
|
|
}
|
||
|
|
|
||
|
|
const HighlightText: React.FC<HighlightTextProps> = ({ text, keyword, style }) => {
|
||
|
|
const parts = useMemo(() => {
|
||
|
|
if (!text || !keyword) return [{ text, highlight: false }];
|
||
|
|
|
||
|
|
const result: Array<{ text: string; highlight: boolean }> = [];
|
||
|
|
const lowerText = text.toLowerCase();
|
||
|
|
const lowerKeyword = keyword.toLowerCase();
|
||
|
|
let lastIndex = 0;
|
||
|
|
|
||
|
|
let searchFrom = 0;
|
||
|
|
while (searchFrom < lowerText.length) {
|
||
|
|
const index = lowerText.indexOf(lowerKeyword, searchFrom);
|
||
|
|
if (index === -1) break;
|
||
|
|
|
||
|
|
if (index > lastIndex) {
|
||
|
|
result.push({ text: text.substring(lastIndex, index), highlight: false });
|
||
|
|
}
|
||
|
|
result.push({ text: text.substring(index, index + keyword.length), highlight: true });
|
||
|
|
lastIndex = index + keyword.length;
|
||
|
|
searchFrom = lastIndex;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (lastIndex < text.length) {
|
||
|
|
result.push({ text: text.substring(lastIndex), highlight: false });
|
||
|
|
}
|
||
|
|
|
||
|
|
return result.length > 0 ? result : [{ text, highlight: false }];
|
||
|
|
}, [text, keyword]);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Text>
|
||
|
|
{parts.map((part, i) =>
|
||
|
|
part.highlight ? (
|
||
|
|
<Text key={i} style={style}>
|
||
|
|
{part.text}
|
||
|
|
</Text>
|
||
|
|
) : (
|
||
|
|
<Text key={i}>{part.text}</Text>
|
||
|
|
)
|
||
|
|
)}
|
||
|
|
</Text>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default HighlightText;
|