import React, { useCallback, useEffect, useRef, useState } from 'react' import type { Period, TimePickerProps } from '../types' import dayjs, { cloneTime, getDateWithTimezone, getHourIn12Hour } from '../utils/dayjs' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' import Footer from './footer' import Options from './options' import Header from './header' import { useTranslation } from 'react-i18next' import { RiCloseCircleFill, RiTimeLine } from '@remixicon/react' import cn from '@/utils/classnames' const TimePicker = ({ value, timezone, placeholder, onChange, onClear, renderTrigger, }: TimePickerProps) => { const { t } = useTranslation() const [isOpen, setIsOpen] = useState(false) const containerRef = useRef(null) const isInitial = useRef(true) const [selectedTime, setSelectedTime] = useState(value ? getDateWithTimezone({ timezone, date: value }) : undefined) useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(event.target as Node)) setIsOpen(false) } document.addEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside) }, []) useEffect(() => { if (isInitial.current) { isInitial.current = false return } if (value) { const newValue = getDateWithTimezone({ date: value, timezone }) setSelectedTime(newValue) onChange(newValue) } else { setSelectedTime(prev => prev ? getDateWithTimezone({ date: prev, timezone }) : undefined) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [timezone]) const handleClickTrigger = (e: React.MouseEvent) => { e.stopPropagation() if (isOpen) { setIsOpen(false) return } setIsOpen(true) if (value) setSelectedTime(value) } const handleClear = (e: React.MouseEvent) => { e.stopPropagation() setSelectedTime(undefined) if (!isOpen) onClear() } const handleTimeSelect = (hour: string, minute: string, period: Period) => { const newTime = cloneTime(dayjs(), dayjs(`1/1/2000 ${hour}:${minute} ${period}`)) setSelectedTime((prev) => { return prev ? cloneTime(prev, newTime) : newTime }) } const handleSelectHour = useCallback((hour: string) => { const time = selectedTime || dayjs().startOf('day') handleTimeSelect(hour, time.minute().toString().padStart(2, '0'), time.format('A') as Period) }, [selectedTime]) const handleSelectMinute = useCallback((minute: string) => { const time = selectedTime || dayjs().startOf('day') handleTimeSelect(getHourIn12Hour(time).toString().padStart(2, '0'), minute, time.format('A') as Period) }, [selectedTime]) const handleSelectPeriod = useCallback((period: Period) => { const time = selectedTime || dayjs().startOf('day') handleTimeSelect(getHourIn12Hour(time).toString().padStart(2, '0'), time.minute().toString().padStart(2, '0'), period) }, [selectedTime]) const handleSelectCurrentTime = useCallback(() => { const newDate = getDateWithTimezone({ timezone }) setSelectedTime(newDate) onChange(newDate) setIsOpen(false) }, [onChange, timezone]) const handleConfirm = useCallback(() => { onChange(selectedTime) setIsOpen(false) }, [onChange, selectedTime]) const timeFormat = 'hh:mm A' const displayValue = value?.format(timeFormat) || '' const placeholderDate = isOpen && selectedTime ? selectedTime.format(timeFormat) : (placeholder || t('time.defaultPlaceholder')) return ( {renderTrigger ? (renderTrigger()) : (
)}
{/* Header */}
{/* Time Options */} {/* Footer */}
) } export default TimePicker