Skip to content

Commit 9388e95

Browse files
committed
feat : version 1.1
1 parent 4c3e5d2 commit 9388e95

File tree

6 files changed

+177
-70
lines changed

6 files changed

+177
-70
lines changed

package/src/assets/ReactDatepicker.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
background-color: transparent;
2424
cursor: pointer;
2525
}
26-
.react-datepicker__label:hover {
26+
.react-datepicker__label:not(:disabled):hover {
2727
background-color: #f7f7f7;
2828
}
29+
.react-datepicker__label:disabled {
30+
cursor: default;
31+
}
2932
.react-datepicker__controller-arrow {
3033
width: 36px;
3134
height: 36px;

package/src/components/Container.tsx

Lines changed: 79 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

33
import * as React from 'react';
4-
import { useState, useMemo } from 'react';
4+
import { useState, useMemo, useRef, useEffect } from 'react';
55
import { getFormatDatetime } from '../utils/datetime';
66
import {
77
setCenturyPage,
@@ -15,6 +15,8 @@ import Controller from './Controller';
1515
import ViewDecade from './view/Decade';
1616
import ViewYear from './view/Year';
1717
import ViewMonth from './view/Month';
18+
import { addLeadingZero } from '../utils/string';
19+
import useOutsideClick from '../hooks/useOutsideClick';
1820

1921
function Container() {
2022
// 인수가 없을 땐 LOCAL 기준 현재 시간을 반환한다.
@@ -26,25 +28,61 @@ function Container() {
2628
const [viewType, setViewType] = useState<
2729
'century' | 'decade' | 'year' | 'month'
2830
>('century');
31+
const [isVisible, setIsVisible] = useState<boolean>(false);
2932

3033
const centuryPage = useMemo(() => setCenturyPage(viewDate), [viewDate]);
3134
const decadePage = useMemo(() => setDecadePage(viewDate), [viewDate]);
3235
const yearPage = useMemo(() => setYearPage(viewDate), [viewDate]);
3336
const monthPage = useMemo(() => setMonthPage(viewDate), [viewDate]);
37+
const container = useRef(null);
38+
39+
useOutsideClick(container, () => {
40+
setIsVisible(false);
41+
});
3442

3543
const setViewDateByType = (
36-
value: string,
44+
value: string | number,
3745
type: 'year' | 'month' | 'date'
3846
) => {
39-
const split = viewDate.split('-');
47+
type Tsplit = string | number;
48+
const split = viewDate.split('-') as [Tsplit, Tsplit, Tsplit];
49+
const valueNum = Number(value);
4050

41-
if (type === 'year') split[0] = value;
42-
if (type === 'month') split[1] = value;
43-
if (type === 'date') split[2] = value;
51+
if (type === 'year') {
52+
if (valueNum < 1) {
53+
split[0] = 1;
54+
} else {
55+
split[0] = valueNum;
56+
}
57+
}
58+
if (type === 'month') {
59+
if (valueNum === 0) {
60+
if (Number(split[0]) > 1) {
61+
split[0] = Number(split[0]) - 1;
62+
split[1] = 12;
63+
}
64+
} else if (valueNum === 13) {
65+
split[0] = Number(split[0]) + 1;
66+
split[1] = 1;
67+
} else {
68+
split[1] = valueNum;
69+
}
70+
split[1] = addLeadingZero(split[1]);
71+
}
72+
if (type === 'date') split[2] = addLeadingZero(valueNum);
4473

4574
setViewDate(split.join('-'));
4675
};
4776

77+
const handleFocus = () => {
78+
console.log('handleFocus');
79+
setIsVisible(true);
80+
};
81+
82+
useEffect(() => {
83+
setIsVisible(false);
84+
}, [activeDate]);
85+
4886
// const [centuryPage, setCenturyPage] = useState<number>(0);
4987
// const [decadePage, setDecadePage] = useState<number>(0);
5088
// const [yearPage, setYearPage] = useState<number>(0);
@@ -56,49 +94,51 @@ function Container() {
5694
// yearPage 2041
5795
// monthPage 24487
5896

59-
console.log(NEW_DATE);
60-
6197
return (
6298
<div className={`${NAME_SPACE}__wrapper`}>
6399
<div className={`${NAME_SPACE}__input-container`}>
64100
<input
65101
type="text"
66102
value={getFormatDatetime(activeDate, 'YYYY-MM-DD')}
67103
readOnly
104+
onFocus={handleFocus}
68105
/>
69106
</div>
70-
<div className={`${NAME_SPACE}__datepicker-container`}>
71-
<Controller
72-
viewType={viewType}
73-
setViewType={setViewType}
74-
viewDate={viewDate}
75-
/>
76-
<div className={`${NAME_SPACE}__datepicker`}>
77-
{viewType === 'month' && (
78-
<ViewMonth monthPage={monthPage} setActiveDate={setActiveDate} />
79-
)}
80-
{viewType === 'year' && (
81-
<ViewYear
82-
setViewDateByType={setViewDateByType}
83-
setViewType={setViewType}
84-
/>
85-
)}
86-
{viewType === 'decade' && (
87-
<ViewDecade
88-
decadePage={decadePage}
89-
setViewDateByType={setViewDateByType}
90-
setViewType={setViewType}
91-
/>
92-
)}
93-
{viewType === 'century' && (
94-
<ViewCentury
95-
centuryPage={centuryPage}
96-
setViewDateByType={setViewDateByType}
97-
setViewType={setViewType}
98-
/>
99-
)}
107+
{isVisible && (
108+
<div className={`${NAME_SPACE}__datepicker-container`} ref={container}>
109+
<Controller
110+
viewType={viewType}
111+
setViewType={setViewType}
112+
viewDate={viewDate}
113+
setViewDateByType={setViewDateByType}
114+
/>
115+
<div className={`${NAME_SPACE}__datepicker`}>
116+
{viewType === 'month' && (
117+
<ViewMonth monthPage={monthPage} setActiveDate={setActiveDate} />
118+
)}
119+
{viewType === 'year' && (
120+
<ViewYear
121+
setViewDateByType={setViewDateByType}
122+
setViewType={setViewType}
123+
/>
124+
)}
125+
{viewType === 'decade' && (
126+
<ViewDecade
127+
decadePage={decadePage}
128+
setViewDateByType={setViewDateByType}
129+
setViewType={setViewType}
130+
/>
131+
)}
132+
{viewType === 'century' && (
133+
<ViewCentury
134+
centuryPage={centuryPage}
135+
setViewDateByType={setViewDateByType}
136+
setViewType={setViewType}
137+
/>
138+
)}
139+
</div>
100140
</div>
101-
</div>
141+
)}
102142
<div
103143
className="dashboard"
104144
style={{

package/src/components/Controller.tsx

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,18 @@ interface IProps {
1717
viewType: TviewType;
1818
setViewType: (value: TviewType) => void;
1919
viewDate: string;
20+
setViewDateByType: (
21+
value: string | number,
22+
type: 'year' | 'month' | 'date'
23+
) => void;
2024
}
2125

22-
function Controller({ viewDate, viewType, setViewType }: IProps) {
26+
function Controller({
27+
viewDate,
28+
viewType,
29+
setViewType,
30+
setViewDateByType,
31+
}: IProps) {
2332
const setLabel = (date: string, type: TviewType): string => {
2433
if (type === 'century') {
2534
const centuryPage = setCenturyPage(date);
@@ -67,15 +76,51 @@ function Controller({ viewDate, viewType, setViewType }: IProps) {
6776
}
6877
};
6978

79+
const getViewDateUnit = (type: string): number => {
80+
if (type === 'year') return Number(viewDate.split('-')[0]);
81+
else if (type === 'month') return Number(viewDate.split('-')[1]);
82+
else return Number(viewDate.split('-')[2]);
83+
};
84+
85+
const handleControl = (action: string) => {
86+
console.log(viewDate);
87+
88+
const isExtra = action.startsWith('extra');
89+
const unit = viewType === 'month' && !isExtra ? 'month' : 'year';
90+
91+
const deltas: { [key: string]: number } = {
92+
month: 1,
93+
year: 1,
94+
decade: 10,
95+
century: 100,
96+
};
97+
98+
let delta = deltas[viewType] as number;
99+
100+
if (viewType !== 'month' && isExtra) {
101+
delta *= 10;
102+
}
103+
104+
if (action === 'extraPrev' || action === 'prev') {
105+
delta *= -1;
106+
}
107+
108+
setViewDateByType(getViewDateUnit(unit) + delta, unit);
109+
};
110+
70111
return (
71112
<div className={`${NAME_SPACE}__controller`}>
72-
<button
73-
className={`${NAME_SPACE}__controller-arrow ${NAME_SPACE}__controller-extra-prev`}
74-
>
75-
Extra Previous
76-
</button>
113+
{viewType !== 'century' && (
114+
<button
115+
className={`${NAME_SPACE}__controller-arrow ${NAME_SPACE}__controller-extra-prev`}
116+
onClick={() => handleControl('extraPrev')}
117+
>
118+
Extra Previous
119+
</button>
120+
)}
77121
<button
78122
className={`${NAME_SPACE}__controller-arrow ${NAME_SPACE}__controller-prev`}
123+
onClick={() => handleControl('prev')}
79124
>
80125
Previous
81126
</button>
@@ -89,14 +134,18 @@ function Controller({ viewDate, viewType, setViewType }: IProps) {
89134
</button>
90135
<button
91136
className={`${NAME_SPACE}__controller-arrow ${NAME_SPACE}__controller-next`}
137+
onClick={() => handleControl('next')}
92138
>
93139
Extra Next
94140
</button>
95-
<button
96-
className={`${NAME_SPACE}__controller-arrow ${NAME_SPACE}__controller-extra-next`}
97-
>
98-
Next
99-
</button>
141+
{viewType !== 'century' && (
142+
<button
143+
className={`${NAME_SPACE}__controller-arrow ${NAME_SPACE}__controller-extra-next`}
144+
onClick={() => handleControl('extraNext')}
145+
>
146+
Next
147+
</button>
148+
)}
100149
</div>
101150
);
102151
}

package/src/components/view/Month.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,13 @@ function ViewMonth({ monthPage, setActiveDate }: Iprops) {
1818
const lastDay = lastDateValue.getDay(); // 이달 말 일의 요일
1919
const prevLastDate = new Date(year, month - 1, 0).getDate(); // 이전달의 말 일
2020

21-
console.log(firstDay);
22-
2321
return (
2422
<div className={`${NAME_SPACE}__month-view`}>
2523
{Array.apply(0, Array(firstDay)).map((x, i) => {
2624
// prevMonth
2725

2826
const date = prevLastDate - (firstDay - i - 1);
29-
const value = new Date(year, month - 2, date);
27+
const value = new Date(-1, monthPage + 22, date);
3028
const day = value.getDay();
3129
const title = getFormatDatetime(value, 'YYYY-MM-DD');
3230

@@ -46,7 +44,7 @@ function ViewMonth({ monthPage, setActiveDate }: Iprops) {
4644
{Array.apply(0, Array(lastDate)).map((x, i) => {
4745
// thisMonth
4846
const date = i + 1;
49-
const value = new Date(year, month - 1, date);
47+
const value = new Date(-1, monthPage + 23, date);
5048
const day = value.getDay();
5149
const title = getFormatDatetime(value, 'YYYY-MM-DD');
5250

@@ -65,7 +63,7 @@ function ViewMonth({ monthPage, setActiveDate }: Iprops) {
6563
})}
6664
{Array.apply(0, Array(6 - lastDay)).map((x, i) => {
6765
const date = i + 1;
68-
const value = new Date(year, month, date);
66+
const value = new Date(-1, monthPage + 24, date);
6967
const day = value.getDay();
7068
const title = getFormatDatetime(value, 'YYYY-MM-DD');
7169

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useEffect, RefObject } from 'react';
2+
3+
const useOutsideClick = (
4+
targetRef: RefObject<HTMLElement>,
5+
callback: () => void
6+
) => {
7+
useEffect(() => {
8+
const handleClickOutside = (e: MouseEvent) => {
9+
const target = e.target as HTMLDivElement;
10+
if (targetRef.current && !targetRef.current.contains(target)) {
11+
console.log('click outside');
12+
callback();
13+
}
14+
};
15+
// Bind the event listener
16+
document.addEventListener('mousedown', handleClickOutside);
17+
return () => {
18+
// Unbind the event listener on clean up
19+
document.removeEventListener('mousedown', handleClickOutside);
20+
};
21+
}, [targetRef, callback]);
22+
};
23+
24+
export default useOutsideClick;

package/src/utils/datetime.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,16 @@ export const toLocalISOString = (date: Date): string => {
1111
};
1212

1313
export const getFormatDatetime = (datetime: Date, format: string) => {
14-
const origin = toLocalISOString(datetime);
14+
const origin = toLocalISOString(datetime).split('T')[0] as string;
1515

16-
const year = format.includes('YYYY')
17-
? origin.substring(0, 4)
18-
: origin.substring(2, 4);
19-
const month = origin.substring(5, 7);
20-
const date = origin.substring(8, 10);
21-
const hour = origin.substring(11, 13);
22-
const minute = origin.substring(14, 16);
16+
const year = origin.split('-')[0];
17+
const month = origin.split('-')[1];
18+
const date = origin.split('-')[2];
2319

2420
const result = format
25-
.replace(/YYYY/g, year)
26-
.replace(/YY/g, year)
27-
.replace(/MM/g, month)
28-
.replace(/DD/g, date)
29-
.replace(/HH/g, hour)
30-
.replace(/mm/g, minute);
21+
.replace(/YYYY/g, String(year))
22+
.replace(/MM/g, String(month))
23+
.replace(/DD/g, String(date));
3124

3225
return result;
3326
};

0 commit comments

Comments
 (0)