Skip to content

Commit 999aca1

Browse files
authored
Merge pull request #107 from projects200/feature/exercise-move-list
(#102) 운동기록 리스트 캘린더 페이지 달력 아래로 이동
2 parents ec5fe2b + 73944d1 commit 999aca1

10 files changed

Lines changed: 170 additions & 142 deletions

File tree

Lines changed: 118 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { useState, useMemo, useRef } from 'react'
3+
import { useState, useMemo, useRef, forwardRef } from 'react'
44
import { startOfMonth, subMonths, addMonths, format } from 'date-fns'
55
import { ko } from 'date-fns/locale'
66
import { useSpring, animated } from '@react-spring/web'
@@ -17,129 +17,132 @@ type Props = {
1717
selectedDate: string
1818
}
1919

20-
export default function ExerciseCalendar({
21-
onDateSelect,
22-
selectedDate,
23-
}: Props) {
24-
const today = new Date()
25-
const [currentMonth, setCurrentMonth] = useState<Date>(startOfMonth(today))
26-
const prevMonth = useMemo(() => subMonths(currentMonth, 1), [currentMonth])
27-
const nextMonth = useMemo(() => addMonths(currentMonth, 1), [currentMonth])
28-
const containerRef = useRef<HTMLDivElement>(null)
29-
const [{ x }, api] = useSpring(() => ({ x: 0 }))
20+
const ExerciseCalendar = forwardRef<HTMLDivElement, Props>(
21+
({ onDateSelect, selectedDate }, ref) => {
22+
const today = new Date()
23+
const [currentMonth, setCurrentMonth] = useState<Date>(startOfMonth(today))
24+
const prevMonth = useMemo(() => subMonths(currentMonth, 1), [currentMonth])
25+
const nextMonth = useMemo(() => addMonths(currentMonth, 1), [currentMonth])
26+
const containerRef = useRef<HTMLDivElement>(null)
27+
const [{ x }, api] = useSpring(() => ({ x: 0 }))
3028

31-
const bind = useDrag(
32-
({ down, movement: [mx] }) => {
33-
if (down) {
34-
api.start({ x: mx, immediate: true })
35-
return
36-
}
29+
const bind = useDrag(
30+
({ down, movement: [mx] }) => {
31+
if (down) {
32+
api.start({ x: mx, immediate: true })
33+
return
34+
}
3735

38-
const containerWidth = containerRef.current?.clientWidth || 0
39-
const threshold = containerWidth / 4
36+
const containerWidth = containerRef.current?.clientWidth || 0
37+
const threshold = containerWidth / 4
4038

41-
if (Math.abs(mx) <= threshold) {
42-
api.start({ x: 0 })
43-
return
44-
}
39+
if (Math.abs(mx) <= threshold) {
40+
api.start({ x: 0 })
41+
return
42+
}
4543

46-
const newMonth = mx > 0 ? prevMonth : nextMonth
47-
const isMovingToFuture =
48-
newMonth.getTime() > startOfMonth(today).getTime()
49-
if (isMovingToFuture) {
50-
api.start({ x: 0 })
51-
return
52-
}
44+
const newMonth = mx > 0 ? prevMonth : nextMonth
45+
const isMovingToFuture =
46+
newMonth.getTime() > startOfMonth(today).getTime()
47+
if (isMovingToFuture) {
48+
api.start({ x: 0 })
49+
return
50+
}
5351

54-
const direction = mx > 0 ? 1 : -1
55-
const targetX = direction * containerWidth
52+
const direction = mx > 0 ? 1 : -1
53+
const targetX = direction * containerWidth
5654

57-
api.start({
58-
x: targetX,
59-
config: { tension: 250, friction: 30 },
60-
onRest: () => {
61-
setCurrentMonth(newMonth)
62-
},
63-
})
64-
},
65-
{
66-
axis: 'x',
67-
filterTaps: true,
68-
},
69-
)
70-
const handlePrev = () => setCurrentMonth(subMonths(currentMonth, 1))
71-
const handleNext = () => {
72-
if (currentMonth.getTime() >= startOfMonth(today).getTime()) return
73-
setCurrentMonth(addMonths(currentMonth, 1))
74-
}
55+
api.start({
56+
x: targetX,
57+
config: { tension: 250, friction: 30 },
58+
onRest: () => {
59+
setCurrentMonth(newMonth)
60+
},
61+
})
62+
},
63+
{
64+
axis: 'x',
65+
filterTaps: true,
66+
},
67+
)
68+
const handlePrev = () => setCurrentMonth(subMonths(currentMonth, 1))
69+
const handleNext = () => {
70+
if (currentMonth.getTime() >= startOfMonth(today).getTime()) return
71+
setCurrentMonth(addMonths(currentMonth, 1))
72+
}
7573

76-
// 애니메이션이 종료되면서 currentMonth 상태가 변경되면, x축을 즉각적으로 맞춰줍니다.
77-
useMemo(() => {
78-
api.start({ x: 0, immediate: true })
79-
}, [currentMonth])
74+
// 애니메이션이 종료되면서 currentMonth 상태가 변경되면, x축을 즉각적으로 맞춰줍니다.
75+
useMemo(() => {
76+
api.start({ x: 0, immediate: true })
77+
}, [currentMonth])
8078

81-
return (
82-
<div className={styles['container']}>
83-
<div className={styles['header']}>
84-
<button onClick={handlePrev} className={styles['nav-button']}>
85-
<LeftArrow />
86-
</button>
87-
<Typography as="span" variant="text15" weight="bold">
88-
{format(currentMonth, 'yyyy년 M월', { locale: ko })}
89-
</Typography>
90-
<button
91-
onClick={handleNext}
92-
className={styles['nav-button']}
93-
disabled={currentMonth.getTime() >= startOfMonth(today).getTime()}
94-
>
95-
<RightArrow />
96-
</button>
97-
</div>
79+
return (
80+
<div className={styles['container']} ref={ref}>
81+
<div className={styles['header']}>
82+
<button onClick={handlePrev} className={styles['nav-button']}>
83+
<LeftArrow />
84+
</button>
85+
<Typography as="span" variant="text15" weight="bold">
86+
{format(currentMonth, 'yyyy년 M월', { locale: ko })}
87+
</Typography>
88+
<button
89+
onClick={handleNext}
90+
className={styles['nav-button']}
91+
disabled={currentMonth.getTime() >= startOfMonth(today).getTime()}
92+
>
93+
<RightArrow />
94+
</button>
95+
</div>
9896

99-
<div className={styles['weekdays']}>
100-
{['일', '월', '화', '수', '목', '금', '토'].map((weekday) => (
101-
<div key={weekday} className={styles['weekday']}>
102-
<Typography as="span" variant="text14" weight="bold">
103-
{weekday}
104-
</Typography>
105-
</div>
106-
))}
107-
</div>
97+
<div className={styles['weekdays']}>
98+
{['일', '월', '화', '수', '목', '금', '토'].map((weekday) => (
99+
<div key={weekday} className={styles['weekday']}>
100+
<Typography as="span" variant="text14" weight="bold">
101+
{weekday}
102+
</Typography>
103+
</div>
104+
))}
105+
</div>
108106

109-
<div
110-
className={styles['calendar-carousel-container']}
111-
ref={containerRef}
112-
{...bind()}
113-
>
114-
<animated.div
115-
className={styles['calendar-carousel-view']}
116-
style={{
117-
transform: x.to((val) => `translateX(calc(-100% / 3 + ${val}px))`),
118-
}}
107+
<div
108+
className={styles['calendar-carousel-container']}
109+
ref={containerRef}
110+
{...bind()}
119111
>
120-
<MonthViewWithData
121-
today={today}
122-
monthToShow={prevMonth}
123-
isActive={false}
124-
onDateSelect={onDateSelect}
125-
selectedDate={selectedDate}
126-
/>
127-
<MonthViewWithData
128-
today={today}
129-
monthToShow={currentMonth}
130-
isActive={true}
131-
onDateSelect={onDateSelect}
132-
selectedDate={selectedDate}
133-
/>
134-
<MonthViewWithData
135-
today={today}
136-
monthToShow={nextMonth}
137-
isActive={false}
138-
onDateSelect={onDateSelect}
139-
selectedDate={selectedDate}
140-
/>
141-
</animated.div>
112+
<animated.div
113+
className={styles['calendar-carousel-view']}
114+
style={{
115+
transform: x.to(
116+
(val) => `translateX(calc(-100% / 3 + ${val}px))`,
117+
),
118+
}}
119+
>
120+
<MonthViewWithData
121+
today={today}
122+
monthToShow={prevMonth}
123+
isActive={false}
124+
onDateSelect={onDateSelect}
125+
selectedDate={selectedDate}
126+
/>
127+
<MonthViewWithData
128+
today={today}
129+
monthToShow={currentMonth}
130+
isActive={true}
131+
onDateSelect={onDateSelect}
132+
selectedDate={selectedDate}
133+
/>
134+
<MonthViewWithData
135+
today={today}
136+
monthToShow={nextMonth}
137+
isActive={false}
138+
onDateSelect={onDateSelect}
139+
selectedDate={selectedDate}
140+
/>
141+
</animated.div>
142+
</div>
142143
</div>
143-
</div>
144-
)
145-
}
144+
)
145+
},
146+
)
147+
ExerciseCalendar.displayName = 'ExerciseCalendar'
148+
export default ExerciseCalendar

src/app/exercise/_components/exerciseCalendar/monthView.module.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
cursor: default;
3333
}
3434

35-
.today {
35+
.selected {
3636
margin: auto;
3737
width: 38px;
3838
height: 38px;

src/app/exercise/_components/exerciseCalendar/monthView.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ const MonthView = memo(function MonthView({
6767
const dateStr = format(day, 'yyyy-MM-dd')
6868
const isCurrent = isSameMonth(day, month)
6969
const isFuture = isAfter(day, today)
70-
const isToday = isSameDay(day, today)
7170
const isSelected = isSameDay(day, parseISO(selectedDate))
7271
const count = counts[dateStr] ?? 0
7372

@@ -83,7 +82,6 @@ const MonthView = memo(function MonthView({
8382
styles['cell'],
8483
!isCurrent && styles['empty'],
8584
isFuture && styles['disabled'],
86-
isToday && styles['today'],
8785
isSelected && styles['selected'],
8886
)}
8987
onClick={() => {
Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import Typography from '@/components/ui/typography'
22
import styles from './completeButton.module.css'
33

4-
const CompleteButton = (
5-
<div className={styles['complete-button']}>
6-
<Typography
7-
as="span"
8-
variant="text15"
9-
weight="bold"
10-
className={styles['complete-button-text']}
11-
>
12-
완료
13-
</Typography>
14-
</div>
15-
)
4+
const CompleteButton = () => {
5+
return (
6+
<div className={styles['complete-button']}>
7+
<Typography
8+
as="span"
9+
variant="text15"
10+
weight="bold"
11+
className={styles['complete-button-text']}
12+
>
13+
완료
14+
</Typography>
15+
</div>
16+
)
17+
}
1618

1719
export default CompleteButton

src/app/exercise/_components/exerciseList/exerciseList.module.css

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
.container {
2-
height: 400px;
2+
min-height: 600px;
33
width: calc(100% + 40px);
44
margin-left: -20px;
55
padding: 0 20px;
66
background-color: var(--color-background);
77
display: flex;
88
flex-direction: column;
9-
overflow: auto;
10-
scrollbar-width: none;
119
}
1210
.empty-container {
1311
display: flex;

src/app/exercise/_components/exercisePoint/scoreBoard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const startDate = format(startOfMonth(new Date()), 'yyyy-MM-dd')
2323
const endDate = format(new Date(), 'yyyy-MM-dd')
2424

2525
const LEGAL_DOC_URLS = {
26-
SCORE_POLICY: '/legal/exercise-score-policy',
26+
SCORE_POLICY: 'https://www.undabang.store/legal/exercise-point-policy.html',
2727
}
2828

2929
const ScoreBoard = () => {

src/app/exercise/create/page.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import ExerciseForm, {
2020
ExerciseFormHandle,
2121
} from '../_components/exerciseForm/exerciseForm'
2222

23-
2423
export default function Create() {
2524
const [celebration, setCelebration] = useState(false)
2625
const [createdExerciseId, setCreatedExerciseId] = useState<number | null>(
@@ -89,7 +88,7 @@ export default function Create() {
8988

9089
return (
9190
<>
92-
<Header rightIcon={CompleteButton} onClick={triggerFormSubmit}>
91+
<Header rightIcon={<CompleteButton />} onClick={triggerFormSubmit}>
9392
운동 기록하기
9493
</Header>
9594
<ExerciseForm

src/app/exercise/edit/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export default function Edit() {
116116

117117
return (
118118
<>
119-
<Header rightIcon={CompleteButton} onClick={triggerFormSubmit}>
119+
<Header rightIcon={<CompleteButton />} onClick={triggerFormSubmit}>
120120
운동 기록 수정
121121
</Header>
122122
<ExerciseForm

0 commit comments

Comments
 (0)