2022-02-13 18:48:52 +01:00
|
|
|
import { useRef, useState, useEffect } from "react";
|
|
|
|
import "./SelectBox.css";
|
2021-10-25 23:41:42 +02:00
|
|
|
|
2022-01-03 13:30:41 +01:00
|
|
|
function Option({ option, ...props }) {
|
2022-02-13 18:48:52 +01:00
|
|
|
return (
|
|
|
|
<div className="option" {...props}>
|
|
|
|
<input type="radio" className="radio" id={option.id} />
|
|
|
|
<label htmlFor={option.id}>
|
|
|
|
<div className="item">{option.name}</div>
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
);
|
2021-10-25 23:41:42 +02:00
|
|
|
}
|
|
|
|
|
2021-10-26 14:17:11 +02:00
|
|
|
export function SelectBox({ options, selectedItem, setSelectedItem }) {
|
2022-02-13 18:48:52 +01:00
|
|
|
if (!Array.isArray(options)) {
|
|
|
|
throw new Error("Items must be an array!");
|
|
|
|
}
|
2021-10-25 23:41:42 +02:00
|
|
|
|
2022-02-13 18:48:52 +01:00
|
|
|
const [active, setActive] = useState(false);
|
2021-10-25 23:41:42 +02:00
|
|
|
|
2022-02-13 18:48:52 +01:00
|
|
|
const containerRef = useRef();
|
2021-10-25 23:41:42 +02:00
|
|
|
|
2022-02-13 18:48:52 +01:00
|
|
|
const handleClick = (e) => {
|
|
|
|
if (containerRef.current.contains(e.target)) {
|
|
|
|
// inside click
|
|
|
|
return;
|
2021-10-25 23:41:42 +02:00
|
|
|
}
|
2022-02-13 18:48:52 +01:00
|
|
|
// outside click
|
|
|
|
closeDropdown();
|
|
|
|
};
|
2021-10-25 23:41:42 +02:00
|
|
|
|
2022-02-13 18:48:52 +01:00
|
|
|
const closeDropdown = () => {
|
|
|
|
setActive(false);
|
|
|
|
};
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
// add when mounted
|
|
|
|
document.addEventListener("mousedown", handleClick);
|
|
|
|
// return function to be called when unmounted
|
|
|
|
return () => {
|
|
|
|
document.removeEventListener("mousedown", handleClick);
|
|
|
|
};
|
2021-10-26 14:29:04 +02:00
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2022-02-13 18:48:52 +01:00
|
|
|
}, []);
|
2021-10-25 23:41:42 +02:00
|
|
|
|
2022-02-13 18:48:52 +01:00
|
|
|
const onOptionClick = (e, option, i) => {
|
|
|
|
e.stopPropagation();
|
|
|
|
setSelectedItem(i);
|
|
|
|
closeDropdown();
|
|
|
|
};
|
2021-10-25 23:41:42 +02:00
|
|
|
|
2022-02-13 18:48:52 +01:00
|
|
|
const handleSelectedKeyPress = (event) => {
|
|
|
|
if (event.code === "Enter" || event.code === "Space") {
|
|
|
|
setActive((a) => !a);
|
2022-01-03 13:30:41 +01:00
|
|
|
}
|
2022-02-13 18:48:52 +01:00
|
|
|
};
|
2022-01-03 13:30:41 +01:00
|
|
|
|
2022-02-13 18:48:52 +01:00
|
|
|
const handleOptionKeyPress = (option, i) => (event) => {
|
|
|
|
if (event.code === "Enter" || event.code === "Space") {
|
|
|
|
onOptionClick(event, option, i);
|
2022-01-03 13:30:41 +01:00
|
|
|
}
|
2022-02-13 18:48:52 +01:00
|
|
|
};
|
2022-01-03 13:30:41 +01:00
|
|
|
|
2022-02-13 18:48:52 +01:00
|
|
|
return (
|
|
|
|
<div
|
|
|
|
className="select-box"
|
|
|
|
ref={containerRef}
|
|
|
|
onClick={() => setActive((a) => !a)}
|
|
|
|
>
|
|
|
|
<div
|
|
|
|
className="selected"
|
|
|
|
tabIndex={0}
|
|
|
|
onKeyPress={handleSelectedKeyPress}
|
|
|
|
>
|
|
|
|
{options ? <Option option={options[selectedItem]} /> : null}
|
|
|
|
</div>
|
|
|
|
<div className={"options-container" + (active ? " active" : "")}>
|
|
|
|
{options.map((opt, i) => (
|
|
|
|
<Option
|
|
|
|
option={opt}
|
|
|
|
key={i}
|
|
|
|
onClick={(e) => onOptionClick(e, opt, i)}
|
|
|
|
tabIndex={active ? 0 : undefined}
|
|
|
|
onKeyPress={active ? handleOptionKeyPress(opt, i) : undefined}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
2021-10-26 14:25:34 +02:00
|
|
|
}
|