diff --git a/src/components/SearchModal/SearchModal.tsx b/src/components/SearchModal/SearchModal.tsx
index cdd18a0..39a4443 100644
--- a/src/components/SearchModal/SearchModal.tsx
+++ b/src/components/SearchModal/SearchModal.tsx
@@ -2,7 +2,6 @@ import ButtonLink from 'components/ButtonLink';
import Modal from 'components/Modal/Modal';
import TextBox from 'components/TextBox';
import * as Keys from 'constants/keys';
-import PATHS from 'constants/paths';
import { GOOGLE_SEARCH_URL } from 'constants/search-urls';
import { AnimatePresence } from 'framer-motion';
import useActiveCategory from 'hooks/useActiveCategory';
@@ -11,24 +10,13 @@ import useCategories from 'hooks/useCategories';
import useGlobalHotkeys from 'hooks/useGlobalHotkeys';
import { useLocalStorage } from 'hooks/useLocalStorage';
import useModal from 'hooks/useModal';
+import useQueryParam from 'hooks/useQueryParam';
+import useSearchItem from 'hooks/useSearchItem';
import { useTranslation } from 'next-i18next';
-import { useRouter } from 'next/router';
-import {
- FormEvent,
- ReactNode,
- useCallback,
- useEffect,
- useMemo,
- useState,
-} from 'react';
+import { FormEvent, ReactNode, useCallback, useEffect, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { BsSearch } from 'react-icons/bs';
-import {
- CategoryWithLinks,
- LinkWithCategory,
- SearchItem,
- SearchResult,
-} from 'types/types';
+import { SearchResult } from 'types/types';
import LabelSearchWithGoogle from './LabelSearchWithGoogle';
import { SearchFilter } from './SearchFilter';
import SearchList from './SearchList';
@@ -40,62 +28,12 @@ interface SearchModalProps {
childClassname?: string;
}
-function buildSearchItem(
- item: CategoryWithLinks | LinkWithCategory,
- type: SearchItem['type'],
-): SearchItem {
- return {
- id: item.id,
- name: item.name,
- url:
- type === 'link'
- ? (item as LinkWithCategory).url
- : `${PATHS.HOME}?categoryId=${item.id}`,
- type,
- category: type === 'link' ? (item as LinkWithCategory).category : undefined,
- };
-}
-
-function formatSearchItem(
- item: SearchItem,
- searchTerm: string,
-): SearchResult | null {
- const lowerCaseSearchTerm = searchTerm.toLowerCase().trim();
- const lowerCaseName = item.name.toLowerCase().trim();
-
- let currentIndex = 0;
- let formattedName = '';
-
- for (let i = 0; i < lowerCaseName.length; i++) {
- if (lowerCaseName[i] === lowerCaseSearchTerm[currentIndex]) {
- formattedName += `${item.name[i]}`;
- currentIndex++;
- } else {
- formattedName += item.name[i];
- }
- }
-
- if (currentIndex !== lowerCaseSearchTerm.length) {
- // Search term not fully matched
- return null;
- }
-
- return {
- id: item.id,
- name:
,
- url: item.url,
- type: item.type,
- category: item.category,
- };
-}
-
function SearchModal({
noHeader = true,
children,
childClassname = '',
}: Readonly) {
const { t } = useTranslation();
- const router = useRouter();
const autoFocusRef = useAutoFocus();
const { categories } = useCategories();
const { setActiveCategory } = useActiveCategory();
@@ -108,12 +46,11 @@ function SearchModal({
'search-category',
false,
);
- const [search, setSearch] = useState(
- (router.query.q as string) || '',
- );
+ const { queryParam, clearQuery } = useQueryParam('q');
+ const [searchTerm, setSearchTerm] = useState(queryParam);
const [selectedItem, setSelectedItem] = useState(null);
- const searchModal = useModal(!!search && typeof window !== 'undefined');
+ const searchModal = useModal(!!searchTerm);
useEffect(
() => setGlobalHotkeysEnabled(!searchModal.isShowing),
@@ -122,63 +59,28 @@ function SearchModal({
const handleCloseModal = useCallback(() => {
searchModal.close();
- setSearch('');
- if (!!search) {
- router.replace({
- query: undefined,
- });
- }
- }, [router, search, searchModal]);
+ clearQuery();
+ setSearchTerm('');
+ }, [searchModal, clearQuery]);
- const itemsSearch = useMemo(() => {
- return categories.reduce((acc, category) => {
- const categoryItem = buildSearchItem(category, 'category');
- const items: SearchItem[] = category.links.map((link) =>
- buildSearchItem(link, 'link'),
- );
- return [...acc, ...items, categoryItem];
- }, [] as SearchItem[]);
- }, [categories]);
+ const searchItemsResult = useSearchItem({
+ searchTerm,
+ disableLinks: !canSearchLink,
+ disableCategories: !canSearchCategory,
+ });
- const itemsCompletion = useMemo(() => {
- return itemsSearch.reduce((acc, item) => {
- const formattedItem = formatSearchItem(item, search);
-
- if (
- (canSearchLink && item.type === 'link') ||
- (canSearchCategory && item.type === 'category')
- ) {
- return formattedItem ? [...acc, formattedItem] : acc;
- }
-
- return acc;
- }, [] as SearchResult[]);
- }, [itemsSearch, search, canSearchLink, canSearchCategory]);
-
- const canSubmit = useMemo(() => search.length > 0, [search]);
-
- const handleSearchInputChange = useCallback(
- (value: string) => setSearch(value),
- [],
- );
-
- const handleCanSearchLink = useCallback(
- (checked: boolean) => setCanSearchLink(checked),
- [setCanSearchLink],
- );
-
- const handleCanSearchCategory = useCallback(
- (checked: boolean) => setCanSearchCategory(checked),
- [setCanSearchCategory],
- );
+ const handleSearchInputChange = (value: string) => setSearchTerm(value);
+ const handleCanSearchLink = (checked: boolean) => setCanSearchLink(checked);
+ const handleCanSearchCategory = (checked: boolean) =>
+ setCanSearchCategory(checked);
const handleSubmit = useCallback(
(event: FormEvent) => {
event.preventDefault();
handleCloseModal();
- if (itemsCompletion.length === 0) {
- return window.open(GOOGLE_SEARCH_URL + encodeURI(search.trim()));
+ if (searchItemsResult.length === 0) {
+ return window.open(GOOGLE_SEARCH_URL + encodeURI(searchTerm.trim()));
}
if (!selectedItem) return;
@@ -193,8 +95,8 @@ function SearchModal({
[
categories,
handleCloseModal,
- itemsCompletion.length,
- search,
+ searchItemsResult.length,
+ searchTerm,
selectedItem,
setActiveCategory,
],
@@ -240,7 +142,7 @@ function SearchModal({
- {search.length > 0 && (
+ {searchTerm.length > 0 && (
}
@@ -264,7 +166,7 @@ function SearchModal({
)}