11'use client' ;
22
33import * as React from 'react' ;
4- import { useEffect , useMemo , useRef , useState } from 'react' ;
4+ import { Fragment , useEffect , useMemo , useRef , useState } from 'react' ;
55import { NAME_SPACE } from '../constants/core' ;
6+ import { useElementSize } from '../hooks/useElementSize' ;
67import useOutsideClick from '../hooks/useOutsideClick' ;
8+ import { IDateValue , ITimeValue , ITimeselector } from '../types/props' ;
79import { formatDate } from '../utils/datetime' ;
810import { setMonthPage } from '../utils/page' ;
911import Layer from './common/Layer' ;
@@ -13,6 +15,8 @@ import DatepickerDecade from './datepicker/Decade';
1315import DatepickerMonth from './datepicker/Month' ;
1416import DatepickerYear from './datepicker/Year' ;
1517import InputDate from './input/Date' ;
18+ import TimeselectorHeader from './timeselector/Header' ;
19+ import TimeselectorSelector from './timeselector/Selector' ;
1620
1721interface IProps {
1822 initValue ?: Date | null ;
@@ -26,26 +30,47 @@ interface IProps {
2630 className ?: string ;
2731 placeholder ?: string ;
2832 disabled ?: boolean ;
33+ timeselector ?: false | ITimeselector ;
34+ hourStep ?: number ;
35+ minuteStep ?: number ;
36+ secondStep ?: number ;
2937 onChange ?: ( activeDate : Date | null ) => void ;
3038}
3139
3240function Datepicker ( {
3341 initValue = null ,
3442 useClearButton = false ,
3543 showsMultipleCalendar = false ,
36- valueFormat = 'YYYY-MM-DD ' ,
44+ valueFormat = '' ,
3745 labelFormat = 'YYYY / MM' ,
3846 closesAfterChange = true ,
3947 weekdayLabels = [ 'Su' , 'Mo' , 'Tu' , 'We' , 'Th' , 'Fr' , 'Sa' ] ,
4048 withPortal = false ,
4149 className = '' ,
4250 placeholder = '' ,
4351 disabled = false ,
52+ timeselector = false ,
53+ hourStep = 1 ,
54+ minuteStep = 1 ,
55+ secondStep = 1 ,
4456 onChange,
4557} : IProps ) {
46- // 인수가 없을 땐 LOCAL 기준 현재 시간을 반환한다.
4758 const NEW_DATE = new Date ( ) ;
59+ const initialValueFormat = timeselector
60+ ? 'YYYY-MM-DD HH:mm:ss'
61+ : 'YYYY-MM-DD' ;
62+ const comValueFormat = valueFormat ? valueFormat : initialValueFormat ;
4863 const [ value , setValue ] = useState < Date | null > ( initValue ) ;
64+ const [ timeValue , setTimeValue ] = useState < ITimeValue > ( {
65+ hour : initValue ?. getHours ( ) || 0 ,
66+ minute : initValue ?. getMinutes ( ) || 0 ,
67+ second : initValue ?. getSeconds ( ) || 0 ,
68+ } ) ;
69+ const [ dateValue , setDateValue ] = useState < IDateValue > ( {
70+ year : initValue ?. getFullYear ( ) || null ,
71+ month : initValue ?. getMonth ( ) || null ,
72+ date : initValue ?. getDate ( ) || null ,
73+ } ) ;
4974 const [ viewDate , setViewDate ] = useState < string > (
5075 formatDate ( value || NEW_DATE , 'YYYY-MM-DD' )
5176 ) ;
@@ -56,14 +81,15 @@ function Datepicker({
5681
5782 const monthPage = useMemo ( ( ) => setMonthPage ( viewDate ) , [ viewDate ] ) ;
5883 const layer = useRef ( null ) ;
84+ const [ , datepickerContainerRef , { height : datepickerContainerHeight } ] =
85+ useElementSize ( ) ;
5986
6087 useOutsideClick ( layer , ( ) => {
6188 setIsVisible ( false ) ;
6289 } ) ;
6390
6491 useEffect ( ( ) => {
65- // setViewDate(formatDate(value || NEW_DATE, 'YYYY-MM-DD'));
66- if ( closesAfterChange ) {
92+ if ( closesAfterChange && ! timeselector ) {
6793 setIsVisible ( false ) ;
6894 }
6995 if ( onChange ) {
@@ -72,11 +98,48 @@ function Datepicker({
7298 // eslint-disable-next-line react-hooks/exhaustive-deps
7399 } , [ value , onChange , setViewDate ] ) ;
74100
101+ useEffect ( ( ) => {
102+ if ( ! value ) return ;
103+
104+ const newDate = new Date (
105+ value . getFullYear ( ) ,
106+ value . getMonth ( ) ,
107+ value . getDate ( ) ,
108+ timeValue . hour ,
109+ timeValue . minute ,
110+ timeValue . second
111+ ) ;
112+
113+ setValue ( newDate ) ;
114+ // eslint-disable-next-line react-hooks/exhaustive-deps
115+ } , [ timeValue ] ) ;
116+
117+ useEffect ( ( ) => {
118+ if (
119+ dateValue . year === null ||
120+ dateValue . month === null ||
121+ dateValue . date === null
122+ )
123+ return ;
124+
125+ const newDate = new Date (
126+ dateValue . year ,
127+ dateValue . month ,
128+ dateValue . date ,
129+ value ?. getHours ( ) || 0 ,
130+ value ?. getMinutes ( ) || 0 ,
131+ value ?. getSeconds ( ) || 0
132+ ) ;
133+
134+ setValue ( newDate ) ;
135+ // eslint-disable-next-line react-hooks/exhaustive-deps
136+ } , [ dateValue ] ) ;
137+
75138 return (
76139 < div className = { `${ NAME_SPACE } __wrapper ${ className } ` } >
77140 < InputDate
78141 value = { value }
79- valueFormat = { valueFormat }
142+ valueFormat = { comValueFormat }
80143 useClearButton = { useClearButton }
81144 placeholder = { placeholder }
82145 disabled = { disabled }
@@ -88,7 +151,10 @@ function Datepicker({
88151 setIsVisible = { setIsVisible }
89152 withPortal = { withPortal }
90153 >
91- < div className = { `${ NAME_SPACE } __datepicker-container` } >
154+ < div
155+ className = { `${ NAME_SPACE } __datepicker-container` }
156+ ref = { datepickerContainerRef }
157+ >
92158 < ControllerContainer
93159 viewDate = { viewDate }
94160 viewType = { viewType }
@@ -98,26 +164,20 @@ function Datepicker({
98164 setViewDate = { setViewDate }
99165 />
100166 < div className = { `${ NAME_SPACE } __datepicker` } >
101- { viewType === 'month' && (
102- < >
103- < DatepickerMonth
104- value = { value }
105- valueFormat = { valueFormat }
106- monthPage = { monthPage }
107- weekdayLabels = { weekdayLabels }
108- setValue = { setValue }
109- />
110- { showsMultipleCalendar && (
111- < DatepickerMonth
112- value = { value }
113- valueFormat = { valueFormat }
114- monthPage = { monthPage + 1 }
115- weekdayLabels = { weekdayLabels }
116- setValue = { setValue }
117- />
118- ) }
119- </ >
120- ) }
167+ { viewType === 'month' &&
168+ [ true , showsMultipleCalendar ] . map ( ( isShow , index ) => (
169+ < Fragment key = { index } >
170+ { isShow && (
171+ < DatepickerMonth
172+ dateValue = { dateValue }
173+ setDateValue = { setDateValue }
174+ valueFormat = { comValueFormat }
175+ monthPage = { monthPage + index }
176+ weekdayLabels = { weekdayLabels }
177+ />
178+ ) }
179+ </ Fragment >
180+ ) ) }
121181 { viewType === 'year' && (
122182 < DatepickerYear
123183 value = { value }
@@ -144,6 +204,27 @@ function Datepicker({
144204 ) }
145205 </ div >
146206 </ div >
207+ { timeselector && (
208+ < div
209+ className = { `${ NAME_SPACE } __timeselector-container` }
210+ style = { {
211+ height : datepickerContainerHeight ,
212+ } }
213+ >
214+ < TimeselectorHeader
215+ timeValue = { timeValue }
216+ timeselector = { timeselector }
217+ />
218+ < TimeselectorSelector
219+ timeValue = { timeValue }
220+ setTimeValue = { setTimeValue }
221+ timeselector = { timeselector }
222+ hourStep = { hourStep }
223+ minuteStep = { minuteStep }
224+ secondStep = { secondStep }
225+ />
226+ </ div >
227+ ) }
147228 </ Layer >
148229 </ div >
149230 ) ;
0 commit comments