2024-07-29 11:48:55 +02:00

64 lines
1.8 KiB
TypeScript

import classNames from "classnames";
import React, { useCallback, useEffect, useRef } from "react";
import classes from "./classes.module.scss";
import DropdownOption, { IOption } from "./DropdownOption";
type IProps = {
options: IOption[];
selectedOptions: IOption[];
children: React.ReactNode;
openable: {
isOpen: boolean;
open: () => void;
close: () => void;
toggle: () => void;
};
onSelect?: (newOption: IOption, options: IOption[]) => void;
};
export default function DropdownMenu(props: IProps) {
const { children, options, onSelect, openable, selectedOptions } = props;
const ref = useRef<HTMLDivElement>(null);
const handleSelect = useCallback(
(option: IOption) => {
const newOptions = selectedOptions.some((selectedOption) => selectedOption.id === option.id)
? selectedOptions
: [...selectedOptions, option];
onSelect?.(option, newOptions);
openable.close();
},
[onSelect, openable, selectedOptions],
);
const handleClickOutside = useCallback(
(event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
openable.close();
}
},
[openable],
);
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, [handleClickOutside]);
return (
<div className={classNames([classes["root"], openable.isOpen && classes["open"]])} ref={ref}>
{children}
<div className={classes["content"]}>
{options.map((option) => {
return <DropdownOption key={option.id} option={option} onClick={handleSelect} isActive={isActive(option)} />;
})}
</div>
</div>
);
function isActive(option: IOption): boolean {
return selectedOptions.some((selectedOption) => selectedOption.id === option.id);
}
}