From df05240f9517d4072f211d9d4ee1c2f00e3fe723 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 18 Dec 2025 07:16:02 -0600 Subject: [PATCH] add generic multi select dialog --- .../overlay/dialog/MultiSelectDialog.tsx | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 web/src/components/overlay/dialog/MultiSelectDialog.tsx diff --git a/web/src/components/overlay/dialog/MultiSelectDialog.tsx b/web/src/components/overlay/dialog/MultiSelectDialog.tsx new file mode 100644 index 000000000..b30ac9cf5 --- /dev/null +++ b/web/src/components/overlay/dialog/MultiSelectDialog.tsx @@ -0,0 +1,96 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { cn } from "@/lib/utils"; +import { useState } from "react"; +import { isMobile } from "react-device-detect"; +import { useTranslation } from "react-i18next"; +import FilterSwitch from "@/components/filter/FilterSwitch"; + +type MultiSelectDialogProps = { + open: boolean; + title: string; + description?: string; + setOpen: (open: boolean) => void; + onSave: (selectedItems: string[]) => void; + selectedItems: string[]; + availableItems: string[]; + allowEmpty?: boolean; +}; + +export default function MultiSelectDialog({ + open, + title, + description, + setOpen, + onSave, + selectedItems = [], + availableItems = [], + allowEmpty = false, +}: MultiSelectDialogProps) { + const { t } = useTranslation("common"); + const [internalSelection, setInternalSelection] = + useState(selectedItems); + + // Reset internal selection when dialog opens + const handleOpenChange = (isOpen: boolean) => { + if (isOpen) { + setInternalSelection(selectedItems); + } + setOpen(isOpen); + }; + + const toggleItem = (item: string) => { + setInternalSelection((prev) => + prev.includes(item) ? prev.filter((i) => i !== item) : [...prev, item], + ); + }; + + const handleSave = () => { + if (!allowEmpty && internalSelection.length === 0) { + return; + } + onSave(internalSelection); + setOpen(false); + }; + + return ( + + + + {title} + {description && {description}} + +
+ {availableItems.map((item) => ( + toggleItem(item)} + /> + ))} +
+ + + + +
+
+ ); +}