diff --git a/src/components/BookGrid/BookGrid.tsx b/src/components/BookGrid/BookGrid.tsx index b13a909..26c8444 100644 --- a/src/components/BookGrid/BookGrid.tsx +++ b/src/components/BookGrid/BookGrid.tsx @@ -1,6 +1,9 @@ +import { useState } from "react"; import { BookCard } from "../BookCard/BookCard"; +import { SearchBar } from "../SearchBar/SearchBar"; import styles from "./BookGrid.module.css"; import { useBooks } from "../../api/useBooks"; +import { useDebounce } from "./useDebounce"; import type { Book } from "../../api/types"; import { Pagination } from "../Pagination/Pagination"; import { SEARCH_LIMIT } from "../../api/openLibrary"; @@ -12,44 +15,66 @@ interface BookGridProps { } function BookGrid({ page, onBookSelect, onPageChange }: BookGridProps) { + const [searchQuery, setSearchQuery] = useState(""); + + const debouncedSearch = useDebounce(searchQuery, 500); + const query = debouncedSearch.trim() || "subject:fantasy"; + const { data, isPending, isError, error } = useBooks({ - query: "subject:fantasy", + query, page, }); + const searchBar = ( + + ); + if (isPending) { return ( -

- Loading books... -

+ <> + {searchBar} +

+ Loading books... +

+ ); } if (isError) { return ( -

- Error: {error.message} -

+ <> + {searchBar} +

+ Error: {error.message} +

+ ); } if (!data || data.books.length === 0) { return ( -

- No books found for this search -

+ <> + {searchBar} +

+ No books found for this search +

+ ); } return ( <> - + {searchBar} + + <> + + (value: T, delay: number): T { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect(() => { + const timeoutId = window.setTimeout(() => { + setDebouncedValue(value); + }, delay); + + return () => { + window.clearTimeout(timeoutId); + }; + }, [value, delay]); + + return debouncedValue; +} + +export { useDebounce }; diff --git a/src/components/SearchBar/SearchBar.module.css b/src/components/SearchBar/SearchBar.module.css new file mode 100644 index 0000000..ea27572 --- /dev/null +++ b/src/components/SearchBar/SearchBar.module.css @@ -0,0 +1,22 @@ +.search-bar { + display: block; + width: 100%; + max-width: 500px; + padding: 10px 16px; + margin: 80px auto 0 auto; + + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + font-size: 1rem; + font-family: inherit; + + &:hover { + border-color: var(--theme-color); + } + + @media only screen and (max-width: 600px) { + width: calc(100% - 80px); /* Adjust width to account for the margins*/ + margin: 40px 40px 0 40px; + max-width: none; + } +} diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx new file mode 100644 index 0000000..549df0b --- /dev/null +++ b/src/components/SearchBar/SearchBar.tsx @@ -0,0 +1,22 @@ +import styles from "./SearchBar.module.css"; + +interface SearchBarProps { + query: string; + onQueryChange: (newQuery: string) => void; +} + +function SearchBar({ query, onQueryChange }: SearchBarProps) { + return ( + onQueryChange(e.target.value)} + /> + ); +} + +export { SearchBar };