/** * BulkContactTagActions — the Contacts table's bulk "Tags" action. A single * checklist of the user's tags shows which are already on the selection * (checked = on all selected, indeterminate = on some); toggling adds to, or * removes from, every selected contact. A new tag can be created from the * search. Pure & controlled. */ import { type ReactElement, useState } from "react"; import { ChevronDownIcon, PlusIcon, SearchIcon, TagIcon } from "lucide-react"; import { Button } from "./button"; import { Checkbox } from "./checkbox"; import { Popover, PopoverContent, PopoverTrigger } from "./popover"; export interface BulkTagOption { name: string; /** How many of the selected contacts already carry this tag. */ onCount: number; } export interface BulkContactTagActionsProps { /** How many contacts are selected. */ selectedCount: number; /** The tag library with per-selection counts. */ options: BulkTagOption[]; /** Add a tag to every selected contact. */ onAdd: (name: string) => void; /** Remove a tag from every selected contact. */ onRemove: (name: string) => void; /** Allow creating a new tag from the search. Defaults to true. */ allowCreate?: boolean; } const norm = (s: string): string => s.trim().toLowerCase(); export function BulkContactTagActions({ selectedCount, options, onAdd, onRemove, allowCreate = true, }: BulkContactTagActionsProps): ReactElement { const [query, setQuery] = useState(""); const filtered = options.filter((o) => norm(o.name).includes(norm(query))); const trimmed = query.trim(); const canCreate = allowCreate && trimmed.length > 0 && !options.some((o) => norm(o.name) === norm(trimmed)); return ( } >

Apply to {selectedCount} selected contact {selectedCount === 1 ? "" : "s"}

setQuery(e.target.value)} placeholder="Search or create a tag…" aria-label="Search or create a tag" className="h-9 w-full bg-transparent text-body-small outline-none placeholder:text-muted-foreground" />
); } export default BulkContactTagActions;