-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[2주차] 윤영준 미션 제출합니다. #2
base: master
Are you sure you want to change the base?
Changes from 6 commits
2007795
02c7fe6
75bd661
d31c894
27583c1
9a5d35a
1f251fe
0d0427c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"printWidth": 80, | ||
"tabWidth": 2, | ||
"useTabs": false, | ||
"semi": true, | ||
"singleQuote": true, | ||
"trailingComma": "es5", | ||
"bracketSpacing": true, | ||
"arrowParens": "always", | ||
"htmlWhitespaceSensitivity": "css", | ||
"cssWhitespaceSensitivity": "css" | ||
} |
Large diffs are not rendered by default.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
@font-face { | ||
font-family: 'SUIT-Regular'; | ||
src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/[email protected]/SUIT-Regular.woff2') format('woff2'); | ||
font-weight: normal; | ||
font-style: normal; | ||
} | ||
* { | ||
box-sizing: border-box; | ||
} | ||
body { | ||
margin: 0; | ||
padding: 0; | ||
height: 100vh; /* 전체 화면 높이 */ | ||
background-color: #0000ff8d; | ||
position: relative; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
font-family: 'SUIT-Regular', sans-serif; | ||
} | ||
|
||
/* 사용자 정의 SweetAlert2 스타일 */ | ||
.custom-popup { | ||
font-family: 'SUIT-Regular', sans-serif; | ||
position: relative; | ||
border-radius: 10px; /* 둥근 모서리 */ | ||
padding: 20px; | ||
box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); /* 그림자 효과 */ | ||
overflow: hidden; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import React, { useState } from 'react'; | ||
import './App.css'; | ||
import CalendarContainer from '../component/Calendar/CalendarContainer'; | ||
import Task from '../component/Task/Task'; | ||
import styled from 'styled-components'; | ||
|
||
function App() { | ||
// 선택된 날짜 상태 -> Date 객체 정의 | ||
const [selectedDate, setSelectedDate] = useState(new Date()); | ||
const [isTaskModalOpen, setIsTaskModalOpen] = useState(false); | ||
|
||
// 날짜 선택 핸들러 | ||
const handleDateChange = (date) => { | ||
setSelectedDate(date); | ||
setIsTaskModalOpen(true); | ||
}; | ||
|
||
const handleCloseModal = () => { | ||
setIsTaskModalOpen(false); // 모달 닫기 | ||
}; | ||
|
||
return ( | ||
<> | ||
<Container> | ||
{/* Task가 보이는 상태면 달력을 숨기고, 그렇지 않으면 달력을 표시 */} | ||
{!isTaskModalOpen ? ( | ||
<CalendarContainer onDateChange={handleDateChange} /> | ||
) : ( | ||
<> | ||
<Task selectedDate={selectedDate} handleCloseModal={handleCloseModal} /> | ||
</> | ||
)} | ||
</Container> | ||
</> | ||
); | ||
} | ||
const Container = styled.div` | ||
z-index: 1; | ||
width: 560px; | ||
height: 560px; | ||
border-radius: 28px; | ||
background-color: white; | ||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); | ||
transition: width 0.5s ease; | ||
|
||
@media (max-width: 768px) { | ||
width: 360px; | ||
} | ||
`; | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,99 @@ | ||||||||||
import React, { useState } from 'react'; | ||||||||||
import Calendar from 'react-calendar'; | ||||||||||
import styled from 'styled-components'; | ||||||||||
import './custom-calendar.css'; // 커스텀 CSS 파일 | ||||||||||
import { getTodoProgressForDate } from '../../utils/LocalStorageUtil'; | ||||||||||
import CustomHeader from './components/CustomHeader'; | ||||||||||
|
||||||||||
|
||||||||||
function CalendarContainer({ onDateChange }) { | ||||||||||
const [date, setDate] = useState(new Date()); | ||||||||||
const [month, setMonth] = useState(date.getMonth()); | ||||||||||
|
||||||||||
const handleChange = (selectedDate) => { | ||||||||||
setDate(selectedDate); | ||||||||||
onDateChange(selectedDate); // 부모 컴포넌트로 선택된 날짜 전달 | ||||||||||
}; | ||||||||||
// 월 변경 핸들러 | ||||||||||
const handleMonthChange = (e) => { | ||||||||||
const selectedMonth = parseInt(e.target.value); | ||||||||||
const newDate = new Date(date.getFullYear(), selectedMonth, 1); | ||||||||||
setDate(newDate); | ||||||||||
setMonth(selectedMonth); | ||||||||||
}; | ||||||||||
// 각 타일에 내용을 추가하는 함수 | ||||||||||
const tileContent = ({ date, view }) => { | ||||||||||
if (view === 'month') { // 월간 뷰일 때만 표시 | ||||||||||
const { completed, total } = getTodoProgressForDate(date); // 성취도 계산 | ||||||||||
if (total > 0) { // 할 일이 있는 경우에만 표시 | ||||||||||
return ( | ||||||||||
<TodoProgress> | ||||||||||
{completed}/{total} | ||||||||||
</TodoProgress> | ||||||||||
); | ||||||||||
} | ||||||||||
} | ||||||||||
return null; | ||||||||||
}; | ||||||||||
|
||||||||||
// 특정 조건에 따라 날짜에 클래스 이름을 할당하는 함수 | ||||||||||
const getTileClassName = ({ date, view }) => { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요 함수 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. month 기준으로 타일이 세팅되는 구조라 말씀대로 useCallback으로 묶고, month를 의존성 배열값으로 주면 더 효율적일 수 있을 것 같아요! 감사합니다! |
||||||||||
if (view === 'month') { | ||||||||||
const { completed, total } = getTodoProgressForDate(date); // 성취도 계산 | ||||||||||
if (date === new Date().toISOString().split('T')[0]) { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그리고 혹시 Date를 포맷팅할 때 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아뇨! 큰 이유는 없습니다. 계속 쓰던 걸 썼던 것이라 더 효율적인 게 있다면 그걸로 갈아타겠습니다! |
||||||||||
return 'today'; // 오늘 날짜를 하이라이트 | ||||||||||
Comment on lines
+43
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기서 date는 date 객체이고, new Date().toISOString().split('T')[0] 는 문자열이라서, 아마 TS에서는 에러가 날 수도 있을 것 같다는 생각이 들었습니다! date도 문자열로 변경해서 타입을 맞춘 다음에, 비교를 하는 건 어떨까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋은 지적 감사합니다! js를 사용하더라도 ts에 대한 의식을 했어야 했는데 놓치고 있었던 것 같습니다. 깨닫게 해주셔서 감사합니다! |
||||||||||
} else if (completed === total && completed > 0 && total > 0) { | ||||||||||
return 'great'; // 모든 할 일이 완료된 경우 | ||||||||||
Comment on lines
+45
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 지금 조건으로도 모든 할 일이 완료된 경우에 대한 if문이 충분히 잘 기능하지만 조건을 조금 더 간결하게
Suggested change
|
||||||||||
} else if (completed !== total && total > 0) { | ||||||||||
return 'sorry'; // 할 일이 없는 경우 | ||||||||||
} else if (completed === 0 && total === 0) { | ||||||||||
return 'none'; // 할 일이 없는 경우 | ||||||||||
} | ||||||||||
} | ||||||||||
return null; | ||||||||||
}; | ||||||||||
|
||||||||||
return ( | ||||||||||
<> | ||||||||||
<Wrapper> | ||||||||||
<CustomHeader | ||||||||||
date={date} | ||||||||||
month={month} | ||||||||||
setDate={setDate} | ||||||||||
handleMonthChange={handleMonthChange} | ||||||||||
/> | ||||||||||
<Calendar | ||||||||||
locale='en-GB' // 영국 버전 -> 월요일부터 시작 | ||||||||||
onChange={handleChange} | ||||||||||
value={date} | ||||||||||
tileContent={tileContent} // 타일 콘텐츠 추가 | ||||||||||
tileClassName={getTileClassName} // tileClassName 속성 사용 | ||||||||||
showNeighboringMonth={true} // 이전 및 다음 달 날짜 표시 | ||||||||||
showNavigation={false} // 기본 네비게이션 숨기기 | ||||||||||
/> | ||||||||||
</Wrapper> | ||||||||||
</> | ||||||||||
); | ||||||||||
} | ||||||||||
const Wrapper = styled.div` | ||||||||||
box-sizing: border-box; | ||||||||||
display: flex; | ||||||||||
flex-direction: column; | ||||||||||
align-items: center; | ||||||||||
justify-content: center; | ||||||||||
width: 100%; | ||||||||||
height: 100%; | ||||||||||
margin: auto; | ||||||||||
background-color: white; /* 배경 색상 */ | ||||||||||
border-radius: 28px; | ||||||||||
padding: 20px; | ||||||||||
`; | ||||||||||
const TodoProgress = styled.div` | ||||||||||
position: absolute; | ||||||||||
top: 40px; | ||||||||||
font-size: 0.75rem; | ||||||||||
color: #666; | ||||||||||
margin-top: 5px; | ||||||||||
|
||||||||||
`; | ||||||||||
export default CalendarContainer; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,107 @@ | ||||||||||||||||||||||||||||||
import React from "react"; | ||||||||||||||||||||||||||||||
import styled from "styled-components"; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
export default function CustomHeader({ date, setDate, month, handleMonthChange }) { | ||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||
<Wrapper> | ||||||||||||||||||||||||||||||
<ColorBox> | ||||||||||||||||||||||||||||||
<span className='today'>Today</span> | ||||||||||||||||||||||||||||||
<span className='great'>Great</span> | ||||||||||||||||||||||||||||||
<span className='sorry'>Sorry</span> | ||||||||||||||||||||||||||||||
</ColorBox> | ||||||||||||||||||||||||||||||
<Title> | ||||||||||||||||||||||||||||||
<h2>To Do Calendar</h2> | ||||||||||||||||||||||||||||||
<span>({date.toISOString().split('T')[0]})</span> | ||||||||||||||||||||||||||||||
</Title> | ||||||||||||||||||||||||||||||
<SelectMonth value={month} onChange={handleMonthChange}> | ||||||||||||||||||||||||||||||
{Array.from({ length: 12 }, (_, i) => ( | ||||||||||||||||||||||||||||||
<option key={i} value={i}> | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. key값을 index보다는 다른 더 유니크한 값으로 바꿔 주셔도 좋을 것 같아요! |
||||||||||||||||||||||||||||||
{new Date(0, i).toLocaleString('en-US', { month: 'long' })} {/* 월 이름 표시 */} | ||||||||||||||||||||||||||||||
</option> | ||||||||||||||||||||||||||||||
))} | ||||||||||||||||||||||||||||||
</SelectMonth> | ||||||||||||||||||||||||||||||
</Wrapper> | ||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
const Wrapper = styled.div` | ||||||||||||||||||||||||||||||
display: flex; | ||||||||||||||||||||||||||||||
justify-content: space-between; | ||||||||||||||||||||||||||||||
padding: 0 20px; | ||||||||||||||||||||||||||||||
align-items: center; | ||||||||||||||||||||||||||||||
width: 100%; | ||||||||||||||||||||||||||||||
height: 10%; | ||||||||||||||||||||||||||||||
margin-top: 10px; | ||||||||||||||||||||||||||||||
margin-bottom: 10px; | ||||||||||||||||||||||||||||||
`; | ||||||||||||||||||||||||||||||
const ColorBox = styled.div` | ||||||||||||||||||||||||||||||
display: flex; | ||||||||||||||||||||||||||||||
flex-direction: column; | ||||||||||||||||||||||||||||||
justify-content: center; | ||||||||||||||||||||||||||||||
align-items: flex-start; | ||||||||||||||||||||||||||||||
gap: 5px; | ||||||||||||||||||||||||||||||
span { | ||||||||||||||||||||||||||||||
width: 50px; | ||||||||||||||||||||||||||||||
height: 20px; | ||||||||||||||||||||||||||||||
border-radius: 10px; | ||||||||||||||||||||||||||||||
padding: 5px; | ||||||||||||||||||||||||||||||
text-align: center; | ||||||||||||||||||||||||||||||
font-size: 0.75rem; | ||||||||||||||||||||||||||||||
color: white; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
@media (max-width: 768px) { | ||||||||||||||||||||||||||||||
span { | ||||||||||||||||||||||||||||||
width: 35px; | ||||||||||||||||||||||||||||||
height: 15px; | ||||||||||||||||||||||||||||||
font-size: 0.5rem; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
.today { | ||||||||||||||||||||||||||||||
background-color: #FF8200; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
.great { | ||||||||||||||||||||||||||||||
background-color: #32AAFF; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
.sorry { | ||||||||||||||||||||||||||||||
background-color: #FF5A5A; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
Comment on lines
+59
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 지금 코드도 충분히 직관적이고 간결하지만 ColorBox 내부의 클래스들의 색상 값과 텍스트를 상수로 분리하면 유지 보수성을 조금 더 높일 수 있을 것 같아요!
Suggested change
|
||||||||||||||||||||||||||||||
`; | ||||||||||||||||||||||||||||||
const Title = styled.div` | ||||||||||||||||||||||||||||||
display: flex; | ||||||||||||||||||||||||||||||
flex-direction: column; | ||||||||||||||||||||||||||||||
justify-content: center; | ||||||||||||||||||||||||||||||
align-items: center; | ||||||||||||||||||||||||||||||
gap: 5px; | ||||||||||||||||||||||||||||||
font-family: "Sofadi One", system-ui; | ||||||||||||||||||||||||||||||
font-weight: 400; | ||||||||||||||||||||||||||||||
font-style: normal; | ||||||||||||||||||||||||||||||
color: rgb(62, 76, 247); | ||||||||||||||||||||||||||||||
h2 { | ||||||||||||||||||||||||||||||
font-size: 1.5rem; | ||||||||||||||||||||||||||||||
margin: 0; | ||||||||||||||||||||||||||||||
@media (max-width: 768px) { | ||||||||||||||||||||||||||||||
font-size: 1.0rem; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
span { | ||||||||||||||||||||||||||||||
font-size: 0.75rem; | ||||||||||||||||||||||||||||||
@media (max-width: 768px) { | ||||||||||||||||||||||||||||||
font-size: 0.6rem; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
`; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
const SelectMonth = styled.select` | ||||||||||||||||||||||||||||||
padding: 5px; | ||||||||||||||||||||||||||||||
font-size: 1rem; | ||||||||||||||||||||||||||||||
border: none; | ||||||||||||||||||||||||||||||
color: rgb(62, 76, 247); | ||||||||||||||||||||||||||||||
background-color: transparent; | ||||||||||||||||||||||||||||||
box-shadow: inset 0 -1px 0 0 rgb(62, 76, 247); | ||||||||||||||||||||||||||||||
&:focus { | ||||||||||||||||||||||||||||||
outline: none; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
@media (max-width: 768px) { | ||||||||||||||||||||||||||||||
font-size: 0.75rem; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
구조분해할당 잘 쓰신 것 같습니다!