Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature - Implement basic search functionality v2 #159

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions cspell-dict.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
quicksnip
slugified
slugifyed
69 changes: 46 additions & 23 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"prismjs": "^1.29.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.27.0",
"react-router-dom": "^7.1.1",
"react-syntax-highlighter": "^15.6.1"
},
"devDependencies": {
Expand Down
18 changes: 18 additions & 0 deletions src/AppRouter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Route, Routes } from "react-router-dom";

import App from "@components/App";
import SnippetList from "@components/SnippetList";

const AppRouter = () => {
return (
<Routes>
<Route element={<App />}>
<Route path="/" element={<SnippetList />} />
<Route path="/:languageName" element={<SnippetList />} />
<Route path="/:languageName/:categoryName" element={<SnippetList />} />
</Route>
</Routes>
);
};

export default AppRouter;
17 changes: 17 additions & 0 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { FC } from "react";

import { AppProvider } from "@contexts/AppContext";

import Container from "./Container";

interface AppProps {}

const App: FC<AppProps> = () => {
return (
<AppProvider>
<Container />
</AppProvider>
);
};

export default App;
56 changes: 38 additions & 18 deletions src/components/CategoryList.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,53 @@
import { useEffect } from "react";
import { FC } from "react";
import { useNavigate } from "react-router-dom";

import { useAppContext } from "@contexts/AppContext";
import { useCategories } from "@hooks/useCategories";
import { slugify } from "@utils/slugify";

interface CategoryListItemProps {
name: string;
}

const CategoryListItem: FC<CategoryListItemProps> = ({ name }) => {
const navigate = useNavigate();

const { language, category, setCategory } = useAppContext();

const handleSelect = () => {
setCategory(name);
navigate(`/${slugify(language.name)}/${slugify(name)}`);
};

return (
<li className="category">
<button
className={`category__btn ${
slugify(name) === slugify(category) ? "category__btn--active" : ""
}`}
onClick={handleSelect}
>
{name}
</button>
</li>
);
};

const CategoryList = () => {
const { category, setCategory } = useAppContext();
const { fetchedCategories, loading, error } = useCategories();

useEffect(() => {
setCategory(fetchedCategories[0]);
}, [setCategory, fetchedCategories]);

if (loading) return <div>Loading...</div>;
if (loading) {
return <div>Loading...</div>;
}

if (error) return <div>Error occurred: {error}</div>;
if (error) {
return <div>Error occurred: {error}</div>;
}

return (
<ul role="list" className="categories">
{fetchedCategories.map((name, idx) => (
<li key={idx} className="category">
<button
className={`category__btn ${
name === category ? "category__btn--active" : ""
}`}
onClick={() => setCategory(name)}
>
{name}
</button>
</li>
<CategoryListItem key={idx} name={name} />
))}
</ul>
);
Expand Down
12 changes: 8 additions & 4 deletions src/App.tsx → src/components/Container.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import SnippetList from "@components/SnippetList";
import { FC } from "react";
import { Outlet } from "react-router-dom";

import { useAppContext } from "@contexts/AppContext";
import Banner from "@layouts/Banner";
import Footer from "@layouts/Footer";
import Header from "@layouts/Header";
import Sidebar from "@layouts/Sidebar";

const App = () => {
interface ContainerProps {}

const Container: FC<ContainerProps> = () => {
const { category } = useAppContext();

return (
Expand All @@ -18,12 +22,12 @@ const App = () => {
<h2 className="section-title">
{category ? category : "Select a category"}
</h2>
<SnippetList />
<Outlet />
</section>
</main>
<Footer />
</div>
);
};

export default App;
export default Container;
33 changes: 26 additions & 7 deletions src/components/LanguageSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
/**
* Inspired by https://blog.logrocket.com/creating-custom-select-dropdown-css/
*/

import { useRef, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";

import { useAppContext } from "@contexts/AppContext";
import { useKeyboardNavigation } from "@hooks/useKeyboardNavigation";
import { useLanguages } from "@hooks/useLanguages";
import { LanguageType } from "@types";

// Inspired by https://blog.logrocket.com/creating-custom-select-dropdown-css/
import { configureProfile } from "@utils/configureProfile";
import { slugify } from "@utils/slugify";

const LanguageSelector = () => {
const { language, setLanguage } = useAppContext();
const navigate = useNavigate();

const { language, setLanguage, setCategory } = useAppContext();
const { fetchedLanguages, loading, error } = useLanguages();

const dropdownRef = useRef<HTMLDivElement>(null);
const [isOpen, setIsOpen] = useState(false);

const handleSelect = (selected: LanguageType) => {
setLanguage(selected);
const handleSelect = async (selected: LanguageType) => {
const { language: newLanguage, category: newCategory } =
await configureProfile({
languageName: selected.name,
});

setLanguage(newLanguage);
setCategory(newCategory);
navigate(`/${slugify(newLanguage.name)}/${slugify(newCategory)}`);
setIsOpen(false);
};

Expand Down Expand Up @@ -61,8 +75,13 @@ const LanguageSelector = () => {
}
}, [isOpen, focusedIndex]);

if (loading) return <p>Loading languages...</p>;
if (error) return <p>Error fetching languages: {error}</p>;
if (loading) {
return <p>Loading languages...</p>;
}

if (error) {
return <p>Error fetching languages: {error}</p>;
}

return (
<div
Expand Down
Loading
Loading