Untitled
unknown
javascript
3 years ago
4.7 kB
5
Indexable
import React, { useCallback, useEffect, useRef, useState } from "react"; import styles from "./AutosuggestionSelect.module.css"; const AutosuggestionSelect = ({ title, endpoint, onItemSelect, propertyKey, toggle, }) => { const [openBox, setOpenBox] = useState(false); const [rawOptions, setRawOptions] = useState([]); const [selectedItems, setSelectedItems] = useState([]); const [optionsToDisplay, setOptionsToDisplay] = useState([]); const [search, setSearch] = useState(""); const wrapperRef = useRef(null); const searchInputRef = useRef(); const prepareOptions = () => { if (!selectedItems?.length) setOptionsToDisplay(rawOptions); if (!rawOptions?.length) setOptionsToDisplay(selectedItems); const preparedOptions = rawOptions?.filter( (option) => !selectedItems.includes(option) ); setOptionsToDisplay([...selectedItems, ...preparedOptions]); }; const debounce = (fn, timeout = 350) => { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { fn(...args); }, timeout); }; }; const onHide = () => { setRawOptions([]); setSearch([]); setOpenBox(false); }; const handleResetButtonClick = () => { setSelectedItems([]); onItemSelect("", []); }; const onMainButtonClick = () => { setOpenBox((prev) => !prev); if (openBox) onHide(); }; const getData = (query, propertyKeyArg) => { if (!query.length) return setRawOptions([]); fetch(endpoint + query) .then((response) => response.json()) .then((data) => { const mappedData = data.map((item) => item[propertyKeyArg]); setRawOptions(mappedData); }); }; const getDebouncedData = useCallback( debounce((search, propertyKey) => getData(search, propertyKey)), [] ); const handleOptionClick = (e) => { const item = e.target.innerHTML; setSelectedItems((prev) => { const selectedItemsArray = prev.includes(item) ? prev.filter((i) => i !== item) : [...prev, item]; onItemSelect(item, selectedItemsArray); return selectedItemsArray; }); }; const handleOutsideClick = (e) => { if (e.target.className.includes("AutosuggestionSelect_optionItem")) return; if (wrapperRef.current && !wrapperRef.current.contains(e.target)) { onHide(); } }; const handleEscape = (e) => { if (e.key && e.key === "Escape") onHide(); }; useEffect(() => { if (searchInputRef.current) searchInputRef.current.focus(); }, [openBox]); useEffect(() => { getDebouncedData(search, propertyKey); }, [search]); useEffect(() => { prepareOptions(); }, [rawOptions, selectedItems]); useEffect(() => { document.addEventListener("click", handleOutsideClick); document.addEventListener("keydown", handleEscape); return () => { document.removeEventListener("click", handleOutsideClick); document.removeEventListener("keydown", handleEscape); }; }, []); return ( <> <div ref={wrapperRef}> <div className={styles.title} onClick={onMainButtonClick}> {title}{" "} <span className={styles.counter}>{selectedItems.length} </span> </div> {openBox && ( <div className={styles.selectWrapper}> <div className={styles.inputContainer}> <input ref={searchInputRef} placeholder="Search..." className={styles.inputField} type="text" value={search} onChange={(e) => { setSearch(e.target.value); }} /> </div> <div className={styles.optionsContainer}> {optionsToDisplay.map((option, idx) => ( <div onClick={handleOptionClick} key={idx} className={`${styles.optionItem} ${ selectedItems.includes(option) ? styles.optionSelected : null }`} > {option} </div> ))} </div> {selectedItems.length ? ( <button onClick={handleResetButtonClick} className={styles.resetButton} > Reset </button> ) : null} </div> )} </div> </> ); }; export default AutosuggestionSelect;
Editor is loading...