From 40d2778e185477a158ffce625e4c85b5dc9ffdbf Mon Sep 17 00:00:00 2001 From: joanShim Date: Wed, 10 Jan 2024 16:28:46 +0900 Subject: [PATCH 1/9] =?UTF-8?q?Feat:=20createTrip=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 + pnpm-lock.yaml | 73 +++++++++++- src/components/DatePicker/DatePicker.tsx | 106 ++++++++++++++++++ .../DatePicker/DatePickerInfinite.tsx | 29 +++++ src/components/common/header/BackHeader.tsx | 16 +++ src/components/createTrip/SelectDate.tsx | 12 ++ src/components/search/ResultCategory.tsx | 2 +- src/components/search/SearchRegion.tsx | 3 + src/pages/create/createTrip.page.tsx | 43 +++++++ src/router/mainRouter.tsx | 4 + src/router/routerLayout.tsx | 1 + 11 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 src/components/DatePicker/DatePicker.tsx create mode 100644 src/components/DatePicker/DatePickerInfinite.tsx create mode 100644 src/components/common/header/BackHeader.tsx create mode 100644 src/components/createTrip/SelectDate.tsx create mode 100644 src/components/search/SearchRegion.tsx create mode 100644 src/pages/create/createTrip.page.tsx diff --git a/package.json b/package.json index 2af0d0d1..f06c44ef 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,12 @@ "@tanstack/react-query": "^5.14.6", "@tanstack/react-query-devtools": "^5.14.6", "axios": "^1.6.2", + "date-fns": "^3.1.0", "msw": "0.36.3", "path": "^0.12.7", "react": "^18.2.0", + "react-date-range": "2.0.0-alpha.4", + "react-day-picker": "^8.10.0", "react-dom": "^18.2.0", "react-infinite-scroller": "^1.2.6", "react-kakao-maps-sdk": "^1.1.24", @@ -37,6 +40,7 @@ }, "devDependencies": { "@types/react": "^18.2.43", + "@types/react-date-range": "^1.4.9", "@types/react-dom": "^18.2.17", "@types/react-infinite-scroller": "^1.2.5", "@types/react-modal": "^3.16.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12b2de0d..a6a746aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ dependencies: axios: specifier: ^1.6.2 version: 1.6.3 + date-fns: + specifier: ^3.1.0 + version: 3.1.0 msw: specifier: 0.36.3 version: 0.36.3 @@ -44,6 +47,12 @@ dependencies: react: specifier: ^18.2.0 version: 18.2.0 + react-date-range: + specifier: 2.0.0-alpha.4 + version: 2.0.0-alpha.4(date-fns@3.1.0)(react@18.2.0) + react-day-picker: + specifier: ^8.10.0 + version: 8.10.0(date-fns@3.1.0)(react@18.2.0) react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) @@ -82,6 +91,9 @@ devDependencies: '@types/react': specifier: ^18.2.43 version: 18.2.45 + '@types/react-date-range': + specifier: ^1.4.9 + version: 1.4.9 '@types/react-dom': specifier: ^18.2.17 version: 18.2.18 @@ -1405,7 +1417,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 - dev: false /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} @@ -2688,6 +2699,13 @@ packages: /@types/prop-types@15.7.11: resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + /@types/react-date-range@1.4.9: + resolution: {integrity: sha512-5oVEDW0ElYmY1+YVSzdMUR8stxSI5QrRJCgCFUvuEAV5197t412vimD9aVTW6g4JTaxCnMmB1BdEOT/odpaBxQ==} + dependencies: + '@types/react': 18.2.45 + date-fns: 2.30.0 + dev: true + /@types/react-dom@18.2.18: resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==} dependencies: @@ -3180,6 +3198,10 @@ packages: optionalDependencies: fsevents: 2.3.3 + /classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + dev: false + /cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -3356,6 +3378,17 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + dependencies: + '@babel/runtime': 7.23.6 + dev: true + + /date-fns@3.1.0: + resolution: {integrity: sha512-ZO7yefXV/wCWzd3I9haCHmfzlfA3i1a2HHO7ZXjtJrRjXt8FULKJ2Vl8wji3XYF4dQ0ZJ/tokXDZeYlFvgms9Q==} + dev: false + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -4602,6 +4635,30 @@ packages: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true + /react-date-range@2.0.0-alpha.4(date-fns@3.1.0)(react@18.2.0): + resolution: {integrity: sha512-8IP6DVW6nGQu1PUUw7iCAOnRfLP8cKrjDNKZFb9z7SAWUZ/RiUYNbUfdZxGySlcx5V52BpgFpEuAKD16pEN+MA==} + peerDependencies: + date-fns: 3.0.6 || >=3.0.0 + react: ^0.14 || ^15.0.0-rc || >=15.0 + dependencies: + classnames: 2.5.1 + date-fns: 3.1.0 + prop-types: 15.8.1 + react: 18.2.0 + react-list: 0.8.17(react@18.2.0) + shallow-equal: 1.2.1 + dev: false + + /react-day-picker@8.10.0(date-fns@3.1.0)(react@18.2.0): + resolution: {integrity: sha512-mz+qeyrOM7++1NCb1ARXmkjMkzWVh2GL9YiPbRjKe0zHccvekk4HE+0MPOZOrosn8r8zTHIIeOUXTmXRqmkRmg==} + peerDependencies: + date-fns: ^2.28.0 || ^3.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + date-fns: 3.1.0 + react: 18.2.0 + dev: false + /react-dom@18.2.0(react@18.2.0): resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -4641,6 +4698,15 @@ packages: resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} dev: false + /react-list@0.8.17(react@18.2.0): + resolution: {integrity: sha512-pgmzGi0G5uGrdHzMhgO7KR1wx5ZXVvI3SsJUmkblSAKtewIhMwbQiMuQiTE83ozo04BQJbe0r3WIWzSO0dR1xg==} + peerDependencies: + react: 0.14 || 15 - 18 + dependencies: + prop-types: 15.8.1 + react: 18.2.0 + dev: false + /react-modal@3.16.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==} engines: {node: '>=8'} @@ -4806,7 +4872,6 @@ packages: /regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: false /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} @@ -4938,6 +5003,10 @@ packages: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} dev: false + /shallow-equal@1.2.1: + resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} + dev: false + /shallowequal@1.1.0: resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} dev: false diff --git a/src/components/DatePicker/DatePicker.tsx b/src/components/DatePicker/DatePicker.tsx new file mode 100644 index 00000000..4f33dcae --- /dev/null +++ b/src/components/DatePicker/DatePicker.tsx @@ -0,0 +1,106 @@ +import { format, addDays } from 'date-fns'; +import { ko } from 'date-fns/locale'; +import { useState } from 'react'; +import { DateFormatter, DayPicker, DateRange } from 'react-day-picker'; +import 'react-day-picker/dist/style.css'; +import styled from 'styled-components'; + +const formatCaption: DateFormatter = (date, options) => { + const y = date.getFullYear(); + const m = format(date, 'LLLL', { locale: options?.locale }); + return `${y} ${m} `; +}; + +export const DatePicker = () => { + const today = new Date(); + const defaultSelected: DateRange = { + from: today, + to: addDays(today, 4), + }; + const [range, setRange] = useState(defaultSelected); + + return ( + +
+ {range?.from ? ( + !range.to ? ( +

{format(range.from, 'PPP', { locale: ko })}

+ ) : ( +

+ {format(range.from, 'PP', { locale: ko })} –{' '} + {format(range.to, 'PP', { locale: ko })} +

+ ) + ) : ( +

날짜를 선택하세요.

+ )} +
+ +
+ ); +}; + +const DatePickerSection = styled.section` + .rdp { + --rdp-cell-size: 40px; + --rdp-caption-font-size: 14px; + --rdp-accent-color: #0000ff; + --rdp-background-color: #fff; + --rdp-accent-color-dark: #3003e1; + --rdp-background-color-dark: #180270; + --rdp-outline: none; + --rdp-outline-selected: none; + --rdp-selected-color: #fff; + margin: 0; + } + .rdp-months { + flex-direction: column; + width: 100%; + } + .rdp-month { + margin: 0; + width: 100%; + } + + .rdp-caption, + select.rdp-dropdown { + text-align: left; + } + + .rdp-caption_label { + font-size: 15px; + } + + .rdp-caption_start { + margin-bottom: 30px; + } + + .rdp-table { + max-width: 100%; + width: 100%; + } + + .rdp-row { + /* display: flex; */ + width: 100%; + } + + .rdp-day_selected { + background-color: #ddd; + } + + .rdp-button:hover:not([disabled]):not(.rdp-day_selected) { + background-color: #eee; + } +`; diff --git a/src/components/DatePicker/DatePickerInfinite.tsx b/src/components/DatePicker/DatePickerInfinite.tsx new file mode 100644 index 00000000..46aeaf22 --- /dev/null +++ b/src/components/DatePicker/DatePickerInfinite.tsx @@ -0,0 +1,29 @@ +import 'react-date-range/dist/styles.css'; // main css file +import 'react-date-range/dist/theme/default.css'; // theme css file +import { DateRange } from 'react-date-range'; +import { useState } from 'react'; +import { addDays } from 'date-fns'; + +export const DatePickerInfinite = () => { + const [state, setState] = useState([ + { + startDate: new Date(), + endDate: null, + key: 'selection', + }, + ]); + return ( +
+ setState([item.selection])} + moveRangeOnFirstSelection={false} + direction="vertical" + scroll={{ enabled: true }} + ranges={state} + minDate={new Date()} + maxDate={addDays(new Date(), 900)} + /> +
+ ); +}; diff --git a/src/components/common/header/BackHeader.tsx b/src/components/common/header/BackHeader.tsx new file mode 100644 index 00000000..d5c36214 --- /dev/null +++ b/src/components/common/header/BackHeader.tsx @@ -0,0 +1,16 @@ +import { useNavigate } from 'react-router-dom'; +import { BackIcon } from '../icons/Icons'; + +export default function BackHeader() { + const navigate = useNavigate(); + + const goBack = () => { + navigate(-1); + }; + + return ( +
+ +
+ ); +} diff --git a/src/components/createTrip/SelectDate.tsx b/src/components/createTrip/SelectDate.tsx new file mode 100644 index 00000000..4ba07441 --- /dev/null +++ b/src/components/createTrip/SelectDate.tsx @@ -0,0 +1,12 @@ +import { DatePicker } from '@components/DatePicker/DatePicker'; +import { DatePickerInfinite } from '@components/DatePicker/DatePickerInfinite'; + +export const SelectDate = ({ onClose }) => { + return ( +
+ {/* */} + + +
+ ); +}; diff --git a/src/components/search/ResultCategory.tsx b/src/components/search/ResultCategory.tsx index 6707f6c0..e6447af1 100644 --- a/src/components/search/ResultCategory.tsx +++ b/src/components/search/ResultCategory.tsx @@ -1,7 +1,7 @@ import { ButtonWhite } from '@components/common/button/Button'; -import { ResultItem } from './ResultItem'; import { TourType } from '@/@types/tours.types'; import { InfiniteQueryObserverResult } from '@tanstack/react-query'; +import { ResultItem } from './ResultItem'; interface ResultCategoryProps { data: TourType[]; diff --git a/src/components/search/SearchRegion.tsx b/src/components/search/SearchRegion.tsx new file mode 100644 index 00000000..cb2ab6ea --- /dev/null +++ b/src/components/search/SearchRegion.tsx @@ -0,0 +1,3 @@ +export const SearchRegion = () => { + return
SearchRegion
; +}; diff --git a/src/pages/create/createTrip.page.tsx b/src/pages/create/createTrip.page.tsx new file mode 100644 index 00000000..978af6cc --- /dev/null +++ b/src/pages/create/createTrip.page.tsx @@ -0,0 +1,43 @@ +import BackHeader from '@components/common/header/BackHeader'; +import { useState } from 'react'; +import { SelectDate } from '../../components/createTrip/SelectDate'; + +export const CreateTrip = () => { + const [inputValue, setInputValue] = useState('나의 여정'); + const [showSelectDate, setShowSelectDate] = useState(false); + + const handleChange = (e) => { + setInputValue(e.target.value); + }; + + const handleDateButtonClick = () => { + setShowSelectDate(true); + }; + + const handleCloseSelectDate = () => { + setShowSelectDate(false); + }; + + if (showSelectDate) { + return ; + } + return ( +
+ +
여행 생성하기
+ + +
+ ); +}; diff --git a/src/router/mainRouter.tsx b/src/router/mainRouter.tsx index 549ca215..85c184b2 100644 --- a/src/router/mainRouter.tsx +++ b/src/router/mainRouter.tsx @@ -5,6 +5,8 @@ import Detail from '@pages/detail/detail.page'; import ReviewComment from '@pages/reviewComment/reviewComment.page'; import ReviewPosting from '@pages/reviewPosting/reviewPosting.page'; import MainLayout from './routerLayout'; +import { SelectDate } from '@components/createTrip/SelectDate'; +import { CreateTrip } from '@pages/create/createTrip.page'; const MainRouter = () => { return ( @@ -16,6 +18,8 @@ const MainRouter = () => { } /> } /> } /> + } /> + } /> diff --git a/src/router/routerLayout.tsx b/src/router/routerLayout.tsx index 6a37f7d9..435e01cd 100644 --- a/src/router/routerLayout.tsx +++ b/src/router/routerLayout.tsx @@ -13,6 +13,7 @@ const MainLayout = () => { '/detail', '/reviewPosting', '/reviewComment', + '/create', ]; const showNav = !hideNavPaths.some((path) => location.pathname.includes(path), From 55739c9aacaa2daccc5315639d6c367947ae966e Mon Sep 17 00:00:00 2001 From: joanShim Date: Thu, 11 Jan 2024 15:27:01 +0900 Subject: [PATCH 2/9] =?UTF-8?q?Feat:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 171 +++++++++++++++++++++++++++ src/pages/create/createTrip.page.tsx | 14 ++- src/router/mainRouter.tsx | 2 - 4 files changed, 182 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index f06c44ef..fa16e9cf 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-radio-group": "^1.1.3", + "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-toggle-group": "^1.0.4", "@svgr/rollup": "^8.1.0", "@tanstack/react-query": "^5.14.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6a746aa..f54144a4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ dependencies: '@radix-ui/react-radio-group': specifier: ^1.1.3 version: 1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-select': + specifier: ^2.0.0 + version: 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-toggle-group': specifier: ^1.0.4 version: 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) @@ -1709,6 +1712,34 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@floating-ui/core@1.5.3: + resolution: {integrity: sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q==} + dependencies: + '@floating-ui/utils': 0.2.1 + dev: false + + /@floating-ui/dom@1.5.4: + resolution: {integrity: sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ==} + dependencies: + '@floating-ui/core': 1.5.3 + '@floating-ui/utils': 0.2.1 + dev: false + + /@floating-ui/react-dom@2.0.5(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-UsBK30Bg+s6+nsgblXtZmwHhgS2vmbuQK22qgt2pTQM6M3X6H1+cQcLXqgRY3ihVLcZJE6IvqDQozhsnIVqK/Q==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/dom': 1.5.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@floating-ui/utils@0.2.1: + resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} + dev: false + /@humanwhocodes/config-array@0.11.13: resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} engines: {node: '>=10.10.0'} @@ -1829,12 +1860,39 @@ packages: resolution: {integrity: sha512-SseX4HmwtyCYp51GFwFNqwTTcsoRwG/2iUUicVtlEtJrIee4al63o4UzpvD3b0/XUxBbJdxAUXVDYPlC6HQytw==} dev: false + /@radix-ui/number@1.0.1: + resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==} + dependencies: + '@babel/runtime': 7.23.6 + dev: false + /@radix-ui/primitive@1.0.1: resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} dependencies: '@babel/runtime': 7.23.6 dev: false + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.6 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.45 + '@types/react-dom': 18.2.18 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==} peerDependencies: @@ -2048,6 +2106,36 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.6 + '@floating-ui/react-dom': 2.0.5(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/rect': 1.0.1 + '@types/react': 18.2.45 + '@types/react-dom': 18.2.18 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} peerDependencies: @@ -2171,6 +2259,47 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-select@2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.6 + '@radix-ui/number': 1.0.1 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.45)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.45 + '@types/react-dom': 18.2.18 + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.45)(react@18.2.0) + dev: false + /@radix-ui/react-slot@1.0.2(@types/react@18.2.45)(react@18.2.0): resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: @@ -2308,6 +2437,21 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.45)(react@18.2.0): + resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.6 + '@radix-ui/rect': 1.0.1 + '@types/react': 18.2.45 + react: 18.2.0 + dev: false + /@radix-ui/react-use-size@1.0.1(@types/react@18.2.45)(react@18.2.0): resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} peerDependencies: @@ -2323,6 +2467,33 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.6 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.45 + '@types/react-dom': 18.2.18 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/rect@1.0.1: + resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} + dependencies: + '@babel/runtime': 7.23.6 + dev: false + /@remix-run/router@1.14.1: resolution: {integrity: sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow==} engines: {node: '>=14.0.0'} diff --git a/src/pages/create/createTrip.page.tsx b/src/pages/create/createTrip.page.tsx index 978af6cc..190c1b26 100644 --- a/src/pages/create/createTrip.page.tsx +++ b/src/pages/create/createTrip.page.tsx @@ -6,7 +6,7 @@ export const CreateTrip = () => { const [inputValue, setInputValue] = useState('나의 여정'); const [showSelectDate, setShowSelectDate] = useState(false); - const handleChange = (e) => { + const handleChange = (e: React.ChangeEvent) => { setInputValue(e.target.value); }; @@ -24,7 +24,7 @@ export const CreateTrip = () => { return (
-
여행 생성하기
+
여행 생성하기
{ /> + {}} + />
); }; diff --git a/src/router/mainRouter.tsx b/src/router/mainRouter.tsx index 85c184b2..cebc13ab 100644 --- a/src/router/mainRouter.tsx +++ b/src/router/mainRouter.tsx @@ -5,7 +5,6 @@ import Detail from '@pages/detail/detail.page'; import ReviewComment from '@pages/reviewComment/reviewComment.page'; import ReviewPosting from '@pages/reviewPosting/reviewPosting.page'; import MainLayout from './routerLayout'; -import { SelectDate } from '@components/createTrip/SelectDate'; import { CreateTrip } from '@pages/create/createTrip.page'; const MainRouter = () => { @@ -19,7 +18,6 @@ const MainRouter = () => { } /> } /> } /> - } /> From 98bb34f18f766cdf732a10336165fc7c8f738bfe Mon Sep 17 00:00:00 2001 From: joanShim Date: Thu, 11 Jan 2024 23:30:03 +0900 Subject: [PATCH 3/9] =?UTF-8?q?Fix:=20=EB=92=A4=EB=A1=9C=EA=B0=80=EA=B8=B0?= =?UTF-8?q?=20=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DatePicker/Calendar.tsx | 8 ++++---- src/components/createTrip/SelectDate.tsx | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/DatePicker/Calendar.tsx b/src/components/DatePicker/Calendar.tsx index 058b3bf0..2e7af75b 100644 --- a/src/components/DatePicker/Calendar.tsx +++ b/src/components/DatePicker/Calendar.tsx @@ -85,7 +85,7 @@ const Calendar: React.FC = () => { let afterClass = ''; if (isStart || isEnd) { - dayClass = 'rounded-full bg-main2 text-white z-10'; + dayClass = 'rounded-full w-[48px] bg-main2 text-white z-10'; } else if (isMiddle) { dayClass = 'bg-[#DAFBFF] z-0'; beforeClass = isSecondDayInRange(date) @@ -140,7 +140,7 @@ const Calendar: React.FC = () => { } return ( -
+
{format(monthDate, 'yyyy년 MM월', { locale: ko })}
@@ -156,9 +156,9 @@ const Calendar: React.FC = () => { <>
{startDate && endDate - ? `${format(startDate, 'MM. dd', { locale: ko })} - ${format( + ? `${format(startDate, 'MM.dd', { locale: ko })} - ${format( endDate, - 'MM. dd', + 'MM.dd', { locale: ko }, )} (${differenceInDays(endDate, startDate)}박 ${ differenceInDays(endDate, startDate) + 1 diff --git a/src/components/createTrip/SelectDate.tsx b/src/components/createTrip/SelectDate.tsx index ce57efdc..6d7e319b 100644 --- a/src/components/createTrip/SelectDate.tsx +++ b/src/components/createTrip/SelectDate.tsx @@ -1,11 +1,13 @@ import { ButtonPrimary } from '@components/common/button/Button'; import Calendar from '@components/DatePicker/Calendar'; -import BackHeader from '@components/common/header/BackHeader'; +import { BackIcon } from '@components/common/icons/Icons'; export const SelectDate = ({ onClose }: { onClose: () => void }) => { return (
- +
+ +
완료 From bfea9cff50e672911c619d2dae0960538aed9428 Mon Sep 17 00:00:00 2001 From: joanShim Date: Thu, 11 Jan 2024 23:34:17 +0900 Subject: [PATCH 4/9] =?UTF-8?q?Feat:=20=EB=A9=94=EC=9D=B8=20FAB=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EA=B2=BD=EB=A1=9C=20=EC=9D=B4=EB=8F=99=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Tours/CreateTripButton.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/Tours/CreateTripButton.tsx b/src/components/Tours/CreateTripButton.tsx index f131de28..4851ece0 100644 --- a/src/components/Tours/CreateTripButton.tsx +++ b/src/components/Tours/CreateTripButton.tsx @@ -1,7 +1,16 @@ +import { useNavigate } from 'react-router-dom'; + const CreateTripButton = () => { + const navigate = useNavigate(); + + const onClick = () => { + navigate('/create'); + }; return (
- +
); }; From 39d894476d3e0cee00a3f6eec6a2f293386f96af Mon Sep 17 00:00:00 2001 From: joanShim Date: Thu, 11 Jan 2024 23:35:38 +0900 Subject: [PATCH 5/9] =?UTF-8?q?Feat:=20=EB=A9=94=EC=9D=B8=20FAB=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EA=B2=BD=EB=A1=9C=20=EC=9D=B4=EB=8F=99=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Tours/CreateTripButton.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/Tours/CreateTripButton.tsx b/src/components/Tours/CreateTripButton.tsx index 4851ece0..60e444d1 100644 --- a/src/components/Tours/CreateTripButton.tsx +++ b/src/components/Tours/CreateTripButton.tsx @@ -3,12 +3,13 @@ import { useNavigate } from 'react-router-dom'; const CreateTripButton = () => { const navigate = useNavigate(); - const onClick = () => { - navigate('/create'); - }; return (
-
From 875b5b075674d945839e504d6e0c462ef70664cc Mon Sep 17 00:00:00 2001 From: joanShim Date: Thu, 11 Jan 2024 23:52:34 +0900 Subject: [PATCH 6/9] =?UTF-8?q?Fix:=20=EC=9D=B8=ED=92=8B=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/create/createTrip.page.tsx | 29 +++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/pages/create/createTrip.page.tsx b/src/pages/create/createTrip.page.tsx index 190c1b26..1d8a2685 100644 --- a/src/pages/create/createTrip.page.tsx +++ b/src/pages/create/createTrip.page.tsx @@ -1,11 +1,16 @@ import BackHeader from '@components/common/header/BackHeader'; import { useState } from 'react'; import { SelectDate } from '../../components/createTrip/SelectDate'; +import { CloseIcon } from '@components/common/icons/Icons'; export const CreateTrip = () => { const [inputValue, setInputValue] = useState('나의 여정'); const [showSelectDate, setShowSelectDate] = useState(false); + const clearInput = () => { + setInputValue(''); + }; + const handleChange = (e: React.ChangeEvent) => { setInputValue(e.target.value); }; @@ -25,13 +30,23 @@ export const CreateTrip = () => {
여행 생성하기
- +
+ + {inputValue && ( +
+ +
+ )} +
Date: Fri, 12 Jan 2024 16:09:11 +0900 Subject: [PATCH 7/9] =?UTF-8?q?Feat:=20=EC=9D=B8=ED=92=8B=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/icons/Icons.tsx | 41 ++++++++++++ src/components/createTrip/InputField.tsx | 84 ++++++++++++++++++++++++ src/components/search/SearchInput.tsx | 2 +- src/index.css | 8 +++ src/pages/create/createTrip.page.tsx | 82 ++++++++++++++--------- 5 files changed, 184 insertions(+), 33 deletions(-) create mode 100644 src/components/createTrip/InputField.tsx diff --git a/src/components/common/icons/Icons.tsx b/src/components/common/icons/Icons.tsx index 05317068..2809744d 100644 --- a/src/components/common/icons/Icons.tsx +++ b/src/components/common/icons/Icons.tsx @@ -925,3 +925,44 @@ export const SuccessIcon = () => { ); }; + +export const DocumentIcon: React.FC = ({ + size = 24, + fill = '#1E1E1E', + cursor = 'pointer', +}) => { + return ( + + + + + + + ); +}; diff --git a/src/components/createTrip/InputField.tsx b/src/components/createTrip/InputField.tsx new file mode 100644 index 00000000..bbb41929 --- /dev/null +++ b/src/components/createTrip/InputField.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { CloseIcon } from '@components/common/icons/Icons'; + +interface InputFieldProps { + icon: React.ElementType; + placeholder: string; + value?: string | number; + onChange?: (e: React.ChangeEvent | number) => void; + onClear?: () => void; + type?: string; + onClick?: () => void; + minValue?: number; + maxValue?: number; +} + +export const InputField: React.FC = ({ + icon: IconComponent, + placeholder, + value, + onChange, + onClear, + type = 'text', + onClick, + minValue = -Infinity, + maxValue = Infinity, + ...props +}) => { + const handleDecrease = () => { + if (typeof value === 'number' && onChange) { + onChange(Math.max(value - 1, minValue)); + } + }; + + const handleIncrease = () => { + if (typeof value === 'number' && onChange) { + onChange(Math.min(value + 1, maxValue)); + } + }; + + return ( +
+
+ +
+ {onClick ? ( +
+ {placeholder} +
+ ) : ( + onChange && onChange(e.target.value)} + {...props} + /> + )} + {!onClick && type === 'number' && ( +
+ + +
+ )} + {!onClick && type !== 'number' && value && ( +
+ +
+ )} +
+ ); +}; diff --git a/src/components/search/SearchInput.tsx b/src/components/search/SearchInput.tsx index dd7902b6..c3dbbabf 100644 --- a/src/components/search/SearchInput.tsx +++ b/src/components/search/SearchInput.tsx @@ -65,7 +65,7 @@ const SearchInput = () => { /> {inputValue && (
diff --git a/src/index.css b/src/index.css index c2b70377..ba55d149 100644 --- a/src/index.css +++ b/src/index.css @@ -129,3 +129,11 @@ code { .no-scrollbar::-webkit-scrollbar { display: none; } + +@layer base { + input[type='number']::-webkit-inner-spin-button, + input[type='number']::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; + } +} diff --git a/src/pages/create/createTrip.page.tsx b/src/pages/create/createTrip.page.tsx index 1d8a2685..eca20c70 100644 --- a/src/pages/create/createTrip.page.tsx +++ b/src/pages/create/createTrip.page.tsx @@ -1,18 +1,26 @@ import BackHeader from '@components/common/header/BackHeader'; import { useState } from 'react'; import { SelectDate } from '../../components/createTrip/SelectDate'; -import { CloseIcon } from '@components/common/icons/Icons'; +import { + CalendarIcon, + DocumentIcon, + SearchIcon, + UserIcon, +} from '@components/common/icons/Icons'; +import { ButtonPrimary } from '@components/common/button/Button'; +import { InputField } from '@components/createTrip/InputField'; export const CreateTrip = () => { - const [inputValue, setInputValue] = useState('나의 여정'); + const [title, setTitle] = useState(''); + const [numOfMembers, setNumOfMembers] = useState(2); const [showSelectDate, setShowSelectDate] = useState(false); const clearInput = () => { - setInputValue(''); + setTitle(''); }; const handleChange = (e: React.ChangeEvent) => { - setInputValue(e.target.value); + setTitle(e.target.value); }; const handleDateButtonClick = () => { @@ -27,38 +35,48 @@ export const CreateTrip = () => { return ; } return ( -
+
여행 생성하기
-
- - {inputValue && ( -
- -
- )} -
- - {}} + + setNumOfMembers(newValue)} + minValue={2} + maxValue={100} /> + + + +
{}}> +
+ +
+
+ 여행지 (선택) +
+
+ +
+ {}}>완료 +
); }; From f0fedddd86559edc017b33eeb7faa7daeb3479ae Mon Sep 17 00:00:00 2001 From: joanShim Date: Sat, 13 Jan 2024 19:54:24 +0900 Subject: [PATCH 8/9] =?UTF-8?q?Refactor:=20=EC=9D=B8=ED=92=8B=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DatePicker/Calendar.tsx | 13 +- src/components/createTrip/InputField.tsx | 81 ++---------- src/components/createTrip/SelectDate.tsx | 16 ++- .../createTrip/SelectDestination.tsx | 16 +++ src/pages/create/createTrip.page.tsx | 116 +++++++++++------- 5 files changed, 125 insertions(+), 117 deletions(-) create mode 100644 src/components/createTrip/SelectDestination.tsx diff --git a/src/components/DatePicker/Calendar.tsx b/src/components/DatePicker/Calendar.tsx index 2e7af75b..ac5a9266 100644 --- a/src/components/DatePicker/Calendar.tsx +++ b/src/components/DatePicker/Calendar.tsx @@ -14,11 +14,15 @@ import { import { ko } from 'date-fns/locale'; import 'tailwindcss/tailwind.css'; +interface CalendarProps { + onDateSelect: (startDate: Date | null, endDate: Date | null) => void; +} + const CALENDAR_LENGTH = 42; const DAY_OF_WEEK = 7; const DAY_LIST = ['일', '월', '화', '수', '목', '금', '토']; -const Calendar: React.FC = () => { +const Calendar: React.FC = ({ onDateSelect }) => { const [startDate, setStartDate] = useState(null); const [endDate, setEndDate] = useState(null); const currentDate = new Date(); @@ -30,6 +34,11 @@ const Calendar: React.FC = () => { ]); const calendarRef = useRef(null); + // 날짜 변경될 때 콜백함수 + useEffect(() => { + onDateSelect(startDate, endDate); + }, [startDate, endDate, onDateSelect]); + const handleScroll = () => { const { scrollTop, scrollHeight, clientHeight } = calendarRef.current!; if (scrollTop + clientHeight >= scrollHeight - 100) { @@ -166,7 +175,7 @@ const Calendar: React.FC = () => { : '날짜를 선택해주세요.'}
-
+
{visibleMonths.map((month, idx) => (
{renderCalendar(month)}
))} diff --git a/src/components/createTrip/InputField.tsx b/src/components/createTrip/InputField.tsx index bbb41929..738ed840 100644 --- a/src/components/createTrip/InputField.tsx +++ b/src/components/createTrip/InputField.tsx @@ -1,84 +1,27 @@ -import React from 'react'; -import { CloseIcon } from '@components/common/icons/Icons'; +import { FC, ReactNode } from 'react'; interface InputFieldProps { - icon: React.ElementType; - placeholder: string; - value?: string | number; - onChange?: (e: React.ChangeEvent | number) => void; - onClear?: () => void; - type?: string; + icon: FC; onClick?: () => void; - minValue?: number; - maxValue?: number; + isClickable?: boolean; + children: ReactNode; } -export const InputField: React.FC = ({ - icon: IconComponent, - placeholder, - value, - onChange, - onClear, - type = 'text', +export const InputField: FC = ({ + icon: Icon, onClick, - minValue = -Infinity, - maxValue = Infinity, - ...props + isClickable, + children, }) => { - const handleDecrease = () => { - if (typeof value === 'number' && onChange) { - onChange(Math.max(value - 1, minValue)); - } - }; - - const handleIncrease = () => { - if (typeof value === 'number' && onChange) { - onChange(Math.min(value + 1, maxValue)); - } - }; - return (
- +
- {onClick ? ( -
- {placeholder} -
- ) : ( - onChange && onChange(e.target.value)} - {...props} - /> - )} - {!onClick && type === 'number' && ( -
- - -
- )} - {!onClick && type !== 'number' && value && ( -
- -
- )} + {children}
); }; diff --git a/src/components/createTrip/SelectDate.tsx b/src/components/createTrip/SelectDate.tsx index 6d7e319b..78e640e0 100644 --- a/src/components/createTrip/SelectDate.tsx +++ b/src/components/createTrip/SelectDate.tsx @@ -2,13 +2,23 @@ import { ButtonPrimary } from '@components/common/button/Button'; import Calendar from '@components/DatePicker/Calendar'; import { BackIcon } from '@components/common/icons/Icons'; -export const SelectDate = ({ onClose }: { onClose: () => void }) => { +export const SelectDate = ({ + onClose, + // onDatesSelected, +}: { + onClose: () => void; + // onDatesSelected: (startDate: Date | null, endDate: Date | null) => void; +}) => { + const handleDateSelect = (startDate: Date | null, endDate: Date | null) => { + // onDatesSelected(startDate, endDate); + }; + return (
-
+
- +
완료
diff --git a/src/components/createTrip/SelectDestination.tsx b/src/components/createTrip/SelectDestination.tsx new file mode 100644 index 00000000..c4abe304 --- /dev/null +++ b/src/components/createTrip/SelectDestination.tsx @@ -0,0 +1,16 @@ +import { ButtonPrimary } from '@components/common/button/Button'; +import { BackIcon } from '@components/common/icons/Icons'; + +export const SelectDestination = ({ onClose }: { onClose: () => void }) => { + return ( +
+
+ +
+
검색
+
+ 완료 +
+
+ ); +}; diff --git a/src/pages/create/createTrip.page.tsx b/src/pages/create/createTrip.page.tsx index eca20c70..a43b3ecd 100644 --- a/src/pages/create/createTrip.page.tsx +++ b/src/pages/create/createTrip.page.tsx @@ -3,76 +3,106 @@ import { useState } from 'react'; import { SelectDate } from '../../components/createTrip/SelectDate'; import { CalendarIcon, - DocumentIcon, + CloseIcon, SearchIcon, UserIcon, } from '@components/common/icons/Icons'; import { ButtonPrimary } from '@components/common/button/Button'; import { InputField } from '@components/createTrip/InputField'; +import { SelectDestination } from '@components/createTrip/SelectDestination'; export const CreateTrip = () => { const [title, setTitle] = useState(''); const [numOfMembers, setNumOfMembers] = useState(2); const [showSelectDate, setShowSelectDate] = useState(false); + const [showSelectDestination, setShowSelectDestination] = useState(false); - const clearInput = () => { - setTitle(''); + const handleIncrease = () => { + setNumOfMembers((prevNum) => prevNum + 1); }; - const handleChange = (e: React.ChangeEvent) => { - setTitle(e.target.value); - }; - - const handleDateButtonClick = () => { - setShowSelectDate(true); - }; - - const handleCloseSelectDate = () => { - setShowSelectDate(false); + const handleDecrease = () => { + setNumOfMembers((prevNum) => Math.max(prevNum - 1, 2)); }; if (showSelectDate) { - return ; + return ( + { + setShowSelectDate(false); + }} + /> + ); + } + if (showSelectDestination) { + return ( + { + setShowSelectDestination(false); + }} + /> + ); } return (
여행 생성하기
- + + { + setTitle(e.target.value); + }} + autoFocus + /> + {title && ( +
{ + setTitle(''); + }}> + +
+ )} +
- setNumOfMembers(newValue)} - minValue={2} - maxValue={100} - /> + +
{numOfMembers}명
+
+ + +
+
+ onClick={() => { + setShowSelectDate(true); + }} + isClickable> +
여행 날짜 (선택)
+
-
{}}> -
- -
-
- 여행지 (선택) -
-
+ { + setShowSelectDestination(true); + }} + isClickable> +
여행지 (선택)
+
{}}>완료 From b57b00aab6b811c6574c830867e9d120b3a4e0e7 Mon Sep 17 00:00:00 2001 From: joanShim Date: Sat, 13 Jan 2024 20:26:48 +0900 Subject: [PATCH 9/9] =?UTF-8?q?Feat:=20=EC=97=AC=ED=96=89=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=20=EB=A6=AC=EC=BD=94=EC=9D=BC=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A0=8C=EB=8D=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DatePicker/Calendar.tsx | 28 +++++++++++---------- src/components/createTrip/SelectDate.tsx | 31 +++++++++++++++--------- src/pages/create/createTrip.page.tsx | 22 ++++++++++++++--- src/recoil/tripDate.ts | 9 +++++++ 4 files changed, 61 insertions(+), 29 deletions(-) create mode 100644 src/recoil/tripDate.ts diff --git a/src/components/DatePicker/Calendar.tsx b/src/components/DatePicker/Calendar.tsx index ac5a9266..0c317d68 100644 --- a/src/components/DatePicker/Calendar.tsx +++ b/src/components/DatePicker/Calendar.tsx @@ -1,30 +1,32 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { + addDays, + addMonths, + differenceInDays, format, getDaysInMonth, - addMonths, - startOfMonth, - isWithinInterval, - setDate, isSameDay, - differenceInDays, - addDays, + isWithinInterval, + startOfMonth, subDays, } from 'date-fns'; import { ko } from 'date-fns/locale'; import 'tailwindcss/tailwind.css'; -interface CalendarProps { - onDateSelect: (startDate: Date | null, endDate: Date | null) => void; -} - const CALENDAR_LENGTH = 42; -const DAY_OF_WEEK = 7; +// const DAY_OF_WEEK = 7; const DAY_LIST = ['일', '월', '화', '수', '목', '금', '토']; -const Calendar: React.FC = ({ onDateSelect }) => { +const Calendar: React.FC<{ + onDateSelect: (startDate: Date | null, endDate: Date | null) => void; +}> = ({ onDateSelect }) => { const [startDate, setStartDate] = useState(null); const [endDate, setEndDate] = useState(null); + + useEffect(() => { + onDateSelect(startDate, endDate); + }, [startDate, endDate, onDateSelect]); + const currentDate = new Date(); const [visibleMonths, setVisibleMonths] = useState([ currentDate, diff --git a/src/components/createTrip/SelectDate.tsx b/src/components/createTrip/SelectDate.tsx index 78e640e0..e5fdaa0b 100644 --- a/src/components/createTrip/SelectDate.tsx +++ b/src/components/createTrip/SelectDate.tsx @@ -1,16 +1,18 @@ -import { ButtonPrimary } from '@components/common/button/Button'; import Calendar from '@components/DatePicker/Calendar'; +import { ButtonPrimary } from '@components/common/button/Button'; import { BackIcon } from '@components/common/icons/Icons'; +import { tripDateState } from '@recoil/tripDate'; +import { useState } from 'react'; +import { useRecoilState } from 'recoil'; + +export const SelectDate = ({ onClose }: { onClose: () => void }) => { + const [, setTripDate] = useRecoilState(tripDateState); + const [selectedStartDate, setSelectedStartDate] = useState(null); + const [selectedEndDate, setSelectedEndDate] = useState(null); -export const SelectDate = ({ - onClose, - // onDatesSelected, -}: { - onClose: () => void; - // onDatesSelected: (startDate: Date | null, endDate: Date | null) => void; -}) => { - const handleDateSelect = (startDate: Date | null, endDate: Date | null) => { - // onDatesSelected(startDate, endDate); + const handleComplete = () => { + setTripDate({ startDate: selectedStartDate, endDate: selectedEndDate }); + onClose(); }; return ( @@ -18,9 +20,14 @@ export const SelectDate = ({
- + { + setSelectedStartDate(startDate); + setSelectedEndDate(endDate); + }} + />
- 완료 + 완료
); diff --git a/src/pages/create/createTrip.page.tsx b/src/pages/create/createTrip.page.tsx index a43b3ecd..7a35c568 100644 --- a/src/pages/create/createTrip.page.tsx +++ b/src/pages/create/createTrip.page.tsx @@ -1,15 +1,19 @@ +import { ButtonPrimary } from '@components/common/button/Button'; import BackHeader from '@components/common/header/BackHeader'; -import { useState } from 'react'; -import { SelectDate } from '../../components/createTrip/SelectDate'; import { CalendarIcon, CloseIcon, SearchIcon, UserIcon, } from '@components/common/icons/Icons'; -import { ButtonPrimary } from '@components/common/button/Button'; import { InputField } from '@components/createTrip/InputField'; import { SelectDestination } from '@components/createTrip/SelectDestination'; +import { tripDateState } from '@recoil/tripDate'; +import { format } from 'date-fns'; +import { ko } from 'date-fns/locale'; +import { useState } from 'react'; +import { useRecoilValue } from 'recoil'; +import { SelectDate } from '../../components/createTrip/SelectDate'; export const CreateTrip = () => { const [title, setTitle] = useState(''); @@ -25,6 +29,16 @@ export const CreateTrip = () => { setNumOfMembers((prevNum) => Math.max(prevNum - 1, 2)); }; + const tripDate = useRecoilValue(tripDateState); + const formattedTripDate = + tripDate.startDate && tripDate.endDate + ? `${format(tripDate.startDate, 'MM.dd', { locale: ko })} - ${format( + tripDate.endDate, + 'MM.dd', + { locale: ko }, + )}` + : '여행 날짜(선택)'; + if (showSelectDate) { return ( { setShowSelectDate(true); }} isClickable> -
여행 날짜 (선택)
+
{formattedTripDate}
({ + key: 'tripDateState', + default: { startDate: null, endDate: null }, +});