"use client" import { useMemo, useRef, useState } from "react" import { X } from "lucide-react" import { Input } from "@/components/ui/input" import type { AgendaInvitationExclusion } from "@/lib/agenda/agenda-settings-types" import { exclusionKey } from "@/lib/agenda/agenda-settings-types" import { useAgendaInvitationSuggestions } from "@/lib/agenda/use-agenda-invitation-suggestions" import { cn } from "@/lib/utils" const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ export function AgendaSettingsChipPicker({ items, onChange, placeholder, allowedTypes, emptyHint, }: { items: AgendaInvitationExclusion[] onChange: (items: AgendaInvitationExclusion[]) => void placeholder: string allowedTypes?: AgendaInvitationExclusion["type"][] emptyHint?: string }) { const [query, setQuery] = useState("") const [focused, setFocused] = useState(false) const [activeIndex, setActiveIndex] = useState(0) const blurTimer = useRef(null) const taken = useMemo(() => new Set(items.map(exclusionKey)), [items]) const suggestions = useAgendaInvitationSuggestions(query, taken, { types: allowedTypes, }) const addItem = (item: AgendaInvitationExclusion) => { if (taken.has(exclusionKey(item))) return onChange([...items, item]) setQuery("") setActiveIndex(0) } const removeItem = (id: string) => { onChange(items.filter((item) => exclusionKey(item) !== id)) } const tryAddEmail = () => { const email = query.trim().replace(/[,;]$/, "") if (!EMAIL_RE.test(email)) return false addItem({ type: "email", value: email, label: email }) return true } const showSuggestions = focused && (suggestions.length > 0 || EMAIL_RE.test(query.trim())) const grouped = useMemo(() => { const map = new Map() for (const s of suggestions) { const list = map.get(s.group) ?? [] list.push(s) map.set(s.group, list) } return [...map.entries()] }, [suggestions]) let flatIndex = -1 return (
{items.length > 0 ? (
{items.map((item) => ( {item.label} ))}
) : null} { setQuery(e.target.value) setActiveIndex(0) }} onFocus={() => { if (blurTimer.current) window.clearTimeout(blurTimer.current) setFocused(true) }} onBlur={() => { blurTimer.current = window.setTimeout(() => setFocused(false), 120) }} onKeyDown={(e) => { if (e.key === "Backspace" && !query && items.length > 0) { removeItem(exclusionKey(items[items.length - 1]!)) return } if (e.key === "Enter" || e.key === ",") { e.preventDefault() if (suggestions[activeIndex]) addItem(suggestions[activeIndex]!) else tryAddEmail() return } if (!showSuggestions || suggestions.length === 0) return if (e.key === "ArrowDown") { e.preventDefault() setActiveIndex((i) => (i + 1) % suggestions.length) } else if (e.key === "ArrowUp") { e.preventDefault() setActiveIndex((i) => (i - 1 + suggestions.length) % suggestions.length) } }} />
{showSuggestions ? (
    {grouped.map(([group, groupItems]) => (
  • {group}

    {groupItems.map((s) => { flatIndex += 1 const idx = flatIndex return ( ) })}
  • ))}
) : null} {emptyHint && items.length === 0 ? (

{emptyHint}

) : null}
) }