From 1466f2f56efcd721c631b942c49e52e38f694ae1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Autumn=20=28=EC=A1=B0=ED=99=8D=EB=B9=84=29?=
<60209518+dyongdi@users.noreply.github.com>
Date: Tue, 1 Jun 2021 10:57:59 +0900
Subject: [PATCH] =?UTF-8?q?[FE]=20=EC=9D=B8=EC=9B=90=20=EB=AA=A8=EB=8B=AC?=
=?UTF-8?q?=20=EA=B5=AC=ED=98=84=20(#52)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: 인원 모달 UI 작업
- 인원 모달에 필요한 상태를 SearchBar에 context로 생성
- 인원 모달 UI 생성
Dae-Hwa/airbnb/#48
* feat: guest type에 따라 HeadCount 컴포넌트의 내용을 다르게 렌더링
- +,- 버튼을 컴포넌트로 분리
- Counter 컴포넌트 분리
Dae-Hwa/airbnb/#48
* refactor: HeadCount에서 객체 리터럴에 바로 접근할 수 있도록 타입 추가
Dae-Hwa/airbnb/#48
* feat: counter 기본 동작 구현
- 분리해둔 PlusButton, MinusButton에 onClick 이벤트를 넣으려니 또 타입을 선언해줘야 해서
임시로 svg 컴포넌트를 가져와서 onClick 이벤트 등록함
- useReducer 사용하지 않고 그냥 만들어본 버전
- 타입.. 아찔하다.
Dae-Hwa/airbnb/#48
* feat & fix: 인원모달 관련된 상태를 SearchBar 컴포넌트로 끌어올림, 게스트 타입 별 상태 조작 로직 추가, 타입 에러 해결
- 상태 끌어올리고 Counter 컴포넌트에서 useContext를 사용하려고 하니 타입에러가 나서 Counter 컴포넌트가 위치한 곳에서 커스텀훅을 사용하여 해결
Dae-Hwa/airbnb/#48
* refactor: Counter 컴포넌트에서 useContext 부분 타입 에러 다른 방법으로 해결
- HeadCountContextType 정의 부분에서 null 타입 추가
- setGuestCountState null 체크하는 코드 추가 (Counter.tsx 18라인)
- createContext 부분에서 초기값을 key별로 null 설정
- 커스텀훅 삭제
Dae-Hwa/airbnb/#48
---
FE/fe-airbnb/src/components/MinusButton.tsx | 14 ++++++
FE/fe-airbnb/src/components/PlusButton.tsx | 14 ++++++
FE/fe-airbnb/src/components/calendar/Day.tsx | 3 +-
.../src/components/headcount/Counter.tsx | 46 +++++++++++++++++
.../src/components/headcount/HeadCount.tsx | 50 +++++++++++++++++++
.../components/headcount/HeadCountModal.tsx | 15 ++++--
.../components/headcount/HeadCountTypes.tsx | 14 ++++++
.../src/components/searchBar/SearchBar.tsx | 37 +++++++++-----
.../components/searchBar/searchBarTypes.ts | 29 ++++++++++-
FE/fe-airbnb/src/icon/minus-circle.svg | 6 +--
.../src/icon/plus-circle-disabled.svg | 5 ++
FE/fe-airbnb/src/icon/plus-circle.svg | 8 +--
12 files changed, 214 insertions(+), 27 deletions(-)
create mode 100644 FE/fe-airbnb/src/components/MinusButton.tsx
create mode 100644 FE/fe-airbnb/src/components/PlusButton.tsx
create mode 100644 FE/fe-airbnb/src/components/headcount/Counter.tsx
create mode 100644 FE/fe-airbnb/src/components/headcount/HeadCount.tsx
create mode 100644 FE/fe-airbnb/src/components/headcount/HeadCountTypes.tsx
create mode 100644 FE/fe-airbnb/src/icon/plus-circle-disabled.svg
diff --git a/FE/fe-airbnb/src/components/MinusButton.tsx b/FE/fe-airbnb/src/components/MinusButton.tsx
new file mode 100644
index 000000000..ac9581913
--- /dev/null
+++ b/FE/fe-airbnb/src/components/MinusButton.tsx
@@ -0,0 +1,14 @@
+import styled from 'styled-components';
+import { ReactComponent as MinusIcon } from '../icon/minus-circle.svg';
+
+function MinusButton() {
+ return (
+
+ )
+}
+
+const Button = styled.button``
+
+export default MinusButton
diff --git a/FE/fe-airbnb/src/components/PlusButton.tsx b/FE/fe-airbnb/src/components/PlusButton.tsx
new file mode 100644
index 000000000..edd2c655c
--- /dev/null
+++ b/FE/fe-airbnb/src/components/PlusButton.tsx
@@ -0,0 +1,14 @@
+import styled from 'styled-components';
+import { ReactComponent as PlusIcon } from '../icon/plus-circle.svg';
+
+function PlusButton() {
+ return (
+
+ )
+}
+
+const Button = styled.button``
+
+export default PlusButton
diff --git a/FE/fe-airbnb/src/components/calendar/Day.tsx b/FE/fe-airbnb/src/components/calendar/Day.tsx
index eefab845b..2acc5cb23 100644
--- a/FE/fe-airbnb/src/components/calendar/Day.tsx
+++ b/FE/fe-airbnb/src/components/calendar/Day.tsx
@@ -4,8 +4,7 @@ import styled from 'styled-components'
import moment, { Moment } from 'moment';
import { CalendarContext } from '@components/searchBar/SearchBar'
-import { CalendarContextType } from '@components/searchBar/SearchBar'
-import { DayContainerProps } from '@components/searchBar/searchBarTypes';
+import { CalendarContextType, DayContainerProps } from '@components/searchBar/searchBarTypes';
type DayProps = {
day: Moment;
diff --git a/FE/fe-airbnb/src/components/headcount/Counter.tsx b/FE/fe-airbnb/src/components/headcount/Counter.tsx
new file mode 100644
index 000000000..98aad1cb5
--- /dev/null
+++ b/FE/fe-airbnb/src/components/headcount/Counter.tsx
@@ -0,0 +1,46 @@
+import { useContext } from 'react';
+
+import { Center, Flex } from '@chakra-ui/layout';
+import styled from 'styled-components';
+
+// import PlusButton from '@components/PlusButton';
+// import MinusButton from '@components/MinusButton';
+import { ReactComponent as PlusIcon } from '../../icon/plus-circle.svg';
+import { ReactComponent as MinusIcon } from '../../icon/minus-circle.svg';
+import { HandleCountType, HeadCountProps } from './HeadCountTypes';
+import { HeadCountContext } from '@components/searchBar/SearchBar';
+import { guestCountStateType, HeadCountContextType } from '@components/searchBar/searchBarTypes';
+
+function Counter({ guestType }: HeadCountProps) {
+ const { guestCountState, setGuestCountState } = useContext(HeadCountContext);
+
+ const handleCount = ({ guestType, count }: HandleCountType) => {
+ if(setGuestCountState === null) throw Error('setGuestCountState가 null임!');
+ setGuestCountState((guestCountState: guestCountStateType) => {
+ const checkYoung = guestType === 'children' || guestType === 'infants';
+ const checkParents = guestCountState.adults === 0;
+ const result = { ...guestCountState, [guestType]: guestCountState[guestType] + count};
+ return (checkYoung && checkParents && count === 1) ? { ...result, adults: 1 } : result;
+ })
+ }
+
+ return (
+
+ handleCount({ guestType, count: -1 })}/>
+
+ {guestCountState?.[guestType]}
+
+ handleCount({ guestType, count: 1 })}/>
+
+ )
+}
+
+const Count = styled.div`
+ margin: 0 16px;
+ width: 32px;
+ font-weight: bold;
+ font-size: ${({ theme }) => theme.fontSizes.L};
+ color: ${({ theme }) => theme.colors.gray1};
+`
+
+export default Counter
diff --git a/FE/fe-airbnb/src/components/headcount/HeadCount.tsx b/FE/fe-airbnb/src/components/headcount/HeadCount.tsx
new file mode 100644
index 000000000..a6ec4fe30
--- /dev/null
+++ b/FE/fe-airbnb/src/components/headcount/HeadCount.tsx
@@ -0,0 +1,50 @@
+import styled from 'styled-components';
+
+import Counter from '@components/headcount/Counter';
+import { HeadCountProps } from './HeadCountTypes';
+
+function HeadCount({ guestType }: HeadCountProps) {
+ const guestTypeTitle: string = {
+ adults: '성인',
+ children: '어린이',
+ infants: '유아',
+ }[guestType];
+
+ const guestTypeCaption: string = {
+ adults: '만 13세 이상',
+ children: '만 2~12세',
+ infants: '만 2세 미만',
+ }[guestType];
+
+ return (
+
+
+ {guestTypeTitle}
+ {guestTypeCaption}
+
+
+
+ )
+}
+
+const HeadCountContainer = styled.li`
+ display: flex;
+ justify-content: space-between;
+`
+
+const GuestTypeContainer = styled.div`
+ width: 80px;
+`
+
+const GuestTypeTitle = styled.div`
+ font-size: ${({ theme }) => theme.fontSizes.SM};
+ font-weight: bold;
+ color: ${({ theme }) => theme.colors.black};
+`
+
+const GuestTypeCaption = styled.div`
+ font-size: ${({ theme }) => theme.fontSizes.S};
+ color: ${({ theme }) => theme.colors.gray3};
+`
+
+export default HeadCount
diff --git a/FE/fe-airbnb/src/components/headcount/HeadCountModal.tsx b/FE/fe-airbnb/src/components/headcount/HeadCountModal.tsx
index 4bb50b7ae..3475c4a55 100644
--- a/FE/fe-airbnb/src/components/headcount/HeadCountModal.tsx
+++ b/FE/fe-airbnb/src/components/headcount/HeadCountModal.tsx
@@ -1,14 +1,21 @@
import styled from 'styled-components';
+import HeadCount from "@components/headcount/HeadCount"
+import { GuestType } from './HeadCountTypes';
function HeadCountModal() {
+ const guestTypes: GuestType[] = ['adults', 'children', 'infants'];
+
return (
-
- 인원 모달
-
+
+ {guestTypes.map((guestType, i) => )}
+
);
}
-const HeadCountContainer = styled.div`
+const HeadCountModalContainer = styled.ul`
+ display: flex;
+ flex-direction: column;
+ gap: 42px;
width: 400px;
height: fit-content;
border-radius: ${({ theme }) => theme.borders.L};
diff --git a/FE/fe-airbnb/src/components/headcount/HeadCountTypes.tsx b/FE/fe-airbnb/src/components/headcount/HeadCountTypes.tsx
new file mode 100644
index 000000000..2aac804ff
--- /dev/null
+++ b/FE/fe-airbnb/src/components/headcount/HeadCountTypes.tsx
@@ -0,0 +1,14 @@
+export type PlusButtonProps = {
+ disabled: boolean;
+};
+
+export type HeadCountProps = {
+ guestType: GuestType;
+}
+
+export type GuestType = 'adults' | 'children' | 'infants';
+
+export type HandleCountType = {
+ guestType: GuestType;
+ count: number;
+}
\ No newline at end of file
diff --git a/FE/fe-airbnb/src/components/searchBar/SearchBar.tsx b/FE/fe-airbnb/src/components/searchBar/SearchBar.tsx
index b08583fac..6bd2474e6 100644
--- a/FE/fe-airbnb/src/components/searchBar/SearchBar.tsx
+++ b/FE/fe-airbnb/src/components/searchBar/SearchBar.tsx
@@ -1,4 +1,4 @@
-import { createContext, Dispatch, ReactElement, SetStateAction, useState } from 'react';
+import { createContext, ReactElement, useState } from 'react';
import styled from 'styled-components';
import moment, { Moment } from 'moment';
import { Center, Flex } from '@chakra-ui/layout';
@@ -8,22 +8,16 @@ import HeadCountModal from '@components/headcount/HeadCountModal';
import PriceModal from '@components/price/PriceModal';
import SearchButton from '../SearchButton';
import SearchBarBtn from './SearchBarBtn';
-import { SearchBarBtnType, SelectedContentProps } from './searchBarTypes';
-
-export type CalendarContextType = {
- calendars: Moment[];
- setCalendars: Dispatch>;
- checkInMoment: Moment | null;
- setCheckInMoment: Dispatch>;
- checkOutMoment: Moment | null;
- setCheckOutMoment: Dispatch>;
-}
+import { CalendarContextType, HeadCountContextType, SearchBarBtnType, SelectedContentProps } from './searchBarTypes';
export const CalendarContext = createContext(null);
+export const HeadCountContext = createContext({guestCountState: null, setGuestCountState: null});
function SearchBar() {
const [selectedBtn, setSelectedBtn] = useState(null);
+ // ============================ calendar 상태 ============================
+
const initialCalendars = [
moment().add(-1, 'M'),
moment(),
@@ -46,6 +40,17 @@ function SearchBar() {
},
};
+ // ============================ headcount 상태 ============================
+
+ const [guestCountState, setGuestCountState] = useState({ adults: 0, children: 0, infants: 0 });
+ const { adults, children, infants } = guestCountState;
+ const headCountState = {
+ values: {
+ guestCountState,
+ setGuestCountState
+ }
+ }
+
const renderModal = (): ReactElement | void => {
switch (selectedBtn) {
case SearchBarBtnType.CHECK_IN_OUT:
@@ -62,7 +67,9 @@ function SearchBar() {
case SearchBarBtnType.HEAD_COUNT:
return (
-
+
+
+
);
}
}
@@ -108,7 +115,11 @@ function SearchBar() {
handleClickSearchBarBtn(SearchBarBtnType.HEAD_COUNT)}>
인원
- 게스트 추가
+ {
+ guestCountState.adults || guestCountState.children || guestCountState.infants
+ ? 게스트 {adults + children}명, 유아 {infants}명
+ : 게스트 추가
+ }
diff --git a/FE/fe-airbnb/src/components/searchBar/searchBarTypes.ts b/FE/fe-airbnb/src/components/searchBar/searchBarTypes.ts
index df84a35c8..eeb4e29c2 100644
--- a/FE/fe-airbnb/src/components/searchBar/searchBarTypes.ts
+++ b/FE/fe-airbnb/src/components/searchBar/searchBarTypes.ts
@@ -1,3 +1,6 @@
+import { Dispatch, SetStateAction } from 'react'
+import { Moment } from 'moment';
+
export enum SearchBarBtnType {
CHECK_IN_OUT = 'check-in-out',
PRICE = 'price',
@@ -12,4 +15,28 @@ export type DayContainerProps = {
isSelected: boolean,
isBetween: boolean,
disabled: boolean
-}
\ No newline at end of file
+}
+
+export type CalendarContextType = {
+ calendars: Moment[];
+ setCalendars: Dispatch>;
+ checkInMoment: Moment | null;
+ setCheckInMoment: Dispatch>;
+ checkOutMoment: Moment | null;
+ setCheckOutMoment: Dispatch>;
+}
+
+export type guestCountStateType = {
+ adults: number,
+ children: number,
+ infants: number
+}
+
+export type HeadCountContextType = {
+ guestCountState: guestCountStateType | null,
+ setGuestCountState: React.Dispatch> | null
+}
diff --git a/FE/fe-airbnb/src/icon/minus-circle.svg b/FE/fe-airbnb/src/icon/minus-circle.svg
index 31b966e83..ec387d311 100644
--- a/FE/fe-airbnb/src/icon/minus-circle.svg
+++ b/FE/fe-airbnb/src/icon/minus-circle.svg
@@ -1,4 +1,4 @@
-