From 34624dd1c90997216d898d0326a47cd7fe309fbe Mon Sep 17 00:00:00 2001 From: Gabriel Borges Date: Mon, 3 Jun 2024 23:43:56 -0300 Subject: [PATCH] feat(frontend): implement reusable search input component --- frontend/src/components/Form/Search.tsx | 87 +++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 frontend/src/components/Form/Search.tsx diff --git a/frontend/src/components/Form/Search.tsx b/frontend/src/components/Form/Search.tsx new file mode 100644 index 0000000..19803c8 --- /dev/null +++ b/frontend/src/components/Form/Search.tsx @@ -0,0 +1,87 @@ +import React, { useEffect, useRef, useState } from "react"; + +export interface SearchItem { + label: string; + value: any; +} + +interface SearchComponentProps { + items: SearchItem[]; + searchLabel: string; + name: string; + onClick: (value: any) => void; +} + +const SearchComponent: React.FC = ({ items, name, searchLabel, onClick }) => { + const [query, setQuery] = useState(""); + const [isListVisible, setIsListVisible] = useState(false); + const [filteredItems, setFilteredItems] = useState([]); + const ulRef = useRef(null); + + useEffect(() => { + document.addEventListener("click", handleClickOutside, true); + + return () => { + document.removeEventListener("click", handleClickOutside, true); + }; + }, []); + + const handleClickOutside = (event: MouseEvent) => { + const isClickOutside = ulRef?.current && !ulRef.current.contains(event.target as Node); + + if (isClickOutside) { + setIsListVisible(false); + setQuery(""); + } + }; + + const handleSearch = (value: string) => { + setFilteredItems(items.filter(({ label }) => label.toLowerCase().includes(value.toLowerCase()))); + setQuery(value); + setIsListVisible(true); + }; + + const handleSelectItem = (item: SearchItem) => { + setIsListVisible(false); + onClick(item.value); + setQuery(""); + handleSearch(""); + }; + + return ( +
+ +
+ handleSearch(e.target.value)} + onFocus={() => setIsListVisible(true)} + className="w-full px-4 py-2 border rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500" + placeholder="Search..." + /> + {query && isListVisible && ( +
    + {filteredItems.length > 0 ? ( + filteredItems.map((item, index) => ( +
  • handleSelectItem(item)} + > + {item.label} +
  • + )) + ) : ( +
  • No results found
  • + )} +
+ )} +
+
+ ); +}; + +export default SearchComponent;