transcriptor-web/resources/js/Components/MultiSelect.tsx
2025-04-05 15:58:14 +02:00

87 lines
2.9 KiB
TypeScript

import React, {useEffect, useRef, useState} from "react";
interface Option {
value: string;
label: string;
}
interface MultiSelectProps {
label: string;
options: Option[];
selected: string[];
onChange: (selected: string[]) => void;
}
export const MultiSelect: React.FC<MultiSelectProps> = ({ label, options, selected, onChange }) => {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const toggleDropdown = () => setIsOpen(!isOpen);
// Close dropdown if clicked outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
// Toggle individual option
const handleOptionToggle = (value: string) => {
if (selected.includes(value)) {
onChange(selected.filter(item => item !== value));
} else {
onChange([...selected, value]);
}
};
// Display "All ..." if nothing is selected.
const buttonLabel =
selected.length === 0
? `All ${label}`
: selected
.map(val => {
const opt = options.find(o => o.value === val);
return opt ? opt.label : val;
})
.join(', ');
// Sort options so that selected items appear at the top
const sortedOptions = [...options].sort((a, b) => {
const aSelected = selected.includes(a.value);
const bSelected = selected.includes(b.value);
if (aSelected && !bSelected) return -1;
if (!aSelected && bSelected) return 1;
return 0;
});
return (
<div className="dropdown" ref={dropdownRef}>
<button onClick={toggleDropdown} className="btn m-1">
{buttonLabel}
</button>
{isOpen && (
<ul className="dropdown-content p-2 shadow bg-base-100 rounded-box w-52 max-h-60 overflow-y-auto flex flex-col">
{sortedOptions.map(opt => (
<li key={opt.value}>
<label className="cursor-pointer flex items-center">
<input
type="checkbox"
checked={selected.includes(opt.value)}
onChange={() => handleOptionToggle(opt.value)}
className="checkbox checkbox-sm checkbox-primary mr-2"
/>
{opt.label}
</label>
</li>
))}
</ul>
)}
</div>
);
};