Skip to content

Commit 965cce5

Browse files
committed
navigation concept (wip)
1 parent 558dff5 commit 965cce5

File tree

12 files changed

+128
-55
lines changed

12 files changed

+128
-55
lines changed

src/appRouter.tsx renamed to src/AppRouter.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const AppRouter = () => {
88
<Routes>
99
<Route element={<Container />}>
1010
<Route path="/" element={<SnippetList />} />
11+
<Route path="/:languageName" element={<SnippetList />} />
12+
<Route path="/:languageName/:categoryName" element={<SnippetList />} />
1113
</Route>
1214
</Routes>
1315
);

src/components/CategoryList.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { FC } from "react";
22

33
import { useAppContext } from "@contexts/AppContext";
44
import { useCategories } from "@hooks/useCategories";
5-
import { defaultCategory } from "@utils/consts";
65

76
interface CategoryListItemProps {
87
name: string;
@@ -11,13 +10,17 @@ interface CategoryListItemProps {
1110
const CategoryListItem: FC<CategoryListItemProps> = ({ name }) => {
1211
const { category, setCategory } = useAppContext();
1312

13+
const handleSelect = () => {
14+
setCategory(name);
15+
};
16+
1417
return (
1518
<li className="category">
1619
<button
1720
className={`category__btn ${
1821
name === category ? "category__btn--active" : ""
1922
}`}
20-
onClick={() => setCategory(name)}
23+
onClick={handleSelect}
2124
>
2225
{name}
2326
</button>
@@ -34,7 +37,6 @@ const CategoryList = () => {
3437

3538
return (
3639
<ul role="list" className="categories">
37-
<CategoryListItem name={defaultCategory} />
3840
{fetchedCategories.map((name, idx) => (
3941
<CategoryListItem key={idx} name={name} />
4042
))}

src/components/Container.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { FC } from "react";
22
import { Outlet } from "react-router-dom";
33

4-
import { useAppContext } from "@contexts/AppContext";
4+
import { AppProvider, useAppContext } from "@contexts/AppContext";
55
import Banner from "@layouts/Banner";
66
import Footer from "@layouts/Footer";
77
import Header from "@layouts/Header";
@@ -13,20 +13,22 @@ const Container: FC<ContainerProps> = () => {
1313
const { category } = useAppContext();
1414

1515
return (
16-
<div className="container flow">
17-
<Header />
18-
<Banner />
19-
<main className="main">
20-
<Sidebar />
21-
<section className="flow">
22-
<h2 className="section-title">
23-
{category ? category : "Select a category"}
24-
</h2>
25-
<Outlet />
26-
</section>
27-
</main>
28-
<Footer />
29-
</div>
16+
<AppProvider>
17+
<div className="container flow">
18+
<Header />
19+
<Banner />
20+
<main className="main">
21+
<Sidebar />
22+
<section className="flow">
23+
<h2 className="section-title">
24+
{category ? category : "Select a category"}
25+
</h2>
26+
<Outlet />
27+
</section>
28+
</main>
29+
<Footer />
30+
</div>
31+
</AppProvider>
3032
);
3133
};
3234

src/components/LanguageSelector.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { useRef, useEffect, useState } from "react";
2+
import { useNavigate } from "react-router-dom";
23

34
import { useAppContext } from "@contexts/AppContext";
45
import { useKeyboardNavigation } from "@hooks/useKeyboardNavigation";
56
import { useLanguages } from "@hooks/useLanguages";
67
import { LanguageType } from "@types";
8+
import { slugify } from "@utils/slugify";
79

810
// Inspired by https://blog.logrocket.com/creating-custom-select-dropdown-css/
911

1012
const LanguageSelector = () => {
13+
const navigate = useNavigate();
14+
1115
const { language, setLanguage } = useAppContext();
1216
const { fetchedLanguages, loading, error } = useLanguages();
1317

@@ -16,6 +20,7 @@ const LanguageSelector = () => {
1620

1721
const handleSelect = (selected: LanguageType) => {
1822
setLanguage(selected);
23+
navigate(`/${slugify(selected.lang)}`);
1924
setIsOpen(false);
2025
};
2126

src/components/SearchInput.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ import { useCallback, useEffect, useRef, useState } from "react";
22
import { useSearchParams } from "react-router-dom";
33

44
import { useAppContext } from "@contexts/AppContext";
5-
import { defaultCategory } from "@utils/consts";
65

76
import { SearchIcon } from "./Icons";
87

98
const SearchInput = () => {
109
const [searchParams, setSearchParams] = useSearchParams();
1110

12-
const { searchText, setSearchText, setCategory } = useAppContext();
11+
const { searchText, setSearchText } = useAppContext();
1312

1413
const inputRef = useRef<HTMLInputElement | null>(null);
1514

@@ -28,10 +27,10 @@ const SearchInput = () => {
2827

2928
const clearSearch = useCallback(() => {
3029
setInputVal("");
31-
setCategory(defaultCategory);
30+
// setCategory(defaultCategory);
3231
setSearchText("");
3332
setSearchParams({});
34-
}, [setCategory, setSearchParams, setSearchText]);
33+
}, [setSearchParams, setSearchText]);
3534

3635
const handleEscapePress = useCallback(
3736
(e: KeyboardEvent) => {
@@ -62,15 +61,15 @@ const SearchInput = () => {
6261

6362
const formattedVal = inputVal.trim().toLowerCase();
6463

65-
setCategory(defaultCategory);
64+
// setCategory(defaultCategory);
6665
setSearchText(formattedVal);
6766
if (!formattedVal) {
6867
setSearchParams({});
6968
} else {
7069
setSearchParams({ search: formattedVal });
7170
}
7271
},
73-
[inputVal, setCategory, setSearchParams, setSearchText]
72+
[inputVal, setSearchParams, setSearchText]
7473
);
7574

7675
useEffect(() => {

src/components/SnippetModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import ReactDOM from "react-dom";
44

55
import { useEscapeKey } from "@hooks/useEscapeKey";
66
import { SnippetType } from "@types";
7-
import { slugify } from "@utils/helpers/slugify";
7+
import { slugify } from "@utils/slugify";
88

99
import Button from "./Button";
1010
import CodePreview from "./CodePreview";

src/contexts/AppContext.tsx

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1-
import { createContext, FC, useContext, useState } from "react";
1+
import {
2+
createContext,
3+
FC,
4+
useCallback,
5+
useContext,
6+
useEffect,
7+
useState,
8+
} from "react";
9+
import { useParams } from "react-router-dom";
210

3-
import { AppState, LanguageType, SnippetType } from "@types";
4-
import { defaultCategory, defaultLanguage } from "@utils/consts";
11+
import { useLanguages } from "@hooks/useLanguages";
12+
import { AppState, CategoryType, LanguageType, SnippetType } from "@types";
13+
import { defaultLanguage } from "@utils/consts";
14+
import { slugify } from "@utils/slugify";
515

616
// TODO: add custom loading and error handling
717
const defaultState: AppState = {
8-
language: defaultLanguage,
18+
language: null as unknown as AppState["language"],
919
setLanguage: () => {},
10-
category: defaultCategory,
20+
category: null as unknown as AppState["category"],
1121
setCategory: () => {},
1222
snippet: null,
1323
setSnippet: () => {},
@@ -20,18 +30,73 @@ const AppContext = createContext<AppState>(defaultState);
2030
export const AppProvider: FC<{ children: React.ReactNode }> = ({
2131
children,
2232
}) => {
23-
const [language, setLanguage] = useState<LanguageType>(defaultLanguage);
24-
const [category, setCategory] = useState<string>(defaultCategory);
33+
const { languageName, categoryName } = useParams();
34+
35+
const { fetchedLanguages } = useLanguages();
36+
37+
const [language, setLanguage] = useState<LanguageType | null>(null);
38+
const [category, setCategory] = useState<string | null>(null);
2539
const [snippet, setSnippet] = useState<SnippetType | null>(null);
2640
const [searchText, setSearchText] = useState<string>("");
2741

42+
const assignLanguage = useCallback(() => {
43+
if (fetchedLanguages.length === 0) {
44+
return;
45+
}
46+
47+
const language = fetchedLanguages.find(
48+
(lang) => slugify(lang.lang) === languageName
49+
);
50+
if (!language) {
51+
setLanguage(defaultLanguage);
52+
return;
53+
}
54+
setLanguage(language);
55+
}, [fetchedLanguages, languageName]);
56+
57+
const assignCategory = useCallback(async () => {
58+
if (!language) {
59+
return;
60+
}
61+
62+
let category: CategoryType | undefined;
63+
try {
64+
const res = await fetch(`/consolidated/${slugify(language.lang)}.json`);
65+
const data: CategoryType[] = await res.json();
66+
category = data.find(
67+
(item: { categoryName: string }) =>
68+
slugify(item.categoryName) === categoryName
69+
);
70+
if (!category) {
71+
setCategory(data[0].categoryName);
72+
return;
73+
}
74+
setCategory(category.categoryName);
75+
} catch (_error) {
76+
// no-op
77+
}
78+
}, [language, categoryName]);
79+
80+
useEffect(() => {
81+
assignLanguage();
82+
}, [assignLanguage, languageName]);
83+
84+
useEffect(() => {
85+
assignCategory();
86+
}, [assignCategory, categoryName]);
87+
88+
if (!language || !category) {
89+
return <div>Loading...</div>;
90+
}
91+
92+
console.log({ language });
2893
return (
2994
<AppContext.Provider
3095
value={{
3196
language,
32-
setLanguage,
97+
setLanguage: setLanguage as AppState["setLanguage"],
3398
category,
34-
setCategory,
99+
setCategory: setCategory as AppState["setCategory"],
35100
snippet,
36101
setSnippet,
37102
searchText,

src/hooks/useCategories.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useMemo } from "react";
22

33
import { useAppContext } from "@contexts/AppContext";
44
import { SnippetType } from "@types";
5-
import { slugify } from "@utils/helpers/slugify";
5+
import { slugify } from "@utils/slugify";
66

77
import { useFetch } from "./useFetch";
88

@@ -13,6 +13,7 @@ type CategoryData = {
1313

1414
export const useCategories = () => {
1515
const { language } = useAppContext();
16+
console.log(language);
1617
const { data, loading, error } = useFetch<CategoryData[]>(
1718
`/consolidated/${slugify(language.lang)}.json`
1819
);

src/hooks/useSnippets.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import { useMemo } from "react";
22

33
import { useAppContext } from "@contexts/AppContext";
44
import { SnippetType } from "@types";
5-
import { defaultCategory } from "@utils/consts";
6-
import { slugify } from "@utils/helpers/slugify";
5+
import { slugify } from "@utils/slugify";
76

87
import { useFetch } from "./useFetch";
98

@@ -14,6 +13,7 @@ type CategoryData = {
1413

1514
export const useSnippets = () => {
1615
const { language, category, searchText } = useAppContext();
16+
// console.log({ language, category });
1717
const { data, loading, error } = useFetch<CategoryData[]>(
1818
`/consolidated/${slugify(language.lang)}.json`
1919
);
@@ -23,16 +23,16 @@ export const useSnippets = () => {
2323
return [];
2424
}
2525

26-
if (category === defaultCategory) {
27-
if (searchText) {
28-
return data
29-
.flatMap((item) => item.snippets)
30-
.filter((item) =>
31-
item.title.toLowerCase().includes(searchText.toLowerCase())
32-
);
33-
}
34-
return data.flatMap((item) => item.snippets);
35-
}
26+
// if (category === defaultCategory) {
27+
// if (searchText) {
28+
// return data
29+
// .flatMap((item) => item.snippets)
30+
// .filter((item) =>
31+
// item.title.toLowerCase().includes(searchText.toLowerCase())
32+
// );
33+
// }
34+
// return data.flatMap((item) => item.snippets);
35+
// }
3636

3737
if (searchText) {
3838
return data

src/main.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,12 @@ import { StrictMode } from "react";
44
import { createRoot } from "react-dom/client";
55
import { BrowserRouter } from "react-router-dom";
66

7-
import AppRouter from "@appRouter";
8-
import { AppProvider } from "@contexts/AppContext";
7+
import AppRouter from "@AppRouter";
98

109
createRoot(document.getElementById("root")!).render(
1110
<StrictMode>
12-
<AppProvider>
13-
<BrowserRouter>
14-
<AppRouter />
15-
</BrowserRouter>
16-
</AppProvider>
11+
<BrowserRouter>
12+
<AppRouter />
13+
</BrowserRouter>
1714
</StrictMode>
1815
);

0 commit comments

Comments
 (0)