111 lines
4.0 KiB
TypeScript
111 lines
4.0 KiB
TypeScript
"use client"
|
|
|
|
import * as React from "react"
|
|
import { Check, ChevronsUpDown, X } from "lucide-react"
|
|
|
|
import { cn } from "@/lib/utils"
|
|
import { Button } from "@/components/ui/button"
|
|
import {
|
|
Command,
|
|
CommandEmpty,
|
|
CommandGroup,
|
|
CommandInput,
|
|
CommandItem,
|
|
CommandList,
|
|
} from "@/components/ui/command"
|
|
import {
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
} from "@/components/ui/popover"
|
|
import { Badge } from "@/components/ui/badge"
|
|
|
|
interface MemberAutocompleteProps {
|
|
allMembers: string[];
|
|
selectedMembers: string[];
|
|
onChange: (selectedMembers: string[]) => void;
|
|
}
|
|
|
|
export function MemberAutocomplete({
|
|
allMembers,
|
|
selectedMembers,
|
|
onChange,
|
|
}: MemberAutocompleteProps) {
|
|
const [open, setOpen] = React.useState(false)
|
|
|
|
// Liste des membres qui ne sont PAS encore sélectionnés
|
|
const availableMembers = allMembers.filter(
|
|
(member) => !selectedMembers.includes(member)
|
|
)
|
|
|
|
// Gère la sélection d'un membre dans la liste
|
|
const handleSelect = (memberId: string) => {
|
|
onChange([...selectedMembers, memberId])
|
|
setOpen(false) // Ferme le popover après sélection
|
|
}
|
|
|
|
// Gère la suppression d'un membre (clic sur le 'X' du badge)
|
|
const handleRemove = (memberId: string) => {
|
|
onChange(selectedMembers.filter((m) => m !== memberId))
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-2">
|
|
{/* 1. Affichage des membres déjà sélectionnés (Badges) */}
|
|
<div className="flex flex-wrap gap-1">
|
|
{selectedMembers.map((member) => (
|
|
<Badge
|
|
key={member}
|
|
variant="secondary"
|
|
className="flex items-center gap-1"
|
|
>
|
|
<span className="truncate max-w-[200px]" title={member}>{member}</span>
|
|
<button
|
|
type="button"
|
|
onClick={() => handleRemove(member)}
|
|
className="rounded-full hover:bg-red-500/20 p-0.5"
|
|
>
|
|
<X className="h-3 w-3" />
|
|
</button>
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
|
|
{/* 2. Le Popover avec le bouton de recherche */}
|
|
<Popover open={open} onOpenChange={setOpen}>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
type="button" // Important pour ne pas soumettre le formulaire
|
|
variant="outline"
|
|
role="combobox"
|
|
aria-expanded={open}
|
|
className="w-full justify-between bg-gray-100 dark:bg-gray-700"
|
|
>
|
|
Ajouter un membre...
|
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent className="w-[--radix-popover-trigger-width] p-0">
|
|
<Command>
|
|
<CommandInput placeholder="Rechercher un membre..." />
|
|
<CommandList>
|
|
<CommandEmpty>Aucun membre trouvé.</CommandEmpty>
|
|
<CommandGroup>
|
|
{availableMembers.map((member) => (
|
|
<CommandItem
|
|
key={member}
|
|
value={member} // 'value' est utilisé pour la recherche
|
|
onSelect={() => handleSelect(member)}
|
|
className="truncate"
|
|
>
|
|
{member}
|
|
</CommandItem>
|
|
))}
|
|
</CommandGroup>
|
|
</CommandList>
|
|
</Command>
|
|
</PopoverContent>
|
|
</Popover>
|
|
</div>
|
|
)
|
|
} |