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 (
+
+ setCategory(e.target.value)}>
+ All Categories
+ {fetchedCategories.map((cat, idx) => (
+
+ {cat}
+
+ ))}
+
+
+ );
+};
+
+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) => (
{
-
{snippet.title}
+ {snippet.description}
))}
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: "…";
}
/*------------------------------------*\
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy