Untitled
unknown
javascript
4 years ago
4.7 kB
7
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...