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 @@ - - - + + + diff --git a/FE/fe-airbnb/src/icon/plus-circle-disabled.svg b/FE/fe-airbnb/src/icon/plus-circle-disabled.svg new file mode 100644 index 000000000..42abf961f --- /dev/null +++ b/FE/fe-airbnb/src/icon/plus-circle-disabled.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/FE/fe-airbnb/src/icon/plus-circle.svg b/FE/fe-airbnb/src/icon/plus-circle.svg index 0db5e0015..ad9be6937 100644 --- a/FE/fe-airbnb/src/icon/plus-circle.svg +++ b/FE/fe-airbnb/src/icon/plus-circle.svg @@ -1,5 +1,5 @@ - - - - + + + + \ No newline at end of file