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>
This commit is contained in:
2026-04-27 18:17:56 +02:00
parent e4f3e7750a
commit 71f4ad1792
3 changed files with 35 additions and 18 deletions

View File

@@ -54,9 +54,10 @@ export default function BeoSelector({ selected, onChange }: Props) {
{available.length > 0 && ( {available.length > 0 && (
<CustomSelect <CustomSelect
placeholder="+ BEO hinzufügen" placeholder="+ BEOs hinzufügen"
options={available.map((b) => ({ value: String(b.ID), label: `${b.Kuerzel}${b.Name}` }))} options={available.map((b) => ({ value: String(b.ID), label: `${b.Kuerzel}${b.Name}` }))}
onChange={add} onChange={add}
keepOpen
/> />
)} )}
</div> </div>

View File

@@ -12,9 +12,10 @@ interface Props {
options: SelectOption[]; options: SelectOption[];
placeholder: string; placeholder: string;
onChange: (value: string) => void; onChange: (value: string) => void;
keepOpen?: boolean;
} }
export default function CustomSelect({ options, placeholder, onChange }: Props) { export default function CustomSelect({ options, placeholder, onChange, keepOpen = false }: Props) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
@@ -29,7 +30,7 @@ export default function CustomSelect({ options, placeholder, onChange }: Props)
}, [open]); }, [open]);
function select(value: string) { function select(value: string) {
setOpen(false); if (!keepOpen) setOpen(false);
onChange(value); onChange(value);
} }
@@ -62,6 +63,15 @@ export default function CustomSelect({ options, placeholder, onChange }: Props)
{opt.label} {opt.label}
</button> </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>
)} )}
</div> </div>

View File

@@ -25,10 +25,6 @@ export default function ObjektSelector({ selected, onChange }: Props) {
const available = all.filter((o) => !selectedNames.has(o.Name.toLowerCase())); const available = all.filter((o) => !selectedNames.has(o.Name.toLowerCase()));
function add(value: string) { function add(value: string) {
if (value === 'neu') {
setShowNewInput(true);
return;
}
const obj = all.find((o) => o.ID === parseInt(value)); const obj = all.find((o) => o.ID === parseInt(value));
if (obj && !selectedNames.has(obj.Name.toLowerCase())) { if (obj && !selectedNames.has(obj.Name.toLowerCase())) {
onChange([...selected, { ID: obj.ID, Name: obj.Name }]); onChange([...selected, { ID: obj.ID, Name: obj.Name }]);
@@ -47,11 +43,6 @@ export default function ObjektSelector({ selected, onChange }: Props) {
onChange(selected.filter((o) => o.Name !== name)); onChange(selected.filter((o) => o.Name !== name));
} }
const options = [
{ value: 'neu', label: '— Neues Objekt eingeben —' },
...available.map((o) => ({ value: String(o.ID), label: o.Name })),
];
return ( return (
<div className="space-y-3"> <div className="space-y-3">
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
@@ -73,11 +64,25 @@ export default function ObjektSelector({ selected, onChange }: Props) {
))} ))}
</div> </div>
<div className="flex gap-2">
{available.length > 0 && (
<div className="flex-1">
<CustomSelect <CustomSelect
placeholder="+ Objekt hinzufügen" placeholder="+ Objekte hinzufügen"
options={options} options={available.map((o) => ({ value: String(o.ID), label: o.Name }))}
onChange={add} onChange={add}
keepOpen
/> />
</div>
)}
<button
type="button"
onClick={() => setShowNewInput((v) => !v)}
className="px-4 py-3 border-2 border-gray-400 rounded-lg bg-white text-base text-gray-700 hover:bg-gray-50 whitespace-nowrap"
>
+ Neu
</button>
</div>
{showNewInput && ( {showNewInput && (
<div className="flex gap-2"> <div className="flex gap-2">
@@ -88,6 +93,7 @@ export default function ObjektSelector({ selected, onChange }: Props) {
onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); addNew(); } }} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); addNew(); } }}
placeholder="Objektname eingeben" placeholder="Objektname eingeben"
className="flex-1 px-3 py-3 border-2 border-gray-400 rounded-lg bg-white text-base focus:border-blue-500 focus:outline-none" className="flex-1 px-3 py-3 border-2 border-gray-400 rounded-lg bg-white text-base focus:border-blue-500 focus:outline-none"
autoFocus
/> />
<button <button
type="button" type="button"
@@ -101,7 +107,7 @@ export default function ObjektSelector({ selected, onChange }: Props) {
onClick={() => { setShowNewInput(false); setNewName(''); }} onClick={() => { setShowNewInput(false); setNewName(''); }}
className="px-4 py-3 bg-gray-200 text-gray-700 text-base rounded-lg hover:bg-gray-300" className="px-4 py-3 bg-gray-200 text-gray-700 text-base rounded-lg hover:bg-gray-300"
> >
Abbrechen
</button> </button>
</div> </div>
)} )}