-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(search): add search functionality
- add new search page - refactor to use redux hooks - install package [email protected] - install package @types/[email protected]
- Loading branch information
1 parent
85bb3b5
commit 860ac9c
Showing
7 changed files
with
893 additions
and
928 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import React, { useState } from 'react'; | ||
import { | ||
IonButtons, | ||
IonContent, | ||
IonHeader, | ||
IonMenuButton, | ||
IonPage, | ||
IonRouterLink, | ||
IonSearchbar, | ||
IonTitle, | ||
IonToolbar, | ||
} from '@ionic/react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { useLunr } from '../../utils/lunr'; | ||
import { Chapter, getChapterIdsByUrl } from '../../components/Chapters'; | ||
|
||
interface ContainerProps {} | ||
|
||
export const SearchPage: React.FC<ContainerProps> = (props) => { | ||
const { t } = useTranslation(); | ||
|
||
const [searchValue, setSearchValue] = useState(''); | ||
const { results } = useLunr(searchValue); | ||
|
||
return ( | ||
<IonPage> | ||
<IonHeader> | ||
<IonToolbar color="primary" style={{ '--opacity': 1 }}> | ||
<IonButtons slot="start"> | ||
<IonMenuButton /> | ||
</IonButtons> | ||
<IonTitle>{t('SEARCH.TITLE')}</IonTitle> | ||
</IonToolbar> | ||
</IonHeader> | ||
|
||
<IonContent fullscreen={true}> | ||
<IonHeader collapse="condense"> | ||
<IonToolbar color="primary" style={{ '--opacity': 1 }}> | ||
<IonTitle size="large">{t('SEARCH.TITLE')}</IonTitle> | ||
</IonToolbar> | ||
</IonHeader> | ||
<IonSearchbar | ||
value={searchValue} | ||
onIonChange={(event) => setSearchValue(event.detail.value!)} | ||
></IonSearchbar> | ||
<div style={{ margin: 'auto', width: 'fit-content' }}> | ||
{results.map((result, resultIndex) => { | ||
const chapterUrl = result.id; | ||
const { id, subId } = getChapterIdsByUrl(chapterUrl); | ||
return ( | ||
<IonRouterLink | ||
key={resultIndex} | ||
routerLink={chapterUrl} | ||
routerDirection="forward" | ||
> | ||
<Chapter id={id} isCard subId={subId} /> | ||
</IonRouterLink> | ||
); | ||
})} | ||
</div> | ||
</IonContent> | ||
</IonPage> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { useTranslation } from 'react-i18next'; | ||
import { CHAPTER_01 } from '../components/Chapters/01/config'; | ||
import { useMemo } from 'react'; | ||
import lunr from 'lunr'; | ||
import { CHAPTER_02 } from '../components/Chapters/02/config'; | ||
|
||
type LunrDocs = { | ||
[key: string]: { | ||
id: string; | ||
title: string; | ||
body: string; | ||
}; | ||
}; | ||
|
||
const createLunrDocs = () => { | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const { t } = useTranslation(); | ||
|
||
const chapters = [...CHAPTER_01, ...CHAPTER_02]; | ||
|
||
return chapters.reduce<LunrDocs>((docs, chapter) => { | ||
if (chapter.isHeader) { | ||
return docs; | ||
} | ||
|
||
const doc = { | ||
[chapter.url]: { | ||
id: chapter.url, | ||
title: t(chapter.title), | ||
body: chapter.body?.map((text) => t(text)).join(' ') || '', | ||
}, | ||
}; | ||
|
||
return { ...docs, ...doc }; | ||
}, {}); | ||
}; | ||
|
||
export const useLunr = (query: string) => { | ||
const docs = createLunrDocs(); | ||
|
||
const idx = useMemo(() => { | ||
return lunr((builder) => { | ||
builder.field('body'); | ||
builder.field('title'); | ||
|
||
Object.values(docs).forEach((doc) => { | ||
builder.add(doc); | ||
}); | ||
}); | ||
}, [docs]); | ||
|
||
const rankings = useMemo(() => { | ||
if (!query || !idx) { | ||
return []; | ||
} | ||
return idx.search(query); | ||
}, [query, idx]); | ||
|
||
const results = useMemo(() => { | ||
if (!rankings) { | ||
return []; | ||
} | ||
return rankings.map(({ ref }) => docs[ref]); | ||
}, [rankings, docs]); | ||
|
||
return { rankings, results }; | ||
}; |