imporvements
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useRef, useCallback, useEffect } from 'react';
|
||||
import { Participant } from '@/types/calendar';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { X, Plus, Search, AlertCircle } from 'lucide-react';
|
||||
@@ -17,6 +17,19 @@ export const ParticipantSelector = ({
|
||||
}: ParticipantSelectorProps) => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const [highlightedIndex, setHighlightedIndex] = useState(0);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
|
||||
setIsDropdownOpen(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
const filteredParticipants = participants.filter(
|
||||
(p) =>
|
||||
@@ -25,14 +38,40 @@ export const ParticipantSelector = ({
|
||||
p.email.toLowerCase().includes(searchQuery.toLowerCase()))
|
||||
);
|
||||
|
||||
const addParticipant = (participant: Participant) => {
|
||||
const addParticipant = useCallback((participant: Participant) => {
|
||||
onSelectionChange([...selectedParticipants, participant]);
|
||||
setSearchQuery('');
|
||||
setHighlightedIndex(0);
|
||||
setIsDropdownOpen(false);
|
||||
// Re-focus input after selection
|
||||
requestAnimationFrame(() => inputRef.current?.focus());
|
||||
}, [onSelectionChange, selectedParticipants]);
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (!isDropdownOpen || filteredParticipants.length === 0) return;
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
setHighlightedIndex((prev) =>
|
||||
prev < filteredParticipants.length - 1 ? prev + 1 : 0
|
||||
);
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
setHighlightedIndex((prev) =>
|
||||
prev > 0 ? prev - 1 : filteredParticipants.length - 1
|
||||
);
|
||||
} else if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
addParticipant(filteredParticipants[highlightedIndex]);
|
||||
} else if (e.key === 'Escape') {
|
||||
setIsDropdownOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const removeParticipant = (participantId: string) => {
|
||||
onSelectionChange(selectedParticipants.filter((p) => p.id !== participantId));
|
||||
setIsDropdownOpen(false);
|
||||
inputRef.current?.blur();
|
||||
};
|
||||
|
||||
const getInitials = (name: string) => {
|
||||
@@ -45,27 +84,34 @@ export const ParticipantSelector = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div ref={containerRef} className="space-y-4">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
<Input
|
||||
ref={inputRef}
|
||||
placeholder="Search people..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => {
|
||||
setSearchQuery(e.target.value);
|
||||
setHighlightedIndex(0);
|
||||
setIsDropdownOpen(true);
|
||||
}}
|
||||
onFocus={() => setIsDropdownOpen(true)}
|
||||
onKeyDown={handleKeyDown}
|
||||
className="pl-10 h-12 bg-background border-border"
|
||||
/>
|
||||
|
||||
{isDropdownOpen && filteredParticipants.length > 0 && (
|
||||
<div className="absolute z-10 w-full mt-2 bg-popover border border-border rounded-lg shadow-popover animate-scale-in overflow-hidden">
|
||||
{filteredParticipants.map((participant) => (
|
||||
{filteredParticipants.map((participant, index) => (
|
||||
<button
|
||||
key={participant.id}
|
||||
onClick={() => addParticipant(participant)}
|
||||
className="w-full px-4 py-3 flex items-center gap-3 hover:bg-accent transition-colors text-left"
|
||||
onMouseEnter={() => setHighlightedIndex(index)}
|
||||
className={cn(
|
||||
"w-full px-4 py-3 flex items-center gap-3 hover:bg-accent transition-colors text-left",
|
||||
index === highlightedIndex && "bg-accent"
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="w-8 h-8 rounded-full flex items-center justify-center text-xs font-medium text-white"
|
||||
|
||||
Reference in New Issue
Block a user