'use client'; import * as React from 'react'; import { Clock } from 'lucide-react'; import { format } from 'date-fns'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; interface ClockTimePickerProps { time: string; // HH:mm format (e.g., "09:00") setTime: (time: string) => void; label?: string; isDark?: boolean; } export function ClockTimePicker({ time, setTime, label, isDark = false }: ClockTimePickerProps) { const [isOpen, setIsOpen] = React.useState(false); const [mode, setMode] = React.useState<'hour' | 'minute'>('hour'); const wrapperRef = React.useRef(null); // Parse time string to hours and minutes const [hours, minutes] = React.useMemo(() => { if (!time) return [9, 0]; const parts = time.split(':').map(Number); return [parts[0] || 9, parts[1] || 0]; }, [time]); // Convert to 12-hour format for display const displayHours = hours % 12 || 12; const ampm = hours >= 12 ? 'PM' : 'AM'; // Close picker when clicking outside React.useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) { setIsOpen(false); setMode('hour'); } }; if (isOpen) { document.addEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [isOpen]); // Handle hour selection const handleHourClick = (selectedHour: number) => { const newHours = ampm === 'PM' && selectedHour !== 12 ? selectedHour + 12 : ampm === 'AM' && selectedHour === 12 ? 0 : selectedHour; setTime(`${newHours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`); setMode('minute'); }; // Handle minute selection const handleMinuteClick = (selectedMinute: number) => { setTime(`${hours.toString().padStart(2, '0')}:${selectedMinute.toString().padStart(2, '0')}`); setIsOpen(false); setMode('hour'); }; // Generate hour numbers (1-12) const hourNumbers = Array.from({ length: 12 }, (_, i) => i + 1); // Generate minute numbers (0, 15, 30, 45 or 0-59) const minuteNumbers = Array.from({ length: 12 }, (_, i) => i * 5); // 0, 5, 10, 15, ..., 55 // Calculate position for clock numbers const getClockPosition = (index: number, total: number, radius: number = 90) => { const angle = (index * 360) / total - 90; // Start from top (-90 degrees) const radian = (angle * Math.PI) / 180; const x = Math.cos(radian) * radius; const y = Math.sin(radian) * radius; return { x, y }; }; // Format display time const displayTime = time ? `${displayHours}:${minutes.toString().padStart(2, '0')} ${ampm}` : 'Select time'; return (
{label && ( )}
{isOpen && (
{/* Mode selector */}
{/* Clock face */}
{/* Clock circle */}
{/* Center dot */}
{/* Hour numbers */} {mode === 'hour' && hourNumbers.map((hour, index) => { const { x, y } = getClockPosition(index, 12, 90); const isSelected = displayHours === hour; return ( ); })} {/* Minute numbers */} {mode === 'minute' && minuteNumbers.map((minute, index) => { const { x, y } = getClockPosition(index, 12, 90); const isSelected = minutes === minute; return ( ); })}
{/* AM/PM toggle */}
)}
); }