From b6486cfa3299a563a3226ecc2114dbff9ba4a456 Mon Sep 17 00:00:00 2001 From: Ansh Saini Date: Sun, 20 Oct 2024 12:38:47 +0530 Subject: [PATCH 1/4] feat: implement search highlight --- components/ProjectCard.tsx | 64 ++++++++++++++++++------ components/SearchFilter/SearchFilter.tsx | 37 +++++++------- pages/projects.tsx | 7 ++- 3 files changed, 75 insertions(+), 33 deletions(-) diff --git a/components/ProjectCard.tsx b/components/ProjectCard.tsx index edc66a4..212e36b 100644 --- a/components/ProjectCard.tsx +++ b/components/ProjectCard.tsx @@ -7,7 +7,8 @@ interface ProjectCardProps { project: Project; vertical?: boolean; preload?: boolean; - githubColors: GitHubColors + githubColors: GitHubColors; + searchQuery?: string; } interface ProjectCardImageProps { @@ -43,19 +44,43 @@ function ProjectCardImage({ project, preload }: ProjectCardImageProps) { } interface ProjectCardBodyProps { - githubColors: GitHubColors, - project: Project + githubColors: GitHubColors; + project: Project; + searchQuery?: string; } -function ProjectCardBody(props: ProjectCardBodyProps) { - const { - name, - description, - repo, - link, - lang, - topics, - } = props.project; +const highlightText = (text: string, query: string) => { + if (!query) return text; + const parts = text.split(new RegExp(`(${query})`, 'gi')); + return parts.map((part, index) => + part.toLowerCase() === query.toLowerCase() ? ( + {part} + ) : ( + part + ), + ); +}; + +function ProjectCardBody({ + project, + searchQuery = '', + ...props +}: ProjectCardBodyProps) { + const { repo, link, lang } = project; + + const name = highlightText(project.name, searchQuery); + const description = highlightText(project.description, searchQuery); + + const topics = (() => { + if (project.topics.length === 0) return []; + return project.topics.map((topic, index) => ( + + {highlightText(topic, searchQuery)} + {index < project.topics.length - 1 && ', '} + + )); + })(); + return (

@@ -71,7 +96,7 @@ function ProjectCardBody(props: ProjectCardBodyProps) { }} >{' '} {lang || 'Markdown'} - {topics.length > 0 && • {topics.join(', ')}} + {topics.length > 0 && • {topics}}

{description}

GitHub Repository @@ -86,12 +111,17 @@ function ProjectCard({ vertical = false, preload = false, githubColors, + searchQuery = '', }: ProjectCardProps): JSX.Element { if (vertical) { return (
- +
); } @@ -102,7 +132,11 @@ function ProjectCard({

- +
diff --git a/components/SearchFilter/SearchFilter.tsx b/components/SearchFilter/SearchFilter.tsx index 0a1f3a7..ad7bf25 100644 --- a/components/SearchFilter/SearchFilter.tsx +++ b/components/SearchFilter/SearchFilter.tsx @@ -1,10 +1,12 @@ -import React, { ChangeEvent, useState, useEffect } from 'react'; +import React, { ChangeEvent, useEffect } from 'react'; import Searchbar from './Searchbar'; import { Project } from '../../util'; interface SearchFilterProps { projects: Project[]; setFilteredProjects: (projectList: Project[]) => void; + searchQuery: string; + setSearchQuery: (search: string) => void; } function lowercaseRemove(s: string) { @@ -16,12 +18,14 @@ function lowercaseRemove(s: string) { return newString; } -function SearchFilter({projects, setFilteredProjects}: SearchFilterProps): JSX.Element { - - const [searchbarText, setSearchbarText] = useState(''); - +function SearchFilter({ + projects, + setFilteredProjects, + setSearchQuery, + searchQuery, +}: SearchFilterProps): JSX.Element { const handleSearchInput = (e: ChangeEvent) => { - setSearchbarText(e.target.value); + setSearchQuery(e.target.value); }; const filterProjectsBySearchText = (project: Project) => { @@ -30,21 +34,20 @@ function SearchFilter({projects, setFilteredProjects}: SearchFilterProps): JSX.E // remove - and _ and white spaces from search, and make it lowercase to make it easier for the user to // search things without typing the exact name // e.g. if the user searches "devpathways" or "dev pathways" they should still be able to see "Dev-Pathways" - const search = lowercaseRemove(searchbarText); + const search = lowercaseRemove(searchQuery); - const { - name, - description, - lang, - topics, - } = project; + const { name, description, lang, topics } = project; // can search by name, description, or language const lowercaseName = lowercaseRemove(name); const lowercaseDescription = lowercaseRemove(description); const lowercaseLang = lowercaseRemove(lang); - if (lowercaseName.includes(search) || lowercaseDescription.includes(search) || lowercaseLang.includes(search)) { + if ( + lowercaseName.includes(search) || + lowercaseDescription.includes(search) || + lowercaseLang.includes(search) + ) { return true; } @@ -64,16 +67,16 @@ function SearchFilter({projects, setFilteredProjects}: SearchFilterProps): JSX.E useEffect(() => { const tempProjects = projects.filter(filterProjectsBySearchText); setFilteredProjects(tempProjects); - }, [searchbarText]); + }, [searchQuery]); return (
) => handleSearchInput(e)} />
); } -export default SearchFilter; \ No newline at end of file +export default SearchFilter; diff --git a/pages/projects.tsx b/pages/projects.tsx index 3901db4..44cb34f 100644 --- a/pages/projects.tsx +++ b/pages/projects.tsx @@ -4,7 +4,7 @@ import React, { useState } from 'react'; import Layout from '../components/Layout'; import ProjectCard from '../components/ProjectCard'; import SearchFilter from '../components/SearchFilter/SearchFilter'; -import { getProjects, Project, GitHubColors, getGithubColors } from '../util'; +import { getGithubColors, getProjects, GitHubColors, Project } from '../util'; interface ProjectsProps { projects: Project[]; @@ -15,6 +15,8 @@ function Projects({ projects, githubColors }: ProjectsProps): JSX.Element { // projects is a master list of all the projects that we fetched, filteredProjects is the one that we render // to the user const [filteredProjects, setFilteredProjects] = useState(projects); + const [searchQuery, setSearchQuery] = useState(''); + return ( @@ -43,6 +45,8 @@ function Projects({ projects, githubColors }: ProjectsProps): JSX.Element {
@@ -51,6 +55,7 @@ function Projects({ projects, githubColors }: ProjectsProps): JSX.Element { return (
Date: Sun, 20 Oct 2024 12:46:30 +0530 Subject: [PATCH 2/4] fix: add highlight on `lang` as well --- components/ProjectCard.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/components/ProjectCard.tsx b/components/ProjectCard.tsx index 212e36b..f061dd4 100644 --- a/components/ProjectCard.tsx +++ b/components/ProjectCard.tsx @@ -66,10 +66,9 @@ function ProjectCardBody({ searchQuery = '', ...props }: ProjectCardBodyProps) { - const { repo, link, lang } = project; + const { repo, link, description, lang } = project; const name = highlightText(project.name, searchQuery); - const description = highlightText(project.description, searchQuery); const topics = (() => { if (project.topics.length === 0) return []; @@ -95,10 +94,10 @@ function ProjectCardBody({ : 'black', }} >{' '} - {lang || 'Markdown'} + {highlightText(lang, searchQuery) || 'Markdown'} {topics.length > 0 && • {topics}}

-

{description}

+

{highlightText(description, searchQuery)}

GitHub Repository
); From b215dceb09dd6f531bbbee7c15659173ae081c08 Mon Sep 17 00:00:00 2001 From: Ansh Saini Date: Sun, 27 Oct 2024 14:37:02 +0530 Subject: [PATCH 3/4] fix: handle special characters --- components/ProjectCard.tsx | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/components/ProjectCard.tsx b/components/ProjectCard.tsx index f061dd4..3a66695 100644 --- a/components/ProjectCard.tsx +++ b/components/ProjectCard.tsx @@ -49,16 +49,26 @@ interface ProjectCardBodyProps { searchQuery?: string; } +const escapeSpecialCharacters = (string: string) => { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +}; + const highlightText = (text: string, query: string) => { if (!query) return text; - const parts = text.split(new RegExp(`(${query})`, 'gi')); - return parts.map((part, index) => - part.toLowerCase() === query.toLowerCase() ? ( - {part} - ) : ( - part - ), - ); + + try { + const escapedQuery = escapeSpecialCharacters(query); + const parts = text.split(new RegExp(`(${escapedQuery})`, 'gi')); + return parts.map((part, index) => + part.toLowerCase() === query.toLowerCase() ? ( + {part} + ) : ( + part + ), + ); + } catch (e) { + return text; + } }; function ProjectCardBody({ From a840df3c6daf09c3eb5322f52305ea3845ffdd3e Mon Sep 17 00:00:00 2001 From: Ansh Saini Date: Mon, 28 Oct 2024 19:06:16 +0530 Subject: [PATCH 4/4] fix: handle empty spaces --- components/ProjectCard.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/components/ProjectCard.tsx b/components/ProjectCard.tsx index 3a66695..e37cb6e 100644 --- a/components/ProjectCard.tsx +++ b/components/ProjectCard.tsx @@ -54,6 +54,7 @@ const escapeSpecialCharacters = (string: string) => { }; const highlightText = (text: string, query: string) => { + query = query.trim(); if (!query) return text; try {