Refactor Dropdown component: integrate useOutsideClick for improved click handling, manage dropdown state with useRef and useState, and enhance button functionality for better user experience.
This commit is contained in:
		
							parent
							
								
									5a50387685
								
							
						
					
					
						commit
						52d032d518
					
				|  | @ -1,5 +1,6 @@ | ||||||
| "use client"; | "use client"; | ||||||
| import { FC, useEffect, useState } from "react"; | import { useOutsideClick } from "@/hooks/useOutsideClick"; | ||||||
|  | import { FC, useEffect, useRef, useState } from "react"; | ||||||
| import { FaFilter } from "react-icons/fa"; | import { FaFilter } from "react-icons/fa"; | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|  | @ -12,19 +13,31 @@ type Props = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const Dropdown: FC<Props> = ({ items, defaultValue, callback }) => { | export const Dropdown: FC<Props> = ({ items, defaultValue, callback }) => { | ||||||
|  |   const ref = useRef<HTMLDivElement>(null); | ||||||
|  |   const [isOpen, setIsOpen] = useState(false); | ||||||
|   const [value, setValue] = useState<string>(defaultValue); |   const [value, setValue] = useState<string>(defaultValue); | ||||||
| 
 | 
 | ||||||
|  |   useOutsideClick(ref, () => setIsOpen(false)); | ||||||
|  | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     callback?.(value); |     callback?.(value); | ||||||
|   }, [value]); |   }, [value]); | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div className="relative inline-block"> |     <div ref={ref} className="relative inline-block"> | ||||||
|       <button className="focus:[&+div]:opacity-100 relative z-10 block p-2 shadow-sm cursor-pointer bg-white text-primary rounded-md"> |       <button | ||||||
|  |         onClick={() => setIsOpen(!isOpen)} | ||||||
|  |         className="relative z-10 block p-2 shadow-sm cursor-pointer bg-white text-primary rounded-md" | ||||||
|  |       > | ||||||
|         <FaFilter /> |         <FaFilter /> | ||||||
|       </button> |       </button> | ||||||
| 
 | 
 | ||||||
|       <div className="absolute left-0 z-20 w-48 py-2 mt-2 origin-top-right bg-white rounded-md shadow-xl dark:bg-gray-800 opacity-0"> |       <div | ||||||
|  |         className="absolute left-0 z-20 w-48 py-2 mt-2 origin-top-right bg-white rounded-md shadow-xl dark:bg-gray-800" | ||||||
|  |         style={{ | ||||||
|  |           opacity: isOpen ? 1 : 0, | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|         {items.map((item) => ( |         {items.map((item) => ( | ||||||
|           <p |           <p | ||||||
|             key={item.value} |             key={item.value} | ||||||
|  |  | ||||||
|  | @ -58,7 +58,7 @@ export const MovieCard: FC<Props> = ({ layout = "default", ...movie }) => { | ||||||
|       <article className="flex flex-col w-full shadow-lg rounded-t-lg overflow-hidden bg-black/50 shadow-white/5"> |       <article className="flex flex-col w-full shadow-lg rounded-t-lg overflow-hidden bg-black/50 shadow-white/5"> | ||||||
|         <figure className="relative "> |         <figure className="relative "> | ||||||
|           <img |           <img | ||||||
|             className="w-full object-cover xl:h-[420px]" |             className="w-full object-cover h-[285px] xl:h-[420px]" | ||||||
|             src={`http://image.tmdb.org/t/p/w342${poster_path}`} |             src={`http://image.tmdb.org/t/p/w342${poster_path}`} | ||||||
|           /> |           /> | ||||||
|           <span className="absolute inset-0 bg-black/30 backdrop-blur-md opacity-0 hover-any:opacity-100 transition-opacity duration-300 flex items-center justify-center cursor-pointer"> |           <span className="absolute inset-0 bg-black/30 backdrop-blur-md opacity-0 hover-any:opacity-100 transition-opacity duration-300 flex items-center justify-center cursor-pointer"> | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import { SearchResult } from "@/lib/tmdb/types"; | ||||||
| import { FC } from "react"; | import { FC } from "react"; | ||||||
| import { useEffect } from "react"; | import { useEffect } from "react"; | ||||||
| import { Pagination } from "@/components/atoms/Pagination"; | import { Pagination } from "@/components/atoms/Pagination"; | ||||||
|  | import { MovieList } from "../MovieList"; | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|   query: string; |   query: string; | ||||||
|  | @ -44,18 +45,16 @@ export const SearchList: FC<Props> = ({ query }) => { | ||||||
|         <p className="text-sm text-gray-500"> |         <p className="text-sm text-gray-500"> | ||||||
|           {total_results} movies found for your search |           {total_results} movies found for your search | ||||||
|         </p> |         </p> | ||||||
|         <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6 mt-8"> | 
 | ||||||
|           {results?.map((result) => ( |         <MovieList | ||||||
|             <MovieCard |           overrideMovies={results?.map((m) => ({ | ||||||
|               key={result.id} |             ...m, | ||||||
|               layout="zeus" |             favorite: false, | ||||||
|               {...result} |             seen: false, | ||||||
|               genre_ids={result.genre_ids.join(",")} |             genre_ids: JSON.stringify(m.genre_ids), | ||||||
|               seen={false} |           }))} | ||||||
|               favorite={false} |           fluid | ||||||
|             /> |         /> | ||||||
|           ))} |  | ||||||
|         </div> |  | ||||||
|         <Pagination |         <Pagination | ||||||
|           totalPages={total_pages} |           totalPages={total_pages} | ||||||
|           currentPage={page} |           currentPage={page} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue