Skip to content

Commit

Permalink
Merge pull request #76 from TEAM-DAWM/feat/#49/datepicker-view
Browse files Browse the repository at this point in the history
[FEAT] Date Picker 제작
  • Loading branch information
wrryu09 authored Jul 10, 2024
2 parents 89019f3 + 701e7d3 commit 278e05b
Show file tree
Hide file tree
Showing 16 changed files with 447 additions and 16 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['react', 'react-hooks', 'react-refresh', '@typescript-eslint'],
rules: {
'no-alert': 'off',
'react/jsx-props-no-spreading': 'off',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@svgr/cli": "^8.1.0",
"fullcalendar": "^6.1.14",
"react": "^18.3.1",
"react-datepicker": "^7.2.0",
"react-datepicker": "^7.3.0",
"react-dom": "^18.3.1",
"react-router-dom": "^6.24.1",
"vite-plugin-svgr": "^4.2.0"
Expand All @@ -36,10 +36,12 @@
"@storybook/react-vite": "^8.1.11",
"@storybook/test": "^8.1.11",
"@types/react": "^18.3.3",
"@types/react-datepicker": "^6.2.0",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"@vitejs/plugin-react": "^4.3.1",
"date-fns": "^3.6.0",
"eslint": "^8.57.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^18.0.0",
Expand All @@ -61,4 +63,4 @@
"typescript": "^5.2.2",
"vite": "^5.3.1"
}
}
}
14 changes: 14 additions & 0 deletions src/components/common/NavBarIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ const IconLayout = styled.div`
align-items: center;
width: 100%;
height: 6.4rem;
&:hover {
div {
background-color: ${({ theme }) => theme.palette.Grey.Grey1};
}
p {
color: ${({ theme }) => theme.palette.Primary};
}
svg {
color: ${({ theme }) => theme.palette.Primary};
}
}
`;
const IconContainer = styled.div<{ $iscurrent: boolean }>`
display: flex;
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/arrangeBtn/ArrangeBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const iconMap: Record<string, FunctionComponent<SVGProps<SVGSVGElement>>> = {
set: Icons.ArrangeBtn.IcnArrangeSet,
calendar: Icons.ArrangeBtn.IcnArrangeCalendar,
};
function ArrangeBtn({ type, mode, color, size }: ArrangeBtnType) {
function ArrangeBtn({ type, mode, color, size, onClick }: ArrangeBtnType) {
const IconComponent = iconMap[type];
const StyledIcon = styled(IconComponent)<{ size: string; color: string; mode: string }>`
${({ size }) => (size === 'small' ? smallSize : bigSize)};
Expand Down Expand Up @@ -45,7 +45,7 @@ function ArrangeBtn({ type, mode, color, size }: ArrangeBtnType) {
}
`;

return <StyledIcon size={size} color={color} mode={mode} />;
return <StyledIcon size={size} color={color} mode={mode} onClick={onClick} />;
}

export default ArrangeBtn;
116 changes: 116 additions & 0 deletions src/components/common/datePicker/CustomHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import styled from '@emotion/styled';

import ArrangeBtn from '../arrangeBtn/ArrangeBtn';
import TextboxInput from '../textbox/TextboxInput';

import DayDiffText from './DayDiffText';

import { warnRef } from '@/utils/refStatus';

interface CustonHeaderProps {
startDate: Date | null;
endDate: Date | null;
decreaseMonth: () => void;
increaseMonth: () => void;
prevMonthButtonDisabled: boolean;
nextMonthButtonDisabled: boolean;
onChange: (date: Date, mode: 'start' | 'end') => void;
startDateTextRef: React.RefObject<HTMLInputElement>;
endDateTextRef: React.RefObject<HTMLInputElement>;
}

function CustomHeader({
startDate,
endDate,
decreaseMonth,
increaseMonth,
prevMonthButtonDisabled,
nextMonthButtonDisabled,
onChange,
startDateTextRef,
endDateTextRef,
}: CustonHeaderProps) {
/** 일 차이 계산 */
let diff = 0;
if (endDate && startDate) {
diff = Math.abs(endDate.getTime() - startDate.getTime());
diff = Math.ceil(diff / (1000 * 60 * 60 * 24)) + 1;
}

const onStartChange = (date: Date) => {
if (endDate && endDate <= date) {
warnRef(startDateTextRef);
if (startDateTextRef.current) {
Object.assign(startDateTextRef.current, { value: '' });
}
} else {
onChange(date, 'start');
}
};
const onEndChange = (date: Date) => {
if (startDate && date <= startDate) {
warnRef(endDateTextRef);
if (endDateTextRef.current) {
Object.assign(endDateTextRef.current, { value: '' });
}
} else {
onChange(date, 'end');
}
};

return (
<div className="react-datepicker__header-custom">
<InfoBox>
<InfoWrapper>
<TextboxInput
variant="smallDate"
dateValue={startDate}
onChange={onStartChange}
dateTextRef={startDateTextRef}
/>
<TextboxInput variant="smallDate" dateValue={endDate} onChange={onEndChange} dateTextRef={endDateTextRef} />
</InfoWrapper>
<DayDiffText diff={diff} />
</InfoBox>
<div className="react-datepicker__navigation-wrapper">
<BtnWrapper className="react-datepicker__navigation-container">
<ArrangeBtn
color="WHITE"
mode="DEFAULT"
size="small"
type="left"
className="react-datepicker__navigation react-datepicker__navigation--previous"
onClick={decreaseMonth}
disabled={prevMonthButtonDisabled}
aria-label="Previous Month"
/>
<ArrangeBtn
color="WHITE"
mode="DEFAULT"
size="small"
type="right"
className="react-datepicker__navigation react-datepicker__navigation--next"
onClick={increaseMonth}
disabled={nextMonthButtonDisabled}
aria-label="Next Month"
/>
</BtnWrapper>
</div>
</div>
);
}
const BtnWrapper = styled.div`
display: flex;
justify-content: flex-end;
`;
const InfoWrapper = styled.div`
display: flex;
gap: 0.4rem;
`;
const InfoBox = styled.div`
display: flex;
justify-content: space-between;
width: 22.8rem;
padding-bottom: 0.6rem;
`;
export default CustomHeader;
69 changes: 69 additions & 0 deletions src/components/common/datePicker/DatePickerCustom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { ko } from 'date-fns/locale';
import { useRef, useState } from 'react';
import DatePicker from 'react-datepicker';

import 'react-datepicker/dist/react-datepicker.css';
import TextBtn from '../button/textBtn/TextBtn';

import CustomHeader from './CustomHeader';
import CalendarStyle from './DatePickerStyle';

import formatDatetoString from '@/utils/formatDatetoString';
import { blurRef } from '@/utils/refStatus';

function DatePickerCustom() {
const [startDate, setStartDate] = useState<Date | null>(new Date());
const [endDate, setEndDate] = useState<Date | null>(null);
const startDateTextRef = useRef<HTMLInputElement>(null);
const endDateTextRef = useRef<HTMLInputElement>(null);

const onChange = (dates: [Date | null, Date | null]) => {
const [start, end] = dates;
setStartDate(start);
blurRef(startDateTextRef);
if (startDateTextRef.current) {
startDateTextRef.current.value = formatDatetoString(start);
}

setEndDate(end);
blurRef(endDateTextRef);
if (endDateTextRef.current) {
endDateTextRef.current.value = formatDatetoString(end);
}
};
const onDateChange = (date: Date, mode: 'start' | 'end') => {
if (mode === 'start') {
blurRef(startDateTextRef);
setStartDate(date);
} else {
blurRef(endDateTextRef);
setEndDate(date);
}
};

return (
<DatePicker
locale={ko}
selected={startDate}
onChange={onChange}
startDate={startDate as Date | undefined}
endDate={endDate as Date | undefined}
selectsRange
inline
calendarContainer={CalendarStyle}
renderCustomHeader={(props) => (
<CustomHeader
{...props}
startDate={startDate}
endDate={endDate}
onChange={onDateChange}
startDateTextRef={startDateTextRef}
endDateTextRef={endDateTextRef}
/>
)}
>
<TextBtn text="닫기" color="BLACK" size="small" mode="DEFAULT" isHover isPressed />
</DatePicker>
);
}
export default DatePickerCustom;
118 changes: 118 additions & 0 deletions src/components/common/datePicker/DatePickerStyle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import styled from '@emotion/styled';

const CalendarStyle = styled.div`
display: flex;
flex-direction: column;
align-items: center;
width: 22.8rem;
padding: 1.6rem 1.2rem;
overflow: hidden;
box-shadow: 0 3px 7px 0 rgb(0 0 0 / 38%);
border: 0;
border-radius: 12px;
/* stylelint-disable selector-class-pattern */
.react-datepicker__month-container {
display: flex;
flex-direction: column;
align-items: center;
}
.react-datepicker__month {
display: flex;
flex-wrap: wrap;
place-content: center center;
gap: 0.6rem;
margin: 0;
margin-bottom: 1.9rem;
${({ theme }) => theme.fontTheme.CAPTION_02};
}
/* 현재 달 안 보이게 */
.react-datepicker__current-month {
display: none;
}
.react-datepicker__header {
padding: 0;
background-color: transparent;
border: 0;
}
.react-datepicker__day-names {
margin-bottom: 0.7rem;
}
.react-datepicker__day-name {
width: 2.8rem;
margin: 0;
color: ${({ theme }) => theme.palette.Grey.Grey5};
${({ theme }) => theme.fontTheme.CAPTION_04};
}
/** 주 날짜 */
.react-datepicker__week {
display: flex;
width: 19.6rem;
}
/* 선택된 날짜 */
.react-datepicker__day {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 2.9rem;
margin: 0;
border-radius: 0;
}
.react-datepicker__day--keyboard-selected {
background-color: transparent;
}
.react-datepicker__day--selected {
background-color: ${({ theme }) => theme.palette.Primary};
border-radius: 6px;
}
.react-datepicker__day--in-selecting-range {
color: ${({ theme }) => theme.palette.Grey.Grey7};
background-color: ${({ theme }) => theme.palette.Blue.Blue2};
}
.react-datepicker__day--in-range {
color: ${({ theme }) => theme.palette.Grey.Grey7};
background-color: ${({ theme }) => theme.palette.Blue.Blue2};
}
.react-datepicker__day--range-start,
.react-datepicker__day--selecting-range-start,
.react-datepicker__day--range-end {
color: ${({ theme }) => theme.palette.Grey.White};
background-color: ${({ theme }) => theme.palette.Primary};
border-radius: 6px;
}
/* 이번 월에 포함되지 않는 날짜 */
.react-datepicker__day--outside-month {
color: ${({ theme }) => theme.palette.Grey.Grey4};
}
.react-datepicker__children-container {
align-self: flex-end;
width: fit-content;
margin: 0;
padding: 0;
}
/* stylelint-enable selector-class-pattern */
`;

export default CalendarStyle;
Loading

0 comments on commit 278e05b

Please sign in to comment.