Skip to content

Commit 6193c90

Browse files
committed
Revert "search feature logic rework to handle search across all languages, categories and snippets"
This reverts commit 31e622c.
1 parent 31e622c commit 6193c90

File tree

11 files changed

+110
-348
lines changed

11 files changed

+110
-348
lines changed

src/components/CategoryList.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ const CategoryListItem: FC<CategoryListItemProps> = ({ name }) => {
1313
const navigate = useNavigate();
1414
const [searchParams] = useSearchParams();
1515

16-
const { language, category } = useAppContext();
16+
const { language, category, setCategory } = useAppContext();
1717

1818
const handleSelect = () => {
19+
setCategory(name);
1920
navigate({
2021
pathname: `/${slugify(language.name)}/${slugify(name)}`,
2122
search: searchParams.toString(),

src/components/Icons.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ const ACCENT_ICON_COLOR = "var(--clr-accent)";
55

66
interface IconProps {
77
fillColor?: string;
8-
width?: string;
9-
height?: string;
108
}
119

1210
export const LogoIcon: FC<IconProps> = ({ fillColor = ACCENT_ICON_COLOR }) => (
@@ -125,12 +123,10 @@ export const ExpandIcon: FC<IconProps> = ({
125123

126124
export const CloseIcon: FC<IconProps> = ({
127125
fillColor = DEFAULT_ICON_COLOR,
128-
width = "31",
129-
height = "30",
130126
}) => (
131127
<svg
132-
width={width}
133-
height={height}
128+
width="31"
129+
height="30"
134130
viewBox="0 0 31 30"
135131
fill="none"
136132
xmlns="http://www.w3.org/2000/svg"

src/components/LanguageSelector.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { slugify } from "@utils/slugify";
1515
const LanguageSelector = () => {
1616
const navigate = useNavigate();
1717

18-
const { language, setSearchText } = useAppContext();
18+
const { language, setLanguage, setCategory, setSearchText } = useAppContext();
1919
const { fetchedLanguages, loading, error } = useLanguages();
2020

2121
const dropdownRef = useRef<HTMLDivElement>(null);
@@ -33,6 +33,8 @@ const LanguageSelector = () => {
3333
});
3434

3535
setSearchText("");
36+
setLanguage(newLanguage);
37+
setCategory(newCategory);
3638
navigate(`/${slugify(newLanguage.name)}/${slugify(newCategory)}`);
3739
setIsOpen(false);
3840
};

src/components/SearchInput.tsx

Lines changed: 59 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,19 @@
1-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2-
import { useNavigate, useSearchParams } from "react-router-dom";
1+
import { useCallback, useEffect, useRef } from "react";
2+
import { useSearchParams } from "react-router-dom";
33

44
import { useAppContext } from "@contexts/AppContext";
5-
import { useFetch } from "@hooks/useFetch";
6-
import { AllSnippetsType, SearchItemType } from "@types";
75
import { QueryParams } from "@utils/enums";
8-
import { slugify } from "@utils/slugify";
96

10-
import Button from "./Button";
11-
import { CloseIcon, SearchIcon } from "./Icons";
7+
import { SearchIcon } from "./Icons";
128

139
const SearchInput = () => {
14-
const navigate = useNavigate();
1510
const [searchParams, setSearchParams] = useSearchParams();
1611

1712
const { searchText, setSearchText } = useAppContext();
18-
const { data } = useFetch<AllSnippetsType[]>(`/consolidated/all.json`);
19-
20-
const filteredData: SearchItemType[] = useMemo(() => {
21-
if (!data) {
22-
return [];
23-
}
24-
25-
const searchTerm = searchText.toLowerCase();
26-
27-
return data
28-
.map((language) => {
29-
const filteredCategories = language.categories
30-
.map((category) => {
31-
const filteredSnippets = category.snippets.filter(
32-
(snippet) =>
33-
snippet.title.toLowerCase().includes(searchTerm) ||
34-
snippet.description.toLowerCase().includes(searchTerm) ||
35-
snippet.tags.some((tag) =>
36-
tag.toLowerCase().includes(searchTerm)
37-
)
38-
);
39-
40-
if (filteredSnippets.length > 0) {
41-
return {
42-
categoryName: category.name,
43-
snippets: filteredSnippets,
44-
};
45-
}
46-
47-
return null;
48-
})
49-
.filter(Boolean); // Remove null categories
50-
51-
if (filteredCategories.length > 0) {
52-
return filteredCategories.map((filteredCategory) => ({
53-
languageName: language.languageName,
54-
languageIcon: language.languageIcon,
55-
categoryName: filteredCategory!.categoryName,
56-
snippets: filteredCategory!.snippets,
57-
}));
58-
}
59-
60-
return [];
61-
})
62-
.flat();
63-
}, [data, searchText]);
6413

6514
const inputRef = useRef<HTMLInputElement | null>(null);
6615

67-
const [searchOpen, setSearchOpen] = useState<boolean>(false);
68-
6916
const handleSearchFieldClick = () => {
70-
setSearchOpen(true);
71-
};
72-
73-
const handleInnerSearchFieldClick = () => {
7417
inputRef.current?.focus();
7518
};
7619

@@ -80,13 +23,31 @@ const SearchInput = () => {
8023
setSearchParams(searchParams);
8124
}, [searchParams, setSearchParams, setSearchText]);
8225

26+
const performSearch = useCallback(() => {
27+
// Check if the input element is focused.
28+
if (document.activeElement !== inputRef.current) {
29+
return;
30+
}
31+
32+
const formattedVal = searchText.toLowerCase();
33+
34+
setSearchText(formattedVal);
35+
if (!formattedVal) {
36+
searchParams.delete(QueryParams.SEARCH);
37+
setSearchParams(searchParams);
38+
} else {
39+
searchParams.set(QueryParams.SEARCH, formattedVal);
40+
setSearchParams(searchParams);
41+
}
42+
}, [searchParams, searchText, setSearchParams, setSearchText]);
43+
8344
/**
8445
* Focus the search input when the user presses the `/` key.
8546
*/
8647
const handleSearchKeyPress = (e: KeyboardEvent) => {
8748
if (e.key === "/") {
8849
e.preventDefault();
89-
setSearchOpen(true);
50+
inputRef.current?.focus();
9051
}
9152
};
9253

@@ -99,30 +60,18 @@ const SearchInput = () => {
9960
return;
10061
}
10162

102-
setSearchOpen(false);
63+
// Check if the input element is focused.
64+
if (document.activeElement !== inputRef.current) {
65+
return;
66+
}
67+
68+
inputRef.current?.blur();
69+
10370
clearSearch();
10471
},
10572
[clearSearch]
10673
);
10774

108-
const handleSearchItemClick =
109-
({
110-
languageName,
111-
categoryName,
112-
snippetName,
113-
}: {
114-
languageName: string;
115-
categoryName: string;
116-
snippetName: string;
117-
}) =>
118-
() => {
119-
navigate(
120-
`/${slugify(languageName)}/${slugify(categoryName)}?${QueryParams.SEARCH}=${searchText.toLowerCase()}&${QueryParams.SNIPPET}=${slugify(snippetName)}`,
121-
{ replace: true }
122-
);
123-
setSearchOpen(false);
124-
};
125-
12675
useEffect(() => {
12776
window.addEventListener("keydown", handleSearchKeyPress);
12877
window.addEventListener("keyup", handleEscapeKeyPress);
@@ -133,6 +82,13 @@ const SearchInput = () => {
13382
};
13483
}, [handleEscapeKeyPress]);
13584

85+
/**
86+
* Update the search query in the URL when the search text changes.
87+
*/
88+
useEffect(() => {
89+
performSearch();
90+
}, [searchText, performSearch]);
91+
13692
/**
13793
* Set the search text to the search query from the URL on mount.
13894
*/
@@ -146,108 +102,30 @@ const SearchInput = () => {
146102
// eslint-disable-next-line react-hooks/exhaustive-deps
147103
}, []);
148104

149-
useEffect(() => {
150-
if (searchOpen) {
151-
inputRef.current?.focus();
152-
}
153-
}, [searchOpen]);
154-
155105
return (
156-
<>
157-
<div className="search-field" onClick={handleSearchFieldClick}>
158-
<SearchIcon />
159-
<input
160-
disabled
161-
id="search"
162-
type="text"
163-
value={searchText}
164-
onChange={() => {}}
165-
/>
166-
{!searchText && (
167-
<label htmlFor="search">
168-
Type <kbd>/</kbd> to search
169-
</label>
170-
)}
171-
{searchText && (
172-
<Button
173-
isIcon={true}
174-
className="search-field__clear"
175-
onClick={(e: React.MouseEvent) => {
176-
e.stopPropagation();
177-
clearSearch();
178-
}}
179-
>
180-
<CloseIcon width="20" height="20" />
181-
</Button>
182-
)}
183-
</div>
184-
185-
<div
186-
className={`search-field__results search-field__results${searchOpen ? "--open" : "--closed"}`}
187-
>
188-
<div
189-
className="search-field search-field--inner"
190-
onClick={handleInnerSearchFieldClick}
191-
>
192-
<SearchIcon />
193-
<input
194-
ref={inputRef}
195-
value={searchText}
196-
type="text"
197-
autoComplete="off"
198-
onChange={(e) => {
199-
const newValue = e.target.value;
200-
if (!newValue) {
201-
clearSearch();
202-
return;
203-
}
204-
setSearchText(newValue);
205-
}}
206-
/>
207-
<Button
208-
isIcon={true}
209-
onClick={() => {
210-
setSearchOpen(false);
211-
clearSearch();
212-
}}
213-
>
214-
<CloseIcon />
215-
</Button>
216-
</div>
217-
218-
<div className="search-field__results__list">
219-
{filteredData.map(
220-
(
221-
{ languageName, languageIcon, categoryName, snippets },
222-
languageIndex
223-
) => (
224-
<div key={`${languageName}-${languageIndex}`}>
225-
<ul>
226-
{snippets.map((snippet, snippetIndex) => (
227-
<li
228-
key={`${languageName}-${categoryName}-${snippetIndex}`}
229-
onClick={handleSearchItemClick({
230-
languageName,
231-
categoryName,
232-
snippetName: snippet.title,
233-
})}
234-
>
235-
<img src={languageIcon} alt={languageName} />
236-
<div>
237-
<h4>
238-
{snippet.title} ({languageName})
239-
</h4>
240-
<p>{snippet.description}</p>
241-
</div>
242-
</li>
243-
))}
244-
</ul>
245-
</div>
246-
)
247-
)}
248-
</div>
249-
</div>
250-
</>
106+
<div className="search-field" onClick={handleSearchFieldClick}>
107+
<SearchIcon />
108+
<input
109+
ref={inputRef}
110+
value={searchText}
111+
type="search"
112+
id="search"
113+
autoComplete="off"
114+
onChange={(e) => {
115+
const newValue = e.target.value;
116+
if (!newValue) {
117+
clearSearch();
118+
return;
119+
}
120+
setSearchText(newValue);
121+
}}
122+
/>
123+
{!searchText && (
124+
<label htmlFor="search">
125+
Type <kbd>/</kbd> to search
126+
</label>
127+
)}
128+
</div>
251129
);
252130
};
253131

src/components/SnippetList.tsx

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,6 @@ const SnippetList = () => {
3434
setSearchParams(searchParams);
3535
};
3636

37-
const handleSearchKeyPress = (e: KeyboardEvent) => {
38-
if (e.key === "/") {
39-
e.preventDefault();
40-
setIsModalOpen(false);
41-
}
42-
};
43-
4437
/**
4538
* open the relevant modal if the snippet is in the search params
4639
*/
@@ -59,14 +52,6 @@ const SnippetList = () => {
5952
// eslint-disable-next-line react-hooks/exhaustive-deps
6053
}, [fetchedSnippets, searchParams]);
6154

62-
useEffect(() => {
63-
window.addEventListener("keydown", handleSearchKeyPress);
64-
65-
return () => {
66-
window.removeEventListener("keydown", handleSearchKeyPress);
67-
};
68-
}, []);
69-
7055
if (!fetchedSnippets) {
7156
return (
7257
<div>

src/contexts/AppContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const AppProvider: FC<{ children: React.ReactNode }> = ({
3535
useEffect(() => {
3636
configure();
3737
// eslint-disable-next-line react-hooks/exhaustive-deps
38-
}, [fetchedLanguages, languageName, categoryName]);
38+
}, [fetchedLanguages]);
3939

4040
/**
4141
* Set the default language if the language is not found in the URL.

0 commit comments

Comments
 (0)