-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[team-33 포키 & bangtae] 2주차 첫번째 pr보냅니다. (#145)
* Feature/3 (#26) * rename: gnb 폴더 생성하여 관련 컴포넌트 이동 - styled -> customStyled 로 변경 적용 * refactor: gnb 컴포넌트 css 수정 * feat: main banner UI 구현 * refactor: gnb 컴포넌트 hover 및 cursor pointer 적용 * Feature/2 (#27) * chore: mui 버전 통일 * feat: 글로벌 스타일 추가 * feat: SearchBar 컴포넌트 style 작성 및 이벤트 추가 * Feature/alias (#28) * chore: alias 경로 설정을 위한 react-app-rewired 설치 및 설정파일 생성 * refactor: 절대경로 반영 (index, app, gnb, main-banner) * fix: search input width 고정 * Feature/4 (#29) * feat: calender page component 구현 * feat: 달력 월~일요일 부분 컴포넌트 구현 * feat: 해당 연-월에 맞는 날짜 영역 컴포넌트 구현 * feat: 이전, 다음 버튼 컴포넌트 및 클릭 기능 구현 * feat: calender 컴포넌트 구현 - 보여줄 달력 개수를 정하는 page 옵션 - curData 기반으로 page 만큼의 달력 렌더링 * chore: 브라우저 확인용 App 에 calender 반영 * feat: calender 관련 상태 context 사용하여 분리 및 provider 컴포넌트 생성 - 불필요한 주석 제거 * refactor: 컴포넌트 이름 변경, DatesOfMonth 에서 DateBox 컴포넌트 분리 * feat: DateBox 컴포넌트 구현 - 클릭 시 checkIn, checkOut 상태 변경 - checkIn, checkOut 날짜에 따라 동적 css 적용 * chore: 오타 수정, app.js 에 provider 적용 * feat: header 컴포넌트 생성 - gnb, search-bar, calender를 header 하위 컴포넌트로 수정 * Feature/30 (#32) * refactor: 캘린더 체크인/체크아웃 모드와 date box 체크 위치 상태를 나타내는 문자열을 상수화 * refactor: 컴포넌트 별 데이터 가공 유틸 함수를 컴포넌트 밖으로 분리 * refactor: Date box 내부에 있던 날짜 비교용 checkInTime, checkOutTime 을 콘텍스트 provider 로 이동 - checkInDate, checkOutDate 변수명 변경 -> checkInInfo, checkOutInfo - checkIn, checkOut, current 변수명 변경 -> checkInTime, checkOutTime, currentTime * refactor: 캘린더 month 빈배열 생성 시 0 -> Null 로 변경 * Feature/31 (#33) * refactor: 코드 리뷰 반영 - visibility 속성 display로 변경 - bool 타입 결과값 함수 중복 검사 제거 - SearchMenu 컴포넌트 div 태그로 변경 * feat & refactor: SearchBar 컴포넌트 컨텍스트 추가 - props로 내려주던 상태 context로 관리 하도록 함 * feat : 검색바 반응형 컴포넌트로 수정 * refactor: isFocus를 상태 관리에서 제외 - currentInput의 상태를 통해 얻을 수 있도록 함 * feat&refactor: Header 컴포넌트 반응형으로 수정 - GNB, MainBanner, SearchBar 모두 적용 * feat: 체크인, 체크아웃 인풋 영역 클릭 시 캘린더 모달 팝업 기능 추가 * feat: Header 컴포넌트 fixed 속성 추가 * feat: 모달창 클릭시 검색바 blur가 적용되지 않게 함 * Feature/34 (#36) * feat: 체크인-체크아웃 사이 날짜 1일과 마지막일 전후 음영 반영 - DatesOfMonth 컴포넌트 last date 월이 안맞는 오류 수정 : getDate() 는 현재달 인덱스에 date 0을 주면 이전달 마지막날 반환 * feat: 현재 날짜 이전 날짜 선택 불가 및 회색 표시 * feat: 검색바 체크인, 체크아웃 날짜 출력 기능 추가 (#38) * feat: 검색바 체크인, 체크아웃 날짜 출력 기능 추가 - 캘린더에서 선택한 날짜를 출력하도록 함 * Feature/20 (#40) * refactor: ResetButton 동적으로 추가하도록 수정 - input요소의 value 유무에 따라 추가, 삭제 * feat: ResetButton 클릭 이벤트 추가 - 버튼 클릭시 input value 초기화 - 검색바가 포커스되어 있을 때만 버튼이 노출되도록 수정 * feat: 검색바 캘린더 체크인 체크아웃 상태 연동 (#41) * fix: 포커스 이벤트 에러 수정 - ResetButton 포커스 시에만 노출되도록 수정 - 검색바 포커스 시 세로로 늘어나는 오류 수정 * fix: 캘린더 날짜 선택 오류 수정 - 체크아웃을 먼저 선택 후 체크인 선택 시 체크아웃보다 뒷날이어도 선택되는 오류 수정 - DateBox 컴포넌트 내 handle click 로직 수정 * feat: 캘린더 모달 선택 시 보여지는 달 업데이트 - 체크인 날짜 있는 경우 체크인 달로 시작 - 없는 경우 현재 달로 시작 * fix: 캘린더 토요일과 날짜 정렬 Co-authored-by: bangdler <90082464+bangdler@users.noreply.github.com> Co-authored-by: bangdler <zbthz90@gmail.com>
- Loading branch information
1 parent
fd418d3
commit 21c4ad7
Showing
25 changed files
with
703 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import customStyled from '@/custom-styled-component/customStyled'; | ||
import { CalenderDateProvider } from '@/component/header/calender/CalenderDateProvider'; | ||
import GNB from '@/component/header/gnb/GNB'; | ||
import SearchBar from '@/component/header/search-bar/SearchBar'; | ||
import { SearchBarProvider } from '@/component/header/search-bar/SearchBarProvider'; | ||
|
||
function Header() { | ||
return ( | ||
<Container> | ||
<GNB /> | ||
<SearchBarProvider> | ||
<CalenderDateProvider> | ||
<SearchBar /> | ||
</CalenderDateProvider> | ||
</SearchBarProvider> | ||
</Container> | ||
); | ||
} | ||
|
||
const Container = customStyled.div` | ||
box-sizing: border-box; | ||
position: fixed; | ||
width: 100%; | ||
min-width: 900px; | ||
padding: 0 30px; | ||
`; | ||
|
||
export default Header; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import styled from 'styled-components'; | ||
import { useContext } from 'react'; | ||
import { CalenderDateContext } from '@/component/header/calender/CalenderDateProvider'; | ||
import CalenderPage from '@/component/header/calender/CalenderPage'; | ||
import PrevButton from '@/component/header/calender/PrevButton'; | ||
import NextButton from '@/component/header/calender/NextButton'; | ||
|
||
function Calender({ page = 1 }) { | ||
const { curDate } = useContext(CalenderDateContext); | ||
|
||
const displayPageArray = getDisplayPageArray({ curDate, page }); | ||
|
||
return ( | ||
<StyledContainer | ||
page={page} | ||
onMouseDown={e => { | ||
e.preventDefault(); | ||
}} | ||
> | ||
<PrevButton /> | ||
<NextButton /> | ||
<StyledCalenderPageWrapper page={page}> | ||
{displayPageArray.map((date, idx) => ( | ||
<CalenderPage key={idx} date={date} /> | ||
))} | ||
</StyledCalenderPageWrapper> | ||
</StyledContainer> | ||
); | ||
} | ||
|
||
function getDisplayPageArray({ curDate, page }) { | ||
const displayPageArray = [curDate]; | ||
for (let i = 1; i < page; i++) { | ||
const prevYear = displayPageArray[i - 1].year; | ||
const prevMonth = displayPageArray[i - 1].month; | ||
const newDate = prevMonth === 12 ? { year: prevYear + 1, month: 1 } : { year: prevYear, month: prevMonth + 1 }; | ||
displayPageArray.push(newDate); | ||
} | ||
return displayPageArray; | ||
} | ||
|
||
const StyledContainer = styled.div` | ||
position: relative; | ||
margin: 30px auto 0; | ||
background-color: white; | ||
border-radius: 40px; | ||
box-shadow: 0 4px 10px rgba(51, 51, 51, 0.1), 0 0 4px rgba(51, 51, 51, 0.05); | ||
${({ page }) => | ||
`width: ${page === 1 ? 370 : 828}px; | ||
height: ${382 * Math.ceil(page / 2)}px; | ||
padding: ${page === 1 ? `65px 44px` : `65px 88px`}; | ||
`} | ||
`; | ||
|
||
const StyledCalenderPageWrapper = styled.div` | ||
display: grid; | ||
${({ page }) => | ||
`grid-template-columns: ${page > 1 ? `repeat(2, 1fr)` : ``}; | ||
column-gap: ${page > 1 ? 20 : 0}px; | ||
row-gap: ${page > 2 ? 40 : 0}px; | ||
`} | ||
justify-items: center; | ||
`; | ||
|
||
export default Calender; |
68 changes: 68 additions & 0 deletions
68
airbnb-app/src/component/header/calender/CalenderDateProvider.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { createContext, useEffect, useState } from 'react'; | ||
|
||
export const CalenderDateContext = createContext({}); | ||
|
||
export function CalenderDateProvider({ children }) { | ||
const today = new Date(); | ||
const initialCurDateState = { year: today.getFullYear(), month: today.getMonth() + 1 }; | ||
|
||
const [curDate, setCurDate] = useState(initialCurDateState); | ||
// 체크인 모드에서 클릭하면 체크인 날짜가 선택, 체크아웃 모드에서 클릭하면 체크아웃 날짜가 선택. | ||
const [mode, setMode] = useState(null); | ||
const [checkInInfo, setCheckInInfo] = useState(null); | ||
const [checkOutInfo, setCheckOutInfo] = useState(null); | ||
|
||
const [checkInTime, setCheckInTime] = useState(0); | ||
const [checkOutTime, setCheckOutTime] = useState(0); | ||
|
||
useEffect(() => { | ||
const newCheckInTime = | ||
checkInInfo === null ? 0 : new Date(`${checkInInfo.year}-${checkInInfo.month}-${checkInInfo.date}`).getTime(); | ||
setCheckInTime(newCheckInTime); | ||
}, [checkInInfo]); | ||
|
||
useEffect(() => { | ||
const newCheckOutTime = | ||
checkOutInfo === null ? 0 : new Date(`${checkOutInfo.year}-${checkOutInfo.month}-${checkOutInfo.date}`).getTime(); | ||
setCheckOutTime(newCheckOutTime); | ||
}, [checkOutInfo]); | ||
|
||
const checkInValue = checkInInfo ? `${checkInInfo.month}월 ${checkInInfo.date}일` : ''; | ||
const checkOutValue = checkOutInfo ? `${checkOutInfo.month}월 ${checkOutInfo.date}일` : ''; | ||
|
||
const resetInfos = () => { | ||
setCheckInInfo(null); | ||
setCheckOutInfo(null); | ||
}; | ||
|
||
const resetCurDate = () => { | ||
if (checkInInfo) { | ||
setCurDate(checkInInfo); | ||
} else { | ||
setCurDate(initialCurDateState); | ||
} | ||
}; | ||
|
||
return ( | ||
<CalenderDateContext.Provider | ||
value={{ | ||
curDate, | ||
setCurDate, | ||
mode, | ||
setMode, | ||
checkInInfo, | ||
setCheckInInfo, | ||
checkOutInfo, | ||
setCheckOutInfo, | ||
checkInTime, | ||
checkOutTime, | ||
checkInValue, | ||
checkOutValue, | ||
resetInfos, | ||
resetCurDate, | ||
}} | ||
> | ||
{children} | ||
</CalenderDateContext.Provider> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import Days from '@/component/header/calender/Days'; | ||
import DatesOfMonth from '@/component/header/calender/DatesOfMonth'; | ||
import styled from 'styled-components'; | ||
|
||
function CalenderPage({ date }) { | ||
return ( | ||
<StyledContainer> | ||
<StyledTitle> | ||
{date.year}년 {date.month}월 | ||
</StyledTitle> | ||
<Days /> | ||
<DatesOfMonth date={date} /> | ||
</StyledContainer> | ||
); | ||
} | ||
|
||
const StyledContainer = styled.div` | ||
width: 336px; | ||
`; | ||
|
||
const StyledTitle = styled.h2` | ||
text-align: center; | ||
font-weight: 700; | ||
font-size: 16px; | ||
line-height: 23px; | ||
margin-bottom: 24px; | ||
`; | ||
|
||
export default CalenderPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import styled from 'styled-components'; | ||
import { useContext } from 'react'; | ||
import { CalenderDateContext } from '@/component/header/calender/CalenderDateProvider'; | ||
import { CALENDER_MODE, DATE_CHECK_STATE } from '@/constants/calenderText'; | ||
import { SearchBarContext } from '@component/header/search-bar/SearchBarProvider'; | ||
|
||
function DateBox({ year, month, date, lastDate, disabled }) { | ||
const { setCheckInInfo, setCheckOutInfo, checkInTime, checkOutTime } = useContext(CalenderDateContext); | ||
const { updateFocusState, currentInput } = useContext(SearchBarContext); | ||
|
||
const currentTime = new Date(`${year}-${month}-${date}`).getTime(); | ||
const checkState = getCheckStateOfCurrent({ currentTime, checkInTime, checkOutTime }); | ||
|
||
function handleClick() { | ||
// 검색바에서 체크인을 누른 경우 - 체크인을 바꾼다. 체크아웃 날짜와 비교 | ||
if (currentInput === CALENDER_MODE.CHECKIN) { | ||
if (currentTime > checkOutTime) { | ||
setCheckOutInfo(null); | ||
} | ||
setCheckInInfo({ year, month, date }); | ||
updateFocusState(CALENDER_MODE.CHECKOUT); | ||
return; | ||
// 검색바에서 체크아웃을 누른 경우 - 체크아웃을 바꾼다. 체크인 날짜와 비교 | ||
} | ||
if (currentInput === CALENDER_MODE.CHECKOUT) { | ||
if (checkInTime > currentTime) { | ||
setCheckOutInfo(null); | ||
setCheckInInfo({ year, month, date }); | ||
return; | ||
} | ||
if (!checkInTime) { | ||
setCheckOutInfo({ year, month, date }); | ||
updateFocusState(CALENDER_MODE.CHECKIN); | ||
return; | ||
} | ||
setCheckOutInfo({ year, month, date }); | ||
return; | ||
} | ||
} | ||
|
||
return ( | ||
<StyledBackground checkState={checkState}> | ||
{!date ? null : ( | ||
<StyledDate disabled={disabled} checkState={checkState} onClick={handleClick}> | ||
{date} | ||
</StyledDate> | ||
)} | ||
<StyledExpandBackground checkState={checkState} date={date} lastDate={lastDate} /> | ||
</StyledBackground> | ||
); | ||
} | ||
|
||
function getCheckStateOfCurrent({ currentTime, checkInTime, checkOutTime }) { | ||
if (currentTime === checkInTime) return DATE_CHECK_STATE.CHECKIN; | ||
if (currentTime === checkOutTime) return DATE_CHECK_STATE.CHECKOUT; | ||
if (checkInTime && checkOutTime && currentTime > checkInTime && currentTime < checkOutTime) { | ||
return DATE_CHECK_STATE.BETWEEN; | ||
} | ||
return false; | ||
} | ||
|
||
const StyledBackground = styled.div` | ||
position: relative; | ||
${({ checkState }) => { | ||
if (checkState === DATE_CHECK_STATE.CHECKIN) { | ||
return `background: linear-gradient(90deg, #fff 50%, #F5F5F7 50%)`; | ||
} else if (checkState === DATE_CHECK_STATE.CHECKOUT) { | ||
return `background: linear-gradient(90deg, #F5F5F7 50%, #fff 50%)`; | ||
} else if (checkState === DATE_CHECK_STATE.BETWEEN) { | ||
return `background-color: #F5F5F7`; | ||
} | ||
}} | ||
`; | ||
|
||
const StyledExpandBackground = styled.div` | ||
${({ checkState, date, lastDate }) => { | ||
if (checkState === DATE_CHECK_STATE.BETWEEN && date === 1) { | ||
return ` | ||
position: absolute; | ||
top: 0; | ||
left: -50px; | ||
width: 50px; | ||
height: 50px; | ||
background: linear-gradient(270deg, #F5F5F7 30%, #fff); | ||
`; | ||
} else if (checkState === DATE_CHECK_STATE.BETWEEN && date === lastDate) { | ||
return ` | ||
position: absolute; | ||
top: 0; | ||
right: -50px; | ||
width: 50px; | ||
height: 50px; | ||
background: linear-gradient(90deg, #F5F5F7 30%, #fff); | ||
`; | ||
} | ||
}} | ||
`; | ||
|
||
const StyledDate = styled.div` | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
text-align: center; | ||
width: 48px; | ||
height: 48px; | ||
border-radius: 24px; | ||
font-weight: 400; | ||
font-size: 17px; | ||
line-height: 17px; | ||
cursor: pointer; | ||
border: 1px solid white; | ||
&:hover { | ||
border: 1px solid black; | ||
} | ||
${({ checkState }) => { | ||
if (checkState === DATE_CHECK_STATE.CHECKIN || checkState === DATE_CHECK_STATE.CHECKOUT) { | ||
return `background-color: black; color:white; font-weight:600;`; | ||
} | ||
if (checkState === DATE_CHECK_STATE.BETWEEN) { | ||
return `border: 1px solid #F5F5F7;`; | ||
} | ||
}} | ||
${({ disabled }) => { | ||
if (disabled) { | ||
return ` | ||
color: #BDBDBD; | ||
pointer-events: none; | ||
`; | ||
} | ||
}} | ||
`; | ||
|
||
export default DateBox; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import styled from 'styled-components'; | ||
import DateBox from '@/component/header/calender/DateBox'; | ||
|
||
function DatesOfMonth({ date }) { | ||
const year = date.year; | ||
const month = date.month; | ||
const firstDay = new Date(year, month - 1, 1).getDay(); | ||
const lastDate = new Date(year, month, 0).getDate(); | ||
const dateArray = getDateArray({ firstDay, lastDate }); | ||
|
||
return ( | ||
<StyledDatesWrapper> | ||
{dateArray.map((date, idx) => { | ||
if (isBeforeToday({ year, month, date })) { | ||
return <DateBox key={idx} year={year} month={month} date={date} disabled={true} />; | ||
} | ||
return <DateBox key={idx} year={year} month={month} date={date} lastDate={lastDate} />; | ||
})} | ||
</StyledDatesWrapper> | ||
); | ||
} | ||
|
||
function getDateArray({ firstDay, lastDate }) { | ||
// 이전달 빈칸 | ||
const blanks = Array(firstDay).fill(null); | ||
const dates = Array.from({ length: lastDate }, (_, idx) => idx + 1); | ||
|
||
return [...blanks, ...dates]; | ||
} | ||
|
||
function isBeforeToday({ year, month, date }) { | ||
const today = new Date(); | ||
const todayYear = today.getFullYear(); | ||
const todayMonth = today.getMonth() + 1; | ||
const todayDate = today.getDate(); | ||
if (todayYear > year) return true; | ||
if (todayYear === year && todayMonth > month) return true; | ||
if (todayYear === year && todayMonth === month && todayDate > date) return true; | ||
return false; | ||
} | ||
|
||
const StyledDatesWrapper = styled.div` | ||
display: grid; | ||
grid-template-columns: repeat(7, 1fr); | ||
`; | ||
|
||
export default DatesOfMonth; |
Oops, something went wrong.