"use client"; import { useEffect, useId, useRef, useState } from "react"; import { AnimatePresence, motion } from "framer-motion"; import { IoFilterOutline } from "react-icons/io5"; import { useClickOutside } from "@/hooks/useClickOutside"; import { IconButton } from "@/components/IconButton"; export type FilterGroup = { label: string; param: string; options: { value: string; label: string }[]; }; type Props = { groups: FilterGroup[]; values: Record; onChange: (param: string, selected: string[]) => void; menuClassName?: string; }; export function FilterDropdown({ groups, values, onChange, menuClassName }: Props) { const [open, setOpen] = useState(false); const buttonRef = useRef(null); const menuRef = useRef(null); const menuId = useId(); const activeCount = Object.values(values).reduce((sum, arr) => sum + arr.length, 0); useClickOutside([buttonRef, menuRef], () => setOpen(false), open); useEffect(() => { if (!open) return; const onKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") setOpen(false); }; window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }, [open]); const toggle = (param: string, value: string) => { const current = values[param] ?? []; const next = current.includes(value) ? current.filter((v) => v !== value) : [...current, value]; onChange(param, next); }; return (
setOpen((v) => !v)} className="text-lg" aria-haspopup="true" aria-expanded={open} aria-controls={menuId} aria-label="Filter releases" > {activeCount > 0 ? ( {activeCount} ) : null} {open ? ( {groups.map((group, gi) => (
{gi > 0 ?
: null}

{group.label}

{group.options.map((opt) => { const checked = (values[group.param] ?? []).includes(opt.value); return ( ); })}
))} ) : null}
); }