From 612c1ad2d0003975d06c6b146e39954d82689a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=84?= Date: Wed, 14 Dec 2022 01:26:52 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EA=B2=80=EC=83=89=EC=96=B4=20?= =?UTF-8?q?=ED=82=A4=EC=9B=8C=EB=93=9C=20=EA=B0=95=EC=A1=B0=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/search/ArticleList/index.tsx | 39 +++------------ frontend/components/search/BookList/index.tsx | 34 +------------ frontend/utils/highlight-keyword.tsx | 50 +++++++++++++++++++ 3 files changed, 58 insertions(+), 65 deletions(-) create mode 100644 frontend/utils/highlight-keyword.tsx diff --git a/frontend/components/search/ArticleList/index.tsx b/frontend/components/search/ArticleList/index.tsx index 55779c4b..69a6aef7 100644 --- a/frontend/components/search/ArticleList/index.tsx +++ b/frontend/components/search/ArticleList/index.tsx @@ -1,5 +1,6 @@ import ArticleItem from '@components/search/ArticleItem'; import { IArticleBook } from '@interfaces'; +import { getTextAfterLastNewLine, highlightKeyword } from '@utils/highlight-keyword'; import { markdown2text } from '@utils/parser'; interface ArticleListProps { @@ -8,44 +9,16 @@ interface ArticleListProps { } export default function ArticleList({ articles, keywords }: ArticleListProps) { - const highlightWord = (text: string, words: string[], isFirst = false): React.ReactNode => { - let wordIndexList = words.map((word) => text.toLowerCase().indexOf(word.toLowerCase())); - - const filteredWords = words.filter((_, index) => wordIndexList[index] !== -1); - wordIndexList = wordIndexList.filter((wordIndex) => wordIndex !== -1); - - if (wordIndexList.length === 0) return text; - - const startIndex = Math.min(...wordIndexList); - - const targetWord = filteredWords[wordIndexList.indexOf(startIndex)]; - - const endIndex = startIndex + targetWord.length; - - let paddingIndex = 0; - - if (isFirst) { - const regex = /\n/g; - - while (regex.test(text.slice(0, startIndex))) paddingIndex = regex.lastIndex; - } - - return ( - <> - {text.slice(paddingIndex, startIndex)} - {text.slice(startIndex, endIndex)} - {highlightWord(text.slice(endIndex), words)} - - ); - }; - return ( <> {articles.map((article) => ( { export default function BookList({ books, keywords }: BookListProps) { const [highlightedBooks, setHighlightedBooks] = useState([]); - const highlightWord = (text: string, words: string[], isFirst = false): React.ReactNode => { - let wordIndexList = words.map((word) => text.toLowerCase().indexOf(word.toLowerCase())); - - const filteredWords = words.filter((_, index) => wordIndexList[index] !== -1); - wordIndexList = wordIndexList.filter((wordIndex) => wordIndex !== -1); - - if (wordIndexList.length === 0) return text; - - const startIndex = Math.min(...wordIndexList); - - const targetWord = filteredWords[wordIndexList.indexOf(startIndex)]; - - const endIndex = startIndex + targetWord.length; - - let paddingIndex = 0; - - if (isFirst) { - const regex = /(<([^>]+)>)/g; - - while (regex.test(text.slice(0, startIndex))) paddingIndex = regex.lastIndex; - } - - return ( - <> - {text.slice(paddingIndex, startIndex)} - {text.slice(startIndex, endIndex)} - {highlightWord(text.slice(endIndex).replace(/(<([^>]+)>)/gi, ''), words)} - - ); - }; - useEffect(() => { setHighlightedBooks( books.map((book) => { return { ...book, - title: highlightWord(book.title, keywords), + title: highlightKeyword(book.title, keywords), }; }) ); diff --git a/frontend/utils/highlight-keyword.tsx b/frontend/utils/highlight-keyword.tsx new file mode 100644 index 00000000..168ffb3e --- /dev/null +++ b/frontend/utils/highlight-keyword.tsx @@ -0,0 +1,50 @@ +const getFirstKeyword = (text: string, keywords: string[]) => { + const keywordMap = new Map(); + + keywords.forEach((keyword) => { + const index = text.toLowerCase().indexOf(keyword.toLowerCase()); + + if (index !== -1) keywordMap.set(index, keyword); + }); + + if (keywordMap.size === 0) return { keyword: '', index: -1, validKeywords: [] }; + + const firstKeywordIndex = Math.min(...Array.from(keywordMap.keys())); + const firstKeyword = keywordMap.get(firstKeywordIndex); + + return { + keyword: firstKeyword || '', + index: firstKeywordIndex, + validKeywords: Array.from(new Set(keywordMap.values())), + }; +}; + +export const getTextAfterLastNewLine = (text: string, keywords: string[]) => { + const { index } = getFirstKeyword(text, keywords); + + const newLineIndex = text.slice(0, index).lastIndexOf('\n'); + + return newLineIndex === -1 ? text : text.slice(newLineIndex); +}; + +export const highlightKeyword = (text: string, keywords: string[], length = 0): React.ReactNode => { + if (length > 200) return ''; + + const { keyword, index, validKeywords } = getFirstKeyword(text, keywords); + + if (index === -1) return text; + + const endIndex = index + keyword.length; + + const affixText = text.slice(0, index); + + const accumulatedLength = length + affixText.length + keyword.length; + + return ( + <> + {affixText} + {keyword} + {highlightKeyword(text.slice(endIndex), validKeywords, accumulatedLength)} + + ); +};