|
@@ -1,10 +1,16 @@
|
|
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
|
-import dayjs, { type Dayjs } from 'dayjs'
|
|
|
import { RiCalendarLine, RiCloseCircleFill } from '@remixicon/react'
|
|
|
import cn from '@/utils/classnames'
|
|
|
import type { DatePickerProps, Period } from '../types'
|
|
|
import { ViewType } from '../types'
|
|
|
-import { cloneTime, getDaysInMonth, getHourIn12Hour } from '../utils'
|
|
|
+import type { Dayjs } from 'dayjs'
|
|
|
+import dayjs, {
|
|
|
+ clearMonthMapCache,
|
|
|
+ cloneTime,
|
|
|
+ getDateWithTimezone,
|
|
|
+ getDaysInMonth,
|
|
|
+ getHourIn12Hour,
|
|
|
+} from '../utils/dayjs'
|
|
|
import {
|
|
|
PortalToFollowElem,
|
|
|
PortalToFollowElemContent,
|
|
@@ -22,6 +28,7 @@ import { useTranslation } from 'react-i18next'
|
|
|
|
|
|
const DatePicker = ({
|
|
|
value,
|
|
|
+ timezone,
|
|
|
onChange,
|
|
|
onClear,
|
|
|
placeholder,
|
|
@@ -32,12 +39,15 @@ const DatePicker = ({
|
|
|
const [isOpen, setIsOpen] = useState(false)
|
|
|
const [view, setView] = useState(ViewType.date)
|
|
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
|
+ const isInitial = useRef(true)
|
|
|
+ const inputValue = useRef(value ? value.tz(timezone) : undefined).current
|
|
|
+ const defaultValue = useRef(getDateWithTimezone({ timezone })).current
|
|
|
|
|
|
- const [currentDate, setCurrentDate] = useState(value || dayjs())
|
|
|
- const [selectedDate, setSelectedDate] = useState(value)
|
|
|
+ const [currentDate, setCurrentDate] = useState(inputValue || defaultValue)
|
|
|
+ const [selectedDate, setSelectedDate] = useState(inputValue)
|
|
|
|
|
|
- const [selectedMonth, setSelectedMonth] = useState((value || dayjs()).month())
|
|
|
- const [selectedYear, setSelectedYear] = useState((value || dayjs()).year())
|
|
|
+ const [selectedMonth, setSelectedMonth] = useState((inputValue || defaultValue).month())
|
|
|
+ const [selectedYear, setSelectedYear] = useState((inputValue || defaultValue).year())
|
|
|
|
|
|
useEffect(() => {
|
|
|
const handleClickOutside = (event: MouseEvent) => {
|
|
@@ -50,6 +60,25 @@ const DatePicker = ({
|
|
|
return () => document.removeEventListener('mousedown', handleClickOutside)
|
|
|
}, [])
|
|
|
|
|
|
+ useEffect(() => {
|
|
|
+ if (isInitial.current) {
|
|
|
+ isInitial.current = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+ clearMonthMapCache()
|
|
|
+ if (value) {
|
|
|
+ const newValue = getDateWithTimezone({ date: value, timezone })
|
|
|
+ setCurrentDate(newValue)
|
|
|
+ setSelectedDate(newValue)
|
|
|
+ onChange(newValue)
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ setCurrentDate(prev => getDateWithTimezone({ date: prev, timezone }))
|
|
|
+ setSelectedDate(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) {
|
|
@@ -58,15 +87,15 @@ const DatePicker = ({
|
|
|
}
|
|
|
setView(ViewType.date)
|
|
|
setIsOpen(true)
|
|
|
+ if (value) {
|
|
|
+ setCurrentDate(value)
|
|
|
+ setSelectedDate(value)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
const handleClear = (e: React.MouseEvent) => {
|
|
|
- const newDate = dayjs()
|
|
|
e.stopPropagation()
|
|
|
setSelectedDate(undefined)
|
|
|
- setCurrentDate(prev => prev || newDate)
|
|
|
- setSelectedMonth(prev => prev || newDate.month())
|
|
|
- setSelectedYear(prev => prev || newDate.year())
|
|
|
if (!isOpen)
|
|
|
onClear()
|
|
|
}
|
|
@@ -84,13 +113,13 @@ const DatePicker = ({
|
|
|
}, [currentDate])
|
|
|
|
|
|
const handleDateSelect = useCallback((day: Dayjs) => {
|
|
|
- const newDate = cloneTime(day, selectedDate || dayjs())
|
|
|
+ const newDate = cloneTime(day, selectedDate || getDateWithTimezone({ timezone }))
|
|
|
setCurrentDate(newDate)
|
|
|
setSelectedDate(newDate)
|
|
|
- }, [selectedDate])
|
|
|
+ }, [selectedDate, timezone])
|
|
|
|
|
|
const handleSelectCurrentDate = () => {
|
|
|
- const newDate = dayjs()
|
|
|
+ const newDate = getDateWithTimezone({ timezone })
|
|
|
setCurrentDate(newDate)
|
|
|
setSelectedDate(newDate)
|
|
|
onChange(newDate)
|
|
@@ -119,19 +148,19 @@ const DatePicker = ({
|
|
|
}
|
|
|
|
|
|
const handleSelectHour = useCallback((hour: string) => {
|
|
|
- const selectedTime = selectedDate || dayjs()
|
|
|
+ const selectedTime = selectedDate || getDateWithTimezone({ timezone })
|
|
|
handleTimeSelect(hour, selectedTime.minute().toString().padStart(2, '0'), selectedTime.format('A') as Period)
|
|
|
- }, [selectedDate])
|
|
|
+ }, [selectedDate, timezone])
|
|
|
|
|
|
const handleSelectMinute = useCallback((minute: string) => {
|
|
|
- const selectedTime = selectedDate || dayjs()
|
|
|
+ const selectedTime = selectedDate || getDateWithTimezone({ timezone })
|
|
|
handleTimeSelect(getHourIn12Hour(selectedTime).toString().padStart(2, '0'), minute, selectedTime.format('A') as Period)
|
|
|
- }, [selectedDate])
|
|
|
+ }, [selectedDate, timezone])
|
|
|
|
|
|
const handleSelectPeriod = useCallback((period: Period) => {
|
|
|
- const selectedTime = selectedDate || dayjs()
|
|
|
+ const selectedTime = selectedDate || getDateWithTimezone({ timezone })
|
|
|
handleTimeSelect(getHourIn12Hour(selectedTime).toString().padStart(2, '0'), selectedTime.minute().toString().padStart(2, '0'), period)
|
|
|
- }, [selectedDate])
|
|
|
+ }, [selectedDate, timezone])
|
|
|
|
|
|
const handleOpenYearMonthPicker = () => {
|
|
|
setSelectedMonth(currentDate.month())
|
|
@@ -156,15 +185,13 @@ const DatePicker = ({
|
|
|
}, [])
|
|
|
|
|
|
const handleYearMonthConfirm = () => {
|
|
|
- setCurrentDate((prev) => {
|
|
|
- return prev ? prev.clone().month(selectedMonth).year(selectedYear) : dayjs().month(selectedMonth).year(selectedYear)
|
|
|
- })
|
|
|
+ setCurrentDate(prev => prev.clone().month(selectedMonth).year(selectedYear))
|
|
|
setView(ViewType.date)
|
|
|
}
|
|
|
|
|
|
const timeFormat = needTimePicker ? 'MMMM D, YYYY hh:mm A' : 'MMMM D, YYYY'
|
|
|
const displayValue = value?.format(timeFormat) || ''
|
|
|
- const displayTime = (selectedDate || dayjs().startOf('day')).format('hh:mm A')
|
|
|
+ const displayTime = selectedDate?.format('hh:mm A') || '--:-- --'
|
|
|
const placeholderDate = isOpen && selectedDate ? selectedDate.format(timeFormat) : (placeholder || t('time.defaultPlaceholder'))
|
|
|
|
|
|
return (
|