diff --git a/src/App.tsx b/src/App.tsx
index fc2d1278..9ca9b6dd 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,29 +1,24 @@
-import { useAppContext } from "./contexts/AppContext";
-
+import { Routes, Route, BrowserRouter } from "react-router-dom";
import Header from "./layouts/Header";
import Banner from "./layouts/Banner";
-import Sidebar from "./layouts/Sidebar";
import Footer from "./layouts/Footer";
-
-import SnippetList from "./components/SnippetList";
+import HomePage from "./pages/HomePage.tsx";
+import SearchPage from "./pages/SearchPage.tsx";
const App = () => {
- const { category } = useAppContext();
-
return (
-
-
-
-
-
-
- {category ? category : "Select a category"}
-
-
-
-
-
+
+
+
+
+
+ } />
+ } />
+
+
+
+
);
};
diff --git a/src/components/SearchFilters.tsx b/src/components/SearchFilters.tsx
new file mode 100644
index 00000000..40a44358
--- /dev/null
+++ b/src/components/SearchFilters.tsx
@@ -0,0 +1,22 @@
+import { useCategories } from "../hooks/useCategories";
+import { useAppContext } from "../contexts/AppContext";
+
+const SearchFilters = () => {
+ const { category, setCategory } = useAppContext();
+ const { fetchedCategories } = useCategories();
+
+ return (
+
+
+
+ );
+};
+
+export default SearchFilters;
diff --git a/src/components/SearchInput.tsx b/src/components/SearchInput.tsx
index 03ed5f90..c760cdf6 100644
--- a/src/components/SearchInput.tsx
+++ b/src/components/SearchInput.tsx
@@ -1,18 +1,63 @@
import { SearchIcon } from "./Icons";
+import { useState, useCallback } from "react";
+import { useSearchParams, useNavigate, useLocation } from "react-router-dom";
const SearchInput = () => {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const [searchParams, setSearchParams] = useSearchParams();
+ const [searchValue, setSearchValue] = useState(searchParams.get("q") || "");
+
+ const navigateToSearch = useCallback(
+ (query: string, isCompletedSearch = false) => {
+ const trimmedQuery = query.trim().toLowerCase();
+
+ if (!trimmedQuery) {
+ // Remove search params and navigate to home if query is empty
+ setSearchParams({});
+ navigate("/", { replace: true });
+ return;
+ }
+
+ // Set the search params with the query
+ // Use replace: true for keypresses (when isCompletedSearch is false)
+ setSearchParams({ q: trimmedQuery }, { replace: !isCompletedSearch });
+
+ // Only navigate if we're not already on the search page
+ if (location.pathname !== "/search") {
+ navigate("/search", {
+ replace: isCompletedSearch || location.pathname === "/search",
+ });
+ }
+ },
+ [navigate, location.pathname, setSearchParams]
+ );
+
return (
-
+
+
);
};
diff --git a/src/components/SnippetList.tsx b/src/components/SnippetList.tsx
index 6b4a5ce7..f1756f9e 100644
--- a/src/components/SnippetList.tsx
+++ b/src/components/SnippetList.tsx
@@ -1,22 +1,25 @@
-import { useState } from "react";
+import { useState, useMemo } from "react";
import { SnippetType } from "../types";
import { useAppContext } from "../contexts/AppContext";
import { useSnippets } from "../hooks/useSnippets";
import SnippetModal from "./SnippetModal";
-import { LeftAngleArrowIcon } from "./Icons";
-const SnippetList = () => {
+const SnippetList = ({ query }: { query?: string | null }) => {
const { language, snippet, setSnippet } = useAppContext();
- const { fetchedSnippets } = useSnippets();
+ const { fetchedSnippets, loading } = useSnippets();
const [isModalOpen, setIsModalOpen] = useState(false);
- if (!fetchedSnippets)
- return (
-
-
-
+ const filteredSnippets = useMemo(() => {
+ if (!query) return fetchedSnippets;
+ return fetchedSnippets.filter((snippet) =>
+ snippet.title.toLowerCase().includes(query.toLowerCase())
);
+ }, [fetchedSnippets, query]);
+
+ if (loading) return Loading...
;
+ if (!filteredSnippets || filteredSnippets.length === 0)
+ return No results found for "{query}"
;
const handleOpenModal = (activeSnippet: SnippetType) => {
setIsModalOpen(true);
@@ -31,7 +34,7 @@ const SnippetList = () => {
return (
<>
- {fetchedSnippets.map((snippet, idx) => (
+ {filteredSnippets.map((snippet, idx) => (
-
))}
diff --git a/src/hooks/useCategories.ts b/src/hooks/useCategories.ts
index aea09b89..08518b36 100644
--- a/src/hooks/useCategories.ts
+++ b/src/hooks/useCategories.ts
@@ -16,7 +16,8 @@ export const useCategories = () => {
);
const fetchedCategories = useMemo(() => {
- return data ? data.map((item) => item.categoryName) : [];
+ const categories = data ? data.map((item) => item.categoryName) : [];
+ return ["All snippets", ...categories];
}, [data]);
return { fetchedCategories, loading, error };
diff --git a/src/hooks/useSnippets.ts b/src/hooks/useSnippets.ts
index 9d14e46b..4dbd2206 100644
--- a/src/hooks/useSnippets.ts
+++ b/src/hooks/useSnippets.ts
@@ -8,15 +8,28 @@ type CategoryData = {
snippets: SnippetType[];
};
+const getSnippetsFromData = (
+ data: CategoryData[] | null,
+ category: string
+): SnippetType[] => {
+ if (!data?.length) {
+ return [];
+ }
+
+ if (category === "All snippets") {
+ return data.flatMap((item) => item.snippets);
+ }
+
+ const categoryData = data.find((item) => item.categoryName === category);
+ return categoryData?.snippets ?? [];
+};
+
export const useSnippets = () => {
const { language, category } = useAppContext();
- const { data, loading, error } = useFetch(
- `/data/${slugify(language.lang)}.json`
- );
+ const endpoint = `/data/${slugify(language.lang)}.json`;
+ const { data, loading, error } = useFetch(endpoint);
- const fetchedSnippets = data
- ? data.find((item) => item.categoryName === category)?.snippets
- : [];
+ const fetchedSnippets = getSnippetsFromData(data, category);
return { fetchedSnippets, loading, error };
};
diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx
new file mode 100644
index 00000000..628301db
--- /dev/null
+++ b/src/pages/HomePage.tsx
@@ -0,0 +1,21 @@
+import { useAppContext } from "../contexts/AppContext";
+import SnippetList from "../components/SnippetList";
+import Sidebar from "../layouts/Sidebar";
+
+const HomePage = () => {
+ const { category } = useAppContext();
+
+ return (
+ <>
+
+
+
+ {category ? category : "Select a category"}
+
+
+
+ >
+ );
+};
+
+export default HomePage;
diff --git a/src/pages/SearchPage.tsx b/src/pages/SearchPage.tsx
new file mode 100644
index 00000000..a4b7f2d0
--- /dev/null
+++ b/src/pages/SearchPage.tsx
@@ -0,0 +1,20 @@
+import { useSearchParams } from "react-router-dom";
+import SnippetList from "../components/SnippetList";
+import Sidebar from "../layouts/Sidebar";
+
+const SearchPage = () => {
+ const [searchParams] = useSearchParams();
+ const query = searchParams.get("q");
+
+ return (
+ <>
+
+
+ Search Results for: {query}
+
+
+ >
+ );
+};
+
+export default SearchPage;
diff --git a/src/styles/main.css b/src/styles/main.css
index 576e5a66..8800e4fa 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -528,6 +528,21 @@ abbr {
.snippet__title {
color: var(--text-primary);
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 1;
+ line-clamp: 1;
+ overflow: hidden;
+ text-overflow: "…";
+}
+
+.snippet__description {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 1;
+ line-clamp: 1;
+ overflow: hidden;
+ text-overflow: "…";
}
/*------------------------------------*\