diff --git a/frontend/src/components/common/Header/Header.styled.ts b/frontend/src/components/common/Header/Header.styled.ts index 14e7cd89..ea3048d7 100644 --- a/frontend/src/components/common/Header/Header.styled.ts +++ b/frontend/src/components/common/Header/Header.styled.ts @@ -9,6 +9,7 @@ export const HeaderLayout = styled.header` position: fixed; top: 0; z-index: 500; + width: 100%; height: fit-content; max-width: 48rem; diff --git a/frontend/src/components/common/ModalBottomSheet/BackDrop/BackDrop.styled.ts b/frontend/src/components/common/ModalBottomSheet/BackDrop/BackDrop.styled.ts index d9248bc7..7f3bb085 100644 --- a/frontend/src/components/common/ModalBottomSheet/BackDrop/BackDrop.styled.ts +++ b/frontend/src/components/common/ModalBottomSheet/BackDrop/BackDrop.styled.ts @@ -2,10 +2,8 @@ import styled from "@emotion/styled"; export const BackDrop = styled.div` position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; + inset: 0; + z-index: 600; background-color: ${({ theme }) => theme.colors.dimmed}; `; diff --git a/frontend/src/components/common/ModalBottomSheet/Container/Container.styled.ts b/frontend/src/components/common/ModalBottomSheet/Container/Container.styled.ts index df943c1e..d5a0280b 100644 --- a/frontend/src/components/common/ModalBottomSheet/Container/Container.styled.ts +++ b/frontend/src/components/common/ModalBottomSheet/Container/Container.styled.ts @@ -7,6 +7,7 @@ export const Container = styled.div<{ $currentY: number }>` flex-direction: column; position: absolute; bottom: 0; + z-index: 700; width: 100%; padding: 2.4rem; diff --git a/frontend/src/components/common/ModalBottomSheet/ModalBottomSheet.tsx b/frontend/src/components/common/ModalBottomSheet/ModalBottomSheet.tsx index 06a5a8ef..6e860ae9 100644 --- a/frontend/src/components/common/ModalBottomSheet/ModalBottomSheet.tsx +++ b/frontend/src/components/common/ModalBottomSheet/ModalBottomSheet.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import useBottomSheet from "@hooks/useBottomSheet"; import Button from "../Button/Button"; import BackDrop from "./BackDrop/BackDrop"; @@ -22,91 +22,7 @@ const ModalBottomSheet = ({ onClose, onConfirm, }: ModalBottomSheetProps) => { - const [startY, setStartY] = useState(null); - const [currentY, setCurrentY] = useState(0); - const sheetRef = useRef(null); - - useEffect(() => { - if (!isOpen) { - setCurrentY(0); - } - }, [isOpen]); - - useEffect(() => { - const sheet = sheetRef.current; - if (!sheet) return; - - const handleStart = (clientY: number) => { - setStartY(clientY); - }; - - const handleMove = (clientY: number) => { - if (startY === null) return; - - const diff = clientY - startY; - if (diff > 0) { - setCurrentY(diff); - } - }; - - const handleEnd = () => { - if (currentY > 150) { - onClose(); - } else { - setCurrentY(0); - } - setStartY(null); - }; - - // Touch events - const handleTouchStart = (e: TouchEvent) => handleStart(e.touches[0].clientY); - const handleTouchMove = (e: TouchEvent) => handleMove(e.touches[0].clientY); - const handleTouchEnd = handleEnd; - - // Mouse events - const handleMouseDown = (e: MouseEvent) => handleStart(e.clientY); - const handleMouseMove = (e: MouseEvent) => { - if (startY !== null) { - handleMove(e.clientY); - } - }; - const handleMouseUp = handleEnd; - - // Keyboard events - - sheet.addEventListener("touchstart", handleTouchStart); - sheet.addEventListener("touchmove", handleTouchMove); - sheet.addEventListener("touchend", handleTouchEnd); - - sheet.addEventListener("mousedown", handleMouseDown); - document.addEventListener("mousemove", handleMouseMove); - document.addEventListener("mouseup", handleMouseUp); - - return () => { - sheet.removeEventListener("touchstart", handleTouchStart); - sheet.removeEventListener("touchmove", handleTouchMove); - sheet.removeEventListener("touchend", handleTouchEnd); - - sheet.removeEventListener("mousedown", handleMouseDown); - document.removeEventListener("mousemove", handleMouseMove); - document.removeEventListener("mouseup", handleMouseUp); - }; - }, [startY, currentY, onClose]); - - const handleClickEsc = useCallback( - (e: KeyboardEvent) => { - if (e.key === "Escape") onClose(); - }, - [onClose], - ); - - useEffect(() => { - document.addEventListener("keydown", handleClickEsc); - - return () => { - document.removeEventListener("keydown", handleClickEsc); - }; - }, [handleClickEsc]); + const { sheetRef, currentY } = useBottomSheet(isOpen, onClose); return isOpen ? (
diff --git a/frontend/src/hooks/useBottomSheet.ts b/frontend/src/hooks/useBottomSheet.ts new file mode 100644 index 00000000..a13c5098 --- /dev/null +++ b/frontend/src/hooks/useBottomSheet.ts @@ -0,0 +1,114 @@ +import { useCallback, useEffect, useRef, useState } from "react"; + +const BOTTOM_SHEET = { + openPosition: 0, + closedPosition: 350, + closeThreshold: 200, +}; + +const useBottomSheet = (isOpen: boolean, onClose: () => void) => { + const [startY, setStartY] = useState(null); + const [currentY, setCurrentY] = useState(0); + const sheetRef = useRef(null); + + useEffect(() => { + if (isOpen) { + setCurrentY(BOTTOM_SHEET.openPosition); + } else setCurrentY(BOTTOM_SHEET.closedPosition); + }, [isOpen]); + + useEffect(() => { + const sheet = sheetRef.current; + if (!sheet) return; + + const handleStart = (clientY: number) => setStartY(clientY); + + const handleMove = (clientY: number) => { + if (startY === null) return; + + const diff = clientY - startY; + if (diff > 0) { + setCurrentY(diff); + } + }; + + const handleEnd = () => { + if (currentY > BOTTOM_SHEET.closeThreshold) { + onClose(); + } else { + setCurrentY(BOTTOM_SHEET.openPosition); + } + setStartY(null); + }; + + const handleTouchStart = (e: TouchEvent) => { + const touchedYPosition = e.touches[0].clientY; + + handleStart(touchedYPosition); + }; + const handleTouchMove = (e: TouchEvent) => { + e.preventDefault(); + + const touchedYPosition = e.touches[0].clientY; + + handleMove(touchedYPosition); + }; + const handleTouchEnd = handleEnd; + + const handleMouseDown = (e: MouseEvent) => { + const clickedYPosition = e.clientY; + + handleStart(clickedYPosition); + }; + const handleMouseMove = (e: MouseEvent) => { + const clickedYPosition = e.clientY; + + if (startY !== null) { + handleMove(clickedYPosition); + } + }; + const handleMouseUp = handleEnd; + + sheet.addEventListener("touchstart", handleTouchStart); + sheet.addEventListener("touchmove", handleTouchMove); + sheet.addEventListener("touchend", handleTouchEnd); + + sheet.addEventListener("mousedown", handleMouseDown); + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + + return () => { + sheet.removeEventListener("touchstart", handleTouchStart); + sheet.removeEventListener("touchmove", handleTouchMove); + sheet.removeEventListener("touchend", handleTouchEnd); + + sheet.removeEventListener("mousedown", handleMouseDown); + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + }, [startY, currentY, onClose]); + + const handleClickEsc = useCallback( + (e: KeyboardEvent) => { + const isEscClicked = e.key === "Escape"; + + if (isEscClicked) onClose(); + }, + [onClose], + ); + + useEffect(() => { + document.addEventListener("keydown", handleClickEsc); + + return () => { + document.removeEventListener("keydown", handleClickEsc); + }; + }, [handleClickEsc]); + + return { + sheetRef, + currentY, + }; +}; + +export default useBottomSheet; diff --git a/frontend/src/styles/globalStyle.ts b/frontend/src/styles/globalStyle.ts index 7e5315ea..5973d14b 100644 --- a/frontend/src/styles/globalStyle.ts +++ b/frontend/src/styles/globalStyle.ts @@ -268,13 +268,13 @@ export const globalStyle = css` #root { position: relative; + max-width: 48rem; + min-width: 28rem; + min-height: 100svh; margin: 0 auto; + box-shadow: 0 0 0.315rem rgb(0 0 0 / 25%); background-color: white; - - box-shadow: 0 0 0.315rem rgb(0 0 0 / 25%); - min-width: 28rem; - min-height: 100vh; } `;