11'use client'
22
3- import * as React from 'react'
43import { addDays } from 'date-fns'
4+ import { useState , useEffect , useCallback , ChangeEvent , MouseEvent } from 'react'
55import { Calendar as CalendarIcon , X } from 'lucide-react'
66import { DateRange } from 'react-day-picker'
77import { cn } from '@/lib/utils'
@@ -147,9 +147,9 @@ export function DatePicker({
147147} : DatePickerProps ) {
148148 const { t, i18n } = useTranslation ( )
149149 const isPersianLocale = i18n . language === 'fa'
150- const [ internalOpen , setInternalOpen ] = React . useState ( false )
151- const [ internalDate , setInternalDate ] = React . useState < Date | undefined > ( date || undefined )
152- const [ internalRange , setInternalRange ] = React . useState < DateRange | undefined > (
150+ const [ internalOpen , setInternalOpen ] = useState ( false )
151+ const [ internalDate , setInternalDate ] = useState < Date | undefined > ( date || undefined )
152+ const [ internalRange , setInternalRange ] = useState < DateRange | undefined > (
153153 range || defaultRange || ( mode === 'range' ? { from : addDays ( new Date ( ) , - 7 ) , to : new Date ( ) } : undefined ) ,
154154 )
155155
@@ -163,58 +163,63 @@ export function DatePicker({
163163 }
164164
165165 // Sync internal state with props
166- React . useEffect ( ( ) => {
166+ useEffect ( ( ) => {
167167 if ( date !== undefined ) {
168168 setInternalDate ( date || undefined )
169169 }
170170 } , [ date ] )
171171
172- React . useEffect ( ( ) => {
172+ useEffect ( ( ) => {
173173 if ( range !== undefined ) {
174174 setInternalRange ( range )
175175 }
176176 } , [ range ] )
177177
178- // Propagate initial range for range mode
179- React . useEffect ( ( ) => {
178+ useEffect ( ( ) => {
180179 if ( mode === 'range' && internalRange && onRangeChange ) {
181180 onRangeChange ( internalRange )
182181 }
183- } , [ ] ) // Only on mount
182+ } , [ ] )
184183
185- const handleDateSelect = React . useCallback (
184+ const handleDateSelect = useCallback (
186185 ( selectedDate : Date | undefined ) => {
187186 if ( ! selectedDate ) {
188187 setInternalDate ( undefined )
189188 onDateChange ( undefined )
189+ onFieldChange ?.( fieldName , undefined )
190190 return
191191 }
192192
193193 const now = new Date ( )
194194 const today = new Date ( now . getFullYear ( ) , now . getMonth ( ) , now . getDate ( ) )
195195 const selectedDateOnly = new Date ( selectedDate . getFullYear ( ) , selectedDate . getMonth ( ) , selectedDate . getDate ( ) )
196196
197- // Ensure date is not in the past (for expiry dates)
198197 if ( minDate === undefined && selectedDateOnly < today ) {
199198 selectedDate = new Date ( now )
200199 }
201200
202- // Always use current time (not end of day)
203201 selectedDate . setHours ( now . getHours ( ) , now . getMinutes ( ) , now . getSeconds ( ) , now . getMilliseconds ( ) )
204202
205203 setInternalDate ( selectedDate )
206204 const value = useUtcTimestamp ? Math . floor ( selectedDate . getTime ( ) / 1000 ) : getLocalISOTime ( selectedDate )
207205 onDateChange ( selectedDate )
208206 onFieldChange ?.( fieldName , value )
209-
210- // Close popover when day is clicked
211- setIsOpen ( false )
207+ setTimeout ( ( ) => {
208+ setIsOpen ( false )
209+ } , 0 )
212210 } ,
213211 [ onDateChange , onFieldChange , fieldName , useUtcTimestamp , minDate ] ,
214212 )
215213
216- const handleTimeChange = React . useCallback (
217- ( e : React . ChangeEvent < HTMLInputElement > ) => {
214+ const handleDateSelectWrapper = useCallback (
215+ ( selectedDate : Date | undefined ) => {
216+ handleDateSelect ( selectedDate )
217+ } ,
218+ [ handleDateSelect ] ,
219+ )
220+
221+ const handleTimeChange = useCallback (
222+ ( e : ChangeEvent < HTMLInputElement > ) => {
218223 e . preventDefault ( )
219224 e . stopPropagation ( )
220225 if ( internalDate && e . target . value ) {
@@ -236,7 +241,7 @@ export function DatePicker({
236241 [ internalDate , onDateChange , onFieldChange , fieldName , useUtcTimestamp ] ,
237242 )
238243
239- const handleRangeSelect = React . useCallback (
244+ const handleRangeSelect = useCallback (
240245 ( selectedRange : DateRange | undefined ) => {
241246 setInternalRange ( selectedRange )
242247 onRangeChange ?.( selectedRange )
@@ -249,7 +254,7 @@ export function DatePicker({
249254 [ onRangeChange ] ,
250255 )
251256
252- const formatDate = React . useCallback (
257+ const formatDate = useCallback (
253258 ( date : Date ) => {
254259 if ( customFormatDate ) {
255260 return customFormatDate ( date )
@@ -259,7 +264,7 @@ export function DatePicker({
259264 [ customFormatDate , isPersianLocale , showTime ] ,
260265 )
261266
262- const dateDisabled = React . useCallback (
267+ const dateDisabled = useCallback (
263268 ( date : Date ) => {
264269 if ( mode === 'range' && disableAfter && date > disableAfter ) {
265270 return true
@@ -285,8 +290,8 @@ export function DatePicker({
285290 ? `${ String ( displayDate . getHours ( ) ) . padStart ( 2 , '0' ) } :${ String ( displayDate . getMinutes ( ) ) . padStart ( 2 , '0' ) } `
286291 : ''
287292
288- const handleClear = React . useCallback (
289- ( e : React . MouseEvent ) => {
293+ const handleClear = useCallback (
294+ ( e : MouseEvent < HTMLButtonElement > ) => {
290295 e . preventDefault ( )
291296 e . stopPropagation ( )
292297 setInternalDate ( undefined )
@@ -326,8 +331,7 @@ export function DatePicker({
326331 < PopoverContent
327332 className = "w-auto p-0"
328333 align = "start"
329- onInteractOutside = { ( e : Event ) => {
330- e . preventDefault ( )
334+ onInteractOutside = { ( ) => {
331335 setIsOpen ( false )
332336 } }
333337 onEscapeKeyDown = { ( ) => setIsOpen ( false ) }
@@ -336,7 +340,7 @@ export function DatePicker({
336340 < PersianCalendar
337341 mode = "single"
338342 selected = { displayDate || undefined }
339- onSelect = { handleDateSelect }
343+ onSelect = { handleDateSelectWrapper }
340344 disabled = { dateDisabled }
341345 captionLayout = "dropdown"
342346 defaultMonth = { displayDate || now }
@@ -350,7 +354,7 @@ export function DatePicker({
350354 < Calendar
351355 mode = "single"
352356 selected = { displayDate || undefined }
353- onSelect = { handleDateSelect }
357+ onSelect = { handleDateSelectWrapper }
354358 disabled = { dateDisabled }
355359 captionLayout = "dropdown"
356360 defaultMonth = { displayDate || now }
@@ -363,18 +367,19 @@ export function DatePicker({
363367 ) }
364368 { showTime && (
365369 < >
366- < div className = "hidden lg:flex items-center gap-1.5 flex-wrap border-t p-3 " >
370+ < div className = "hidden lg:flex items-center gap-1 flex-wrap border-t p-2 " >
367371 { [
368- { label : '7d' , days : 7 } ,
369- { label : '1m' , days : 30 } ,
370- { label : '2m' , days : 60 } ,
371- { label : '3m' , days : 90 } ,
372- { label : '1y' , days : 365 } ,
372+ { label : '+ 7d' , days : 7 } ,
373+ { label : '+ 1m' , days : 30 } ,
374+ { label : '+ 2m' , days : 60 } ,
375+ { label : '+ 3m' , days : 90 } ,
376+ { label : '+ 1y' , days : 365 } ,
373377 ] . map ( ( { label, days } ) => {
374378 const handleShortcut = ( ) => {
375- const targetDate = new Date ( now )
376- targetDate . setDate ( now . getDate ( ) + days )
377- // Use current time instead of end of day
379+ const baseDate = displayDate || now
380+ const targetDate = new Date ( baseDate )
381+ targetDate . setDate ( baseDate . getDate ( ) + days )
382+ // Preserve time from base date
378383 handleDateSelect ( targetDate )
379384 }
380385 return (
@@ -383,7 +388,7 @@ export function DatePicker({
383388 type = "button"
384389 variant = "ghost"
385390 size = "sm"
386- className = "h-7 px-2 .5 text-xs text-muted-foreground hover:text-foreground"
391+ className = "h-5 px-1 .5 text-[10px] text-muted-foreground hover:text-foreground"
387392 onClick = { ( e ) => {
388393 e . preventDefault ( )
389394 e . stopPropagation ( )
@@ -400,7 +405,10 @@ export function DatePicker({
400405 type = "time"
401406 value = { timeValue }
402407 onChange = { handleTimeChange }
403- className = "w-full"
408+ className = "w-full [&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:opacity-100 [&::-webkit-calendar-picker-indicator]:invert [&::-webkit-calendar-picker-indicator]:brightness-0 [&::-webkit-calendar-picker-indicator]:saturate-100 [&::-webkit-calendar-picker-indicator]:hue-rotate-0"
409+ style = { {
410+ colorScheme : 'dark' ,
411+ } }
404412 dir = "ltr"
405413 />
406414 </ div >
0 commit comments