From dde6a3403bdd07215a3732ef80e67820bf3afc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JunhyukPark=28=EB=B0=95=EC=A4=80=ED=98=81=29?= Date: Tue, 28 Jan 2025 20:33:51 +0900 Subject: [PATCH 01/13] =?UTF-8?q?[UNI-67]=20feat=20:=20=EB=94=94=EB=A0=89?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20dynamic?= =?UTF-8?q?=20import=20=EB=B0=A9=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...240\353\240\244\353\214\200\355\225\231\352\265\220.svg" | 0 ...234\353\246\275\353\214\200\355\225\231\352\265\220.svg" | 0 ...254\354\236\220\353\214\200\355\225\231\352\265\220.svg" | 0 ...270\355\225\230\353\214\200\355\225\231\352\265\220.svg" | 0 ...234\354\226\221\353\214\200\355\225\231\352\265\220.svg" | 0 uniro_frontend/src/components/universityButton.tsx | 6 +++++- 6 files changed, 5 insertions(+), 1 deletion(-) rename "uniro_frontend/src/assets/\352\263\240\353\240\244\353\214\200\355\225\231\352\265\220.svg" => "uniro_frontend/src/assets/university/\352\263\240\353\240\244\353\214\200\355\225\231\352\265\220.svg" (100%) rename "uniro_frontend/src/assets/\354\204\234\354\232\270\354\213\234\353\246\275\353\214\200\355\225\231\352\265\220.svg" => "uniro_frontend/src/assets/university/\354\204\234\354\232\270\354\213\234\353\246\275\353\214\200\355\225\231\352\265\220.svg" (100%) rename "uniro_frontend/src/assets/\354\235\264\355\231\224\354\227\254\354\236\220\353\214\200\355\225\231\352\265\220.svg" => "uniro_frontend/src/assets/university/\354\235\264\355\231\224\354\227\254\354\236\220\353\214\200\355\225\231\352\265\220.svg" (100%) rename "uniro_frontend/src/assets/\354\235\270\355\225\230\353\214\200\355\225\231\352\265\220.svg" => "uniro_frontend/src/assets/university/\354\235\270\355\225\230\353\214\200\355\225\231\352\265\220.svg" (100%) rename "uniro_frontend/src/assets/\355\225\234\354\226\221\353\214\200\355\225\231\352\265\220.svg" => "uniro_frontend/src/assets/university/\355\225\234\354\226\221\353\214\200\355\225\231\352\265\220.svg" (100%) diff --git "a/uniro_frontend/src/assets/\352\263\240\353\240\244\353\214\200\355\225\231\352\265\220.svg" "b/uniro_frontend/src/assets/university/\352\263\240\353\240\244\353\214\200\355\225\231\352\265\220.svg" similarity index 100% rename from "uniro_frontend/src/assets/\352\263\240\353\240\244\353\214\200\355\225\231\352\265\220.svg" rename to "uniro_frontend/src/assets/university/\352\263\240\353\240\244\353\214\200\355\225\231\352\265\220.svg" diff --git "a/uniro_frontend/src/assets/\354\204\234\354\232\270\354\213\234\353\246\275\353\214\200\355\225\231\352\265\220.svg" "b/uniro_frontend/src/assets/university/\354\204\234\354\232\270\354\213\234\353\246\275\353\214\200\355\225\231\352\265\220.svg" similarity index 100% rename from "uniro_frontend/src/assets/\354\204\234\354\232\270\354\213\234\353\246\275\353\214\200\355\225\231\352\265\220.svg" rename to "uniro_frontend/src/assets/university/\354\204\234\354\232\270\354\213\234\353\246\275\353\214\200\355\225\231\352\265\220.svg" diff --git "a/uniro_frontend/src/assets/\354\235\264\355\231\224\354\227\254\354\236\220\353\214\200\355\225\231\352\265\220.svg" "b/uniro_frontend/src/assets/university/\354\235\264\355\231\224\354\227\254\354\236\220\353\214\200\355\225\231\352\265\220.svg" similarity index 100% rename from "uniro_frontend/src/assets/\354\235\264\355\231\224\354\227\254\354\236\220\353\214\200\355\225\231\352\265\220.svg" rename to "uniro_frontend/src/assets/university/\354\235\264\355\231\224\354\227\254\354\236\220\353\214\200\355\225\231\352\265\220.svg" diff --git "a/uniro_frontend/src/assets/\354\235\270\355\225\230\353\214\200\355\225\231\352\265\220.svg" "b/uniro_frontend/src/assets/university/\354\235\270\355\225\230\353\214\200\355\225\231\352\265\220.svg" similarity index 100% rename from "uniro_frontend/src/assets/\354\235\270\355\225\230\353\214\200\355\225\231\352\265\220.svg" rename to "uniro_frontend/src/assets/university/\354\235\270\355\225\230\353\214\200\355\225\231\352\265\220.svg" diff --git "a/uniro_frontend/src/assets/\355\225\234\354\226\221\353\214\200\355\225\231\352\265\220.svg" "b/uniro_frontend/src/assets/university/\355\225\234\354\226\221\353\214\200\355\225\231\352\265\220.svg" similarity index 100% rename from "uniro_frontend/src/assets/\355\225\234\354\226\221\353\214\200\355\225\231\352\265\220.svg" rename to "uniro_frontend/src/assets/university/\355\225\234\354\226\221\353\214\200\355\225\231\352\265\220.svg" diff --git a/uniro_frontend/src/components/universityButton.tsx b/uniro_frontend/src/components/universityButton.tsx index dbc0dca..2dd99bf 100644 --- a/uniro_frontend/src/components/universityButton.tsx +++ b/uniro_frontend/src/components/universityButton.tsx @@ -7,19 +7,23 @@ interface UniversityButtonProps { onClick: () => void; } +const svgModules = import.meta.glob("/src/assets/university/*.svg", { eager: true }); + export default function UniversityButton({ name, img, selected, onClick }: UniversityButtonProps) { const handleClick = (e: MouseEvent) => { e.stopPropagation(); onClick(); }; + const svgPath = (svgModules[`/src/assets/university/${img}`] as { default: string })?.default; + return (
  • From ddd79b2194bd64e8feff1e836bce611acc689524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JunhyukPark=28=EB=B0=95=EC=A4=80=ED=98=81=29?= Date: Wed, 29 Jan 2025 01:08:26 +0900 Subject: [PATCH 02/13] =?UTF-8?q?[UNI-15]=20feat=20:=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uniro_frontend/src/App.tsx | 4 ++ .../src/assets/icon/cautionText.svg | 4 ++ uniro_frontend/src/assets/icon/close.svg | 4 ++ .../src/assets/icon/destination.svg | 4 ++ .../src/assets/icon/resultDivider.svg | 3 ++ uniro_frontend/src/assets/icon/start.svg | 4 ++ uniro_frontend/src/components/topBar.tsx | 49 +++++++++++++++++++ uniro_frontend/src/pages/detailResult.tsx | 14 ++++++ uniro_frontend/src/pages/result.tsx | 19 +++++++ 9 files changed, 105 insertions(+) create mode 100644 uniro_frontend/src/assets/icon/cautionText.svg create mode 100644 uniro_frontend/src/assets/icon/close.svg create mode 100644 uniro_frontend/src/assets/icon/destination.svg create mode 100644 uniro_frontend/src/assets/icon/resultDivider.svg create mode 100644 uniro_frontend/src/assets/icon/start.svg create mode 100644 uniro_frontend/src/components/topBar.tsx create mode 100644 uniro_frontend/src/pages/detailResult.tsx create mode 100644 uniro_frontend/src/pages/result.tsx diff --git a/uniro_frontend/src/App.tsx b/uniro_frontend/src/App.tsx index 26e9e4d..e51a237 100644 --- a/uniro_frontend/src/App.tsx +++ b/uniro_frontend/src/App.tsx @@ -3,6 +3,8 @@ import "./App.css"; import Demo from "./pages/demo"; import LandingPage from "./pages/landing"; import UniversitySearchPage from "./pages/search"; +import RouteResultPage from "./pages/result"; +import DetailResultPage from "./pages/detailResult"; function App() { return ( @@ -10,6 +12,8 @@ function App() { } /> } /> } /> + } /> + } /> ); } diff --git a/uniro_frontend/src/assets/icon/cautionText.svg b/uniro_frontend/src/assets/icon/cautionText.svg new file mode 100644 index 0000000..1862757 --- /dev/null +++ b/uniro_frontend/src/assets/icon/cautionText.svg @@ -0,0 +1,4 @@ + + + + diff --git a/uniro_frontend/src/assets/icon/close.svg b/uniro_frontend/src/assets/icon/close.svg new file mode 100644 index 0000000..2a1889d --- /dev/null +++ b/uniro_frontend/src/assets/icon/close.svg @@ -0,0 +1,4 @@ + + + + diff --git a/uniro_frontend/src/assets/icon/destination.svg b/uniro_frontend/src/assets/icon/destination.svg new file mode 100644 index 0000000..b356c81 --- /dev/null +++ b/uniro_frontend/src/assets/icon/destination.svg @@ -0,0 +1,4 @@ + + + + diff --git a/uniro_frontend/src/assets/icon/resultDivider.svg b/uniro_frontend/src/assets/icon/resultDivider.svg new file mode 100644 index 0000000..d8576d1 --- /dev/null +++ b/uniro_frontend/src/assets/icon/resultDivider.svg @@ -0,0 +1,3 @@ + + + diff --git a/uniro_frontend/src/assets/icon/start.svg b/uniro_frontend/src/assets/icon/start.svg new file mode 100644 index 0000000..e1fb5c7 --- /dev/null +++ b/uniro_frontend/src/assets/icon/start.svg @@ -0,0 +1,4 @@ + + + + diff --git a/uniro_frontend/src/components/topBar.tsx b/uniro_frontend/src/components/topBar.tsx new file mode 100644 index 0000000..9f2127c --- /dev/null +++ b/uniro_frontend/src/components/topBar.tsx @@ -0,0 +1,49 @@ +import React from "react"; + +import Cancel from "../assets/icon/close.svg?react"; +import CautionIcon from "../assets/icon/cautionText.svg?react"; +import DestinationIcon from "../assets/icon/destination.svg?react"; +import StartIcon from "../assets/icon/start.svg?react"; +import ResultDividier from "../assets/icon/resultDivider.svg?react"; + +const TopBar = () => { + return ( +
    +
    + + 전동휠체어 예상소요시간 + + +
    +
    +
    +
    +
    10
    +
    +
    +
    +
    + 635m +
    +
    +
    + + 가는 길에 주의 요소가 있어요 +
    +
    +
    +
    + + 한양대학교 법학관 +
    + +
    + + 한양대학교 제2공학관 +
    +
    +
    + ); +}; + +export default TopBar; diff --git a/uniro_frontend/src/pages/detailResult.tsx b/uniro_frontend/src/pages/detailResult.tsx new file mode 100644 index 0000000..0592f8a --- /dev/null +++ b/uniro_frontend/src/pages/detailResult.tsx @@ -0,0 +1,14 @@ +import React, { useRef } from "react"; +import Map from "../component/Map"; + +type Props = {}; + +const DetailResultPage = () => { + return ( +
    + +
    + ); +}; + +export default DetailResultPage; diff --git a/uniro_frontend/src/pages/result.tsx b/uniro_frontend/src/pages/result.tsx new file mode 100644 index 0000000..b7d701a --- /dev/null +++ b/uniro_frontend/src/pages/result.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import TopBar from "../components/topBar"; +import Map from "../component/Map"; +import Button from "../components/customButton"; + +const RouteResultPage = () => { + // TODO: Svh 변경사항 알려주기 + return ( +
    + + +
    + +
    +
    + ); +}; + +export default RouteResultPage; From 4ae178c32034f2e3d162def647d57484b98d46fe Mon Sep 17 00:00:00 2001 From: jpark0506 Date: Wed, 29 Jan 2025 11:29:55 +0900 Subject: [PATCH 03/13] =?UTF-8?q?[UNI-15]=20fix:=20layout=20PC=20=EC=99=80?= =?UTF-8?q?=20mobile=20=EB=8B=A4=EB=A5=B8=20=EA=B2=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uniro_frontend/src/components/topBar.tsx | 2 +- uniro_frontend/src/hooks/useScrollControl.tsx | 36 +++++++++++++++++++ uniro_frontend/src/pages/detailResult.tsx | 11 +++--- uniro_frontend/src/pages/result.tsx | 14 +++++--- 4 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 uniro_frontend/src/hooks/useScrollControl.tsx diff --git a/uniro_frontend/src/components/topBar.tsx b/uniro_frontend/src/components/topBar.tsx index 9f2127c..7e5885c 100644 --- a/uniro_frontend/src/components/topBar.tsx +++ b/uniro_frontend/src/components/topBar.tsx @@ -8,7 +8,7 @@ import ResultDividier from "../assets/icon/resultDivider.svg?react"; const TopBar = () => { return ( -
    +
    전동휠체어 예상소요시간 diff --git a/uniro_frontend/src/hooks/useScrollControl.tsx b/uniro_frontend/src/hooks/useScrollControl.tsx new file mode 100644 index 0000000..466efb5 --- /dev/null +++ b/uniro_frontend/src/hooks/useScrollControl.tsx @@ -0,0 +1,36 @@ +import React, { useEffect, useState } from 'react' + + +// 스크롤 비활성화, 맵을 움직일 때, 다른 부분들도 같이 움직이는 것들을 제어함. +// 전역 적용이 필요하다면 다른 방식을 사용할 예정. +const useScrollControl = () => { + + const [scrollState, setScrollState] = useState(false); + + const enableScroll = () => { + setScrollState(true); + } + + const disableScroll = () => { + setScrollState(false); + } + + useEffect(() => { + + if (scrollState) { + document.body.style.overflow = 'hidden'; + } + + else { + document.body.style.overflow = 'auto'; + } + + return () => { + document.body.style.overflow = 'auto'; + } + }, [scrollState]); + + return { enableScroll, disableScroll } +} + +export default useScrollControl; \ No newline at end of file diff --git a/uniro_frontend/src/pages/detailResult.tsx b/uniro_frontend/src/pages/detailResult.tsx index 0592f8a..c86f7f6 100644 --- a/uniro_frontend/src/pages/detailResult.tsx +++ b/uniro_frontend/src/pages/detailResult.tsx @@ -1,14 +1,13 @@ -import React, { useRef } from "react"; +import React, { useCallback, useRef } from "react"; import Map from "../component/Map"; +import BottomSheet, { BottomSheetView } from "@gorhom/bottom-sheet"; type Props = {}; const DetailResultPage = () => { - return ( -
    - -
    - ); + + return null; + }; export default DetailResultPage; diff --git a/uniro_frontend/src/pages/result.tsx b/uniro_frontend/src/pages/result.tsx index b7d701a..881ff5f 100644 --- a/uniro_frontend/src/pages/result.tsx +++ b/uniro_frontend/src/pages/result.tsx @@ -1,17 +1,23 @@ -import React from "react"; +import React, { useEffect } from "react"; import TopBar from "../components/topBar"; import Map from "../component/Map"; import Button from "../components/customButton"; +import { Link, useNavigate } from "react-router"; +import useScrollControl from "../hooks/useScrollControl"; const RouteResultPage = () => { + + useScrollControl(); + // TODO: Svh 변경사항 알려주기 + return ( -
    +
    -
    + -
    +
    ); }; From 4122a85966124d9ec4339f3620bf787ec04d0e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JunhyukPark=28=EB=B0=95=EC=A4=80=ED=98=81=29?= Date: Wed, 29 Jan 2025 11:45:03 +0900 Subject: [PATCH 04/13] =?UTF-8?q?[UNI-15]=20feat:=20map=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20UI=20disable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uniro_frontend/src/map/initializer/googleMapInitializer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/uniro_frontend/src/map/initializer/googleMapInitializer.ts b/uniro_frontend/src/map/initializer/googleMapInitializer.ts index edfb317..e2eecaf 100644 --- a/uniro_frontend/src/map/initializer/googleMapInitializer.ts +++ b/uniro_frontend/src/map/initializer/googleMapInitializer.ts @@ -25,6 +25,7 @@ export const initializeMap = async (mapElement: HTMLElement | null): Promise Date: Wed, 29 Jan 2025 17:42:37 +0900 Subject: [PATCH 05/13] =?UTF-8?q?[UNI-15]=20=EC=83=81=EC=84=B8=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=99=84=EB=A3=8C,=20=EC=A0=84=ED=99=98?= =?UTF-8?q?=20animation=20=EB=B0=8F=20=EB=B0=94=ED=85=80=EC=8B=9C=ED=8A=B8?= =?UTF-8?q?=20animation=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uniro_frontend/package-lock.json | 167 ++++++++++++++++-- uniro_frontend/package.json | 4 + uniro_frontend/src/assets/icon/goBack.svg | 5 + .../src/assets/icon/list/icon/start-24px.svg | 3 + uniro_frontend/src/assets/icon/safeText.svg | 4 + uniro_frontend/src/assets/route/dest.svg | 10 ++ uniro_frontend/src/assets/route/left.svg | 3 + uniro_frontend/src/assets/route/right.svg | 3 + uniro_frontend/src/assets/route/start.svg | 3 + uniro_frontend/src/assets/route/straight.svg | 3 + uniro_frontend/src/component/Map.tsx | 10 +- uniro_frontend/src/components/routeList.tsx | 140 +++++++++++++++ uniro_frontend/src/components/topBar.tsx | 92 ++++++---- .../src/data/factory/edgeFactory.ts | 19 +- .../src/data/factory/navigationFactory.ts | 10 +- .../src/data/mock/hanyangBuildings.ts | 6 +- uniro_frontend/src/data/mock/hanyangRoute.ts | 6 +- uniro_frontend/src/data/types/edge.d.ts | 7 + uniro_frontend/src/data/types/route.d.ts | 7 +- uniro_frontend/src/pages/detailResult.tsx | 134 +++++++++++++- uniro_frontend/src/pages/result.tsx | 31 +++- 21 files changed, 597 insertions(+), 70 deletions(-) create mode 100644 uniro_frontend/src/assets/icon/goBack.svg create mode 100644 uniro_frontend/src/assets/icon/list/icon/start-24px.svg create mode 100644 uniro_frontend/src/assets/icon/safeText.svg create mode 100644 uniro_frontend/src/assets/route/dest.svg create mode 100644 uniro_frontend/src/assets/route/left.svg create mode 100644 uniro_frontend/src/assets/route/right.svg create mode 100644 uniro_frontend/src/assets/route/start.svg create mode 100644 uniro_frontend/src/assets/route/straight.svg create mode 100644 uniro_frontend/src/components/routeList.tsx diff --git a/uniro_frontend/package-lock.json b/uniro_frontend/package-lock.json index 9fb6f47..df33d11 100644 --- a/uniro_frontend/package-lock.json +++ b/uniro_frontend/package-lock.json @@ -9,10 +9,14 @@ "version": "0.0.0", "dependencies": { "@googlemaps/js-api-loader": "^1.16.8", + "@react-spring/web": "^9.7.5", "@tailwindcss/vite": "^4.0.0", + "@use-gesture/react": "^10.3.1", + "framer-motion": "^12.0.6", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router": "^7.1.3", + "react-sheet-slide": "^1.5.0", "tailwindcss": "^4.0.0" }, "devDependencies": { @@ -1048,6 +1052,78 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@react-spring/animated": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.5.tgz", + "integrity": "sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==", + "license": "MIT", + "dependencies": { + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.5.tgz", + "integrity": "sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.7.5", + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.5.tgz", + "integrity": "sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==", + "license": "MIT" + }, + "node_modules/@react-spring/shared": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.5.tgz", + "integrity": "sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==", + "license": "MIT", + "dependencies": { + "@react-spring/rafz": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/types": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.5.tgz", + "integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==", + "license": "MIT" + }, + "node_modules/@react-spring/web": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.5.tgz", + "integrity": "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.7.5", + "@react-spring/core": "~9.7.5", + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@rollup/pluginutils": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", @@ -2098,6 +2174,24 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@use-gesture/core": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", + "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", + "license": "MIT" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", + "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", + "license": "MIT", + "dependencies": { + "@use-gesture/core": "10.3.1" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, "node_modules/@vitejs/plugin-react": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", @@ -2552,6 +2646,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cosmiconfig": { "version": "8.3.6", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", @@ -2579,15 +2682,6 @@ } } }, - "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3462,6 +3556,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/framer-motion": { + "version": "12.0.6", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.0.6.tgz", + "integrity": "sha512-LmrXbXF6Vv5WCNmb+O/zn891VPZrH7XbsZgRLBROw6kFiP+iTK49gxTv2Ur3F0Tbw6+sy9BVtSqnWfMUpH+6nA==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.0.0", + "motion-utils": "^12.0.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -4675,6 +4796,21 @@ "node": "*" } }, + "node_modules/motion-dom": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.0.0.tgz", + "integrity": "sha512-CvYd15OeIR6kHgMdonCc1ihsaUG4MYh/wrkz8gZ3hBX/uamyZCXN9S9qJoYF03GqfTt7thTV/dxnHYX4+55vDg==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.0.0" + } + }, + "node_modules/motion-utils": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.0.0.tgz", + "integrity": "sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5174,6 +5310,18 @@ } } }, + "node_modules/react-sheet-slide": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/react-sheet-slide/-/react-sheet-slide-1.5.0.tgz", + "integrity": "sha512-mCq+/e0HQJp1QoVfpugWAlSEgJyQ/Vit0hJYNa7zrUSCB2VXmQKIubJEg2CJQtokzi1BwLaMU4tqEJbw4XwJlw==", + "license": "MIT", + "peerDependencies": { + "@react-spring/web": "^9.0.0", + "@use-gesture/react": "^10.0.0", + "react": "^16.8.0 || >=17", + "react-dom": "^16.8.0 || >=17" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -5773,7 +5921,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, "node_modules/turbo-stream": { diff --git a/uniro_frontend/package.json b/uniro_frontend/package.json index 6d9da40..d8a8223 100644 --- a/uniro_frontend/package.json +++ b/uniro_frontend/package.json @@ -11,10 +11,14 @@ }, "dependencies": { "@googlemaps/js-api-loader": "^1.16.8", + "@react-spring/web": "^9.7.5", "@tailwindcss/vite": "^4.0.0", + "@use-gesture/react": "^10.3.1", + "framer-motion": "^12.0.6", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router": "^7.1.3", + "react-sheet-slide": "^1.5.0", "tailwindcss": "^4.0.0" }, "devDependencies": { diff --git a/uniro_frontend/src/assets/icon/goBack.svg b/uniro_frontend/src/assets/icon/goBack.svg new file mode 100644 index 0000000..3a3e03b --- /dev/null +++ b/uniro_frontend/src/assets/icon/goBack.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/uniro_frontend/src/assets/icon/list/icon/start-24px.svg b/uniro_frontend/src/assets/icon/list/icon/start-24px.svg new file mode 100644 index 0000000..afaa65c --- /dev/null +++ b/uniro_frontend/src/assets/icon/list/icon/start-24px.svg @@ -0,0 +1,3 @@ + + + diff --git a/uniro_frontend/src/assets/icon/safeText.svg b/uniro_frontend/src/assets/icon/safeText.svg new file mode 100644 index 0000000..20e1ea5 --- /dev/null +++ b/uniro_frontend/src/assets/icon/safeText.svg @@ -0,0 +1,4 @@ + + + + diff --git a/uniro_frontend/src/assets/route/dest.svg b/uniro_frontend/src/assets/route/dest.svg new file mode 100644 index 0000000..90b6d7d --- /dev/null +++ b/uniro_frontend/src/assets/route/dest.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/uniro_frontend/src/assets/route/left.svg b/uniro_frontend/src/assets/route/left.svg new file mode 100644 index 0000000..8ae9bd7 --- /dev/null +++ b/uniro_frontend/src/assets/route/left.svg @@ -0,0 +1,3 @@ + + + diff --git a/uniro_frontend/src/assets/route/right.svg b/uniro_frontend/src/assets/route/right.svg new file mode 100644 index 0000000..427acf4 --- /dev/null +++ b/uniro_frontend/src/assets/route/right.svg @@ -0,0 +1,3 @@ + + + diff --git a/uniro_frontend/src/assets/route/start.svg b/uniro_frontend/src/assets/route/start.svg new file mode 100644 index 0000000..afaa65c --- /dev/null +++ b/uniro_frontend/src/assets/route/start.svg @@ -0,0 +1,3 @@ + + + diff --git a/uniro_frontend/src/assets/route/straight.svg b/uniro_frontend/src/assets/route/straight.svg new file mode 100644 index 0000000..e485217 --- /dev/null +++ b/uniro_frontend/src/assets/route/straight.svg @@ -0,0 +1,3 @@ + + + diff --git a/uniro_frontend/src/component/Map.tsx b/uniro_frontend/src/component/Map.tsx index 1b1ad43..dbd9eeb 100644 --- a/uniro_frontend/src/component/Map.tsx +++ b/uniro_frontend/src/component/Map.tsx @@ -1,9 +1,15 @@ import useMap from "../hooks/useMap"; -const Map = () => { +type MapProps = { + style?: React.CSSProperties; +}; +const Map = ({ style }: MapProps) => { const { mapRef } = useMap(); + if (!style) { + style = { height: "100%", width: "100%" }; + } - return
    ; + return
    ; }; export default Map; diff --git a/uniro_frontend/src/components/routeList.tsx b/uniro_frontend/src/components/routeList.tsx new file mode 100644 index 0000000..da2e04b --- /dev/null +++ b/uniro_frontend/src/components/routeList.tsx @@ -0,0 +1,140 @@ +import React from "react"; +import { RouteEdge } from "../data/types/edge"; +import { Building } from "../data/types/node"; + +import StartIcon from "../assets/route/start.svg?react"; +import DestinationIcon from "../assets/route/dest.svg?react"; +import StraightIcon from "../assets/route/straight.svg?react"; +import RightIcon from "../assets/route/right.svg?react"; +import LeftIcon from "../assets/route/left.svg?react"; + +type Props = { + routes: RouteEdge[]; + startBuilding: Building; + destBuilding: Building; +}; +const Divider = () =>
    ; + +const NumberIcon = ({ index }: { index: number }) => { + return ( +
    +
    {index}
    +
    + ); +}; + +const Route = ({ + index, + route, + startBuilding, + destBuilding, +}: { + index: number; + route: RouteEdge; + startBuilding: Building; + destBuilding: Building; +}) => { + switch (route.direction) { + case "straight": + return ( +
    +
    + +
    {route.distance}m
    +
    +
    + +
    직진
    +
    +
    + ); + case "right": + return ( +
    +
    + +
    {route.distance}m
    +
    +
    + +
    우회전
    +
    +
    + ); + case "left": + return ( +
    +
    + +
    {route.distance}m
    +
    +
    + +
    좌회전
    +
    +
    + ); + case "uturn": + return ( +
    +
    + +
    {route.distance}m
    +
    +
    + +
    U턴
    +
    +
    + ); + case "start": + return ( +
    +
    + +
    출발
    +
    +
    +
    {startBuilding.buildingName}
    +
    {startBuilding.address}
    +
    +
    + ); + case "destination": + return ( +
    +
    + +
    도착
    +
    +
    +
    {destBuilding.buildingName}
    +
    {destBuilding.address}
    +
    +
    + ); + default: + return ( +
    + 알 수 없음 +
    + ); + } +}; + +const RouteList = ({ routes, startBuilding, destBuilding }: Props) => { + return ( +
    + {routes.map((route, index) => ( + <> + +
    + +
    + + ))} +
    + ); +}; + +export default RouteList; diff --git a/uniro_frontend/src/components/topBar.tsx b/uniro_frontend/src/components/topBar.tsx index 7e5885c..f19e90c 100644 --- a/uniro_frontend/src/components/topBar.tsx +++ b/uniro_frontend/src/components/topBar.tsx @@ -1,48 +1,68 @@ -import React from "react"; +import React, { useState } from "react"; import Cancel from "../assets/icon/close.svg?react"; import CautionIcon from "../assets/icon/cautionText.svg?react"; +import SafeIcon from "../assets/icon/safeText.svg?react"; import DestinationIcon from "../assets/icon/destination.svg?react"; import StartIcon from "../assets/icon/start.svg?react"; -import ResultDividier from "../assets/icon/resultDivider.svg?react"; +import ResultDivider from "../assets/icon/resultDivider.svg?react"; +import { NavigationRoute } from "../data/types/route"; +import { mockNavigationRoute } from "../data/mock/hanyangRoute"; +import { AnimatePresence, motion } from "framer-motion"; + +const TITLE = "전동휠체어 예상소요시간"; const TopBar = () => { + const [route, _] = useState(mockNavigationRoute); + return ( -
    -
    - - 전동휠체어 예상소요시간 - - -
    -
    -
    -
    -
    10
    -
    -
    -
    -
    - 635m -
    -
    -
    - - 가는 길에 주의 요소가 있어요 -
    -
    -
    -
    - - 한양대학교 법학관 -
    - -
    - - 한양대학교 제2공학관 + + +
    +
    + {TITLE} + +
    +
    +
    +
    +
    + {route.totalCost} +
    +
    +
    +
    +
    + {`${route.totalDistance}m`} +
    +
    +
    + {route.hasCaution ? : } + + 가는 길에 주의 요소가 {route.hasCaution ? "있어요" : "없어요"} + +
    +
    +
    +
    + + {route.startBuilding.buildingName} +
    + +
    + + {route.destinationBuilding.buildingName} +
    +
    -
    -
    +
    +
    ); }; diff --git a/uniro_frontend/src/data/factory/edgeFactory.ts b/uniro_frontend/src/data/factory/edgeFactory.ts index 445d21f..820e055 100644 --- a/uniro_frontend/src/data/factory/edgeFactory.ts +++ b/uniro_frontend/src/data/factory/edgeFactory.ts @@ -1,4 +1,4 @@ -import { HazardEdge } from "../types/edge"; +import { Direction, HazardEdge, RouteEdge } from "../types/edge"; import { CautionFactor, DangerFactor } from "../types/factor"; import { CustomNode } from "../types/node"; @@ -15,3 +15,20 @@ export const createHazardEdge = ( cautionFactors, dangerFactors, }); + +export const createRouteEdges = (edges: HazardEdge[]): RouteEdge[] => { + const routeEdges: RouteEdge[] = []; + edges.forEach((edge, index) => { + const routeEdge: RouteEdge = { + ...edge, + distance: 100, + direction: ["right", "straight", "left"][index % 3] as Direction, + }; + routeEdges.push(routeEdge); + }); + + routeEdges[0].direction = "start"; + routeEdges[routeEdges.length - 1].direction = "destination"; + + return routeEdges; +}; diff --git a/uniro_frontend/src/data/factory/navigationFactory.ts b/uniro_frontend/src/data/factory/navigationFactory.ts index 900ae92..46e75fd 100644 --- a/uniro_frontend/src/data/factory/navigationFactory.ts +++ b/uniro_frontend/src/data/factory/navigationFactory.ts @@ -1,11 +1,15 @@ -import { HazardEdge } from "../types/edge"; +import { hanyangBuildings } from "../mock/hanyangBuildings"; +import { RouteEdge } from "../types/edge"; import { NavigationRoute } from "../types/route"; -export const createNavigationRoute = (edges: HazardEdge[]): NavigationRoute => { +// TODO: Distance를 m-> km로 자동 변환해주는 util +export const createNavigationRoute = (edges: RouteEdge[]): NavigationRoute => { return { route: edges, hasCaution: edges.some((edge) => edge.cautionFactors !== undefined), - totalDistance: 1.5, + totalDistance: 635, totalCost: 10, + startBuilding: hanyangBuildings[0], + destinationBuilding: hanyangBuildings[1], }; }; diff --git a/uniro_frontend/src/data/mock/hanyangBuildings.ts b/uniro_frontend/src/data/mock/hanyangBuildings.ts index a75dcd5..38f6d64 100644 --- a/uniro_frontend/src/data/mock/hanyangBuildings.ts +++ b/uniro_frontend/src/data/mock/hanyangBuildings.ts @@ -1,12 +1,12 @@ import { Building } from "../types/node"; -export const buildings: Building[] = [ +export const hanyangBuildings: Building[] = [ { id: "101", lng: 127.044755, lat: 37.555994, isCore: true, - buildingName: "역사관", + buildingName: "한양대학교 법학관", buildingImageUrl: "https://upload.wikimedia.org/wikipedia/commons/6/69/Hanyang_University_008.JPG", phoneNumber: "02-2220-0114", address: "서울특별시 성동구 왕십리로 222", @@ -16,7 +16,7 @@ export const buildings: Building[] = [ lng: 127.0455, lat: 37.5565, isCore: true, - buildingName: "본관", + buildingName: "한양대학교 제2공학관", buildingImageUrl: "https://upload.wikimedia.org/wikipedia/commons/6/69/Hanyang_University_008.JPG", phoneNumber: "02-2220-0114", address: "서울특별시 성동구 왕십리로 222", diff --git a/uniro_frontend/src/data/mock/hanyangRoute.ts b/uniro_frontend/src/data/mock/hanyangRoute.ts index 544b166..40c9594 100644 --- a/uniro_frontend/src/data/mock/hanyangRoute.ts +++ b/uniro_frontend/src/data/mock/hanyangRoute.ts @@ -1,4 +1,4 @@ -import { createHazardEdge } from "../factory/edgeFactory"; +import { createHazardEdge, createRouteEdges } from "../factory/edgeFactory"; import { createNavigationRoute } from "../factory/navigationFactory"; import { createNode } from "../factory/nodeFactory"; import { HazardEdge } from "../types/edge"; @@ -17,6 +17,8 @@ const edges: HazardEdge[] = [ createHazardEdge("route2", nodes[1], nodes[2], ["도로에 균열이 있어요"]), createHazardEdge("route3", nodes[2], nodes[3]), createHazardEdge("route4", nodes[3], nodes[4]), + createHazardEdge("route5", nodes[3], nodes[4]), + createHazardEdge("route6", nodes[3], nodes[4]), ]; -export const mockNavigationRoute = createNavigationRoute(edges); +export const mockNavigationRoute = createNavigationRoute(createRouteEdges(edges)); diff --git a/uniro_frontend/src/data/types/edge.d.ts b/uniro_frontend/src/data/types/edge.d.ts index 585186e..aea55c5 100644 --- a/uniro_frontend/src/data/types/edge.d.ts +++ b/uniro_frontend/src/data/types/edge.d.ts @@ -7,9 +7,16 @@ export interface Edge { endNode: CustomNode; } +export type Direction = "start" | "right" | "straight" | "left" | "uturn" | "destination"; + // 위험 요소 & 주의 요소 // 마커를 표시하거나, 길 찾기 결과의 경로를 그릴 때 사용 export interface HazardEdge extends Edge { dangerFactors?: DangerFactor[]; cautionFactors?: CautionFactor[]; } + +export interface RouteEdge extends HazardEdge { + distance: number; + direction: Direction; +} diff --git a/uniro_frontend/src/data/types/route.d.ts b/uniro_frontend/src/data/types/route.d.ts index a335dc5..999b954 100644 --- a/uniro_frontend/src/data/types/route.d.ts +++ b/uniro_frontend/src/data/types/route.d.ts @@ -1,11 +1,14 @@ -import { HazardEdge } from "./edge"; +import { RouteEdge } from "./edge"; +import { Building } from "./node"; export interface Route { - route: HazardEdge[]; + route: RouteEdge[]; } export interface NavigationRoute extends Route { hasCaution: boolean; totalDistance: number; totalCost: number; + startBuilding: Building; + destinationBuilding: Building; } diff --git a/uniro_frontend/src/pages/detailResult.tsx b/uniro_frontend/src/pages/detailResult.tsx index c86f7f6..86eded0 100644 --- a/uniro_frontend/src/pages/detailResult.tsx +++ b/uniro_frontend/src/pages/detailResult.tsx @@ -1,13 +1,139 @@ -import React, { useCallback, useRef } from "react"; +import React, { useEffect, useState } from "react"; +import { motion, useDragControls, AnimatePresence, useMotionValue, useSpring, useTransform } from "framer-motion"; + +import CautionIcon from "../assets/icon/cautionText.svg?react"; +import SafeIcon from "../assets/icon/safeText.svg?react"; +import GoBack from "../assets/icon/goBack.svg?react"; + import Map from "../component/Map"; -import BottomSheet, { BottomSheetView } from "@gorhom/bottom-sheet"; +import useScrollControl from "../hooks/useScrollControl"; +import { NavigationRoute } from "../data/types/route"; +import { mockNavigationRoute } from "../data/mock/hanyangRoute"; +import RouteList from "../components/routeList"; +import { Link, useNavigate } from "react-router"; -type Props = {}; +const TITLE = "전동휠체어 예상소요시간"; const DetailResultPage = () => { + useScrollControl(); + const [route] = useState(mockNavigationRoute); + + const MAX_SHEET_HEIGHT = window.innerHeight * 0.7; + const MAX_SHEET_POSITION = window.innerHeight - MAX_SHEET_HEIGHT; + const MIN_SHEET_HEIGHT = window.innerHeight * 0.3; + const HEADER_HEIGHT = 40; + + const dragControls = useDragControls(); + const navigate = useNavigate(); + + const [isVisible, setIsVisible] = useState(true); + + const handleClick = () => { + setIsVisible(false); // 애니메이션 실행 (이후 컴포넌트 제거됨) + setTimeout(() => { + navigate("/result"); // 애니메이션 후 이동 + }, 300); // transition.duration과 동일한 시간 설정 + }; + + const y = useMotionValue(0); + const smoothY = useSpring(y, { stiffness: 200, damping: 20 }); + + // Map의 줌 값 (드래그할 때 자연스럽게 변함) + const zoom = useMotionValue(1); // 기본 1 (100%) + const smoothZoom = useSpring(zoom, { stiffness: 200, damping: 20 }); + + useEffect(() => { + const unsubscribe = smoothY.onChange((latest) => { + const progress = latest / (MAX_SHEET_HEIGHT - MIN_SHEET_HEIGHT); // 0 ~ 1 사이의 값 + const newZoom = 1 + progress * 0.1; // 1.0 ~ 1.1 (10% 확대) + zoom.set(newZoom); + }); + return () => unsubscribe(); + }, [smoothY]); - return null; + const zoomScale = useTransform(smoothZoom, (value) => `scale(${value})`); + return ( +
    + + {isVisible && ( + + + + )} + + + + + + {isVisible && ( + +
    dragControls.start(e)} + > +
    +
    +
    +
    +
    +
    {TITLE}
    +
    +
    +
    +
    {route.totalCost}
    +
    +
    +
    +
    + {route.totalDistance}m +
    +
    +
    + {route.hasCaution ? : } + + 가는 길에 주의 요소가 {route.hasCaution ? "있어요" : "없어요"} + +
    +
    +
    + +
    + + )} + +
    + ); }; export default DetailResultPage; diff --git a/uniro_frontend/src/pages/result.tsx b/uniro_frontend/src/pages/result.tsx index 881ff5f..dfc0229 100644 --- a/uniro_frontend/src/pages/result.tsx +++ b/uniro_frontend/src/pages/result.tsx @@ -1,23 +1,40 @@ -import React, { useEffect } from "react"; +import React, { useState } from "react"; import TopBar from "../components/topBar"; import Map from "../component/Map"; import Button from "../components/customButton"; -import { Link, useNavigate } from "react-router"; +import { useNavigate } from "react-router"; import useScrollControl from "../hooks/useScrollControl"; +import { AnimatePresence, motion } from "framer-motion"; const RouteResultPage = () => { - useScrollControl(); + const [isVisible, setIsVisible] = useState(true); + const navigate = useNavigate(); - // TODO: Svh 변경사항 알려주기 + const handleClick = () => { + setIsVisible(false); + setTimeout(() => { + navigate("/result/detail"); + }, 300); + }; return (
    - - - + + {isVisible && ( + + + + )} +
    ); }; From 48d394edd924690f28da4f230c710e3ccc2071a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JunhyukPark=28=EB=B0=95=EC=A4=80=ED=98=81=29?= Date: Thu, 30 Jan 2025 01:26:31 +0900 Subject: [PATCH 06/13] =?UTF-8?q?[UNI-15]=20feat:=20marker=EC=B0=8D?= =?UTF-8?q?=EA=B8=B0=20=EC=B4=88=EC=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uniro_frontend/src/App.tsx | 2 + .../src/assets/marker/arriveMarker.svg | 15 ++ .../src/assets/marker/buildingMarker.svg | 24 +++ .../src/assets/marker/cautionMarker.svg | 16 ++ .../src/assets/marker/dangerIcon.svg | 16 ++ .../src/assets/marker/startIcon.svg | 15 ++ .../src/assets/marker/subMarker.svg | 3 + uniro_frontend/src/component/Map.tsx | 6 +- uniro_frontend/src/component/NavgationMap.tsx | 103 +++++++++++++ uniro_frontend/src/components/topBar.tsx | 94 ++++++------ .../src/container/animatedContainer.tsx | 42 +++++ uniro_frontend/src/hooks/useScrollControl.tsx | 47 +++--- uniro_frontend/src/pages/combinedResult.tsx | 145 ++++++++++++++++++ 13 files changed, 454 insertions(+), 74 deletions(-) create mode 100644 uniro_frontend/src/assets/marker/arriveMarker.svg create mode 100644 uniro_frontend/src/assets/marker/buildingMarker.svg create mode 100644 uniro_frontend/src/assets/marker/cautionMarker.svg create mode 100644 uniro_frontend/src/assets/marker/dangerIcon.svg create mode 100644 uniro_frontend/src/assets/marker/startIcon.svg create mode 100644 uniro_frontend/src/assets/marker/subMarker.svg create mode 100644 uniro_frontend/src/component/NavgationMap.tsx create mode 100644 uniro_frontend/src/container/animatedContainer.tsx create mode 100644 uniro_frontend/src/pages/combinedResult.tsx diff --git a/uniro_frontend/src/App.tsx b/uniro_frontend/src/App.tsx index e51a237..48c754d 100644 --- a/uniro_frontend/src/App.tsx +++ b/uniro_frontend/src/App.tsx @@ -5,6 +5,7 @@ import LandingPage from "./pages/landing"; import UniversitySearchPage from "./pages/search"; import RouteResultPage from "./pages/result"; import DetailResultPage from "./pages/detailResult"; +import MergedRoutePage from "./pages/combinedResult"; function App() { return ( @@ -14,6 +15,7 @@ function App() { } /> } /> } /> + } /> ); } diff --git a/uniro_frontend/src/assets/marker/arriveMarker.svg b/uniro_frontend/src/assets/marker/arriveMarker.svg new file mode 100644 index 0000000..80cbe8d --- /dev/null +++ b/uniro_frontend/src/assets/marker/arriveMarker.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/uniro_frontend/src/assets/marker/buildingMarker.svg b/uniro_frontend/src/assets/marker/buildingMarker.svg new file mode 100644 index 0000000..5cb37eb --- /dev/null +++ b/uniro_frontend/src/assets/marker/buildingMarker.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uniro_frontend/src/assets/marker/cautionMarker.svg b/uniro_frontend/src/assets/marker/cautionMarker.svg new file mode 100644 index 0000000..d2a1cb7 --- /dev/null +++ b/uniro_frontend/src/assets/marker/cautionMarker.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/uniro_frontend/src/assets/marker/dangerIcon.svg b/uniro_frontend/src/assets/marker/dangerIcon.svg new file mode 100644 index 0000000..39ed554 --- /dev/null +++ b/uniro_frontend/src/assets/marker/dangerIcon.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/uniro_frontend/src/assets/marker/startIcon.svg b/uniro_frontend/src/assets/marker/startIcon.svg new file mode 100644 index 0000000..d633fcd --- /dev/null +++ b/uniro_frontend/src/assets/marker/startIcon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/uniro_frontend/src/assets/marker/subMarker.svg b/uniro_frontend/src/assets/marker/subMarker.svg new file mode 100644 index 0000000..5107e6a --- /dev/null +++ b/uniro_frontend/src/assets/marker/subMarker.svg @@ -0,0 +1,3 @@ + + + diff --git a/uniro_frontend/src/component/Map.tsx b/uniro_frontend/src/component/Map.tsx index dbd9eeb..14517f5 100644 --- a/uniro_frontend/src/component/Map.tsx +++ b/uniro_frontend/src/component/Map.tsx @@ -1,14 +1,18 @@ +import { useEffect } from "react"; import useMap from "../hooks/useMap"; type MapProps = { style?: React.CSSProperties; }; const Map = ({ style }: MapProps) => { - const { mapRef } = useMap(); + const { mapRef, map, overlay, AdvancedMarker, Polyline, mapLoaded } = useMap(); + if (!style) { style = { height: "100%", width: "100%" }; } + useEffect(() => {}, []); + return
    ; }; diff --git a/uniro_frontend/src/component/NavgationMap.tsx b/uniro_frontend/src/component/NavgationMap.tsx new file mode 100644 index 0000000..683c0f8 --- /dev/null +++ b/uniro_frontend/src/component/NavgationMap.tsx @@ -0,0 +1,103 @@ +import { useEffect } from "react"; +import useMap from "../hooks/useMap"; +import startIcon from "../assets/marker/startIcon.svg?raw"; +import arriveMarker from "../assets/marker/arriveMarker.svg?raw"; +import subMarker from "../assets/marker/subMarker.svg?raw"; +import { to } from "@react-spring/web"; +import { NavigationRoute } from "../data/types/route"; + +// props 타입 정의 (필요 시) +type MapProps = { + style?: React.CSSProperties; + // route, startBuilding, destinationBuilding 등을 밖에서 받아온다고 가정 + routes: NavigationRoute[]; +}; + +const generateStartMarker = ( + map: google.maps.Map, + AdvancedMarker: typeof google.maps.marker.AdvancedMarkerElement, + position: { lat: number; lng: number }, +) => { + const startSvgElement = new DOMParser().parseFromString(startIcon, "image/svg+xml").documentElement; + new AdvancedMarker({ + position: position, + map, + content: startSvgElement, + title: "출발지", + }); +}; + +const NavigationMap = ({ style, routes }: MapProps) => { + const { mapRef, map, AdvancedMarker, Polyline, mapLoaded } = useMap(); + + if (!style) { + style = { height: "100%", width: "100%" }; + } + + useEffect(() => { + if (!map) return; + map.setZoom(18); + + const { route } = routes[0]; + + routes.forEach((route, index) => { + if (index === 0) { + generateStartMarker(map, AdvancedMarker, { + lat: route.route[index].startNode.lat, + lng: route.startBuilding.lng, + }); + } + }); + + const startSvgElement = new DOMParser().parseFromString(startIcon, "image/svg+xml").documentElement; + const endSvgElement = new DOMParser().parseFromString(arriveMarker, "image/svg+xml").documentElement; + + const startNode = route[0].startNode; + const destinationNode = route[route.length - 1].endNode; + + const startMarker = new AdvancedMarker({ + position: { lat: startNode.lat, lng: startNode.lng }, + map, + content: startSvgElement, + title: "출발지", + }); + + route.forEach((segment) => { + const subMarkerElement = new DOMParser().parseFromString(subMarker, "image/svg+xml").documentElement; + + const path = [ + { lat: segment.startNode.lat, lng: segment.startNode.lng }, + { lat: segment.endNode.lat, lng: segment.endNode.lng }, + ]; + + new Polyline({ + path, + map, + strokeColor: "#161616", // 원하는 색상 + strokeWeight: "4", // 원하는 두께 + }); + + new AdvancedMarker({ + position: { lat: segment.startNode.lat, lng: segment.startNode.lng }, + map, + content: subMarkerElement, + }); + + new AdvancedMarker({ + position: { lat: segment.endNode.lat, lng: segment.endNode.lng }, + map, + content: subMarkerElement, + }); + const endMarker = new AdvancedMarker({ + position: { lat: destinationNode.lat, lng: destinationNode.lng }, + map, + content: endSvgElement, + title: "도착지", + }); + }); + }, [map, route]); + + return
    ; +}; + +export default NavigationMap; diff --git a/uniro_frontend/src/components/topBar.tsx b/uniro_frontend/src/components/topBar.tsx index f19e90c..822690d 100644 --- a/uniro_frontend/src/components/topBar.tsx +++ b/uniro_frontend/src/components/topBar.tsx @@ -8,61 +8,61 @@ import StartIcon from "../assets/icon/start.svg?react"; import ResultDivider from "../assets/icon/resultDivider.svg?react"; import { NavigationRoute } from "../data/types/route"; import { mockNavigationRoute } from "../data/mock/hanyangRoute"; -import { AnimatePresence, motion } from "framer-motion"; +import AnimatedContainer from "../container/animatedContainer"; const TITLE = "전동휠체어 예상소요시간"; -const TopBar = () => { +type TopBarProps = { + isDetailView: boolean; +}; + +const TopBar = ({ isDetailView }: TopBarProps) => { const [route, _] = useState(mockNavigationRoute); return ( - - -
    -
    - {TITLE} - -
    -
    -
    -
    -
    - {route.totalCost} -
    -
    -
    -
    -
    - {`${route.totalDistance}m`} -
    -
    -
    - {route.hasCaution ? : } - - 가는 길에 주의 요소가 {route.hasCaution ? "있어요" : "없어요"} - -
    -
    -
    -
    - - {route.startBuilding.buildingName} -
    - -
    - - {route.destinationBuilding.buildingName} -
    + +
    + {TITLE} + +
    +
    +
    +
    +
    + {route.totalCost}
    +
    +
    +
    +
    + {`${route.totalDistance}m`} +
    +
    +
    + {route.hasCaution ? : } + + 가는 길에 주의 요소가 {route.hasCaution ? "있어요" : "없어요"} + +
    +
    +
    +
    + + {route.startBuilding.buildingName} +
    + +
    + + {route.destinationBuilding.buildingName}
    - - +
    + ); }; diff --git a/uniro_frontend/src/container/animatedContainer.tsx b/uniro_frontend/src/container/animatedContainer.tsx new file mode 100644 index 0000000..657afde --- /dev/null +++ b/uniro_frontend/src/container/animatedContainer.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import { AnimatePresence, motion, MotionProps } from "framer-motion"; + +type Props = { + isVisible: boolean; + children: React.ReactNode; + positionDelta: number; + className: string; + isTop?: boolean; + transition?: MotionProps["transition"]; + motionProps?: MotionProps; +}; + +// default는 바텀에서 시작 +const AnimatedContainer = ({ + isVisible, + children, + positionDelta, + className, + isTop = false, + transition = { duration: 0.3, type: "tween" }, + motionProps = {}, +}: Props) => { + return ( + + {isVisible && ( + + {children} + + )} + + ); +}; + +export default AnimatedContainer; diff --git a/uniro_frontend/src/hooks/useScrollControl.tsx b/uniro_frontend/src/hooks/useScrollControl.tsx index 466efb5..271854a 100644 --- a/uniro_frontend/src/hooks/useScrollControl.tsx +++ b/uniro_frontend/src/hooks/useScrollControl.tsx @@ -1,36 +1,31 @@ -import React, { useEffect, useState } from 'react' - +import React, { useEffect, useState } from "react"; // 스크롤 비활성화, 맵을 움직일 때, 다른 부분들도 같이 움직이는 것들을 제어함. // 전역 적용이 필요하다면 다른 방식을 사용할 예정. const useScrollControl = () => { + const [scrollState, setScrollState] = useState(false); - const [scrollState, setScrollState] = useState(false); - - const enableScroll = () => { - setScrollState(true); - } - - const disableScroll = () => { - setScrollState(false); - } - - useEffect(() => { + const enableScroll = () => { + setScrollState(true); + }; - if (scrollState) { - document.body.style.overflow = 'hidden'; - } + const disableScroll = () => { + setScrollState(false); + }; - else { - document.body.style.overflow = 'auto'; - } + useEffect(() => { + if (scrollState) { + document.body.style.overflow = "auto"; + } else { + document.body.style.overflow = "hidden"; + } - return () => { - document.body.style.overflow = 'auto'; - } - }, [scrollState]); + return () => { + document.body.style.overflow = "hidden"; + }; + }, [scrollState]); - return { enableScroll, disableScroll } -} + return { enableScroll, disableScroll }; +}; -export default useScrollControl; \ No newline at end of file +export default useScrollControl; diff --git a/uniro_frontend/src/pages/combinedResult.tsx b/uniro_frontend/src/pages/combinedResult.tsx new file mode 100644 index 0000000..b467dfa --- /dev/null +++ b/uniro_frontend/src/pages/combinedResult.tsx @@ -0,0 +1,145 @@ +import React, { useEffect, useState } from "react"; +import { motion, useDragControls, useMotionValue, useSpring, useTransform } from "framer-motion"; +import TopBar from "../components/topBar"; +import Map from "../component/Map"; +import Button from "../components/customButton"; +import CautionIcon from "../assets/icon/cautionText.svg?react"; +import SafeIcon from "../assets/icon/safeText.svg?react"; +import GoBack from "../assets/icon/goBack.svg?react"; +import RouteList from "../components/routeList"; + +import { mockNavigationRoute } from "../data/mock/hanyangRoute"; +import { NavigationRoute } from "../data/types/route"; +import useScrollControl from "../hooks/useScrollControl"; +import AnimatedContainer from "../container/animatedContainer"; +import NavigationMap from "../component/NavgationMap"; + +const TITLE = "전동휠체어 예상소요시간"; + +const MergedRoutePage = () => { + useScrollControl(); + + const [isDetailView, setIsDetailView] = useState(false); + + const [route, _] = useState(mockNavigationRoute); + + const MAX_SHEET_HEIGHT = window.innerHeight * 0.7; // 최대로 올렸을 때 시트 높이 + const MIN_SHEET_HEIGHT = window.innerHeight * 0.3; // 최소로 내렸을 때 시트 높이 + const HEADER_HEIGHT = 40; + + const dragControls = useDragControls(); + const y = useMotionValue(0); + const smoothY = useSpring(y, { stiffness: 200, damping: 20 }); + const zoom = useMotionValue(1); + const smoothZoom = useSpring(zoom, { stiffness: 200, damping: 20 }); + const zoomScale = useTransform(smoothZoom, (value) => `scale(${value})`); + + useEffect(() => { + const unsubscribe = smoothY.onChange((latest) => { + const progress = latest / (MAX_SHEET_HEIGHT - MIN_SHEET_HEIGHT); + const newZoom = 1 + progress * 0.1; + zoom.set(newZoom); + }); + return () => unsubscribe(); + }, [smoothY, zoom]); + + const showDetailView = () => setIsDetailView(true); + const hideDetailView = () => setIsDetailView(false); + + useEffect(() => { + console.log(route); + }, []); + + return ( +
    + + + + + + + + + + + + + +
    dragControls.start(e)} + > +
    +
    +
    +
    +
    +
    {TITLE}
    +
    +
    +
    +
    {route.totalCost}
    +
    +
    +
    +
    + {route.totalDistance}m +
    +
    +
    + {route.hasCaution ? : } + + 가는 길에 주의 요소가 {route.hasCaution ? "있어요" : "없어요"} + +
    +
    +
    + +
    + +
    + ); +}; + +export default MergedRoutePage; From 35a58343efd850c3adb8796d604fa3ca4891abb8 Mon Sep 17 00:00:00 2001 From: jpark0506 Date: Thu, 30 Jan 2025 10:56:11 +0900 Subject: [PATCH 07/13] =?UTF-8?q?[UNI-15]=20refac=20:=20=EC=A7=80=EB=8F=84?= =?UTF-8?q?=20marker=20=EC=B2=98=EB=A6=AC=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uniro_frontend/src/component/NavgationMap.tsx | 150 +++++++++++------- uniro_frontend/src/pages/combinedResult.tsx | 2 +- 2 files changed, 91 insertions(+), 61 deletions(-) diff --git a/uniro_frontend/src/component/NavgationMap.tsx b/uniro_frontend/src/component/NavgationMap.tsx index 683c0f8..4f2e695 100644 --- a/uniro_frontend/src/component/NavgationMap.tsx +++ b/uniro_frontend/src/component/NavgationMap.tsx @@ -3,28 +3,57 @@ import useMap from "../hooks/useMap"; import startIcon from "../assets/marker/startIcon.svg?raw"; import arriveMarker from "../assets/marker/arriveMarker.svg?raw"; import subMarker from "../assets/marker/subMarker.svg?raw"; -import { to } from "@react-spring/web"; +import cautionMarker from "../assets/marker/cautionMarker.svg?raw"; import { NavigationRoute } from "../data/types/route"; // props 타입 정의 (필요 시) type MapProps = { style?: React.CSSProperties; // route, startBuilding, destinationBuilding 등을 밖에서 받아온다고 가정 - routes: NavigationRoute[]; + routes: NavigationRoute; }; -const generateStartMarker = ( +const generateMarker = ( map: google.maps.Map, AdvancedMarker: typeof google.maps.marker.AdvancedMarkerElement, + type: "start" | "sub" | "end" | "caution", position: { lat: number; lng: number }, ) => { - const startSvgElement = new DOMParser().parseFromString(startIcon, "image/svg+xml").documentElement; - new AdvancedMarker({ - position: position, - map, - content: startSvgElement, - title: "출발지", - }); + if (type === "start") { + const startSvgElement = new DOMParser().parseFromString(startIcon, "image/svg+xml").documentElement; + new AdvancedMarker({ + position: position, + map, + content: startSvgElement, + title: "출발지", + }); + } + else if (type === "end") { + const endSvgElement = new DOMParser().parseFromString(arriveMarker, "image/svg+xml").documentElement; + new AdvancedMarker({ + position: position, + map, + content: endSvgElement, + title: "도착지", + }); + } + else if (type === "caution") { + const cautionSvgElement = new DOMParser().parseFromString(cautionMarker, "image/svg+xml").documentElement; + new AdvancedMarker({ + position: position, + map, + content: cautionSvgElement, + title: "주의사항", + }); + } + else { + const subMarkerElement = new DOMParser().parseFromString(subMarker, "image/svg+xml").documentElement; + new AdvancedMarker({ + position: position, + map, + content: subMarkerElement, + }); + } }; const NavigationMap = ({ style, routes }: MapProps) => { @@ -36,66 +65,67 @@ const NavigationMap = ({ style, routes }: MapProps) => { useEffect(() => { if (!map) return; - map.setZoom(18); - const { route } = routes[0]; - routes.forEach((route, index) => { + if (!routes) return; + + + map.setZoom(18); + // 모든 lat와 lng의 평균값을 중심으로 지도를 보여줌 + const latSum = routes.route.reduce((acc, route) => acc + route.startNode.lat + route.endNode.lat, 0); + const lngSum = routes.route.reduce((acc, route) => acc + route.startNode.lng + route.endNode.lng, 0); + const latCenter = latSum / (routes.route.length * 2); + const lngCenter = lngSum / (routes.route.length * 2); + map.setCenter({ lat: latCenter, lng: lngCenter }); + + routes.route.forEach((route, index) => { + const { startNode, endNode } = route; + + if (route.cautionFactors && route.cautionFactors.length > 0) { + generateMarker(map, AdvancedMarker, "caution", { + lat: (startNode.lat + endNode.lat) / 2, + lng: (startNode.lng + endNode.lng) / 2 + } + ) + } if (index === 0) { - generateStartMarker(map, AdvancedMarker, { - lat: route.route[index].startNode.lat, - lng: route.startBuilding.lng, + generateMarker(map, AdvancedMarker, "start", { + lat: startNode.lat, + lng: startNode.lng, + }); + generateMarker(map, AdvancedMarker, "sub", { + lat: endNode.lat, + lng: endNode.lng }); } - }); - - const startSvgElement = new DOMParser().parseFromString(startIcon, "image/svg+xml").documentElement; - const endSvgElement = new DOMParser().parseFromString(arriveMarker, "image/svg+xml").documentElement; - - const startNode = route[0].startNode; - const destinationNode = route[route.length - 1].endNode; - - const startMarker = new AdvancedMarker({ - position: { lat: startNode.lat, lng: startNode.lng }, - map, - content: startSvgElement, - title: "출발지", - }); - - route.forEach((segment) => { - const subMarkerElement = new DOMParser().parseFromString(subMarker, "image/svg+xml").documentElement; - - const path = [ - { lat: segment.startNode.lat, lng: segment.startNode.lng }, - { lat: segment.endNode.lat, lng: segment.endNode.lng }, - ]; + else if (index === routes.route.length - 1) { + generateMarker(map, AdvancedMarker, "sub", { + lat: startNode.lat, + lng: startNode.lng, + }); + generateMarker(map, AdvancedMarker, "end", { + lat: endNode.lat, + lng: endNode.lng + }); + } else { + generateMarker(map, AdvancedMarker, "sub", { + lat: startNode.lat, + lng: startNode.lng, + }); + generateMarker(map, AdvancedMarker, "sub", { + lat: endNode.lat, + lng: endNode.lng + }); + } new Polyline({ - path, - map, - strokeColor: "#161616", // 원하는 색상 - strokeWeight: "4", // 원하는 두께 - }); - - new AdvancedMarker({ - position: { lat: segment.startNode.lat, lng: segment.startNode.lng }, - map, - content: subMarkerElement, - }); - - new AdvancedMarker({ - position: { lat: segment.endNode.lat, lng: segment.endNode.lng }, - map, - content: subMarkerElement, - }); - const endMarker = new AdvancedMarker({ - position: { lat: destinationNode.lat, lng: destinationNode.lng }, + path: [startNode, endNode], map, - content: endSvgElement, - title: "도착지", + strokeColor: "#000000", + strokeOpacity: 2.0, }); }); - }, [map, route]); + }, [map, routes]); return
    ; }; diff --git a/uniro_frontend/src/pages/combinedResult.tsx b/uniro_frontend/src/pages/combinedResult.tsx index b467dfa..998a64d 100644 --- a/uniro_frontend/src/pages/combinedResult.tsx +++ b/uniro_frontend/src/pages/combinedResult.tsx @@ -62,7 +62,7 @@ const MergedRoutePage = () => { > From 50f6bab01fdbe4cd41cf1079ab367f3d4414a26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JunhyukPark=28=EB=B0=95=EC=A4=80=ED=98=81=29?= Date: Thu, 30 Jan 2025 16:29:44 +0900 Subject: [PATCH 08/13] =?UTF-8?q?[UNI-15]=20feat=20:=20map=20=EB=B0=94?= =?UTF-8?q?=ED=85=80=20=EC=8B=9C=ED=8A=B8=EC=97=90=20=EB=A7=9E=EC=B6=B0?= =?UTF-8?q?=EC=84=9C=20=EB=B3=80=EA=B2=BD=20=EB=A1=9C=EC=A7=81=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uniro_frontend/src/App.tsx | 8 +- uniro_frontend/src/component/NavgationMap.tsx | 158 +++++++----------- .../navigation/bottomSheetHandle.tsx | 20 +++ .../navigation/navigationDescription.tsx | 60 +++++++ uniro_frontend/src/components/routeList.tsx | 8 +- uniro_frontend/src/components/topBar.tsx | 59 +------ uniro_frontend/src/index.css | 7 +- .../map/initializer/googleMapInitializer.ts | 9 +- uniro_frontend/src/pages/combinedResult.tsx | 145 ---------------- uniro_frontend/src/pages/navigationResult.tsx | 126 ++++++++++++++ .../utils/markers/generateAdvancedMarker.ts | 53 ++++++ .../src/utils/navigation/calculateCenter.ts | 9 + 12 files changed, 352 insertions(+), 310 deletions(-) create mode 100644 uniro_frontend/src/components/navigation/bottomSheetHandle.tsx create mode 100644 uniro_frontend/src/components/navigation/navigationDescription.tsx delete mode 100644 uniro_frontend/src/pages/combinedResult.tsx create mode 100644 uniro_frontend/src/pages/navigationResult.tsx create mode 100644 uniro_frontend/src/utils/markers/generateAdvancedMarker.ts create mode 100644 uniro_frontend/src/utils/navigation/calculateCenter.ts diff --git a/uniro_frontend/src/App.tsx b/uniro_frontend/src/App.tsx index 48c754d..41eb866 100644 --- a/uniro_frontend/src/App.tsx +++ b/uniro_frontend/src/App.tsx @@ -3,9 +3,7 @@ import "./App.css"; import Demo from "./pages/demo"; import LandingPage from "./pages/landing"; import UniversitySearchPage from "./pages/search"; -import RouteResultPage from "./pages/result"; -import DetailResultPage from "./pages/detailResult"; -import MergedRoutePage from "./pages/combinedResult"; +import NavigationResultPage from "./pages/navigationResult"; function App() { return ( @@ -13,9 +11,7 @@ function App() { } /> } /> } /> - } /> - } /> - } /> + } /> ); } diff --git a/uniro_frontend/src/component/NavgationMap.tsx b/uniro_frontend/src/component/NavgationMap.tsx index 4f2e695..d889356 100644 --- a/uniro_frontend/src/component/NavgationMap.tsx +++ b/uniro_frontend/src/component/NavgationMap.tsx @@ -1,131 +1,95 @@ -import { useEffect } from "react"; +import { useEffect, useRef } from "react"; import useMap from "../hooks/useMap"; -import startIcon from "../assets/marker/startIcon.svg?raw"; -import arriveMarker from "../assets/marker/arriveMarker.svg?raw"; -import subMarker from "../assets/marker/subMarker.svg?raw"; -import cautionMarker from "../assets/marker/cautionMarker.svg?raw"; import { NavigationRoute } from "../data/types/route"; +import { generateAdvancedMarker } from "../utils/markers/generateAdvancedMarker"; -// props 타입 정의 (필요 시) type MapProps = { style?: React.CSSProperties; - // route, startBuilding, destinationBuilding 등을 밖에서 받아온다고 가정 routes: NavigationRoute; + /** 바텀시트나 상단 UI에 의해 가려지는 영역이 있을 경우, 지도 fitBounds에 추가할 패딩값 */ + topPadding?: number; + bottomPadding?: number; }; -const generateMarker = ( - map: google.maps.Map, - AdvancedMarker: typeof google.maps.marker.AdvancedMarkerElement, - type: "start" | "sub" | "end" | "caution", - position: { lat: number; lng: number }, -) => { - if (type === "start") { - const startSvgElement = new DOMParser().parseFromString(startIcon, "image/svg+xml").documentElement; - new AdvancedMarker({ - position: position, - map, - content: startSvgElement, - title: "출발지", - }); - } - else if (type === "end") { - const endSvgElement = new DOMParser().parseFromString(arriveMarker, "image/svg+xml").documentElement; - new AdvancedMarker({ - position: position, - map, - content: endSvgElement, - title: "도착지", - }); - } - else if (type === "caution") { - const cautionSvgElement = new DOMParser().parseFromString(cautionMarker, "image/svg+xml").documentElement; - new AdvancedMarker({ - position: position, - map, - content: cautionSvgElement, - title: "주의사항", - }); - } - else { - const subMarkerElement = new DOMParser().parseFromString(subMarker, "image/svg+xml").documentElement; - new AdvancedMarker({ - position: position, - map, - content: subMarkerElement, - }); - } -}; +const NavigationMap = ({ style, routes, topPadding = 0, bottomPadding = 0 }: MapProps) => { + const { mapRef, map, AdvancedMarker, Polyline } = useMap(); -const NavigationMap = ({ style, routes }: MapProps) => { - const { mapRef, map, AdvancedMarker, Polyline, mapLoaded } = useMap(); + const boundsRef = useRef(null); if (!style) { style = { height: "100%", width: "100%" }; } useEffect(() => { - if (!map) return; - - - if (!routes) return; - + if (!map || !AdvancedMarker || !routes || !Polyline) return; - map.setZoom(18); - // 모든 lat와 lng의 평균값을 중심으로 지도를 보여줌 - const latSum = routes.route.reduce((acc, route) => acc + route.startNode.lat + route.endNode.lat, 0); - const lngSum = routes.route.reduce((acc, route) => acc + route.startNode.lng + route.endNode.lng, 0); - const latCenter = latSum / (routes.route.length * 2); - const lngCenter = lngSum / (routes.route.length * 2); - map.setCenter({ lat: latCenter, lng: lngCenter }); + const { route: routeList } = routes; + if (!routeList || routeList.length === 0) return; + const bounds = new google.maps.LatLngBounds(); - routes.route.forEach((route, index) => { + routeList.forEach((route, index) => { const { startNode, endNode } = route; + new Polyline({ + path: [startNode, endNode], + map, + strokeColor: "#000000", + strokeOpacity: 2.0, + }); + bounds.extend(new google.maps.LatLng(startNode.lat, startNode.lng)); + bounds.extend(new google.maps.LatLng(endNode.lat, endNode.lng)); + }); - if (route.cautionFactors && route.cautionFactors.length > 0) { - generateMarker(map, AdvancedMarker, "caution", { - lat: (startNode.lat + endNode.lat) / 2, - lng: (startNode.lng + endNode.lng) / 2 + routeList.forEach((route, index) => { + const { startNode, endNode } = route; + if (index !== 0 && index !== routeList.length - 1) { + if (index === 1) { + generateAdvancedMarker(map, AdvancedMarker, "sub", { + lat: startNode.lat, + lng: startNode.lng, + }); } - ) - } - if (index === 0) { - generateMarker(map, AdvancedMarker, "start", { - lat: startNode.lat, - lng: startNode.lng, - }); - generateMarker(map, AdvancedMarker, "sub", { + generateAdvancedMarker(map, AdvancedMarker, "sub", { lat: endNode.lat, - lng: endNode.lng + lng: endNode.lng, }); } - else if (index === routes.route.length - 1) { - generateMarker(map, AdvancedMarker, "sub", { - lat: startNode.lat, - lng: startNode.lng, - }); - generateMarker(map, AdvancedMarker, "end", { - lat: endNode.lat, - lng: endNode.lng - }); + }); - } else { - generateMarker(map, AdvancedMarker, "sub", { + const edgeRoutes = [routeList[0], routeList[routeList.length - 1]]; + + edgeRoutes.forEach((route, index) => { + const { startNode, endNode } = route; + if (index === 0) { + generateAdvancedMarker(map, AdvancedMarker, "start", { lat: startNode.lat, lng: startNode.lng, }); - generateMarker(map, AdvancedMarker, "sub", { + } else { + generateAdvancedMarker(map, AdvancedMarker, "end", { lat: endNode.lat, - lng: endNode.lng + lng: endNode.lng, }); } - new Polyline({ - path: [startNode, endNode], - map, - strokeColor: "#000000", - strokeOpacity: 2.0, - }); }); - }, [map, routes]); + + boundsRef.current = bounds; + map.fitBounds(bounds, { + top: topPadding, + right: 50, + bottom: bottomPadding, + left: 50, + }); + }, [map, AdvancedMarker, Polyline, routes]); + + useEffect(() => { + if (!map || !boundsRef.current) return; + map.fitBounds(boundsRef.current, { + top: topPadding, + right: 50, + bottom: bottomPadding, + left: 50, + }); + }, [map, bottomPadding, topPadding]); return
    ; }; diff --git a/uniro_frontend/src/components/navigation/bottomSheetHandle.tsx b/uniro_frontend/src/components/navigation/bottomSheetHandle.tsx new file mode 100644 index 0000000..9fdb66e --- /dev/null +++ b/uniro_frontend/src/components/navigation/bottomSheetHandle.tsx @@ -0,0 +1,20 @@ +import { DragControls } from "framer-motion"; +import React from "react"; + +type HandleProps = { + dragControls: DragControls; +}; + +const BottomSheetHandle = ({ dragControls }: HandleProps) => { + return ( +
    dragControls.start(e)} + > +
    +
    + ); +}; + +export default BottomSheetHandle; diff --git a/uniro_frontend/src/components/navigation/navigationDescription.tsx b/uniro_frontend/src/components/navigation/navigationDescription.tsx new file mode 100644 index 0000000..648bbbf --- /dev/null +++ b/uniro_frontend/src/components/navigation/navigationDescription.tsx @@ -0,0 +1,60 @@ +import React, { useState } from "react"; +import Cancel from "../../assets/icon/close.svg?react"; +import CautionIcon from "../../assets/icon/cautionText.svg?react"; +import SafeIcon from "../../assets/icon/safeText.svg?react"; +import DestinationIcon from "../../assets/icon/destination.svg?react"; +import StartIcon from "../../assets/icon/start.svg?react"; +import ResultDivider from "../../assets/icon/resultDivider.svg?react"; +import { NavigationRoute } from "../../data/types/route"; +import { mockNavigationRoute } from "../../data/mock/hanyangRoute"; +const TITLE = "전동휠체어 예상소요시간"; + +type TopBarProps = { + isDetailView: boolean; +}; + +const NavigationDescription = ({ isDetailView }: TopBarProps) => { + const [route, _] = useState(mockNavigationRoute); + + return ( +
    +
    + {TITLE} + {!isDetailView && } +
    +
    +
    +
    +
    + {route.totalCost} +
    +
    +
    +
    +
    + {`${route.totalDistance}m`} +
    +
    +
    + {route.hasCaution ? : } + + 가는 길에 주의 요소가 {route.hasCaution ? "있어요" : "없어요"} + +
    +
    +
    +
    + + {route.startBuilding.buildingName} +
    + +
    + + {route.destinationBuilding.buildingName} +
    +
    +
    + ); +}; + +export default NavigationDescription; diff --git a/uniro_frontend/src/components/routeList.tsx b/uniro_frontend/src/components/routeList.tsx index da2e04b..a5109b2 100644 --- a/uniro_frontend/src/components/routeList.tsx +++ b/uniro_frontend/src/components/routeList.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { Fragment } from "react"; import { RouteEdge } from "../data/types/edge"; import { Building } from "../data/types/node"; @@ -124,14 +124,14 @@ const Route = ({ const RouteList = ({ routes, startBuilding, destBuilding }: Props) => { return ( -
    +
    {routes.map((route, index) => ( - <> +
    - +
    ))}
    ); diff --git a/uniro_frontend/src/components/topBar.tsx b/uniro_frontend/src/components/topBar.tsx index 822690d..ef6b239 100644 --- a/uniro_frontend/src/components/topBar.tsx +++ b/uniro_frontend/src/components/topBar.tsx @@ -1,67 +1,20 @@ import React, { useState } from "react"; - -import Cancel from "../assets/icon/close.svg?react"; -import CautionIcon from "../assets/icon/cautionText.svg?react"; -import SafeIcon from "../assets/icon/safeText.svg?react"; -import DestinationIcon from "../assets/icon/destination.svg?react"; -import StartIcon from "../assets/icon/start.svg?react"; -import ResultDivider from "../assets/icon/resultDivider.svg?react"; -import { NavigationRoute } from "../data/types/route"; -import { mockNavigationRoute } from "../data/mock/hanyangRoute"; import AnimatedContainer from "../container/animatedContainer"; - -const TITLE = "전동휠체어 예상소요시간"; - +import NavigationDescription from "./navigation/navigationDescription"; type TopBarProps = { - isDetailView: boolean; + isVisible: boolean; }; -const TopBar = ({ isDetailView }: TopBarProps) => { - const [route, _] = useState(mockNavigationRoute); - +const TopBar = ({ isVisible }: TopBarProps) => { return ( -
    - {TITLE} - -
    -
    -
    -
    -
    - {route.totalCost} -
    -
    -
    -
    -
    - {`${route.totalDistance}m`} -
    -
    -
    - {route.hasCaution ? : } - - 가는 길에 주의 요소가 {route.hasCaution ? "있어요" : "없어요"} - -
    -
    -
    -
    - - {route.startBuilding.buildingName} -
    - -
    - - {route.destinationBuilding.buildingName} -
    -
    + ); }; diff --git a/uniro_frontend/src/index.css b/uniro_frontend/src/index.css index 0dadffa..ae9d964 100644 --- a/uniro_frontend/src/index.css +++ b/uniro_frontend/src/index.css @@ -40,7 +40,7 @@ } * { - font-family: "SF Pro Display", "AppleSDGothicNeo"; + font-family: "SF Pro Display", "AppleSDGothicNeo" !important; outline: none; --webkit-scrollbar-width: none; } @@ -53,6 +53,11 @@ scrollbar-width: none; } +.animated-container { + will-change: transform; /* 하드웨어 가속 활성화 */ + contain: strict; /* 리플로우 범위 제한 */ +} + @theme { --color-primary-100: #cde1fe; --color-primary-200: #9ac2fd; diff --git a/uniro_frontend/src/map/initializer/googleMapInitializer.ts b/uniro_frontend/src/map/initializer/googleMapInitializer.ts index e2eecaf..6bd9e46 100644 --- a/uniro_frontend/src/map/initializer/googleMapInitializer.ts +++ b/uniro_frontend/src/map/initializer/googleMapInitializer.ts @@ -26,10 +26,11 @@ export const initializeMap = async (mapElement: HTMLElement | null): Promise { - useScrollControl(); - - const [isDetailView, setIsDetailView] = useState(false); - - const [route, _] = useState(mockNavigationRoute); - - const MAX_SHEET_HEIGHT = window.innerHeight * 0.7; // 최대로 올렸을 때 시트 높이 - const MIN_SHEET_HEIGHT = window.innerHeight * 0.3; // 최소로 내렸을 때 시트 높이 - const HEADER_HEIGHT = 40; - - const dragControls = useDragControls(); - const y = useMotionValue(0); - const smoothY = useSpring(y, { stiffness: 200, damping: 20 }); - const zoom = useMotionValue(1); - const smoothZoom = useSpring(zoom, { stiffness: 200, damping: 20 }); - const zoomScale = useTransform(smoothZoom, (value) => `scale(${value})`); - - useEffect(() => { - const unsubscribe = smoothY.onChange((latest) => { - const progress = latest / (MAX_SHEET_HEIGHT - MIN_SHEET_HEIGHT); - const newZoom = 1 + progress * 0.1; - zoom.set(newZoom); - }); - return () => unsubscribe(); - }, [smoothY, zoom]); - - const showDetailView = () => setIsDetailView(true); - const hideDetailView = () => setIsDetailView(false); - - useEffect(() => { - console.log(route); - }, []); - - return ( -
    - - - - - - - - - - - - - -
    dragControls.start(e)} - > -
    -
    -
    -
    -
    -
    {TITLE}
    -
    -
    -
    -
    {route.totalCost}
    -
    -
    -
    -
    - {route.totalDistance}m -
    -
    -
    - {route.hasCaution ? : } - - 가는 길에 주의 요소가 {route.hasCaution ? "있어요" : "없어요"} - -
    -
    -
    - -
    - -
    - ); -}; - -export default MergedRoutePage; diff --git a/uniro_frontend/src/pages/navigationResult.tsx b/uniro_frontend/src/pages/navigationResult.tsx new file mode 100644 index 0000000..a7b67b5 --- /dev/null +++ b/uniro_frontend/src/pages/navigationResult.tsx @@ -0,0 +1,126 @@ +import React, { useCallback, useState } from "react"; +import { PanInfo, useDragControls } from "framer-motion"; +import TopBar from "../components/topBar"; +import Button from "../components/customButton"; +import GoBack from "../assets/icon/goBack.svg?react"; +import RouteList from "../components/routeList"; + +import { mockNavigationRoute } from "../data/mock/hanyangRoute"; +import { NavigationRoute } from "../data/types/route"; +import useScrollControl from "../hooks/useScrollControl"; +import AnimatedContainer from "../container/animatedContainer"; +import NavigationMap from "../component/NavgationMap"; +import NavigationDescription from "../components/navigation/navigationDescription"; +import BottomSheetHandle from "../components/navigation/bottomSheetHandle"; + +const TITLE = "전동휠체어 예상소요시간"; + +// 1. 돌아가면 위치 reset ✅ +// 2. 상세경로 scroll 끝까지 가능하게 하기 ❎ +// 3. 레이아웃 맞추기 +// 4. 코드 리팩토링 하기 + +const NavigationResultPage = () => { + const MAX_SHEET_HEIGHT = window.innerHeight * 0.7; + const MIN_SHEET_HEIGHT = window.innerHeight * 0.35; + + const HEADER_HEIGHT = 40; + + const [isDetailView, setIsDetailView] = useState(false); + + const [sheetHeight, setSheetHeight] = useState(0); + const [topBarHeight, setTopBarHeight] = useState(143); + + const [route, setRoute] = useState(mockNavigationRoute); + const [totalHeight, setTotalHeight] = useState(0); + + useScrollControl(); + + const dragControls = useDragControls(); + + const showDetailView = () => { + setSheetHeight(MAX_SHEET_HEIGHT); + setTopBarHeight(50); + setIsDetailView(true); + }; + const hideDetailView = () => { + setSheetHeight(0); + setTopBarHeight(143); + setIsDetailView(false); + }; + + const handleDrag = useCallback((event: Event, info: PanInfo) => { + setSheetHeight((prev) => { + const newHeight = prev - info.delta.y; + return Math.min(Math.max(newHeight, MIN_SHEET_HEIGHT), MAX_SHEET_HEIGHT); + }); + }, []); + + return ( +
    + + + + + + + + + + + + +
    + + +
    +
    +
    + ); +}; + +export default NavigationResultPage; diff --git a/uniro_frontend/src/utils/markers/generateAdvancedMarker.ts b/uniro_frontend/src/utils/markers/generateAdvancedMarker.ts new file mode 100644 index 0000000..9ed2412 --- /dev/null +++ b/uniro_frontend/src/utils/markers/generateAdvancedMarker.ts @@ -0,0 +1,53 @@ +import startIcon from "../../assets/marker/startIcon.svg?raw"; +import arriveMarker from "../../assets/marker/arriveMarker.svg?raw"; +import subMarker from "../../assets/marker/subMarker.svg?raw"; +import cautionMarker from "../../assets/marker/cautionMarker.svg?raw"; + +export const generateAdvancedMarker = ( + map: google.maps.Map, + AdvancedMarker: typeof google.maps.marker.AdvancedMarkerElement, + type: "start" | "sub" | "end" | "caution", + position: { lat: number; lng: number }, +) => { + switch (type) { + case "start": { + const startSvgElement = new DOMParser().parseFromString(startIcon, "image/svg+xml").documentElement; + new AdvancedMarker({ + position: position, + map, + content: startSvgElement, + title: "출발지", + }); + break; + } + case "end": { + const endSvgElement = new DOMParser().parseFromString(arriveMarker, "image/svg+xml").documentElement; + new AdvancedMarker({ + position: position, + map, + content: endSvgElement, + title: "도착지", + }); + break; + } + case "caution": { + const cautionSvgElement = new DOMParser().parseFromString(cautionMarker, "image/svg+xml").documentElement; + new AdvancedMarker({ + position: position, + map, + content: cautionSvgElement, + title: "주의사항", + }); + break; + } + default: { + const subMarkerElement = new DOMParser().parseFromString(subMarker, "image/svg+xml").documentElement; + new AdvancedMarker({ + position: position, + map, + content: subMarkerElement, + }); + break; + } + } +}; diff --git a/uniro_frontend/src/utils/navigation/calculateCenter.ts b/uniro_frontend/src/utils/navigation/calculateCenter.ts new file mode 100644 index 0000000..44f685b --- /dev/null +++ b/uniro_frontend/src/utils/navigation/calculateCenter.ts @@ -0,0 +1,9 @@ +import { RouteEdge } from "../../data/types/edge"; + +export const calculateCenterCoordinate = (routes: RouteEdge[]) => { + const latSum = routes.reduce((acc, route) => acc + route.startNode.lat + route.endNode.lat, 0); + const lngSum = routes.reduce((acc, route) => acc + route.startNode.lng + route.endNode.lng, 0); + const latCenter = latSum / (routes.length * 2); + const lngCenter = lngSum / (routes.length * 2); + return { lat: latCenter, lng: lngCenter }; +}; From 111593dfa0916bf02755992a4676393d6189a8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JunhyukPark=28=EB=B0=95=EC=A4=80=ED=98=81=29?= Date: Thu, 30 Jan 2025 17:27:12 +0900 Subject: [PATCH 09/13] =?UTF-8?q?[UNI-15]=20chore:=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/assets/icon/list/icon/start-24px.svg | 3 - uniro_frontend/src/component/NavgationMap.tsx | 2 +- .../{ => bottomSheet}/bottomSheetHandle.tsx | 0 .../navigation/navigationDescription.tsx | 6 +- .../route/routeCard.tsx} | 56 +++---- .../components/navigation/route/routeList.tsx | 33 +++++ uniro_frontend/src/components/topBar.tsx | 1 - uniro_frontend/src/data/types/edge.d.ts | 2 +- uniro_frontend/src/index.css | 5 - .../map/initializer/googleMapInitializer.ts | 1 - uniro_frontend/src/pages/detailResult.tsx | 139 ------------------ uniro_frontend/src/pages/navigationResult.tsx | 61 ++++---- uniro_frontend/src/pages/result.tsx | 42 ------ .../src/utils/navigation/calculateCenter.ts | 9 -- 14 files changed, 95 insertions(+), 265 deletions(-) delete mode 100644 uniro_frontend/src/assets/icon/list/icon/start-24px.svg rename uniro_frontend/src/components/navigation/{ => bottomSheet}/bottomSheetHandle.tsx (100%) rename uniro_frontend/src/components/{routeList.tsx => navigation/route/routeCard.tsx} (77%) create mode 100644 uniro_frontend/src/components/navigation/route/routeList.tsx delete mode 100644 uniro_frontend/src/pages/detailResult.tsx delete mode 100644 uniro_frontend/src/pages/result.tsx delete mode 100644 uniro_frontend/src/utils/navigation/calculateCenter.ts diff --git a/uniro_frontend/src/assets/icon/list/icon/start-24px.svg b/uniro_frontend/src/assets/icon/list/icon/start-24px.svg deleted file mode 100644 index afaa65c..0000000 --- a/uniro_frontend/src/assets/icon/list/icon/start-24px.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/uniro_frontend/src/component/NavgationMap.tsx b/uniro_frontend/src/component/NavgationMap.tsx index d889356..c130836 100644 --- a/uniro_frontend/src/component/NavgationMap.tsx +++ b/uniro_frontend/src/component/NavgationMap.tsx @@ -27,7 +27,7 @@ const NavigationMap = ({ style, routes, topPadding = 0, bottomPadding = 0 }: Map if (!routeList || routeList.length === 0) return; const bounds = new google.maps.LatLngBounds(); - routeList.forEach((route, index) => { + routeList.forEach((route) => { const { startNode, endNode } = route; new Polyline({ path: [startNode, endNode], diff --git a/uniro_frontend/src/components/navigation/bottomSheetHandle.tsx b/uniro_frontend/src/components/navigation/bottomSheet/bottomSheetHandle.tsx similarity index 100% rename from uniro_frontend/src/components/navigation/bottomSheetHandle.tsx rename to uniro_frontend/src/components/navigation/bottomSheet/bottomSheetHandle.tsx diff --git a/uniro_frontend/src/components/navigation/navigationDescription.tsx b/uniro_frontend/src/components/navigation/navigationDescription.tsx index 648bbbf..950afeb 100644 --- a/uniro_frontend/src/components/navigation/navigationDescription.tsx +++ b/uniro_frontend/src/components/navigation/navigationDescription.tsx @@ -17,7 +17,7 @@ const NavigationDescription = ({ isDetailView }: TopBarProps) => { const [route, _] = useState(mockNavigationRoute); return ( -
    +
    {TITLE} {!isDetailView && } @@ -35,9 +35,9 @@ const NavigationDescription = ({ isDetailView }: TopBarProps) => { {`${route.totalDistance}m`}
    -
    +
    {route.hasCaution ? : } - + 가는 길에 주의 요소가 {route.hasCaution ? "있어요" : "없어요"}
    diff --git a/uniro_frontend/src/components/routeList.tsx b/uniro_frontend/src/components/navigation/route/routeCard.tsx similarity index 77% rename from uniro_frontend/src/components/routeList.tsx rename to uniro_frontend/src/components/navigation/route/routeCard.tsx index a5109b2..64a71ce 100644 --- a/uniro_frontend/src/components/routeList.tsx +++ b/uniro_frontend/src/components/navigation/route/routeCard.tsx @@ -1,19 +1,11 @@ -import React, { Fragment } from "react"; -import { RouteEdge } from "../data/types/edge"; -import { Building } from "../data/types/node"; - -import StartIcon from "../assets/route/start.svg?react"; -import DestinationIcon from "../assets/route/dest.svg?react"; -import StraightIcon from "../assets/route/straight.svg?react"; -import RightIcon from "../assets/route/right.svg?react"; -import LeftIcon from "../assets/route/left.svg?react"; - -type Props = { - routes: RouteEdge[]; - startBuilding: Building; - destBuilding: Building; -}; -const Divider = () =>
    ; +import StartIcon from "../../../assets/route/start.svg?react"; +import DestinationIcon from "../../../assets/route/dest.svg?react"; +import StraightIcon from "../../../assets/route/straight.svg?react"; +import RightIcon from "../../../assets/route/right.svg?react"; +import LeftIcon from "../../../assets/route/left.svg?react"; +import CautionText from "../../../assets/icon/cautionText.svg?react"; +import { RouteEdge } from "../../../data/types/edge"; +import { Building } from "../../../data/types/node"; const NumberIcon = ({ index }: { index: number }) => { return ( @@ -23,7 +15,7 @@ const NumberIcon = ({ index }: { index: number }) => { ); }; -const Route = ({ +export const RouteCard = ({ index, route, startBuilding, @@ -113,6 +105,19 @@ const Route = ({
    ); + case "caution": + return ( +
    +
    + +
    {route.distance}m
    +
    +
    + +
    턱이 있어요
    +
    +
    + ); default: return (
    @@ -121,20 +126,3 @@ const Route = ({ ); } }; - -const RouteList = ({ routes, startBuilding, destBuilding }: Props) => { - return ( -
    - {routes.map((route, index) => ( - - -
    - -
    -
    - ))} -
    - ); -}; - -export default RouteList; diff --git a/uniro_frontend/src/components/navigation/route/routeList.tsx b/uniro_frontend/src/components/navigation/route/routeList.tsx new file mode 100644 index 0000000..0e78b4a --- /dev/null +++ b/uniro_frontend/src/components/navigation/route/routeList.tsx @@ -0,0 +1,33 @@ +import { Fragment } from "react"; +import { RouteEdge } from "../../../data/types/edge"; +import { Building } from "../../../data/types/node"; +import { RouteCard } from "./routeCard"; + +type Props = { + routes: RouteEdge[]; + startBuilding: Building; + destBuilding: Building; +}; +const Divider = () =>
    ; + +const RouteList = ({ routes, startBuilding, destBuilding }: Props) => { + return ( +
    + {routes.map((route, index) => ( + + +
    + +
    +
    + ))} +
    + ); +}; + +export default RouteList; diff --git a/uniro_frontend/src/components/topBar.tsx b/uniro_frontend/src/components/topBar.tsx index ef6b239..5520e2d 100644 --- a/uniro_frontend/src/components/topBar.tsx +++ b/uniro_frontend/src/components/topBar.tsx @@ -1,4 +1,3 @@ -import React, { useState } from "react"; import AnimatedContainer from "../container/animatedContainer"; import NavigationDescription from "./navigation/navigationDescription"; type TopBarProps = { diff --git a/uniro_frontend/src/data/types/edge.d.ts b/uniro_frontend/src/data/types/edge.d.ts index aea55c5..b227254 100644 --- a/uniro_frontend/src/data/types/edge.d.ts +++ b/uniro_frontend/src/data/types/edge.d.ts @@ -7,7 +7,7 @@ export interface Edge { endNode: CustomNode; } -export type Direction = "start" | "right" | "straight" | "left" | "uturn" | "destination"; +export type Direction = "start" | "right" | "straight" | "left" | "uturn" | "destination" | "caution"; // 위험 요소 & 주의 요소 // 마커를 표시하거나, 길 찾기 결과의 경로를 그릴 때 사용 diff --git a/uniro_frontend/src/index.css b/uniro_frontend/src/index.css index ae9d964..f2f44e3 100644 --- a/uniro_frontend/src/index.css +++ b/uniro_frontend/src/index.css @@ -53,11 +53,6 @@ scrollbar-width: none; } -.animated-container { - will-change: transform; /* 하드웨어 가속 활성화 */ - contain: strict; /* 리플로우 범위 제한 */ -} - @theme { --color-primary-100: #cde1fe; --color-primary-200: #9ac2fd; diff --git a/uniro_frontend/src/map/initializer/googleMapInitializer.ts b/uniro_frontend/src/map/initializer/googleMapInitializer.ts index 6bd9e46..2bcdc23 100644 --- a/uniro_frontend/src/map/initializer/googleMapInitializer.ts +++ b/uniro_frontend/src/map/initializer/googleMapInitializer.ts @@ -1,5 +1,4 @@ import { HanyangUniversity } from "../../constant/university"; -import { HanyangUniversityBounds } from "../../constant/bounds"; import loadGoogleMapsLibraries from "../loader/googleMapLoader"; diff --git a/uniro_frontend/src/pages/detailResult.tsx b/uniro_frontend/src/pages/detailResult.tsx deleted file mode 100644 index 86eded0..0000000 --- a/uniro_frontend/src/pages/detailResult.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { motion, useDragControls, AnimatePresence, useMotionValue, useSpring, useTransform } from "framer-motion"; - -import CautionIcon from "../assets/icon/cautionText.svg?react"; -import SafeIcon from "../assets/icon/safeText.svg?react"; -import GoBack from "../assets/icon/goBack.svg?react"; - -import Map from "../component/Map"; -import useScrollControl from "../hooks/useScrollControl"; -import { NavigationRoute } from "../data/types/route"; -import { mockNavigationRoute } from "../data/mock/hanyangRoute"; -import RouteList from "../components/routeList"; -import { Link, useNavigate } from "react-router"; - -const TITLE = "전동휠체어 예상소요시간"; - -const DetailResultPage = () => { - useScrollControl(); - const [route] = useState(mockNavigationRoute); - - const MAX_SHEET_HEIGHT = window.innerHeight * 0.7; - const MAX_SHEET_POSITION = window.innerHeight - MAX_SHEET_HEIGHT; - const MIN_SHEET_HEIGHT = window.innerHeight * 0.3; - const HEADER_HEIGHT = 40; - - const dragControls = useDragControls(); - const navigate = useNavigate(); - - const [isVisible, setIsVisible] = useState(true); - - const handleClick = () => { - setIsVisible(false); // 애니메이션 실행 (이후 컴포넌트 제거됨) - setTimeout(() => { - navigate("/result"); // 애니메이션 후 이동 - }, 300); // transition.duration과 동일한 시간 설정 - }; - - const y = useMotionValue(0); - const smoothY = useSpring(y, { stiffness: 200, damping: 20 }); - - // Map의 줌 값 (드래그할 때 자연스럽게 변함) - const zoom = useMotionValue(1); // 기본 1 (100%) - const smoothZoom = useSpring(zoom, { stiffness: 200, damping: 20 }); - - useEffect(() => { - const unsubscribe = smoothY.onChange((latest) => { - const progress = latest / (MAX_SHEET_HEIGHT - MIN_SHEET_HEIGHT); // 0 ~ 1 사이의 값 - const newZoom = 1 + progress * 0.1; // 1.0 ~ 1.1 (10% 확대) - zoom.set(newZoom); - }); - return () => unsubscribe(); - }, [smoothY]); - - const zoomScale = useTransform(smoothZoom, (value) => `scale(${value})`); - - return ( -
    - - {isVisible && ( - - - - )} - - - - - - {isVisible && ( - -
    dragControls.start(e)} - > -
    -
    -
    -
    -
    -
    {TITLE}
    -
    -
    -
    -
    {route.totalCost}
    -
    -
    -
    -
    - {route.totalDistance}m -
    -
    -
    - {route.hasCaution ? : } - - 가는 길에 주의 요소가 {route.hasCaution ? "있어요" : "없어요"} - -
    -
    -
    - -
    - - )} - -
    - ); -}; - -export default DetailResultPage; diff --git a/uniro_frontend/src/pages/navigationResult.tsx b/uniro_frontend/src/pages/navigationResult.tsx index a7b67b5..7f897ef 100644 --- a/uniro_frontend/src/pages/navigationResult.tsx +++ b/uniro_frontend/src/pages/navigationResult.tsx @@ -1,9 +1,8 @@ import React, { useCallback, useState } from "react"; import { PanInfo, useDragControls } from "framer-motion"; -import TopBar from "../components/topBar"; import Button from "../components/customButton"; import GoBack from "../assets/icon/goBack.svg?react"; -import RouteList from "../components/routeList"; +import RouteList from "../components/navigation/route/routeList"; import { mockNavigationRoute } from "../data/mock/hanyangRoute"; import { NavigationRoute } from "../data/types/route"; @@ -11,28 +10,28 @@ import useScrollControl from "../hooks/useScrollControl"; import AnimatedContainer from "../container/animatedContainer"; import NavigationMap from "../component/NavgationMap"; import NavigationDescription from "../components/navigation/navigationDescription"; -import BottomSheetHandle from "../components/navigation/bottomSheetHandle"; - -const TITLE = "전동휠체어 예상소요시간"; +import BottomSheetHandle from "../components/navigation/bottomSheet/bottomSheetHandle"; // 1. 돌아가면 위치 reset ✅ // 2. 상세경로 scroll 끝까지 가능하게 하기 ❎ -// 3. 레이아웃 맞추기 -// 4. 코드 리팩토링 하기 +// 3. 코드 리팩토링 하기 -const NavigationResultPage = () => { - const MAX_SHEET_HEIGHT = window.innerHeight * 0.7; - const MIN_SHEET_HEIGHT = window.innerHeight * 0.35; +const MAX_SHEET_HEIGHT = window.innerHeight * 0.7; +const MIN_SHEET_HEIGHT = window.innerHeight * 0.35; +const CLOSED_SHEET_HEIGHT = 0; + +const INITIAL_TOP_BAR_HEIGHT = 143; +const BOTTOM_SHEET_HANDLE_HEIGHT = 40; - const HEADER_HEIGHT = 40; +const PADDING_FOR_MAP_BOUNDARY = 50; +const NavigationResultPage = () => { const [isDetailView, setIsDetailView] = useState(false); - const [sheetHeight, setSheetHeight] = useState(0); - const [topBarHeight, setTopBarHeight] = useState(143); + const [sheetHeight, setSheetHeight] = useState(CLOSED_SHEET_HEIGHT); + const [topBarHeight, setTopBarHeight] = useState(INITIAL_TOP_BAR_HEIGHT); const [route, setRoute] = useState(mockNavigationRoute); - const [totalHeight, setTotalHeight] = useState(0); useScrollControl(); @@ -40,25 +39,36 @@ const NavigationResultPage = () => { const showDetailView = () => { setSheetHeight(MAX_SHEET_HEIGHT); - setTopBarHeight(50); + setTopBarHeight(PADDING_FOR_MAP_BOUNDARY); setIsDetailView(true); }; const hideDetailView = () => { - setSheetHeight(0); - setTopBarHeight(143); + setSheetHeight(CLOSED_SHEET_HEIGHT); + setTopBarHeight(INITIAL_TOP_BAR_HEIGHT); setIsDetailView(false); }; - const handleDrag = useCallback((event: Event, info: PanInfo) => { - setSheetHeight((prev) => { - const newHeight = prev - info.delta.y; - return Math.min(Math.max(newHeight, MIN_SHEET_HEIGHT), MAX_SHEET_HEIGHT); - }); - }, []); + const handleDrag = useCallback( + (event: Event, info: PanInfo) => { + setSheetHeight((prev) => { + const newHeight = prev - info.delta.y; + return Math.min(Math.max(newHeight, MIN_SHEET_HEIGHT), MAX_SHEET_HEIGHT); + }); + }, + [setSheetHeight, MAX_SHEET_HEIGHT, MIN_SHEET_HEIGHT], + ); return (
    - + + + {
    diff --git a/uniro_frontend/src/pages/result.tsx b/uniro_frontend/src/pages/result.tsx deleted file mode 100644 index dfc0229..0000000 --- a/uniro_frontend/src/pages/result.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useState } from "react"; -import TopBar from "../components/topBar"; -import Map from "../component/Map"; -import Button from "../components/customButton"; -import { useNavigate } from "react-router"; -import useScrollControl from "../hooks/useScrollControl"; -import { AnimatePresence, motion } from "framer-motion"; - -const RouteResultPage = () => { - useScrollControl(); - const [isVisible, setIsVisible] = useState(true); - const navigate = useNavigate(); - - const handleClick = () => { - setIsVisible(false); - setTimeout(() => { - navigate("/result/detail"); - }, 300); - }; - - return ( -
    - - - - {isVisible && ( - - - - )} - -
    - ); -}; - -export default RouteResultPage; diff --git a/uniro_frontend/src/utils/navigation/calculateCenter.ts b/uniro_frontend/src/utils/navigation/calculateCenter.ts deleted file mode 100644 index 44f685b..0000000 --- a/uniro_frontend/src/utils/navigation/calculateCenter.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { RouteEdge } from "../../data/types/edge"; - -export const calculateCenterCoordinate = (routes: RouteEdge[]) => { - const latSum = routes.reduce((acc, route) => acc + route.startNode.lat + route.endNode.lat, 0); - const lngSum = routes.reduce((acc, route) => acc + route.startNode.lng + route.endNode.lng, 0); - const latCenter = latSum / (routes.length * 2); - const lngCenter = lngSum / (routes.length * 2); - return { lat: latCenter, lng: lngCenter }; -}; From e9d6c02b4818e5b02c0525d7e705f0dc1a6027e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JunhyukPark=28=EB=B0=95=EC=A4=80=ED=98=81=29?= Date: Thu, 30 Jan 2025 17:52:17 +0900 Subject: [PATCH 10/13] =?UTF-8?q?[UNI-15]=20chore=20:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20dependency=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uniro_frontend/package-lock.json | 5 +++-- uniro_frontend/package.json | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uniro_frontend/package-lock.json b/uniro_frontend/package-lock.json index df33d11..8e59f44 100644 --- a/uniro_frontend/package-lock.json +++ b/uniro_frontend/package-lock.json @@ -11,7 +11,6 @@ "@googlemaps/js-api-loader": "^1.16.8", "@react-spring/web": "^9.7.5", "@tailwindcss/vite": "^4.0.0", - "@use-gesture/react": "^10.3.1", "framer-motion": "^12.0.6", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -2178,13 +2177,15 @@ "version": "10.3.1", "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@use-gesture/react": { "version": "10.3.1", "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", "license": "MIT", + "peer": true, "dependencies": { "@use-gesture/core": "10.3.1" }, diff --git a/uniro_frontend/package.json b/uniro_frontend/package.json index d8a8223..d854e29 100644 --- a/uniro_frontend/package.json +++ b/uniro_frontend/package.json @@ -13,7 +13,6 @@ "@googlemaps/js-api-loader": "^1.16.8", "@react-spring/web": "^9.7.5", "@tailwindcss/vite": "^4.0.0", - "@use-gesture/react": "^10.3.1", "framer-motion": "^12.0.6", "react": "^18.3.1", "react-dom": "^18.3.1", From 688d06b7c26c740106231717209e3f7239ceaba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JunhyukPark=28=EB=B0=95=EC=A4=80=ED=98=81=29?= Date: Fri, 31 Jan 2025 14:41:42 +0900 Subject: [PATCH 11/13] =?UTF-8?q?[UNI-15]=20chore=20:=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=EB=AA=85=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uniro_frontend/src/component/NavgationMap.tsx | 16 +++++++------- .../navigation/navigationDescription.tsx | 6 ++--- .../components/navigation/route/routeCard.tsx | 22 +++++++++---------- .../components/navigation/route/routeList.tsx | 13 ++++++----- .../src/data/factory/edgeFactory.ts | 2 +- .../src/data/factory/navigationFactory.ts | 2 +- uniro_frontend/src/data/types/edge.d.ts | 2 +- uniro_frontend/src/data/types/route.d.ts | 2 +- uniro_frontend/src/pages/navigationResult.tsx | 4 ++-- .../utils/markers/generateAdvancedMarker.ts | 22 +++++++++---------- 10 files changed, 46 insertions(+), 45 deletions(-) diff --git a/uniro_frontend/src/component/NavgationMap.tsx b/uniro_frontend/src/component/NavgationMap.tsx index c130836..82a8256 100644 --- a/uniro_frontend/src/component/NavgationMap.tsx +++ b/uniro_frontend/src/component/NavgationMap.tsx @@ -27,8 +27,8 @@ const NavigationMap = ({ style, routes, topPadding = 0, bottomPadding = 0 }: Map if (!routeList || routeList.length === 0) return; const bounds = new google.maps.LatLngBounds(); - routeList.forEach((route) => { - const { startNode, endNode } = route; + routeList.forEach((edge) => { + const { startNode, endNode } = edge; new Polyline({ path: [startNode, endNode], map, @@ -39,8 +39,8 @@ const NavigationMap = ({ style, routes, topPadding = 0, bottomPadding = 0 }: Map bounds.extend(new google.maps.LatLng(endNode.lat, endNode.lng)); }); - routeList.forEach((route, index) => { - const { startNode, endNode } = route; + routeList.forEach((edge, index) => { + const { startNode, endNode } = edge; if (index !== 0 && index !== routeList.length - 1) { if (index === 1) { generateAdvancedMarker(map, AdvancedMarker, "sub", { @@ -57,15 +57,15 @@ const NavigationMap = ({ style, routes, topPadding = 0, bottomPadding = 0 }: Map const edgeRoutes = [routeList[0], routeList[routeList.length - 1]]; - edgeRoutes.forEach((route, index) => { - const { startNode, endNode } = route; + edgeRoutes.forEach((edge, index) => { + const { startNode, endNode } = edge; if (index === 0) { - generateAdvancedMarker(map, AdvancedMarker, "start", { + generateAdvancedMarker(map, AdvancedMarker, "origin", { lat: startNode.lat, lng: startNode.lng, }); } else { - generateAdvancedMarker(map, AdvancedMarker, "end", { + generateAdvancedMarker(map, AdvancedMarker, "destination", { lat: endNode.lat, lng: endNode.lng, }); diff --git a/uniro_frontend/src/components/navigation/navigationDescription.tsx b/uniro_frontend/src/components/navigation/navigationDescription.tsx index 950afeb..e3ab1e0 100644 --- a/uniro_frontend/src/components/navigation/navigationDescription.tsx +++ b/uniro_frontend/src/components/navigation/navigationDescription.tsx @@ -3,7 +3,7 @@ import Cancel from "../../assets/icon/close.svg?react"; import CautionIcon from "../../assets/icon/cautionText.svg?react"; import SafeIcon from "../../assets/icon/safeText.svg?react"; import DestinationIcon from "../../assets/icon/destination.svg?react"; -import StartIcon from "../../assets/icon/start.svg?react"; +import OriginIcon from "../../assets/icon/start.svg?react"; import ResultDivider from "../../assets/icon/resultDivider.svg?react"; import { NavigationRoute } from "../../data/types/route"; import { mockNavigationRoute } from "../../data/mock/hanyangRoute"; @@ -44,8 +44,8 @@ const NavigationDescription = ({ isDetailView }: TopBarProps) => {
    - - {route.startBuilding.buildingName} + + {route.originBuilding.buildingName}
    diff --git a/uniro_frontend/src/components/navigation/route/routeCard.tsx b/uniro_frontend/src/components/navigation/route/routeCard.tsx index 64a71ce..d00f175 100644 --- a/uniro_frontend/src/components/navigation/route/routeCard.tsx +++ b/uniro_frontend/src/components/navigation/route/routeCard.tsx @@ -1,4 +1,4 @@ -import StartIcon from "../../../assets/route/start.svg?react"; +import OriginIcon from "../../../assets/route/start.svg?react"; import DestinationIcon from "../../../assets/route/dest.svg?react"; import StraightIcon from "../../../assets/route/straight.svg?react"; import RightIcon from "../../../assets/route/right.svg?react"; @@ -18,13 +18,13 @@ const NumberIcon = ({ index }: { index: number }) => { export const RouteCard = ({ index, route, - startBuilding, - destBuilding, + originBuilding, + destinationBuilding, }: { index: number; route: RouteEdge; - startBuilding: Building; - destBuilding: Building; + originBuilding: Building; + destinationBuilding: Building; }) => { switch (route.direction) { case "straight": @@ -79,16 +79,16 @@ export const RouteCard = ({
    ); - case "start": + case "origin": return (
    - +
    출발
    -
    {startBuilding.buildingName}
    -
    {startBuilding.address}
    +
    {originBuilding.buildingName}
    +
    {originBuilding.address}
    ); @@ -100,8 +100,8 @@ export const RouteCard = ({
    도착
    -
    {destBuilding.buildingName}
    -
    {destBuilding.address}
    +
    {destinationBuilding.buildingName}
    +
    {destinationBuilding.address}
    ); diff --git a/uniro_frontend/src/components/navigation/route/routeList.tsx b/uniro_frontend/src/components/navigation/route/routeList.tsx index 0e78b4a..6c27124 100644 --- a/uniro_frontend/src/components/navigation/route/routeList.tsx +++ b/uniro_frontend/src/components/navigation/route/routeList.tsx @@ -3,14 +3,15 @@ import { RouteEdge } from "../../../data/types/edge"; import { Building } from "../../../data/types/node"; import { RouteCard } from "./routeCard"; -type Props = { +type RouteListProps = { routes: RouteEdge[]; - startBuilding: Building; - destBuilding: Building; + originBuilding: Building; + destinationBuilding: Building; }; + const Divider = () =>
    ; -const RouteList = ({ routes, startBuilding, destBuilding }: Props) => { +const RouteList = ({ routes, originBuilding, destinationBuilding }: RouteListProps) => { return (
    {routes.map((route, index) => ( @@ -20,8 +21,8 @@ const RouteList = ({ routes, startBuilding, destBuilding }: Props) => {
    diff --git a/uniro_frontend/src/data/factory/edgeFactory.ts b/uniro_frontend/src/data/factory/edgeFactory.ts index 820e055..77ca341 100644 --- a/uniro_frontend/src/data/factory/edgeFactory.ts +++ b/uniro_frontend/src/data/factory/edgeFactory.ts @@ -27,7 +27,7 @@ export const createRouteEdges = (edges: HazardEdge[]): RouteEdge[] => { routeEdges.push(routeEdge); }); - routeEdges[0].direction = "start"; + routeEdges[0].direction = "origin"; routeEdges[routeEdges.length - 1].direction = "destination"; return routeEdges; diff --git a/uniro_frontend/src/data/factory/navigationFactory.ts b/uniro_frontend/src/data/factory/navigationFactory.ts index 46e75fd..d62f342 100644 --- a/uniro_frontend/src/data/factory/navigationFactory.ts +++ b/uniro_frontend/src/data/factory/navigationFactory.ts @@ -9,7 +9,7 @@ export const createNavigationRoute = (edges: RouteEdge[]): NavigationRoute => { hasCaution: edges.some((edge) => edge.cautionFactors !== undefined), totalDistance: 635, totalCost: 10, - startBuilding: hanyangBuildings[0], + originBuilding: hanyangBuildings[0], destinationBuilding: hanyangBuildings[1], }; }; diff --git a/uniro_frontend/src/data/types/edge.d.ts b/uniro_frontend/src/data/types/edge.d.ts index b227254..bf2271f 100644 --- a/uniro_frontend/src/data/types/edge.d.ts +++ b/uniro_frontend/src/data/types/edge.d.ts @@ -7,7 +7,7 @@ export interface Edge { endNode: CustomNode; } -export type Direction = "start" | "right" | "straight" | "left" | "uturn" | "destination" | "caution"; +export type Direction = "origin" | "right" | "straight" | "left" | "uturn" | "destination" | "caution"; // 위험 요소 & 주의 요소 // 마커를 표시하거나, 길 찾기 결과의 경로를 그릴 때 사용 diff --git a/uniro_frontend/src/data/types/route.d.ts b/uniro_frontend/src/data/types/route.d.ts index 999b954..e8f0532 100644 --- a/uniro_frontend/src/data/types/route.d.ts +++ b/uniro_frontend/src/data/types/route.d.ts @@ -9,6 +9,6 @@ export interface NavigationRoute extends Route { hasCaution: boolean; totalDistance: number; totalCost: number; - startBuilding: Building; + originBuilding: Building; destinationBuilding: Building; } diff --git a/uniro_frontend/src/pages/navigationResult.tsx b/uniro_frontend/src/pages/navigationResult.tsx index 7f897ef..73a0c8d 100644 --- a/uniro_frontend/src/pages/navigationResult.tsx +++ b/uniro_frontend/src/pages/navigationResult.tsx @@ -123,8 +123,8 @@ const NavigationResultPage = () => {
    diff --git a/uniro_frontend/src/utils/markers/generateAdvancedMarker.ts b/uniro_frontend/src/utils/markers/generateAdvancedMarker.ts index 9ed2412..f5b2223 100644 --- a/uniro_frontend/src/utils/markers/generateAdvancedMarker.ts +++ b/uniro_frontend/src/utils/markers/generateAdvancedMarker.ts @@ -1,4 +1,4 @@ -import startIcon from "../../assets/marker/startIcon.svg?raw"; +import originIcon from "../../assets/marker/startIcon.svg?raw"; import arriveMarker from "../../assets/marker/arriveMarker.svg?raw"; import subMarker from "../../assets/marker/subMarker.svg?raw"; import cautionMarker from "../../assets/marker/cautionMarker.svg?raw"; @@ -6,27 +6,28 @@ import cautionMarker from "../../assets/marker/cautionMarker.svg?raw"; export const generateAdvancedMarker = ( map: google.maps.Map, AdvancedMarker: typeof google.maps.marker.AdvancedMarkerElement, - type: "start" | "sub" | "end" | "caution", + type: "origin" | "sub" | "destination" | "caution", position: { lat: number; lng: number }, ) => { switch (type) { - case "start": { - const startSvgElement = new DOMParser().parseFromString(startIcon, "image/svg+xml").documentElement; + case "origin": { + const originSvgElement = new DOMParser().parseFromString(originIcon, "image/svg+xml").documentElement; new AdvancedMarker({ position: position, map, - content: startSvgElement, - title: "출발지", + content: originSvgElement, }); break; } - case "end": { - const endSvgElement = new DOMParser().parseFromString(arriveMarker, "image/svg+xml").documentElement; + case "destination": { + const destinationSvgElement = new DOMParser().parseFromString( + arriveMarker, + "image/svg+xml", + ).documentElement; new AdvancedMarker({ position: position, map, - content: endSvgElement, - title: "도착지", + content: destinationSvgElement, }); break; } @@ -36,7 +37,6 @@ export const generateAdvancedMarker = ( position: position, map, content: cautionSvgElement, - title: "주의사항", }); break; } From 1e4567c16a1627cf9774d667c18eadd75fd19322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JunhyukPark=28=EB=B0=95=EC=A4=80=ED=98=81=29?= Date: Fri, 31 Jan 2025 17:48:24 +0900 Subject: [PATCH 12/13] =?UTF-8?q?[UNI-15]=20refac=20:=20Convention?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=BD=94=EB=93=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/assets/marker/buildingMarker.svg | 24 ------ .../src/assets/marker/cautionMarker.svg | 16 ---- .../src/assets/markers/building.svg | 4 + uniro_frontend/src/assets/markers/caution.svg | 16 ++++ .../dangerIcon.svg => markers/danger.svg} | 14 ++-- .../destination.svg} | 0 .../startIcon.svg => markers/origin.svg} | 0 .../src/assets/markers/selectedBuilding.svg | 4 + .../subMarker.svg => markers/waypoint.svg} | 0 uniro_frontend/src/component/NavgationMap.tsx | 57 +++++++------ .../src/components/map/mapMarkers.tsx | 83 +++++++++++++++++++ uniro_frontend/src/constant/enums.ts | 15 ++++ uniro_frontend/src/data/types/marker.d.ts | 13 +++ uniro_frontend/src/index.css | 9 ++ .../src/utils/markers/createAdvanedMarker.ts | 17 ++++ .../utils/markers/generateAdvancedMarker.ts | 53 ------------ 16 files changed, 200 insertions(+), 125 deletions(-) delete mode 100644 uniro_frontend/src/assets/marker/buildingMarker.svg delete mode 100644 uniro_frontend/src/assets/marker/cautionMarker.svg create mode 100644 uniro_frontend/src/assets/markers/building.svg create mode 100644 uniro_frontend/src/assets/markers/caution.svg rename uniro_frontend/src/assets/{marker/dangerIcon.svg => markers/danger.svg} (55%) rename uniro_frontend/src/assets/{marker/arriveMarker.svg => markers/destination.svg} (100%) rename uniro_frontend/src/assets/{marker/startIcon.svg => markers/origin.svg} (100%) create mode 100644 uniro_frontend/src/assets/markers/selectedBuilding.svg rename uniro_frontend/src/assets/{marker/subMarker.svg => markers/waypoint.svg} (100%) create mode 100644 uniro_frontend/src/components/map/mapMarkers.tsx create mode 100644 uniro_frontend/src/constant/enums.ts create mode 100644 uniro_frontend/src/data/types/marker.d.ts create mode 100644 uniro_frontend/src/utils/markers/createAdvanedMarker.ts delete mode 100644 uniro_frontend/src/utils/markers/generateAdvancedMarker.ts diff --git a/uniro_frontend/src/assets/marker/buildingMarker.svg b/uniro_frontend/src/assets/marker/buildingMarker.svg deleted file mode 100644 index 5cb37eb..0000000 --- a/uniro_frontend/src/assets/marker/buildingMarker.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/uniro_frontend/src/assets/marker/cautionMarker.svg b/uniro_frontend/src/assets/marker/cautionMarker.svg deleted file mode 100644 index d2a1cb7..0000000 --- a/uniro_frontend/src/assets/marker/cautionMarker.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/uniro_frontend/src/assets/markers/building.svg b/uniro_frontend/src/assets/markers/building.svg new file mode 100644 index 0000000..4fc91f3 --- /dev/null +++ b/uniro_frontend/src/assets/markers/building.svg @@ -0,0 +1,4 @@ + + + + diff --git a/uniro_frontend/src/assets/markers/caution.svg b/uniro_frontend/src/assets/markers/caution.svg new file mode 100644 index 0000000..556f369 --- /dev/null +++ b/uniro_frontend/src/assets/markers/caution.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/uniro_frontend/src/assets/marker/dangerIcon.svg b/uniro_frontend/src/assets/markers/danger.svg similarity index 55% rename from uniro_frontend/src/assets/marker/dangerIcon.svg rename to uniro_frontend/src/assets/markers/danger.svg index 39ed554..5508d10 100644 --- a/uniro_frontend/src/assets/marker/dangerIcon.svg +++ b/uniro_frontend/src/assets/markers/danger.svg @@ -1,16 +1,16 @@ - - + + - - - + + + - + - + diff --git a/uniro_frontend/src/assets/marker/arriveMarker.svg b/uniro_frontend/src/assets/markers/destination.svg similarity index 100% rename from uniro_frontend/src/assets/marker/arriveMarker.svg rename to uniro_frontend/src/assets/markers/destination.svg diff --git a/uniro_frontend/src/assets/marker/startIcon.svg b/uniro_frontend/src/assets/markers/origin.svg similarity index 100% rename from uniro_frontend/src/assets/marker/startIcon.svg rename to uniro_frontend/src/assets/markers/origin.svg diff --git a/uniro_frontend/src/assets/markers/selectedBuilding.svg b/uniro_frontend/src/assets/markers/selectedBuilding.svg new file mode 100644 index 0000000..1187dc7 --- /dev/null +++ b/uniro_frontend/src/assets/markers/selectedBuilding.svg @@ -0,0 +1,4 @@ + + + + diff --git a/uniro_frontend/src/assets/marker/subMarker.svg b/uniro_frontend/src/assets/markers/waypoint.svg similarity index 100% rename from uniro_frontend/src/assets/marker/subMarker.svg rename to uniro_frontend/src/assets/markers/waypoint.svg diff --git a/uniro_frontend/src/component/NavgationMap.tsx b/uniro_frontend/src/component/NavgationMap.tsx index 82a8256..9e822af 100644 --- a/uniro_frontend/src/component/NavgationMap.tsx +++ b/uniro_frontend/src/component/NavgationMap.tsx @@ -1,7 +1,9 @@ import { useEffect, useRef } from "react"; import useMap from "../hooks/useMap"; import { NavigationRoute } from "../data/types/route"; -import { generateAdvancedMarker } from "../utils/markers/generateAdvancedMarker"; +import createAdvancedMarker from "../utils/markers/createAdvanedMarker"; +import createMarkerElement from "../components/map/mapMarkers"; +import { Markers } from "../constant/enums"; type MapProps = { style?: React.CSSProperties; @@ -27,31 +29,28 @@ const NavigationMap = ({ style, routes, topPadding = 0, bottomPadding = 0 }: Map if (!routeList || routeList.length === 0) return; const bounds = new google.maps.LatLngBounds(); - routeList.forEach((edge) => { - const { startNode, endNode } = edge; - new Polyline({ - path: [startNode, endNode], - map, - strokeColor: "#000000", - strokeOpacity: 2.0, - }); - bounds.extend(new google.maps.LatLng(startNode.lat, startNode.lng)); - bounds.extend(new google.maps.LatLng(endNode.lat, endNode.lng)); + new Polyline({ + path: [...routeList.map((edge) => edge.startNode), routeList[routeList.length - 1].endNode], + map, + strokeColor: "#000000", + strokeOpacity: 2.0, }); routeList.forEach((edge, index) => { const { startNode, endNode } = edge; + const startCoordinate = new google.maps.LatLng(startNode.lat, startNode.lng); + const endCoordinate = new google.maps.LatLng(endNode.lat, endNode.lng); + bounds.extend(startCoordinate); + bounds.extend(endCoordinate); if (index !== 0 && index !== routeList.length - 1) { + const markerElement = createMarkerElement({ + type: Markers.WAYPOINT, + className: "translate-waypoint", + }); if (index === 1) { - generateAdvancedMarker(map, AdvancedMarker, "sub", { - lat: startNode.lat, - lng: startNode.lng, - }); + createAdvancedMarker(AdvancedMarker, map, startCoordinate, markerElement); } - generateAdvancedMarker(map, AdvancedMarker, "sub", { - lat: endNode.lat, - lng: endNode.lng, - }); + createAdvancedMarker(AdvancedMarker, map, startCoordinate, markerElement); } }); @@ -60,15 +59,23 @@ const NavigationMap = ({ style, routes, topPadding = 0, bottomPadding = 0 }: Map edgeRoutes.forEach((edge, index) => { const { startNode, endNode } = edge; if (index === 0) { - generateAdvancedMarker(map, AdvancedMarker, "origin", { - lat: startNode.lat, - lng: startNode.lng, + const startCoordinate = new google.maps.LatLng(startNode.lat, startNode.lng); + const markerElement = createMarkerElement({ + type: Markers.ORIGIN, + title: routes.originBuilding.buildingName, + className: "translate-routemarker", }); + createAdvancedMarker(AdvancedMarker, map, startCoordinate, markerElement); + bounds.extend(startCoordinate); } else { - generateAdvancedMarker(map, AdvancedMarker, "destination", { - lat: endNode.lat, - lng: endNode.lng, + const endCoordinate = new google.maps.LatLng(endNode.lat, endNode.lng); + const markerElement = createMarkerElement({ + type: Markers.DESTINATION, + title: routes.destinationBuilding.buildingName, + className: "translate-routemarker", }); + createAdvancedMarker(AdvancedMarker, map, endCoordinate, markerElement); + bounds.extend(endCoordinate); } }); diff --git a/uniro_frontend/src/components/map/mapMarkers.tsx b/uniro_frontend/src/components/map/mapMarkers.tsx new file mode 100644 index 0000000..9e4eaf9 --- /dev/null +++ b/uniro_frontend/src/components/map/mapMarkers.tsx @@ -0,0 +1,83 @@ +import { MarkerTypes } from "../../data/types/marker"; +import { Markers } from "../../constant/enums"; + +function createTextElement(type: MarkerTypes, title: string): HTMLElement { + const markerTitle = document.createElement("p"); + markerTitle.innerText = title; + + switch (type) { + case Markers.CAUTION: + markerTitle.className = + "h-[38px] py-2 px-4 mb-2 text-kor-body3 font-semibold text-gray-100 bg-system-orange text-center rounded-200"; + return markerTitle; + case Markers.DANGER: + markerTitle.className = + "h-[38px] py-2 px-4 mb-2 text-kor-body3 font-semibold text-gray-100 bg-system-red text-center rounded-200"; + return markerTitle; + case Markers.BUILDING: + markerTitle.className = + "py-1 px-3 text-kor-caption font-medium text-gray-100 bg-gray-900 text-center rounded-200"; + return markerTitle; + case Markers.SELECTED_BUILDING: + markerTitle.className = + "py-1 px-3 text-kor-caption font-medium text-gray-100 bg-primary-500 text-center rounded-200"; + return markerTitle; + case Markers.ORIGIN: + markerTitle.className = + "py-1 px-3 text-kor-caption font-medium text-gray-100 bg-primary-500 text-center rounded-200"; + return markerTitle; + case Markers.DESTINATION: + markerTitle.className = + "py-1 px-3 text-kor-caption font-medium text-gray-100 bg-primary-500 text-center rounded-200"; + return markerTitle; + default: + return markerTitle; + } +} + +function createImageElement(type: MarkerTypes): HTMLElement { + const markerImage = document.createElement("img"); + + markerImage.src = `/src/assets/markers/${type}.svg`; + return markerImage; +} + +function createContainerElement(className?: string) { + const container = document.createElement("div"); + console.log(className); + container.className = `flex flex-col items-center space-y-[7px] ${className}`; + + return container; +} + +export default function createMarkerElement({ + type, + title, + className, + hasTopContent = false, +}: { + type: MarkerTypes; + className?: string; + title?: string; + hasTopContent?: boolean; +}): HTMLElement { + const container = createContainerElement(className); + + const markerImage = createImageElement(type); + + if (title) { + const markerTitle = createTextElement(type, title); + if (hasTopContent) { + container.appendChild(markerTitle); + container.appendChild(markerImage); + return container; + } + + container.appendChild(markerImage); + container.appendChild(markerTitle); + return container; + } + + container.appendChild(markerImage); + return container; +} diff --git a/uniro_frontend/src/constant/enums.ts b/uniro_frontend/src/constant/enums.ts new file mode 100644 index 0000000..e816ddb --- /dev/null +++ b/uniro_frontend/src/constant/enums.ts @@ -0,0 +1,15 @@ +export const enum Markers { + CAUTION = "caution", + DANGER = "danger", + BUILDING = "building", + ORIGIN = "origin", + DESTINATION = "destination", + SELECTED_BUILDING = "selectedBuilding", + WAYPOINT = "waypoint", + NUMBERED_WAYPOINT = "numberedWayPoint", +} + +export const enum RoutePoint { + ORIGIN = "origin", + DESTINATION = "destination", +} diff --git a/uniro_frontend/src/data/types/marker.d.ts b/uniro_frontend/src/data/types/marker.d.ts new file mode 100644 index 0000000..866d06c --- /dev/null +++ b/uniro_frontend/src/data/types/marker.d.ts @@ -0,0 +1,13 @@ +import { Markers } from "../../constant/enums"; + +export type AdvancedMarker = google.maps.marker.AdvancedMarkerElement; + +export type MarkerTypes = + | Markers.BUILDING + | Markers.CAUTION + | Markers.DANGER + | Markers.DESTINATION + | Markers.ORIGIN + | Markers.NUMBERED_WAYPOINT + | Markers.WAYPOINT + | Markers.SELECTED_BUILDING; diff --git a/uniro_frontend/src/index.css b/uniro_frontend/src/index.css index f2f44e3..1823906 100644 --- a/uniro_frontend/src/index.css +++ b/uniro_frontend/src/index.css @@ -123,3 +123,12 @@ --text-eng-caption: 12px; --text-eng-caption--line-height: 160%; } +@utility translate-marker { + transform: translateY(calc(100% - 10px)); +} +@utility translate-routemarker { + transform: translateY(calc(100% - 40px)); +} +@utility translate-waypoint { + transform: translateY(+4px); +} diff --git a/uniro_frontend/src/utils/markers/createAdvanedMarker.ts b/uniro_frontend/src/utils/markers/createAdvanedMarker.ts new file mode 100644 index 0000000..bdbcbd7 --- /dev/null +++ b/uniro_frontend/src/utils/markers/createAdvanedMarker.ts @@ -0,0 +1,17 @@ +export default function createAdvancedMarker( + AdvancedMarker: typeof google.maps.marker.AdvancedMarkerElement, + map: google.maps.Map, + position: google.maps.LatLng, + content: HTMLElement, + onClick?: () => void, +) { + const newMarker = new AdvancedMarker({ + map: map, + position: position, + content: content, + }); + + if (onClick) newMarker.addListener("click", onClick); + + return newMarker; +} diff --git a/uniro_frontend/src/utils/markers/generateAdvancedMarker.ts b/uniro_frontend/src/utils/markers/generateAdvancedMarker.ts deleted file mode 100644 index f5b2223..0000000 --- a/uniro_frontend/src/utils/markers/generateAdvancedMarker.ts +++ /dev/null @@ -1,53 +0,0 @@ -import originIcon from "../../assets/marker/startIcon.svg?raw"; -import arriveMarker from "../../assets/marker/arriveMarker.svg?raw"; -import subMarker from "../../assets/marker/subMarker.svg?raw"; -import cautionMarker from "../../assets/marker/cautionMarker.svg?raw"; - -export const generateAdvancedMarker = ( - map: google.maps.Map, - AdvancedMarker: typeof google.maps.marker.AdvancedMarkerElement, - type: "origin" | "sub" | "destination" | "caution", - position: { lat: number; lng: number }, -) => { - switch (type) { - case "origin": { - const originSvgElement = new DOMParser().parseFromString(originIcon, "image/svg+xml").documentElement; - new AdvancedMarker({ - position: position, - map, - content: originSvgElement, - }); - break; - } - case "destination": { - const destinationSvgElement = new DOMParser().parseFromString( - arriveMarker, - "image/svg+xml", - ).documentElement; - new AdvancedMarker({ - position: position, - map, - content: destinationSvgElement, - }); - break; - } - case "caution": { - const cautionSvgElement = new DOMParser().parseFromString(cautionMarker, "image/svg+xml").documentElement; - new AdvancedMarker({ - position: position, - map, - content: cautionSvgElement, - }); - break; - } - default: { - const subMarkerElement = new DOMParser().parseFromString(subMarker, "image/svg+xml").documentElement; - new AdvancedMarker({ - position: position, - map, - content: subMarkerElement, - }); - break; - } - } -}; From 0e94f77917d2f40e36131efa5219e053f3c8ddba Mon Sep 17 00:00:00 2001 From: YoonDH <114418121+dgfh0450@users.noreply.github.com> Date: Fri, 31 Jan 2025 17:59:54 +0900 Subject: [PATCH 13/13] =?UTF-8?q?[UNI-15]=20fix=20:=20=EB=A6=AC=EB=B7=B0?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uniro_frontend/src/component/NavgationMap.tsx | 4 ++-- uniro_frontend/src/components/topBar.tsx | 21 ------------------- uniro_frontend/src/index.css | 1 + 3 files changed, 3 insertions(+), 23 deletions(-) delete mode 100644 uniro_frontend/src/components/topBar.tsx diff --git a/uniro_frontend/src/component/NavgationMap.tsx b/uniro_frontend/src/component/NavgationMap.tsx index 9e822af..1756403 100644 --- a/uniro_frontend/src/component/NavgationMap.tsx +++ b/uniro_frontend/src/component/NavgationMap.tsx @@ -33,7 +33,7 @@ const NavigationMap = ({ style, routes, topPadding = 0, bottomPadding = 0 }: Map path: [...routeList.map((edge) => edge.startNode), routeList[routeList.length - 1].endNode], map, strokeColor: "#000000", - strokeOpacity: 2.0, + strokeWeight: 2.0, }); routeList.forEach((edge, index) => { @@ -50,7 +50,7 @@ const NavigationMap = ({ style, routes, topPadding = 0, bottomPadding = 0 }: Map if (index === 1) { createAdvancedMarker(AdvancedMarker, map, startCoordinate, markerElement); } - createAdvancedMarker(AdvancedMarker, map, startCoordinate, markerElement); + createAdvancedMarker(AdvancedMarker, map, endCoordinate, markerElement); } }); diff --git a/uniro_frontend/src/components/topBar.tsx b/uniro_frontend/src/components/topBar.tsx deleted file mode 100644 index 5520e2d..0000000 --- a/uniro_frontend/src/components/topBar.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import AnimatedContainer from "../container/animatedContainer"; -import NavigationDescription from "./navigation/navigationDescription"; -type TopBarProps = { - isVisible: boolean; -}; - -const TopBar = ({ isVisible }: TopBarProps) => { - return ( - - - - ); -}; - -export default TopBar; diff --git a/uniro_frontend/src/index.css b/uniro_frontend/src/index.css index 1823906..914f9ac 100644 --- a/uniro_frontend/src/index.css +++ b/uniro_frontend/src/index.css @@ -123,6 +123,7 @@ --text-eng-caption: 12px; --text-eng-caption--line-height: 160%; } + @utility translate-marker { transform: translateY(calc(100% - 10px)); }