diff --git a/src/core/components/Calendar/DatePickerCalendar/DatePickerCalendarDay.tsx b/src/core/components/Calendar/DatePickerCalendar/DatePickerCalendarDay.tsx new file mode 100644 index 0000000..e779ae8 --- /dev/null +++ b/src/core/components/Calendar/DatePickerCalendar/DatePickerCalendarDay.tsx @@ -0,0 +1,100 @@ +import clsx from 'clsx'; +import { memo } from 'react'; + +import { CalendarComponentDayText } from '@/core/components/Calendar/DatePickerCalendar/subs/CalendarComponentDayText'; +import { CalendarComponentDaySubText } from '@/core/components/Calendar/DatePickerCalendar/subs/CalendarComponentDaySubText'; +import { + DatePickerCalendarDayProps, + DatePickerCalendarProps, +} from '@/core/components/Calendar/DatePickerCalendar/types/DatePickerCalendarProps'; +import { getDayjs } from '@/utilities/day'; + +const DatePickerCalendarDay = ({ + useHoliday, + disabledDates, + periodDates, + periodDateArray, + exceptionDay, + calendarDate, + cutoffDate, + cutoffAfterDate, + temporaryDates, + onDateClick, + variants, + label, + afterAllDate = false, + handleDateClick, + setCalendarPeriodDates, +}: DatePickerCalendarDayProps) => { + const { startDate, endDate } = periodDates; + const { date: exceptionDate = '', label: exceptionLabel = '' } = + exceptionDay ?? {}; + const currentDate = calendarDate.dayjs.format('YYYY-MM-DD'); + const isExceptionDate = exceptionDay ? exceptionDate === currentDate : false; + const isTemporaryDate = temporaryDates?.includes(currentDate) ?? false; + + const isCutoffDateValidation = ({ + cutoffDate, + cutoffAfterDate, + calendarDate, + }: Pick & { + calendarDate: string; + }) => { + if (!cutoffDate && !cutoffAfterDate) return false; + + return ( + (!!cutoffDate && getDayjs(calendarDate).isBefore(cutoffDate)) || + (!!cutoffAfterDate && getDayjs(calendarDate).isAfter(cutoffAfterDate)) + ); + }; + + const disabled = + (!useHoliday && calendarDate.isHoliday) || + !calendarDate.isThisMonth || + disabledDates?.includes(calendarDate.dayjs.format('YYYY-MM-DD')) || + isCutoffDateValidation({ + cutoffDate, + cutoffAfterDate, + calendarDate: calendarDate.dayjs.format('YYYY-MM-DD'), + }); + + return ( + + ); +}; + +export default memo(DatePickerCalendarDay); diff --git a/src/core/components/Calendar/DatePickerCalendar/DatePickerCalendarWeek.tsx b/src/core/components/Calendar/DatePickerCalendar/DatePickerCalendarWeek.tsx new file mode 100644 index 0000000..6a1495a --- /dev/null +++ b/src/core/components/Calendar/DatePickerCalendar/DatePickerCalendarWeek.tsx @@ -0,0 +1,47 @@ +import DatePickerCalendarDay from '@/core/components/Calendar/DatePickerCalendar/DatePickerCalendarDay'; +import { DatePickerCalendarWeekProps } from '@/core/components/Calendar/DatePickerCalendar/types/DatePickerCalendarProps'; + +const DatePickerCalendarWeek = ({ + exceptionDay, + calendarWeekDates, + useHoliday, + cutoffDate, + cutoffAfterDate, + disabledDates, + temporaryDates, + handleDateClick, + label, + variants, + periodDates, + setCalendarPeriodDates, + onDateClick, + afterAllDate = false, + periodDateArray, +}: DatePickerCalendarWeekProps) => { + return ( +
+ {calendarWeekDates.map((calendarDate, index) => ( + + ))} +
+ ); +}; + +export default DatePickerCalendarWeek; diff --git a/src/core/components/Calendar/DatePickerCalendar/hooks/useDatePickerCalendar.ts b/src/core/components/Calendar/DatePickerCalendar/hooks/useDatePickerCalendar.ts new file mode 100644 index 0000000..8cf5d07 --- /dev/null +++ b/src/core/components/Calendar/DatePickerCalendar/hooks/useDatePickerCalendar.ts @@ -0,0 +1,136 @@ +import dayjs from 'dayjs'; +import { useCallback, useState } from 'react'; + +import { + DatePickerCalendarProps, + DatePickerType, + PeriodDates, + UseDatePickerCalendarResponse, +} from '@/core/components/Calendar/DatePickerCalendar/types/DatePickerCalendarProps'; +import { CalendarDateDto } from '@/core/components/Calendar/common/types/CalendarDateDto'; +import { DATE_PICKER_TYPE } from '@/core/components/Calendar/DatePickerCalendar/constants'; +import { getDayjs } from '@/utilities/day'; + +export const useDatePickerCalendar = ({ + isFixStartDate, +}: Pick< + DatePickerCalendarProps, + 'isFixStartDate' +>): UseDatePickerCalendarResponse => { + const [periodDates, setPeriodDates] = useState({ + startDate: '', + endDate: '', + }); + const [periodDateArray, setPeriodDateArray] = useState([]); + + const handleDateClick = useCallback( + ( + variants: DatePickerType, + afterAllDate: boolean, + calendarDate: CalendarDateDto, + ) => { + const currentDate = calendarDate.dayjs.format('YYYY-MM-DD'); + const newPeriodDates = periodDates; + + if (variants === DATE_PICKER_TYPE['SINGLE']) { + newPeriodDates.startDate = currentDate; + newPeriodDates.endDate = currentDate; + + return; + } + + if (variants === DATE_PICKER_TYPE['PERIOD']) { + if (!isFixStartDate) { + if (afterAllDate) { + newPeriodDates.startDate = currentDate; + newPeriodDates.endDate = ''; + + return; + } + + if (periodDates.startDate === '' && periodDates.endDate === '') { + newPeriodDates.startDate = currentDate; + newPeriodDates.endDate = currentDate; + + return; + } else if ( + periodDates.startDate !== '' && + periodDates.endDate !== '' + ) { + if (periodDates.startDate === periodDates.endDate) { + if ( + calendarDate.dayjs.isBefore(getDayjs(periodDates.startDate)) + ) { + newPeriodDates.startDate = currentDate; + newPeriodDates.endDate = currentDate; + + return; + } + + newPeriodDates.endDate = currentDate; + + return; + } + newPeriodDates.startDate = currentDate; + newPeriodDates.endDate = currentDate; + + return; + } else if ( + periodDates.startDate !== '' && + periodDates.endDate === '' + ) { + if (calendarDate.dayjs.isBefore(getDayjs(periodDates.startDate))) { + newPeriodDates.startDate = currentDate; + + return; + } + newPeriodDates.endDate = currentDate; + + return; + } + } else { + if (afterAllDate) { + newPeriodDates.startDate = currentDate; + newPeriodDates.endDate = ''; + + return; + } + + newPeriodDates.endDate = currentDate; + } + } + }, + [periodDates], + ); + + const setCalendarPeriodDates = useCallback( + (periodDates: PeriodDates) => { + if (!periodDates.endDate || !periodDates.startDate) { + setPeriodDateArray([]); + return; + } + const newPeriodDateArray = []; + const startDate: dayjs.Dayjs = getDayjs(periodDates.startDate); + const endDate: dayjs.Dayjs = getDayjs(periodDates.endDate); + const diffDate: number = endDate.diff(startDate, 'day'); + + for (let i = 0; i <= diffDate; i++) { + newPeriodDateArray.push(startDate.add(i, 'day').format('YYYY-MM-DD')); + } + setPeriodDateArray(newPeriodDateArray); + }, + [periodDates, setPeriodDateArray], + ); + + return { + models: { + periodDateArray, + periodDates, + }, + operations: { + setCalendarPeriodDates, + setPeriodDates, + handleDateClick, + }, + }; +}; diff --git a/src/core/components/Calendar/DatePickerCalendar/index.tsx b/src/core/components/Calendar/DatePickerCalendar/index.tsx index 4ddd655..0056820 100644 --- a/src/core/components/Calendar/DatePickerCalendar/index.tsx +++ b/src/core/components/Calendar/DatePickerCalendar/index.tsx @@ -1,124 +1,16 @@ -import clsx from 'clsx'; -import dayjs from 'dayjs'; -import { useCallback, useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { useCalendar } from '@/core/components/Calendar/common/hooks/useCalendar'; import { CalendarHeader } from '@/core/components/Calendar/common/subs/CalendarHeader'; import { CalendarWeekDayComponent } from '@/core/components/Calendar/common/subs/CalendarWeekdayComponent'; -import { CalendarDateDto } from '@/core/components/Calendar/common/types/CalendarDateDto'; import { DATE_PICKER_TYPE } from './constants'; import { DatePickerCalendarProps, - DatePickerType, PeriodDates, } from './types/DatePickerCalendarProps'; import { getDayjs, today } from '@/utilities/day'; -import { CalendarComponentDayText } from '@/core/components/Calendar/DatePickerCalendar/subs/CalendarComponentDayText'; -import { CalendarComponentDaySubText } from '@/core/components/Calendar/DatePickerCalendar/subs/CalendarComponentDaySubText'; - -export interface UseDatePickerCalendarResponse { - models: { - periodDates: PeriodDates; - periodDateArray: string[]; - }; - operations: { - setCalendarPeriodDates: (periodDates: PeriodDates) => void; - setPeriodDates: React.Dispatch>; - onDateClick: ( - variants: DatePickerType, - afterAllDate: boolean, - calendarDate: CalendarDateDto, - ) => void; - }; -} - -export const useDatePickerCalendar = ({ - isFixStartDate, -}: Pick< - DatePickerCalendarProps, - 'isFixStartDate' ->): UseDatePickerCalendarResponse => { - const [periodDates, setPeriodDates] = useState({ - startDate: '', - endDate: '', - }); - const [periodDateArray, setPeriodDateArray] = useState([]); - - const onDateClick = useCallback( - ( - variants: DatePickerType, - afterAllDate: boolean, - calendarDate: CalendarDateDto, - ) => { - const currentDate = calendarDate.dayjs.format('YYYY-MM-DD'); - const newPeriodDates = periodDates; - - if ((periodDates.startDate && periodDates.endDate) || afterAllDate) { - if (!isFixStartDate) { - newPeriodDates.startDate = currentDate; - newPeriodDates.endDate = ''; - - return; - } else { - newPeriodDates.startDate = periodDates['startDate']; - newPeriodDates.endDate = currentDate; - - return; - } - } - - if (variants === DATE_PICKER_TYPE['PERIOD'] && periodDates.startDate) { - if (isFixStartDate) { - newPeriodDates.endDate = currentDate; - - return; - } else { - if (!getDayjs(periodDates.startDate).isAfter(calendarDate.dayjs)) { - newPeriodDates.endDate = currentDate; - } else { - newPeriodDates.startDate = currentDate; - newPeriodDates.endDate = ''; - } - } - } else { - newPeriodDates.startDate = currentDate; - newPeriodDates.endDate = ''; - } - }, - [periodDates], - ); - - const setCalendarPeriodDates = useCallback( - (periodDates: PeriodDates) => { - if (!periodDates.endDate || !periodDates.startDate) { - setPeriodDateArray([]); - return; - } - const newPeriodDateArray: string[] = []; - const startDate: dayjs.Dayjs = getDayjs(periodDates.startDate); - const endDate: dayjs.Dayjs = getDayjs(periodDates.endDate); - const diffDate: number = endDate.diff(startDate, 'day'); - - for (let i = 0; i <= diffDate; i++) { - newPeriodDateArray.push(startDate.add(i, 'day').format('YYYY-MM-DD')); - } - setPeriodDateArray(newPeriodDateArray); - }, - [periodDates, setPeriodDateArray], - ); - - return { - models: { - periodDateArray, - periodDates, - }, - operations: { - setCalendarPeriodDates, - setPeriodDates, - onDateClick, - }, - }; -}; +import { useDatePickerCalendar } from '@/core/components/Calendar/DatePickerCalendar/hooks/useDatePickerCalendar'; +import DatePickerCalendarWeek from '@/core/components/Calendar/DatePickerCalendar/DatePickerCalendarWeek'; const DatePickerCalendar = ({ variants = DATE_PICKER_TYPE['SINGLE'], @@ -137,8 +29,6 @@ const DatePickerCalendar = ({ onRender, onDateClick, }: DatePickerCalendarProps) => { - const { date: exceptionDate = '', label: exceptionLabel = '' } = - exceptionDay ?? {}; const { models: commonModels, operations: commonOperations } = useCalendar( initialDate ? getDayjs(initialDate) : today, ); @@ -184,21 +74,6 @@ const DatePickerCalendar = ({ operations.setCalendarPeriodDates(periodDates); }, [periodDates]); - const isCutoffDateValidation = ({ - cutoffDate, - cutoffAfterDate, - calendarDate, - }: Pick & { - calendarDate: string; - }) => { - if (!cutoffDate && !cutoffAfterDate) return false; - - return ( - (!!cutoffDate && getDayjs(calendarDate).isBefore(cutoffDate)) || - (!!cutoffAfterDate && getDayjs(calendarDate).isAfter(cutoffAfterDate)) - ); - }; - return (
- -
- {commonModels.calendarDates.map((calendarWeekDates, index: number) => { - return ( -
- {calendarWeekDates.map( - (calendarDate: CalendarDateDto, index: number) => { - const disabled = - (!useHoliday && calendarDate.isHoliday) || - !calendarDate.isThisMonth || - disabledDates?.includes( - calendarDate.dayjs.format('YYYY-MM-DD'), - ) || - isCutoffDateValidation({ - cutoffDate, - cutoffAfterDate, - calendarDate: calendarDate.dayjs.format('YYYY-MM-DD'), - }); - const currentDate = calendarDate.dayjs.format('YYYY-MM-DD'); - const isExceptionDate = exceptionDay - ? exceptionDate === currentDate - : false; - const isTemporaryDate = - temporaryDates?.includes(currentDate) ?? false; - - return ( -
- -
- ); - }, - )} -
- ); - })} + +
+ {commonModels.calendarDates.map((calendarWeekDates, index) => ( + + ))}
); diff --git a/src/core/components/Calendar/DatePickerCalendar/types/DatePickerCalendarProps.ts b/src/core/components/Calendar/DatePickerCalendar/types/DatePickerCalendarProps.ts index ec5605a..d944f2b 100644 --- a/src/core/components/Calendar/DatePickerCalendar/types/DatePickerCalendarProps.ts +++ b/src/core/components/Calendar/DatePickerCalendar/types/DatePickerCalendarProps.ts @@ -1,5 +1,6 @@ import { CalendarHeaderProps } from '../../common/types/CalendarHeader'; import { DATE_PICKER_TYPE } from '../constants'; +import { CalendarDateDto } from '@/core/components/Calendar/common/types/CalendarDateDto'; export type DatePickerType = (typeof DATE_PICKER_TYPE)[keyof typeof DATE_PICKER_TYPE]; @@ -30,3 +31,49 @@ export interface DatePickerCalendarProps onRender?: (renderDates: PeriodDates) => void; onDateClick: (periodDates: PeriodDates, afterAllDate?: boolean) => void; } + +export interface UseDatePickerCalendarResponse { + models: { + periodDates: PeriodDates; + periodDateArray: string[]; + }; + operations: { + setCalendarPeriodDates: (periodDates: PeriodDates) => void; + setPeriodDates: React.Dispatch>; + handleDateClick: ( + variants: DatePickerType, + afterAllDate: boolean, + calendarDate: CalendarDateDto, + ) => void; + }; +} + +export interface DatePickerCalendarWeekProps + extends Omit { + calendarWeekDates: CalendarDateDto[]; +} + +export interface DatePickerCalendarDayProps + extends Pick< + DatePickerCalendarProps, + | 'useHoliday' + | 'disabledDates' + | 'exceptionDay' + | 'cutoffDate' + | 'cutoffAfterDate' + | 'temporaryDates' + | 'onDateClick' + | 'variants' + | 'label' + | 'afterAllDate' + >, + Pick< + UseDatePickerCalendarResponse['operations'], + 'handleDateClick' | 'setCalendarPeriodDates' + >, + Pick< + UseDatePickerCalendarResponse['models'], + 'periodDates' | 'periodDateArray' + > { + calendarDate: CalendarDateDto; +}