서론
회사 프로젝트에서 datepicker가 필요했다. 라이브러리를 찾다가 커스텀하기에 용이해 보였던 react datepicker를 사용했다. 혼자 해보고 싶다면 다음 링크의 예제를 참고해서 해도 충분히 할 수 있을 것이다.
https://reactdatepicker.com/
목표
- 형태가 일그러지지 않을 것
- 날짜 범위에서 앞의 날짜보다 뒤의 날짜가 앞서지 않을 것
- '이전'의 범위를 할 경우엔 뒤의 날짜에서 오늘 이후의 날짜를 선택할 수 없을 것
- '이후'의 범위를 할 경우엔 앞의 날짜에서 오늘 이전의 날짜를 선택할 수 없을 것
- 월, 년도를 선택할 수 있어야 할 것
구현
기본 Datepicker의 모습은 다음과 같다.
완성된 모습은 다음과 같다. (혹시 검색을 할 때 나처럼 이미지를 보고 내가 구현하고자 하는 것과 유사한지 확인하는 사람도 있을 수 있으니 구현된 이미지도 남긴다.)
import React from 'react';
import DatePicker from 'react-datepicker';
import { ko } from 'date-fns/esm/locale';
import styled from '@emotion/styled';
/* Component */
import Icon from './Icon';
/* Style */
import { theme } from '@/styles/theme';
import { StDatePickerBox } from '@/styles/SearchFilter.style';
interface DatepickerProps {
selectedDate: Date | null;
handleDateChange: (date: Date | null) => void;
isDateDisabled?: boolean;
value?: string | undefined;
time?: string;
minDate?: Date | null;
maxDate?: Date | null;
disabledDates?: Date[];
}
const Datepicker: React.FC<DatepickerProps> = ({
selectedDate,
handleDateChange,
isDateDisabled,
value,
time,
minDate,
maxDate,
disabledDates,
}) => {
return (
<>
<StDatePickerBox className="date-picker-box">
{isDateDisabled ? (
<span>연도-월-일</span>
) : time === '이전' ? (
<StyledDatePicker
locale={ko}
dateFormat="yyyy-MM-dd"
selected={selectedDate}
closeOnScroll={true}
onChange={handleDateChange}
placeholderText="연도-월-일"
value={value}
minDate={minDate && minDate}
maxDate={maxDate ? maxDate : new Date()}
excludeDates={disabledDates ?? []}
className="date-picker"
onKeyDown={(e) => e.preventDefault()}
showMonthDropdown
showYearDropdown
dropdownMode="select"
/>
) : time === '이후' ? (
<StyledDatePicker
locale={ko}
dateFormat="yyyy-MM-dd"
selected={selectedDate}
closeOnScroll={true}
onChange={handleDateChange}
placeholderText="연도-월-일"
value={value}
minDate={minDate ? minDate : new Date()}
maxDate={maxDate && maxDate}
excludeDates={disabledDates ?? []}
className="date-picker"
onKeyDown={(e) => e.preventDefault()}
showMonthDropdown
showYearDropdown
dropdownMode="select"
/>
) : (
<StyledDatePicker
locale={ko}
dateFormat="yyyy-MM-dd"
selected={selectedDate}
closeOnScroll={true}
onChange={handleDateChange}
placeholderText="연도-월-일"
value={value}
minDate={minDate && minDate}
maxDate={maxDate && maxDate}
excludeDates={disabledDates ?? []}
className="date-picker"
onKeyDown={(e) => e.preventDefault()}
showMonthDropdown
showYearDropdown
dropdownMode="select"
/>
)}
<Icon name="IconCalendar" width="1.8rem" height="1.8rem" />
</StDatePickerBox>
</>
);
};
export default Datepicker;
export const StyledDatePicker = styled(DatePicker)`
border: none;
color: ${theme.color.baseMid};
font-size: 1.7rem;
font-style: normal;
font-weight: 400;
line-height: 2.2rem;
`;
/* Styled Component */
export const StDatePickerBox = styled.div`
display: flex;
height: 5.2rem;
padding: 0.8rem 1.6rem;
justify-content: space-between;
align-items: center;
gap: 0.8rem;
align-self: stretch;
border-radius: 0.4rem;
border: 1px solid ${theme.color.baseLight};
& input {
width: 100%;
color: ${theme.color.baseDark};
font-size: 1.7rem;
font-style: normal;
font-weight: 400;
line-height: 2.2rem;
}
& input::placeholder {
color: #c6c6d6;
font-size: 1.7rem;
font-style: normal;
font-weight: 400;
line-height: 2.2rem;
}
& span {
color: #c6c6d6;
font-size: 1.7rem;
font-style: normal;
font-weight: 400;
line-height: 2.2rem;
}
& .react-datepicker-wrapper {
width: 100%;
}
& .react-datepicker {
font-size: 1.5em;
font-family: 'Pretendard';
}
& .react-datepicker__header {
font-size: 1em;
}
& .react-datepicker__month {
margin: 0.4em 1em;
}
& .react-datepicker__day-name,
& .react-datepicker__day {
width: 1.9em;
line-height: 1.9em;
}
& .react-datepicker__current-month {
font-size: 1em;
}
& .react-datepicker__navigation {
top: 1em;
line-height: 1.7em;
border: 0.45em solid transparent;
}
& .react-datepicker__navigation--previous {
top: 0rem;
}
& .react-datepicker__navigation--next {
top: 0em;
}
// 연도
& .react-datepicker__year-read-view--selected-year {
font-size: 1.2em;
color: ${theme.color.baseMidDark};
}
& .react-datepicker__year-read-view--down-arrow {
top: 0.5rem;
font-size: 1.5em;
color: ${theme.color.baseMidDark};
}
& .react-datepicker__month-select,
.react-datepicker__year-select {
// select box
border: none;
padding: 0.3rem 0.7rem;
margin: 0.5rem 0rem;
}
// TimePicker
& .react-datepicker__time {
font-size: 1.6rem;
}
& .react-datepicker-time__header {
font-size: 1.5rem;
}
`;
* react-datepicker의 크기는 font-size로 결정이 된다.
- locale: 지역(언어)
- dateFormat: 날짜 포맷
- selected: 선택된 날짜(Date 타입)
- closeOnScroll: 스크롤하면 닫힘
- onChange: 변화 적용
- placeholderText: placeholder
- value: 값
- minDate/maxDate: 최솟값, 최댓값
- excludeDates: 제외할 일자
- onKeyDown: 입력시 (현재는 입력 방지를 위해 막아둠)
- showMonthDropdown/showYearDropdown/dropdownMode: 월, 년도 선택
사용하는부분은 아래와 같다.
const [selectedStartDate, setSelectedStartDate] = useState<Date | null>(null);
const [startDate, setStartDate] = useState<string | undefined>(
undefined,
);
const [selectedEndDate, setSelectedEndDate] = useState<Date | null>(null);
const [endDate, setEndDate] = useState<string | undefined>(
undefined,
);
const [dateBtn, setDateBtn] = useState('전체');
const [isDateDisabled, setIsDateDisabled] = useState(false);
const handleStartDateChange = (date: Date | null) => {
setDateBtn('');
if (date) {
setSelectedStartDate(date);
setStartDate(String(dayjs(date).format('YYYY-MM-DD')));
}
};
const handleEndDateChange = (date: Date | null) => {
setDateBtn('');
if (date) {
setSelectedEndDate(date);
setEndDate(String(dayjs(date).format('YYYY-MM-DD')));
}
};
/* 아래처럼 사용하시면 됩니다 */
<Datepicker
selectedDate={selectedStartDate}
handleDateChange={handleStartDateChange}
isDateDisabled={isDateDisabled}
value={startDate && startDate}
time="이전"
maxDate={selectedEndDate}
/>
<Icon name="IconDash" width="1rem" />
<Datepicker
selectedDate={selectedEndDate}
handleDateChange={handleEndDateChange}
isDateDisabled={isDateDisabled}
value={endDate && endDate}
time="이전"
minDate={selectedStartDate}
/>
Timepicker
기본 Timepicker와 완성된 모습이다.
Timepicker도 Datepicker와 유사하다.
import DatePicker from 'react-datepicker';
import styled from '@emotion/styled';
import { ko } from 'date-fns/esm/locale';
/* Component */
import Icon from './Icon';
/* Style */
import { theme } from '@/styles/theme';
import { StDatePickerBox } from '@/styles/SearchFilter.style';
import dayjs from 'dayjs';
interface DatepickerProps {
selectedDate: Date | null;
selectedTime: Date | null;
handleDateChange: (date: Date | null) => void;
value?: string | undefined;
}
export const TimePicker = ({
selectedDate,
selectedTime,
handleDateChange,
value,
}: DatepickerProps) => {
const filterPassedTime = (time: any) => {
const currentDate = new Date();
currentDate.setMinutes(currentDate.getMinutes() + 30);
const selectedDate = new Date(time);
return currentDate.getTime() <= selectedDate.getTime();
};
return (
<StDatePickerBox>
<StyledTimePicker
locale={ko}
dateFormat="hh:mm:ss"
selected={selectedTime}
closeOnScroll={true}
onChange={handleDateChange}
value={value && value}
filterTime={
dayjs(selectedDate).format('YYYY-MM-DD') === dayjs(new Date()).format('YYYY-MM-DD')
? filterPassedTime
: undefined
}
placeholderText="00:00"
showTimeSelect
showTimeSelectOnly
timeIntervals={1}
timeCaption="Time"
onKeyDown={(e: any) => e.preventDefault()}
/>
<Icon name="IconClock" width="1.8rem" height="1.8rem" />
</StDatePickerBox>
);
};
export const StyledTimePicker = styled(DatePicker)`
border: none;
color: ${theme.color.baseMid};
font-size: 1.7rem;
font-style: normal;
font-weight: 400;
line-height: 2.2rem;
`;
- timeIntervals: 몇 분 단위로 옵션을 줄 것인지
- filterTime: 현재는 datepicker에서 오늘을 선택했을 때(datepicker와 함께 쓰일 경우) 현재 시간 이전은 선택을 막아뒀습니다. (알림 기능을 위해 만들어진 컴포넌트)
사용하는 부분은 아래와 같다.
const [selectedStartDate, setSelectedStartDate] = useState<Date | null>(null);
const [startDate, setStartDate] = useState<string | undefined>();
const [selectedStartTime, setSelectedStartTime] = useState<Date | null>(null);
const [startTime, setStartTime] = useState<string | undefined>();
const handleStartDateChange = (date: Date | null) => {
if (date) {
setSelectedStartDate(date);
setStartDate(String(dayjs(date).format('YYYY-MM-DD')));
}
};
const handleStartTimeChange = (date: Date | null) => {
if (date) {
setSelectedStartTime(date);
setStartTime(String(dayjs(date).format('HH:mm')));
}
};
<TimePicker
selectedDate={selectedStartDate}
selectedTime={selectedStartTime}
handleDateChange={handleStartTimeChange}
value={startTime && startTime}
/>
'L > Javascript' 카테고리의 다른 글
[toast-ui eidtor/React/Typescript] toast-ui eidtor 커스텀하기 (+ 이미지 저장, youtube/vimeo 링크 변환) (0) | 2024.07.18 |
---|