Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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: nullasunknownasAppState["language"],
919
setLanguage: () => {},
10-
category: defaultCategory,
20+
category: nullasunknownasAppState["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: setLanguageasAppState["setLanguage"],
3398
category,
34-
setCategory,
99+
setCategory: setCategoryasAppState["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)

AltStyle によって変換されたページ (->オリジナル) /