Skip to content

Commit

Permalink
검색 키워드 강조 로직 개선 작업 (#318)
Browse files Browse the repository at this point in the history
  • Loading branch information
doputer authored Dec 14, 2022
2 parents 4bc9e23 + 612c1ad commit 18e8321
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 65 deletions.
39 changes: 6 additions & 33 deletions frontend/components/search/ArticleList/index.tsx
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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)}
<b>{text.slice(startIndex, endIndex)}</b>
{highlightWord(text.slice(endIndex), words)}
</>
);
};

return (
<>
{articles.map((article) => (
<ArticleItem
key={article.id}
title={highlightWord(article.title, keywords)}
content={highlightWord(markdown2text(article.content), keywords, true)}
title={highlightKeyword(article.title, keywords)}
content={highlightKeyword(
getTextAfterLastNewLine(markdown2text(article.content), keywords),
keywords
)}
nickname={article.book.user.nickname}
profileImage={article.book.user.profile_image}
articleUrl={`/viewer/${article.book.id}/${article.id}`}
Expand Down
34 changes: 2 additions & 32 deletions frontend/components/search/BookList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';

import Book from '@components/common/Book';
import { IBookScraps } from '@interfaces';
import { highlightKeyword } from '@utils/highlight-keyword';

import { BookContainer, BookListWrapper } from './styled';

Expand All @@ -17,43 +18,12 @@ interface HighlightedBooks extends Omit<IBookScraps, 'title'> {
export default function BookList({ books, keywords }: BookListProps) {
const [highlightedBooks, setHighlightedBooks] = useState<HighlightedBooks[]>([]);

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)}
<b>{text.slice(startIndex, endIndex)}</b>
{highlightWord(text.slice(endIndex).replace(/(<([^>]+)>)/gi, ''), words)}
</>
);
};

useEffect(() => {
setHighlightedBooks(
books.map((book) => {
return {
...book,
title: highlightWord(book.title, keywords),
title: highlightKeyword(book.title, keywords),
};
})
);
Expand Down
50 changes: 50 additions & 0 deletions frontend/utils/highlight-keyword.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const getFirstKeyword = (text: string, keywords: string[]) => {
const keywordMap = new Map<number, string>();

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}
<b>{keyword}</b>
{highlightKeyword(text.slice(endIndex), validKeywords, accumulatedLength)}
</>
);
};

0 comments on commit 18e8321

Please sign in to comment.