"use client"; import { useMemo, useRef, useState, useCallback } from "react"; import Link from "next/link"; import { ChevronDown, Search, Tag, FolderHeart, Users, Building2, Film, Hash, X } from "lucide-react"; import { useClickOutside } from "@/lib/hooks/useClickOutside"; import { cn } from "@/lib/utils"; export interface FilterOption { id: string | number; label: string; href: string; count?: number; } const ICONS = { tag: Tag, folder: FolderHeart, actress: Users, studio: Building2, series: Film, genre: Hash, } as const; export type FilterIconKey = keyof typeof ICONS; export function FilterDropdown({ label, iconKey, options, emptyMsg = "Nothing here yet", align = "left", activeLabel, clearHref, }: { label: string; iconKey?: FilterIconKey; options: FilterOption[]; emptyMsg?: string; align?: "left" | "right"; activeLabel?: string; clearHref?: string; }) { const Icon = iconKey ? ICONS[iconKey] : null; const [open, setOpen] = useState(false); const [filter, setFilter] = useState(""); const ref = useRef(null); useClickOutside(ref, useCallback(() => setOpen(false), []), open); const filtered = useMemo(() => { const q = filter.trim().toLowerCase(); return q ? options.filter((o) => o.label.toLowerCase().includes(q)) : options; }, [options, filter]); const isActive = activeLabel != null; return (
{isActive && clearHref && ( )}
{open && (
setFilter(e.target.value)} placeholder={`Filter ${label.toLowerCase()}…`} className="w-full bg-[var(--color-bg-1)]/60 text-xs pl-7 pr-2 py-1.5 rounded-md border border-[var(--color-glass-border)] outline-none focus:border-[var(--color-cyan)]" />
{filtered.length === 0 ? (
{filter ? "No matches" : emptyMsg}
) : (
{filtered.map((o) => ( setOpen(false)} className="flex items-center justify-between gap-2 px-3 py-1.5 text-sm hover:bg-[var(--color-glass)]" > {o.label} {o.count != null && ( {o.count} )} ))}
)}
)}
); }