Files
logbuch/components/CustomSelect.tsx
Reinhard X. Fürst 71f4ad1792 Add multi-select mode to BeoSelector and ObjektSelector
CustomSelect gains keepOpen prop: dropdown stays open after each
selection, closes via Fertig button or tap outside. ObjektSelector
separates Neu into its own button to keep the dropdown flow clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:17:56 +02:00

80 lines
2.5 KiB
TypeScript

'use client';
import { useEffect, useRef, useState } from 'react';
export interface SelectOption {
value: string;
label: string;
disabled?: boolean;
}
interface Props {
options: SelectOption[];
placeholder: string;
onChange: (value: string) => void;
keepOpen?: boolean;
}
export default function CustomSelect({ options, placeholder, onChange, keepOpen = false }: Props) {
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleOutside(e: MouseEvent) {
if (ref.current && !ref.current.contains(e.target as Node)) {
setOpen(false);
}
}
if (open) document.addEventListener('mousedown', handleOutside);
return () => document.removeEventListener('mousedown', handleOutside);
}, [open]);
function select(value: string) {
if (!keepOpen) setOpen(false);
onChange(value);
}
return (
<div ref={ref} className="relative">
<button
type="button"
onClick={() => setOpen((v) => !v)}
className="w-full flex items-center justify-between px-4 py-3 border-2 border-gray-400 rounded-lg bg-white text-base text-gray-700 focus:border-blue-500 focus:outline-none"
>
<span>{placeholder}</span>
<svg
className={`w-5 h-5 text-gray-500 transition-transform ${open ? 'rotate-180' : ''}`}
fill="none" viewBox="0 0 24 24" stroke="currentColor"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{open && (
<div className="absolute z-50 left-0 right-0 mt-1 bg-white border-2 border-gray-400 rounded-lg shadow-lg max-h-72 overflow-y-auto">
{options.map((opt) => (
<button
key={opt.value}
type="button"
disabled={opt.disabled}
onClick={() => select(opt.value)}
className="w-full text-left px-4 py-3 text-base hover:bg-blue-50 active:bg-blue-100 border-b border-gray-100 last:border-0 disabled:text-gray-400 disabled:bg-gray-50"
>
{opt.label}
</button>
))}
{keepOpen && (
<button
type="button"
onClick={() => setOpen(false)}
className="w-full px-4 py-3 text-base font-medium text-center bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-b-lg border-t-2 border-gray-300"
>
Fertig
</button>
)}
</div>
)}
</div>
);
}