| 
                        123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 | 
                        - 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<HTMLDivElement>(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 (
 -     <PortalToFollowElem
 -       open={isOpen}
 -       onOpenChange={setIsOpen}
 -       placement='bottom-end'
 -     >
 -       <PortalToFollowElemTrigger>
 -         {renderTrigger ? (renderTrigger()) : (
 -           <div
 -             className='w-[252px] flex items-center gap-x-0.5 rounded-lg px-2 py-1 bg-components-input-bg-normal cursor-pointer group hover:bg-state-base-hover-alt'
 -             onClick={handleClickTrigger}
 -           >
 -             <input
 -               className='flex-1 p-1 bg-transparent text-components-input-text-filled placeholder:text-components-input-text-placeholder truncate system-xs-regular
 -             outline-none appearance-none cursor-pointer'
 -               readOnly
 -               value={isOpen ? '' : displayValue}
 -               placeholder={placeholderDate}
 -             />
 -             <RiTimeLine className={cn(
 -               'shrink-0 w-4 h-4 text-text-quaternary',
 -               isOpen ? 'text-text-secondary' : 'group-hover:text-text-secondary',
 -               (displayValue || (isOpen && selectedTime)) && 'group-hover:hidden',
 -             )} />
 -             <RiCloseCircleFill
 -               className={cn(
 -                 'hidden shrink-0 w-4 h-4 text-text-quaternary',
 -                 (displayValue || (isOpen && selectedTime)) && 'group-hover:inline-block hover:text-text-secondary',
 -               )}
 -               onClick={handleClear}
 -             />
 -           </div>
 -         )}
 -       </PortalToFollowElemTrigger>
 -       <PortalToFollowElemContent className='z-50'>
 -         <div className='w-[252px] mt-1 bg-components-panel-bg rounded-xl shadow-lg shadow-shadow-shadow-5 border-[0.5px] border-components-panel-border'>
 -           {/* Header */}
 -           <Header />
 - 
 -           {/* Time Options */}
 -           <Options
 -             selectedTime={selectedTime}
 -             handleSelectHour={handleSelectHour}
 -             handleSelectMinute={handleSelectMinute}
 -             handleSelectPeriod={handleSelectPeriod}
 -           />
 - 
 -           {/* Footer */}
 -           <Footer
 -             handleSelectCurrentTime={handleSelectCurrentTime}
 -             handleConfirm={handleConfirm}
 -           />
 - 
 -         </div>
 -       </PortalToFollowElemContent>
 -     </PortalToFollowElem>
 -   )
 - }
 - 
 - export default TimePicker
 
 
  |