From 5af13cf2e83de50aaadb283a59a83a51d714925d Mon Sep 17 00:00:00 2001 From: houony Date: Fri, 8 Nov 2024 23:14:55 +0900 Subject: [PATCH 01/18] =?UTF-8?q?[FEAT]=20BottomSheet=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8,=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pnp.cjs | 38 ++++++++++++++ package.json | 1 + .../BottomSheet/BottomSheet.stories.tsx | 41 +++++++++++++++ .../common/BottomSheet/BottomSheet.types.ts | 10 ++++ src/components/common/BottomSheet/index.tsx | 51 ++++++++++++++++++ src/hooks/common/useBottomSheet.tsx | 52 +++++++++++++++++++ yarn.lock | 20 +++++++ 7 files changed, 213 insertions(+) create mode 100644 src/components/common/BottomSheet/BottomSheet.stories.tsx create mode 100644 src/components/common/BottomSheet/BottomSheet.types.ts create mode 100644 src/components/common/BottomSheet/index.tsx create mode 100644 src/hooks/common/useBottomSheet.tsx diff --git a/.pnp.cjs b/.pnp.cjs index baab6a1..9211e06 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -79,6 +79,7 @@ const RAW_RUNTIME_STATE = ["react-dom", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:18.3.1"],\ ["react-error-boundary", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:4.0.13"],\ ["react-router-dom", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:6.26.2"],\ + ["react-use-measure", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:2.1.1"],\ ["storybook", "npm:8.3.5"],\ ["storybook-addon-sass-postcss", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:0.3.2"],\ ["tailwind-merge", "npm:2.5.4"],\ @@ -6581,6 +6582,15 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ + ["debounce", [\ + ["npm:1.2.1", {\ + "packageLocation": "./.yarn/cache/debounce-npm-1.2.1-b09266a260-6c9320aa09.zip/node_modules/debounce/",\ + "packageDependencies": [\ + ["debounce", "npm:1.2.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["debug", [\ ["npm:2.6.9", {\ "packageLocation": "./.yarn/cache/debug-npm-2.6.9-7d4cb597dc-121908fb83.zip/node_modules/debug/",\ @@ -8150,6 +8160,7 @@ const RAW_RUNTIME_STATE = ["react-dom", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:18.3.1"],\ ["react-error-boundary", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:4.0.13"],\ ["react-router-dom", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:6.26.2"],\ + ["react-use-measure", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:2.1.1"],\ ["storybook", "npm:8.3.5"],\ ["storybook-addon-sass-postcss", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:0.3.2"],\ ["tailwind-merge", "npm:2.5.4"],\ @@ -12259,6 +12270,33 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ + ["react-use-measure", [\ + ["npm:2.1.1", {\ + "packageLocation": "./.yarn/cache/react-use-measure-npm-2.1.1-7e53301142-77b035189d.zip/node_modules/react-use-measure/",\ + "packageDependencies": [\ + ["react-use-measure", "npm:2.1.1"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:2.1.1", {\ + "packageLocation": "./.yarn/__virtual__/react-use-measure-virtual-110e8e1c71/0/cache/react-use-measure-npm-2.1.1-7e53301142-77b035189d.zip/node_modules/react-use-measure/",\ + "packageDependencies": [\ + ["react-use-measure", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:2.1.1"],\ + ["@types/react", "npm:18.3.10"],\ + ["@types/react-dom", "npm:18.3.0"],\ + ["debounce", "npm:1.2.1"],\ + ["react", "npm:18.3.1"],\ + ["react-dom", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:18.3.1"]\ + ],\ + "packagePeers": [\ + "@types/react-dom",\ + "@types/react",\ + "react-dom",\ + "react"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["read-cache", [\ ["npm:1.0.0", {\ "packageLocation": "./.yarn/cache/read-cache-npm-1.0.0-00fa89ed05-90cb275021.zip/node_modules/read-cache/",\ diff --git a/package.json b/package.json index 2eded3b..a5063d1 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "react-dom": "^18.3.1", "react-error-boundary": "^4.0.13", "react-router-dom": "^6.26.2", + "react-use-measure": "^2.1.1", "storybook": "^8.3.5", "tailwind-merge": "^2.5.4", "tailwindcss": "^3.4.13", diff --git a/src/components/common/BottomSheet/BottomSheet.stories.tsx b/src/components/common/BottomSheet/BottomSheet.stories.tsx new file mode 100644 index 0000000..bac3463 --- /dev/null +++ b/src/components/common/BottomSheet/BottomSheet.stories.tsx @@ -0,0 +1,41 @@ +/* eslint-disable no-restricted-exports */ +import { useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { BottomSheet } from '.'; +import { Button } from '../Button'; +import { Layout } from '../Layout'; + +const meta = { + title: 'components/common/BottomSheet', + component: BottomSheet, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: () => { + const [resetTrigger, setResetTrigger] = useState(0); + + const handleClickBottomSheet = () => { + setResetTrigger((prev) => prev + 1); + }; + + return ( + +
+ + +
+
Contents
+
+
+
+
+ ); + }, +}; diff --git a/src/components/common/BottomSheet/BottomSheet.types.ts b/src/components/common/BottomSheet/BottomSheet.types.ts new file mode 100644 index 0000000..d5cc600 --- /dev/null +++ b/src/components/common/BottomSheet/BottomSheet.types.ts @@ -0,0 +1,10 @@ +import { ComponentPropsWithoutRef, ReactNode } from 'react'; + +export type Props = ComponentPropsWithoutRef<'div'> & { + resetTrigger?: number; + children?: ReactNode; +}; + +export type DragInfo = { + delta: { x: number; y: number }; +}; diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx new file mode 100644 index 0000000..a7fd52d --- /dev/null +++ b/src/components/common/BottomSheet/index.tsx @@ -0,0 +1,51 @@ +import { memo } from 'react'; +import { motion, useDragControls, AnimatePresence } from 'framer-motion'; + +import { useBottomSheet } from '@/hooks/common/useBottomSheet'; + +import { Props } from './BottomSheet.types'; + +const Content = ({ children }: React.PropsWithChildren) => ( +
{children}
+); + +export const BottomSheet = memo(({ resetTrigger = 0, children }: Props) => { + const initialHeight = 150; + const minVisibleHeight = 60; + const dragControls = useDragControls(); + + const { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, resetSheet } = + useBottomSheet(initialHeight, minVisibleHeight, resetTrigger); + + return ( + <> +
10 ? 'opacity-70 pointer-events-auto' : 'opacity-0 pointer-events-none' + }`} + onClick={resetSheet} + /> + + {!isHidden && ( + !isInteractionDisabled && dragControls.start(e)} + exit={{ height: 0, opacity: 0 }} + > +
+ {children} + + )} + + + ); +}); diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx new file mode 100644 index 0000000..f8d65c7 --- /dev/null +++ b/src/hooks/common/useBottomSheet.tsx @@ -0,0 +1,52 @@ +import { useState, useCallback, useRef, useEffect } from 'react'; + +import { DragInfo } from '@/components/common/BottomSheet/BottomSheet.types'; + +export const useBottomSheet = (initialHeight = 150, minVisibleHeight = 60, resetTrigger = 0) => { + const [sheetHeight, setSheetHeight] = useState(initialHeight); + const [isHidden, setIsHidden] = useState(false); + const [isInteractionDisabled, setIsInteractionDisabled] = useState(false); + const dragOffsetRef = useRef(0); + const initialPositionRef = useRef(initialHeight); + + useEffect(() => { + setSheetHeight(initialHeight); + setIsHidden(false); + setIsInteractionDisabled(false); + dragOffsetRef.current = 0; + initialPositionRef.current = initialHeight; + }, [resetTrigger, initialHeight]); + + const handleDrag = useCallback( + (_: MouseEvent | TouchEvent | PointerEvent, info: DragInfo) => { + if (isInteractionDisabled) return; + + const dragAmount = -info.delta.y; + dragOffsetRef.current += dragAmount; + + const newHeight = Math.max( + initialPositionRef.current + dragOffsetRef.current, + minVisibleHeight + ); + setSheetHeight(newHeight); + }, + [isInteractionDisabled, minVisibleHeight] + ); + + const handleDragEnd = useCallback(() => { + if (sheetHeight <= minVisibleHeight) { + setIsHidden(true); + setIsInteractionDisabled(true); + } + }, [sheetHeight, minVisibleHeight]); + + const resetSheet = useCallback(() => { + setSheetHeight(initialHeight); + setIsHidden(false); + setIsInteractionDisabled(false); + dragOffsetRef.current = 0; + initialPositionRef.current = initialHeight; + }, [initialHeight]); + + return { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, resetSheet }; +}; diff --git a/yarn.lock b/yarn.lock index bc20cd6..1a2452d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4584,6 +4584,13 @@ __metadata: languageName: node linkType: hard +"debounce@npm:^1.2.1": + version: 1.2.1 + resolution: "debounce@npm:1.2.1" + checksum: 10c0/6c9320aa0973fc42050814621a7a8a78146c1975799b5b3cc1becf1f77ba9a5aa583987884230da0842a03f385def452fad5d60db97c3d1c8b824e38a8edf500 + languageName: node + linkType: hard + "debug@npm:2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" @@ -5933,6 +5940,7 @@ __metadata: react-dom: "npm:^18.3.1" react-error-boundary: "npm:^4.0.13" react-router-dom: "npm:^6.26.2" + react-use-measure: "npm:^2.1.1" storybook: "npm:^8.3.5" storybook-addon-sass-postcss: "npm:^0.3.2" tailwind-merge: "npm:^2.5.4" @@ -9293,6 +9301,18 @@ __metadata: languageName: node linkType: hard +"react-use-measure@npm:^2.1.1": + version: 2.1.1 + resolution: "react-use-measure@npm:2.1.1" + dependencies: + debounce: "npm:^1.2.1" + peerDependencies: + react: ">=16.13" + react-dom: ">=16.13" + checksum: 10c0/77b035189dbd613f50014ae56cbfc1363a4eba5104f68f3bc09cbdd20719ae7fb42884e53328175c30b238215c5b8064c60098d70b3fa9b8d902db6ffb07c6a3 + languageName: node + linkType: hard + "react@npm:^16.8.0 || ^17.0.0 || ^18.0.0, react@npm:^18.3.1": version: 18.3.1 resolution: "react@npm:18.3.1" From 3d3cf251d5dbd828d1f6beefee837faad358a315 Mon Sep 17 00:00:00 2001 From: houony Date: Sun, 10 Nov 2024 18:36:20 +0900 Subject: [PATCH 02/18] =?UTF-8?q?[REFACTOR]=20BottomSheet=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 - src/components/common/BottomSheet/index.tsx | 15 ++++++---- src/constants/bottomSheetOptions.ts | 3 ++ src/hooks/common/useBottomSheet.tsx | 33 +++++++++++---------- yarn.lock | 20 ------------- 5 files changed, 30 insertions(+), 42 deletions(-) create mode 100644 src/constants/bottomSheetOptions.ts diff --git a/package.json b/package.json index a5063d1..2eded3b 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ "react-dom": "^18.3.1", "react-error-boundary": "^4.0.13", "react-router-dom": "^6.26.2", - "react-use-measure": "^2.1.1", "storybook": "^8.3.5", "tailwind-merge": "^2.5.4", "tailwindcss": "^3.4.13", diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index a7fd52d..8040a9b 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -9,22 +9,25 @@ const Content = ({ children }: React.PropsWithChildren) => (
{children}
); -export const BottomSheet = memo(({ resetTrigger = 0, children }: Props) => { - const initialHeight = 150; - const minVisibleHeight = 60; +export const BottomSheet = memo(({ children, resetTrigger = 0 }: Props) => { const dragControls = useDragControls(); const { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, resetSheet } = - useBottomSheet(initialHeight, minVisibleHeight, resetTrigger); + useBottomSheet(resetTrigger); return ( <>
10 ? 'opacity-70 pointer-events-auto' : 'opacity-0 pointer-events-none' + isHidden ? 'opacity-0 pointer-events-none' : 'opacity-70 pointer-events-auto' }`} - onClick={resetSheet} + onClick={() => { + if (!isInteractionDisabled) { + resetSheet(); + } + }} /> + {!isHidden && ( { - const [sheetHeight, setSheetHeight] = useState(initialHeight); +export const useBottomSheet = (resetTrigger: number) => { + const [sheetHeight, setSheetHeight] = useState(INITIAL_HEIGHT); const [isHidden, setIsHidden] = useState(false); const [isInteractionDisabled, setIsInteractionDisabled] = useState(false); const dragOffsetRef = useRef(0); - const initialPositionRef = useRef(initialHeight); + const initialPositionRef = useRef(INITIAL_HEIGHT); useEffect(() => { - setSheetHeight(initialHeight); + setSheetHeight(INITIAL_HEIGHT); setIsHidden(false); setIsInteractionDisabled(false); dragOffsetRef.current = 0; - initialPositionRef.current = initialHeight; - }, [resetTrigger, initialHeight]); + initialPositionRef.current = INITIAL_HEIGHT; + }, [resetTrigger]); const handleDrag = useCallback( (_: MouseEvent | TouchEvent | PointerEvent, info: DragInfo) => { @@ -24,29 +25,31 @@ export const useBottomSheet = (initialHeight = 150, minVisibleHeight = 60, reset const dragAmount = -info.delta.y; dragOffsetRef.current += dragAmount; - const newHeight = Math.max( - initialPositionRef.current + dragOffsetRef.current, - minVisibleHeight + const newHeight = Math.min( + Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), + MAX_HEIGHT ); setSheetHeight(newHeight); }, - [isInteractionDisabled, minVisibleHeight] + [isInteractionDisabled] ); const handleDragEnd = useCallback(() => { - if (sheetHeight <= minVisibleHeight) { + if (sheetHeight <= MIN_VISIBLE_HEIGHT) { setIsHidden(true); setIsInteractionDisabled(true); + } else if (sheetHeight > MAX_HEIGHT) { + setSheetHeight(MAX_HEIGHT); } - }, [sheetHeight, minVisibleHeight]); + }, [sheetHeight]); const resetSheet = useCallback(() => { - setSheetHeight(initialHeight); + setSheetHeight(INITIAL_HEIGHT); setIsHidden(false); setIsInteractionDisabled(false); dragOffsetRef.current = 0; - initialPositionRef.current = initialHeight; - }, [initialHeight]); + initialPositionRef.current = INITIAL_HEIGHT; + }, []); return { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, resetSheet }; }; diff --git a/yarn.lock b/yarn.lock index 1a2452d..bc20cd6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4584,13 +4584,6 @@ __metadata: languageName: node linkType: hard -"debounce@npm:^1.2.1": - version: 1.2.1 - resolution: "debounce@npm:1.2.1" - checksum: 10c0/6c9320aa0973fc42050814621a7a8a78146c1975799b5b3cc1becf1f77ba9a5aa583987884230da0842a03f385def452fad5d60db97c3d1c8b824e38a8edf500 - languageName: node - linkType: hard - "debug@npm:2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" @@ -5940,7 +5933,6 @@ __metadata: react-dom: "npm:^18.3.1" react-error-boundary: "npm:^4.0.13" react-router-dom: "npm:^6.26.2" - react-use-measure: "npm:^2.1.1" storybook: "npm:^8.3.5" storybook-addon-sass-postcss: "npm:^0.3.2" tailwind-merge: "npm:^2.5.4" @@ -9301,18 +9293,6 @@ __metadata: languageName: node linkType: hard -"react-use-measure@npm:^2.1.1": - version: 2.1.1 - resolution: "react-use-measure@npm:2.1.1" - dependencies: - debounce: "npm:^1.2.1" - peerDependencies: - react: ">=16.13" - react-dom: ">=16.13" - checksum: 10c0/77b035189dbd613f50014ae56cbfc1363a4eba5104f68f3bc09cbdd20719ae7fb42884e53328175c30b238215c5b8064c60098d70b3fa9b8d902db6ffb07c6a3 - languageName: node - linkType: hard - "react@npm:^16.8.0 || ^17.0.0 || ^18.0.0, react@npm:^18.3.1": version: 18.3.1 resolution: "react@npm:18.3.1" From 366e0cbe589cd01f2a156c4871f72380390ff254 Mon Sep 17 00:00:00 2001 From: houony Date: Sun, 10 Nov 2024 23:58:55 +0900 Subject: [PATCH 03/18] =?UTF-8?q?[CHORE]=20BottomSheet=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8,=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BottomSheet/BottomSheet.stories.tsx | 25 +++---- .../common/BottomSheet/BottomSheet.types.ts | 2 +- src/components/common/BottomSheet/index.tsx | 36 +++++++--- src/hooks/common/useBottomSheet.tsx | 71 ++++++++++++++----- 4 files changed, 91 insertions(+), 43 deletions(-) diff --git a/src/components/common/BottomSheet/BottomSheet.stories.tsx b/src/components/common/BottomSheet/BottomSheet.stories.tsx index bac3463..2fe550d 100644 --- a/src/components/common/BottomSheet/BottomSheet.stories.tsx +++ b/src/components/common/BottomSheet/BottomSheet.stories.tsx @@ -4,7 +4,6 @@ import type { Meta, StoryObj } from '@storybook/react'; import { BottomSheet } from '.'; import { Button } from '../Button'; -import { Layout } from '../Layout'; const meta = { title: 'components/common/BottomSheet', @@ -17,25 +16,23 @@ type Story = StoryObj; export const Basic: Story = { render: () => { - const [resetTrigger, setResetTrigger] = useState(0); + const [isOpen, setIsOpen] = useState(false); - const handleClickBottomSheet = () => { - setResetTrigger((prev) => prev + 1); - }; + const handleClickBottomSheet = () => setIsOpen(!isOpen); return ( - -
- - + <> + + {isOpen && ( +
-
Contents
+
Contents ...
-
-
+ )} + ); }, }; diff --git a/src/components/common/BottomSheet/BottomSheet.types.ts b/src/components/common/BottomSheet/BottomSheet.types.ts index d5cc600..13f9da2 100644 --- a/src/components/common/BottomSheet/BottomSheet.types.ts +++ b/src/components/common/BottomSheet/BottomSheet.types.ts @@ -1,7 +1,7 @@ import { ComponentPropsWithoutRef, ReactNode } from 'react'; export type Props = ComponentPropsWithoutRef<'div'> & { - resetTrigger?: number; + resetTrigger?: boolean; children?: ReactNode; }; diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index 8040a9b..b452258 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -1,31 +1,30 @@ -import { memo } from 'react'; +import { memo, useState } from 'react'; import { motion, useDragControls, AnimatePresence } from 'framer-motion'; import { useBottomSheet } from '@/hooks/common/useBottomSheet'; import { Props } from './BottomSheet.types'; +import { Button } from '../Button'; +import { Portal } from '../Portal'; + const Content = ({ children }: React.PropsWithChildren) => (
{children}
); -export const BottomSheet = memo(({ children, resetTrigger = 0 }: Props) => { +export const BottomSheet = memo(({ children, resetTrigger = false }: Props) => { const dragControls = useDragControls(); const { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, resetSheet } = useBottomSheet(resetTrigger); return ( - <> +
{ - if (!isInteractionDisabled) { - resetSheet(); - } - }} + onClick={resetSheet} /> @@ -49,6 +48,25 @@ export const BottomSheet = memo(({ children, resetTrigger = 0 }: Props) => { )} - + ); }); + +export const BottomSheetDemo = () => { + const [isOpen, setIsOpen] = useState(false); + + const handleClickBottomSheet = () => setIsOpen(!isOpen); + + return ( + <> + + +
+
BottomSheet Content
+
+
+ + ); +}; diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index 7b05d16..3b0368d 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -1,46 +1,78 @@ import { useState, useCallback, useRef, useEffect } from 'react'; +import { animate } from 'framer-motion'; import { DragInfo } from '@/components/common/BottomSheet/BottomSheet.types'; import { INITIAL_HEIGHT, MIN_VISIBLE_HEIGHT, MAX_HEIGHT } from '@/constants/bottomSheetOptions'; -export const useBottomSheet = (resetTrigger: number) => { +export const useBottomSheet = (resetTrigger: boolean) => { const [sheetHeight, setSheetHeight] = useState(INITIAL_HEIGHT); const [isHidden, setIsHidden] = useState(false); const [isInteractionDisabled, setIsInteractionDisabled] = useState(false); const dragOffsetRef = useRef(0); const initialPositionRef = useRef(INITIAL_HEIGHT); + const dragStartTimeRef = useRef(null); + const lastDragPositionRef = useRef(0); + const lastVelocityRef = useRef(0); useEffect(() => { - setSheetHeight(INITIAL_HEIGHT); - setIsHidden(false); - setIsInteractionDisabled(false); - dragOffsetRef.current = 0; - initialPositionRef.current = INITIAL_HEIGHT; + if (resetTrigger) { + setSheetHeight(INITIAL_HEIGHT); + setIsHidden(false); + setIsInteractionDisabled(false); + dragOffsetRef.current = 0; + initialPositionRef.current = INITIAL_HEIGHT; + dragStartTimeRef.current = null; + } }, [resetTrigger]); const handleDrag = useCallback( (_: MouseEvent | TouchEvent | PointerEvent, info: DragInfo) => { if (isInteractionDisabled) return; - const dragAmount = -info.delta.y; - dragOffsetRef.current += dragAmount; + if (!dragStartTimeRef.current) { + dragStartTimeRef.current = Date.now(); + lastDragPositionRef.current = info.delta.y; + } - const newHeight = Math.min( - Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), - MAX_HEIGHT - ); - setSheetHeight(newHeight); + requestAnimationFrame(() => { + const dragAmount = -info.delta.y; + dragOffsetRef.current += dragAmount; + + const velocity = + (lastDragPositionRef.current - info.delta.y) / + (Date.now() - (dragStartTimeRef.current || Date.now())); + lastVelocityRef.current = velocity; + + const newHeight = Math.min( + Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), + MAX_HEIGHT + ); + setSheetHeight(newHeight); + }); }, [isInteractionDisabled] ); const handleDragEnd = useCallback(() => { - if (sheetHeight <= MIN_VISIBLE_HEIGHT) { - setIsHidden(true); - setIsInteractionDisabled(true); - } else if (sheetHeight > MAX_HEIGHT) { - setSheetHeight(MAX_HEIGHT); - } + const velocity = lastVelocityRef.current; + const projectedHeight = sheetHeight + velocity * 100; + + const snapPoints = [MIN_VISIBLE_HEIGHT, INITIAL_HEIGHT, MAX_HEIGHT]; + const nearestSnapPoint = snapPoints.reduce((prev, curr) => + Math.abs(curr - projectedHeight) < Math.abs(prev - projectedHeight) ? curr : prev + ); + + animate(sheetHeight, nearestSnapPoint, { + duration: 0.3, + ease: 'easeOut', + onUpdate: (value) => setSheetHeight(value), + onComplete: () => { + if (nearestSnapPoint === MIN_VISIBLE_HEIGHT) { + setIsHidden(true); + setIsInteractionDisabled(true); + } + }, + }); }, [sheetHeight]); const resetSheet = useCallback(() => { @@ -49,6 +81,7 @@ export const useBottomSheet = (resetTrigger: number) => { setIsInteractionDisabled(false); dragOffsetRef.current = 0; initialPositionRef.current = INITIAL_HEIGHT; + dragStartTimeRef.current = null; }, []); return { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, resetSheet }; From 70ffaf2bb2e08f28b348a349c0f298574d71e3d7 Mon Sep 17 00:00:00 2001 From: houony Date: Mon, 11 Nov 2024 13:45:14 +0900 Subject: [PATCH 04/18] refactor : add error handling, reduce redundancy --- .../BottomSheet/BottomSheet.stories.tsx | 2 +- .../common/BottomSheet/BottomSheet.types.ts | 13 +++- src/components/common/BottomSheet/index.tsx | 35 +++------ src/hooks/common/useBottomSheet.tsx | 72 +++++++++---------- 4 files changed, 60 insertions(+), 62 deletions(-) diff --git a/src/components/common/BottomSheet/BottomSheet.stories.tsx b/src/components/common/BottomSheet/BottomSheet.stories.tsx index 2fe550d..04f0ccb 100644 --- a/src/components/common/BottomSheet/BottomSheet.stories.tsx +++ b/src/components/common/BottomSheet/BottomSheet.stories.tsx @@ -26,7 +26,7 @@ export const Basic: Story = { Open BottomSheet {isOpen && ( - + setIsOpen(false)}>
Contents ...
diff --git a/src/components/common/BottomSheet/BottomSheet.types.ts b/src/components/common/BottomSheet/BottomSheet.types.ts index 13f9da2..853acd0 100644 --- a/src/components/common/BottomSheet/BottomSheet.types.ts +++ b/src/components/common/BottomSheet/BottomSheet.types.ts @@ -1,7 +1,18 @@ import { ComponentPropsWithoutRef, ReactNode } from 'react'; export type Props = ComponentPropsWithoutRef<'div'> & { - resetTrigger?: boolean; + /** + * Indicates whether the BottomSheet is currently open. + * @default false + */ + isOpen: boolean; + /** + * Callback function that is called when the BottomSheet should close. + */ + onClose?: VoidFunction; + /** + * The content to be displayed inside the BottomSheet. + */ children?: ReactNode; }; diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index b452258..8f31c25 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -1,22 +1,28 @@ -import { memo, useState } from 'react'; +import { memo } from 'react'; import { motion, useDragControls, AnimatePresence } from 'framer-motion'; import { useBottomSheet } from '@/hooks/common/useBottomSheet'; import { Props } from './BottomSheet.types'; -import { Button } from '../Button'; import { Portal } from '../Portal'; const Content = ({ children }: React.PropsWithChildren) => (
{children}
); -export const BottomSheet = memo(({ children, resetTrigger = false }: Props) => { +export const BottomSheet = memo(({ children, isOpen, onClose }: Props) => { const dragControls = useDragControls(); const { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, resetSheet } = - useBottomSheet(resetTrigger); + useBottomSheet(isOpen); + + const handleClose = () => { + if (!isInteractionDisabled) { + resetSheet(); + if (onClose) onClose(); + } + }; return ( @@ -24,7 +30,7 @@ export const BottomSheet = memo(({ children, resetTrigger = false }: Props) => { className={`absolute top-0 left-0 w-full h-full transition-opacity duration-300 ${ isHidden ? 'opacity-0 pointer-events-none' : 'opacity-70 pointer-events-auto' }`} - onClick={resetSheet} + onClick={handleClose} /> @@ -51,22 +57,3 @@ export const BottomSheet = memo(({ children, resetTrigger = false }: Props) => { ); }); - -export const BottomSheetDemo = () => { - const [isOpen, setIsOpen] = useState(false); - - const handleClickBottomSheet = () => setIsOpen(!isOpen); - - return ( - <> - - -
-
BottomSheet Content
-
-
- - ); -}; diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index 3b0368d..c58dda0 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -4,7 +4,7 @@ import { animate } from 'framer-motion'; import { DragInfo } from '@/components/common/BottomSheet/BottomSheet.types'; import { INITIAL_HEIGHT, MIN_VISIBLE_HEIGHT, MAX_HEIGHT } from '@/constants/bottomSheetOptions'; -export const useBottomSheet = (resetTrigger: boolean) => { +export const useBottomSheet = (isOpen: boolean) => { const [sheetHeight, setSheetHeight] = useState(INITIAL_HEIGHT); const [isHidden, setIsHidden] = useState(false); const [isInteractionDisabled, setIsInteractionDisabled] = useState(false); @@ -14,41 +14,48 @@ export const useBottomSheet = (resetTrigger: boolean) => { const lastDragPositionRef = useRef(0); const lastVelocityRef = useRef(0); + const resetSheetState = () => { + setSheetHeight(INITIAL_HEIGHT); + setIsHidden(false); + setIsInteractionDisabled(false); + dragOffsetRef.current = 0; + initialPositionRef.current = INITIAL_HEIGHT; + dragStartTimeRef.current = null; + }; + useEffect(() => { - if (resetTrigger) { - setSheetHeight(INITIAL_HEIGHT); - setIsHidden(false); - setIsInteractionDisabled(false); - dragOffsetRef.current = 0; - initialPositionRef.current = INITIAL_HEIGHT; - dragStartTimeRef.current = null; - } - }, [resetTrigger]); + if (isOpen) resetSheetState(); + }, [isOpen]); const handleDrag = useCallback( - (_: MouseEvent | TouchEvent | PointerEvent, info: DragInfo) => { + (_: PointerEvent, info: DragInfo) => { if (isInteractionDisabled) return; - if (!dragStartTimeRef.current) { - dragStartTimeRef.current = Date.now(); - lastDragPositionRef.current = info.delta.y; - } + try { + if (!dragStartTimeRef.current) { + dragStartTimeRef.current = Date.now(); + lastDragPositionRef.current = info.delta.y; + } - requestAnimationFrame(() => { - const dragAmount = -info.delta.y; - dragOffsetRef.current += dragAmount; + requestAnimationFrame(() => { + const dragAmount = -info.delta.y; + dragOffsetRef.current += dragAmount; - const velocity = - (lastDragPositionRef.current - info.delta.y) / - (Date.now() - (dragStartTimeRef.current || Date.now())); - lastVelocityRef.current = velocity; + const velocity = + (lastDragPositionRef.current - info.delta.y) / + (Date.now() - (dragStartTimeRef.current || Date.now())); + lastVelocityRef.current = velocity; - const newHeight = Math.min( - Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), - MAX_HEIGHT - ); - setSheetHeight(newHeight); - }); + const newHeight = Math.min( + Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), + MAX_HEIGHT + ); + setSheetHeight(newHeight); + }); + } catch (error) { + console.error('드래그 중 에러 발생:', error); + resetSheetState(); + } }, [isInteractionDisabled] ); @@ -75,14 +82,7 @@ export const useBottomSheet = (resetTrigger: boolean) => { }); }, [sheetHeight]); - const resetSheet = useCallback(() => { - setSheetHeight(INITIAL_HEIGHT); - setIsHidden(false); - setIsInteractionDisabled(false); - dragOffsetRef.current = 0; - initialPositionRef.current = INITIAL_HEIGHT; - dragStartTimeRef.current = null; - }, []); + const resetSheet = useCallback(resetSheetState, []); return { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, resetSheet }; }; From eb7d761cc602899610f1fb765b35e11b527b1229 Mon Sep 17 00:00:00 2001 From: houony Date: Mon, 11 Nov 2024 13:55:51 +0900 Subject: [PATCH 05/18] refactor: add MouseEvent and TouchEvent --- src/hooks/common/useBottomSheet.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index c58dda0..ab1dc4f 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -28,7 +28,7 @@ export const useBottomSheet = (isOpen: boolean) => { }, [isOpen]); const handleDrag = useCallback( - (_: PointerEvent, info: DragInfo) => { + (_: MouseEvent | TouchEvent | PointerEvent, info: DragInfo) => { if (isInteractionDisabled) return; try { From a92c437660a0e4b5d4040bdadf673efe4d57a515 Mon Sep 17 00:00:00 2001 From: houony Date: Mon, 11 Nov 2024 22:54:06 +0900 Subject: [PATCH 06/18] chore : remove try-catch --- src/hooks/common/useBottomSheet.tsx | 39 +++++++++++++---------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index ab1dc4f..ab0fdff 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -31,31 +31,26 @@ export const useBottomSheet = (isOpen: boolean) => { (_: MouseEvent | TouchEvent | PointerEvent, info: DragInfo) => { if (isInteractionDisabled) return; - try { - if (!dragStartTimeRef.current) { - dragStartTimeRef.current = Date.now(); - lastDragPositionRef.current = info.delta.y; - } + if (!dragStartTimeRef.current) { + dragStartTimeRef.current = Date.now(); + lastDragPositionRef.current = info.delta.y; + } - requestAnimationFrame(() => { - const dragAmount = -info.delta.y; - dragOffsetRef.current += dragAmount; + requestAnimationFrame(() => { + const dragAmount = -info.delta.y; + dragOffsetRef.current += dragAmount; - const velocity = - (lastDragPositionRef.current - info.delta.y) / - (Date.now() - (dragStartTimeRef.current || Date.now())); - lastVelocityRef.current = velocity; + const velocity = + (lastDragPositionRef.current - info.delta.y) / + (Date.now() - (dragStartTimeRef.current || Date.now())); + lastVelocityRef.current = velocity; - const newHeight = Math.min( - Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), - MAX_HEIGHT - ); - setSheetHeight(newHeight); - }); - } catch (error) { - console.error('드래그 중 에러 발생:', error); - resetSheetState(); - } + const newHeight = Math.min( + Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), + MAX_HEIGHT + ); + setSheetHeight(newHeight); + }); }, [isInteractionDisabled] ); From c1a3f12a514261df8a3137881c3c632b36e4fe9e Mon Sep 17 00:00:00 2001 From: houony Date: Mon, 11 Nov 2024 23:37:39 +0900 Subject: [PATCH 07/18] refactor : remove calculation of drag velocity --- src/components/common/BottomSheet/index.tsx | 2 +- src/hooks/common/useBottomSheet.tsx | 84 +++++++-------------- 2 files changed, 30 insertions(+), 56 deletions(-) diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index 8f31c25..cad92eb 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -40,7 +40,7 @@ export const BottomSheet = memo(({ children, isOpen, onClose }: Props) => { style={{ height: sheetHeight }} drag="y" dragControls={dragControls} - dragElastic={0} + dragElastic={0.3} dragConstraints={{ top: 0, bottom: 0 }} onDrag={handleDrag} onDragEnd={handleDragEnd} diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index ab0fdff..0840934 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -1,83 +1,57 @@ import { useState, useCallback, useRef, useEffect } from 'react'; -import { animate } from 'framer-motion'; import { DragInfo } from '@/components/common/BottomSheet/BottomSheet.types'; import { INITIAL_HEIGHT, MIN_VISIBLE_HEIGHT, MAX_HEIGHT } from '@/constants/bottomSheetOptions'; -export const useBottomSheet = (isOpen: boolean) => { +export const useBottomSheet = (resetTrigger: boolean) => { const [sheetHeight, setSheetHeight] = useState(INITIAL_HEIGHT); const [isHidden, setIsHidden] = useState(false); const [isInteractionDisabled, setIsInteractionDisabled] = useState(false); const dragOffsetRef = useRef(0); const initialPositionRef = useRef(INITIAL_HEIGHT); - const dragStartTimeRef = useRef(null); - const lastDragPositionRef = useRef(0); - const lastVelocityRef = useRef(0); - - const resetSheetState = () => { - setSheetHeight(INITIAL_HEIGHT); - setIsHidden(false); - setIsInteractionDisabled(false); - dragOffsetRef.current = 0; - initialPositionRef.current = INITIAL_HEIGHT; - dragStartTimeRef.current = null; - }; useEffect(() => { - if (isOpen) resetSheetState(); - }, [isOpen]); + if (resetTrigger) { + setSheetHeight(INITIAL_HEIGHT); + setIsHidden(false); + setIsInteractionDisabled(false); + dragOffsetRef.current = 0; + initialPositionRef.current = INITIAL_HEIGHT; + } + }, [resetTrigger]); const handleDrag = useCallback( (_: MouseEvent | TouchEvent | PointerEvent, info: DragInfo) => { if (isInteractionDisabled) return; - if (!dragStartTimeRef.current) { - dragStartTimeRef.current = Date.now(); - lastDragPositionRef.current = info.delta.y; - } - - requestAnimationFrame(() => { - const dragAmount = -info.delta.y; - dragOffsetRef.current += dragAmount; - - const velocity = - (lastDragPositionRef.current - info.delta.y) / - (Date.now() - (dragStartTimeRef.current || Date.now())); - lastVelocityRef.current = velocity; + const dragAmount = -info.delta.y; + dragOffsetRef.current += dragAmount; - const newHeight = Math.min( - Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), - MAX_HEIGHT - ); - setSheetHeight(newHeight); - }); + const newHeight = Math.min( + Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), + MAX_HEIGHT + ); + setSheetHeight(newHeight); }, [isInteractionDisabled] ); const handleDragEnd = useCallback(() => { - const velocity = lastVelocityRef.current; - const projectedHeight = sheetHeight + velocity * 100; - - const snapPoints = [MIN_VISIBLE_HEIGHT, INITIAL_HEIGHT, MAX_HEIGHT]; - const nearestSnapPoint = snapPoints.reduce((prev, curr) => - Math.abs(curr - projectedHeight) < Math.abs(prev - projectedHeight) ? curr : prev - ); - - animate(sheetHeight, nearestSnapPoint, { - duration: 0.3, - ease: 'easeOut', - onUpdate: (value) => setSheetHeight(value), - onComplete: () => { - if (nearestSnapPoint === MIN_VISIBLE_HEIGHT) { - setIsHidden(true); - setIsInteractionDisabled(true); - } - }, - }); + if (sheetHeight <= MIN_VISIBLE_HEIGHT) { + setIsHidden(true); + setIsInteractionDisabled(true); + } else if (sheetHeight > MAX_HEIGHT) { + setSheetHeight(MAX_HEIGHT); + } }, [sheetHeight]); - const resetSheet = useCallback(resetSheetState, []); + const resetSheet = useCallback(() => { + setSheetHeight(INITIAL_HEIGHT); + setIsHidden(false); + setIsInteractionDisabled(false); + dragOffsetRef.current = 0; + initialPositionRef.current = INITIAL_HEIGHT; + }, []); return { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, resetSheet }; }; From 2279cf65c768dc5206081f09d6c995fd5d765691 Mon Sep 17 00:00:00 2001 From: houony Date: Tue, 12 Nov 2024 21:16:25 +0900 Subject: [PATCH 08/18] refactor : improve BottomSheet animation and positioning --- src/components/common/BottomSheet/index.tsx | 24 ++++++++++----------- src/hooks/common/useBottomSheet.tsx | 23 +++++++++++++++++--- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index cad92eb..a61fa4c 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -14,21 +14,21 @@ const Content = ({ children }: React.PropsWithChildren) => ( export const BottomSheet = memo(({ children, isOpen, onClose }: Props) => { const dragControls = useDragControls(); - const { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, resetSheet } = - useBottomSheet(isOpen); - - const handleClose = () => { - if (!isInteractionDisabled) { - resetSheet(); - if (onClose) onClose(); - } - }; + const { + sheetHeight, + isHidden, + isInteractionDisabled, + handleDrag, + handleDragEnd, + resetSheet, + handleClose, + } = useBottomSheet(isOpen, onClose); return (
@@ -36,11 +36,11 @@ export const BottomSheet = memo(({ children, isOpen, onClose }: Props) => { {!isHidden && ( { +export const useBottomSheet = (resetTrigger: boolean, onClose?: () => void) => { const [sheetHeight, setSheetHeight] = useState(INITIAL_HEIGHT); const [isHidden, setIsHidden] = useState(false); const [isInteractionDisabled, setIsInteractionDisabled] = useState(false); @@ -40,10 +40,13 @@ export const useBottomSheet = (resetTrigger: boolean) => { if (sheetHeight <= MIN_VISIBLE_HEIGHT) { setIsHidden(true); setIsInteractionDisabled(true); + if (onClose) { + onClose(); + } } else if (sheetHeight > MAX_HEIGHT) { setSheetHeight(MAX_HEIGHT); } - }, [sheetHeight]); + }, [sheetHeight, onClose]); const resetSheet = useCallback(() => { setSheetHeight(INITIAL_HEIGHT); @@ -53,5 +56,19 @@ export const useBottomSheet = (resetTrigger: boolean) => { initialPositionRef.current = INITIAL_HEIGHT; }, []); - return { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, resetSheet }; + const handleClose = useCallback(() => { + if (!isInteractionDisabled) { + resetSheet(); + } + }, [isInteractionDisabled, resetSheet]); + + return { + sheetHeight, + isHidden, + isInteractionDisabled, + handleDrag, + handleDragEnd, + resetSheet, + handleClose, + }; }; From 631810a2df658e60b8c14c9c683a1b8b9057065f Mon Sep 17 00:00:00 2001 From: houony Date: Tue, 12 Nov 2024 21:29:23 +0900 Subject: [PATCH 09/18] refactor : optimize drag handler with requestAnimationFrame --- src/components/common/BottomSheet/index.tsx | 11 ++--------- src/hooks/common/useBottomSheet.tsx | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index a61fa4c..83334e4 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -14,15 +14,8 @@ const Content = ({ children }: React.PropsWithChildren) => ( export const BottomSheet = memo(({ children, isOpen, onClose }: Props) => { const dragControls = useDragControls(); - const { - sheetHeight, - isHidden, - isInteractionDisabled, - handleDrag, - handleDragEnd, - resetSheet, - handleClose, - } = useBottomSheet(isOpen, onClose); + const { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, handleClose } = + useBottomSheet(isOpen, onClose); return ( diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index 162125c..6953713 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -24,16 +24,18 @@ export const useBottomSheet = (resetTrigger: boolean, onClose?: () => void) => { (_: MouseEvent | TouchEvent | PointerEvent, info: DragInfo) => { if (isInteractionDisabled) return; - const dragAmount = -info.delta.y; - dragOffsetRef.current += dragAmount; + requestAnimationFrame(() => { + const dragAmount = -info.delta.y; + dragOffsetRef.current += dragAmount; - const newHeight = Math.min( - Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), - MAX_HEIGHT - ); - setSheetHeight(newHeight); + const newHeight = Math.min( + Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), + MAX_HEIGHT + ); + setSheetHeight(newHeight); + }); }, - [isInteractionDisabled] + [isInteractionDisabled, setSheetHeight] ); const handleDragEnd = useCallback(() => { From 90d4e6aa4c1dfe1cc53dfad6af1f132fa34e3638 Mon Sep 17 00:00:00 2001 From: houony Date: Wed, 13 Nov 2024 01:23:19 +0900 Subject: [PATCH 10/18] refactor : remove unnecessary code, change handleDragEnd logic --- .../common/BottomSheet/BottomSheet.types.ts | 2 +- src/components/common/BottomSheet/index.tsx | 4 +--- src/hooks/common/useBottomSheet.tsx | 11 +++++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/common/BottomSheet/BottomSheet.types.ts b/src/components/common/BottomSheet/BottomSheet.types.ts index 853acd0..e04905f 100644 --- a/src/components/common/BottomSheet/BottomSheet.types.ts +++ b/src/components/common/BottomSheet/BottomSheet.types.ts @@ -13,7 +13,7 @@ export type Props = ComponentPropsWithoutRef<'div'> & { /** * The content to be displayed inside the BottomSheet. */ - children?: ReactNode; + children: ReactNode; }; export type DragInfo = { diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index 83334e4..d9ac582 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -7,9 +7,7 @@ import { Props } from './BottomSheet.types'; import { Portal } from '../Portal'; -const Content = ({ children }: React.PropsWithChildren) => ( -
{children}
-); +const Content = ({ children }: React.PropsWithChildren) =>
{children}
; export const BottomSheet = memo(({ children, isOpen, onClose }: Props) => { const dragControls = useDragControls(); diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index 6953713..be7a461 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -39,14 +39,17 @@ export const useBottomSheet = (resetTrigger: boolean, onClose?: () => void) => { ); const handleDragEnd = useCallback(() => { + if (onClose) { + return onClose(); + } if (sheetHeight <= MIN_VISIBLE_HEIGHT) { setIsHidden(true); setIsInteractionDisabled(true); - if (onClose) { - onClose(); - } - } else if (sheetHeight > MAX_HEIGHT) { + return; + } + if (sheetHeight > MAX_HEIGHT) { setSheetHeight(MAX_HEIGHT); + return; } }, [sheetHeight, onClose]); From f273b494c13244e45351fa3b07aa675c98428206 Mon Sep 17 00:00:00 2001 From: houony Date: Wed, 13 Nov 2024 02:38:47 +0900 Subject: [PATCH 11/18] refactor : remove onClose --- .../common/BottomSheet/BottomSheet.stories.tsx | 2 +- src/components/common/BottomSheet/index.tsx | 4 ++-- src/hooks/common/useBottomSheet.tsx | 17 +++++------------ 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/components/common/BottomSheet/BottomSheet.stories.tsx b/src/components/common/BottomSheet/BottomSheet.stories.tsx index 04f0ccb..7e87b10 100644 --- a/src/components/common/BottomSheet/BottomSheet.stories.tsx +++ b/src/components/common/BottomSheet/BottomSheet.stories.tsx @@ -26,7 +26,7 @@ export const Basic: Story = { Open BottomSheet {isOpen && ( - setIsOpen(false)}> +
Contents ...
diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index d9ac582..4a46e4f 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -9,11 +9,11 @@ import { Portal } from '../Portal'; const Content = ({ children }: React.PropsWithChildren) =>
{children}
; -export const BottomSheet = memo(({ children, isOpen, onClose }: Props) => { +export const BottomSheet = memo(({ children, isOpen }: Props) => { const dragControls = useDragControls(); const { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, handleClose } = - useBottomSheet(isOpen, onClose); + useBottomSheet(isOpen); return ( diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index be7a461..4b3567b 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -3,7 +3,7 @@ import { useState, useCallback, useRef, useEffect } from 'react'; import { DragInfo } from '@/components/common/BottomSheet/BottomSheet.types'; import { INITIAL_HEIGHT, MIN_VISIBLE_HEIGHT, MAX_HEIGHT } from '@/constants/bottomSheetOptions'; -export const useBottomSheet = (resetTrigger: boolean, onClose?: () => void) => { +export const useBottomSheet = (isOpen: boolean) => { const [sheetHeight, setSheetHeight] = useState(INITIAL_HEIGHT); const [isHidden, setIsHidden] = useState(false); const [isInteractionDisabled, setIsInteractionDisabled] = useState(false); @@ -11,14 +11,14 @@ export const useBottomSheet = (resetTrigger: boolean, onClose?: () => void) => { const initialPositionRef = useRef(INITIAL_HEIGHT); useEffect(() => { - if (resetTrigger) { + if (isOpen) { setSheetHeight(INITIAL_HEIGHT); setIsHidden(false); setIsInteractionDisabled(false); dragOffsetRef.current = 0; initialPositionRef.current = INITIAL_HEIGHT; } - }, [resetTrigger]); + }, [isOpen]); const handleDrag = useCallback( (_: MouseEvent | TouchEvent | PointerEvent, info: DragInfo) => { @@ -39,19 +39,13 @@ export const useBottomSheet = (resetTrigger: boolean, onClose?: () => void) => { ); const handleDragEnd = useCallback(() => { - if (onClose) { - return onClose(); - } if (sheetHeight <= MIN_VISIBLE_HEIGHT) { setIsHidden(true); setIsInteractionDisabled(true); - return; - } - if (sheetHeight > MAX_HEIGHT) { + } else if (sheetHeight > MAX_HEIGHT) { setSheetHeight(MAX_HEIGHT); - return; } - }, [sheetHeight, onClose]); + }, [sheetHeight]); const resetSheet = useCallback(() => { setSheetHeight(INITIAL_HEIGHT); @@ -73,7 +67,6 @@ export const useBottomSheet = (resetTrigger: boolean, onClose?: () => void) => { isInteractionDisabled, handleDrag, handleDragEnd, - resetSheet, handleClose, }; }; From f8f8c1fdcd35588fd222104aa434f9dce87b75c9 Mon Sep 17 00:00:00 2001 From: houony Date: Wed, 13 Nov 2024 02:54:43 +0900 Subject: [PATCH 12/18] refactor : add early return at handleDragEnd --- src/hooks/common/useBottomSheet.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index 4b3567b..7d9fd8e 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -42,8 +42,12 @@ export const useBottomSheet = (isOpen: boolean) => { if (sheetHeight <= MIN_VISIBLE_HEIGHT) { setIsHidden(true); setIsInteractionDisabled(true); - } else if (sheetHeight > MAX_HEIGHT) { + return; + } + + if (sheetHeight > MAX_HEIGHT) { setSheetHeight(MAX_HEIGHT); + return; } }, [sheetHeight]); From 390eb14364e81f5bc5888a8179fb48f3009a5894 Mon Sep 17 00:00:00 2001 From: houony Date: Wed, 13 Nov 2024 03:36:09 +0900 Subject: [PATCH 13/18] refactor : change style to tailwind, change dragElastic --- src/components/common/BottomSheet/index.tsx | 5 ++--- src/hooks/common/useBottomSheet.tsx | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index 4a46e4f..0752854 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -27,11 +27,10 @@ export const BottomSheet = memo(({ children, isOpen }: Props) => { {!isHidden && ( { requestAnimationFrame(() => { const dragAmount = -info.delta.y; - dragOffsetRef.current += dragAmount; + dragOffsetRef.current += dragAmount * 10; const newHeight = Math.min( Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), From 5bf5e10675f75f08f438cb66d361b0bd7f00ea3a Mon Sep 17 00:00:00 2001 From: houony Date: Wed, 13 Nov 2024 20:22:19 +0900 Subject: [PATCH 14/18] refactor : change drag amount --- src/hooks/common/useBottomSheet.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index ed63b73..fbd1100 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -26,7 +26,7 @@ export const useBottomSheet = (isOpen: boolean) => { requestAnimationFrame(() => { const dragAmount = -info.delta.y; - dragOffsetRef.current += dragAmount * 10; + dragOffsetRef.current += dragAmount * 5; const newHeight = Math.min( Math.max(initialPositionRef.current + dragOffsetRef.current, MIN_VISIBLE_HEIGHT), From 489baf25f5051b528b401a2252ee7cb920cf59b6 Mon Sep 17 00:00:00 2001 From: houony Date: Thu, 14 Nov 2024 21:01:17 +0900 Subject: [PATCH 15/18] refactor : move BottomSheet framer setting to motions.ts --- src/components/common/BottomSheet/index.tsx | 7 ++++--- src/constants/motions.ts | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index 0752854..b881cb0 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -1,6 +1,7 @@ import { memo } from 'react'; import { motion, useDragControls, AnimatePresence } from 'framer-motion'; +import { BOTTOM_SHEET_ANIMATION } from '@/constants/motions'; import { useBottomSheet } from '@/hooks/common/useBottomSheet'; import { Props } from './BottomSheet.types'; @@ -34,10 +35,10 @@ export const BottomSheet = memo(({ children, isOpen }: Props) => { dragConstraints={{ top: 0, bottom: 0 }} onDrag={handleDrag} onDragEnd={handleDragEnd} - animate={{ height: sheetHeight, opacity: isHidden ? 0 : 1 }} - transition={{ type: 'spring', stiffness: 170, damping: 30, duration: 0.3 }} + animate={BOTTOM_SHEET_ANIMATION.animate(sheetHeight, isHidden)} + transition={BOTTOM_SHEET_ANIMATION.transition} onPointerDown={(e) => !isInteractionDisabled && dragControls.start(e)} - exit={{ height: 0, opacity: 0 }} + exit={BOTTOM_SHEET_ANIMATION.exit} >
{children} diff --git a/src/constants/motions.ts b/src/constants/motions.ts index 056b74b..7d377e2 100644 --- a/src/constants/motions.ts +++ b/src/constants/motions.ts @@ -31,3 +31,20 @@ export const SLIDER_ANIMATION = { }, }, }; + +export const BOTTOM_SHEET_ANIMATION = { + animate: (sheetHeight: number, isHidden: boolean) => ({ + height: sheetHeight, + opacity: isHidden ? 0 : 1, + }), + exit: { + height: 0, + opacity: 0, + }, + transition: { + type: 'spring', + stiffness: 170, + damping: 30, + duration: 0.3, + }, +}; From b2f08220510de200e3cdd60f0afd0c4892473046 Mon Sep 17 00:00:00 2001 From: houony Date: Fri, 15 Nov 2024 03:33:14 +0900 Subject: [PATCH 16/18] refactor : remove onClose, add BottomSheetHeader, remove backdrop, update handleDragEnd --- .../common/BottomSheet/BottomSheet.types.ts | 4 -- .../common/BottomSheet/BottomSheetHeader.tsx | 3 ++ src/components/common/BottomSheet/index.tsx | 25 ++++++----- src/hooks/common/useBottomSheet.tsx | 43 +++++++++++-------- 4 files changed, 41 insertions(+), 34 deletions(-) create mode 100644 src/components/common/BottomSheet/BottomSheetHeader.tsx diff --git a/src/components/common/BottomSheet/BottomSheet.types.ts b/src/components/common/BottomSheet/BottomSheet.types.ts index e04905f..9013a36 100644 --- a/src/components/common/BottomSheet/BottomSheet.types.ts +++ b/src/components/common/BottomSheet/BottomSheet.types.ts @@ -6,10 +6,6 @@ export type Props = ComponentPropsWithoutRef<'div'> & { * @default false */ isOpen: boolean; - /** - * Callback function that is called when the BottomSheet should close. - */ - onClose?: VoidFunction; /** * The content to be displayed inside the BottomSheet. */ diff --git a/src/components/common/BottomSheet/BottomSheetHeader.tsx b/src/components/common/BottomSheet/BottomSheetHeader.tsx new file mode 100644 index 0000000..96a72a9 --- /dev/null +++ b/src/components/common/BottomSheet/BottomSheetHeader.tsx @@ -0,0 +1,3 @@ +export const BottomSheetHeader = () => { + return
; +}; diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index b881cb0..ef5ad7d 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -5,29 +5,28 @@ import { BOTTOM_SHEET_ANIMATION } from '@/constants/motions'; import { useBottomSheet } from '@/hooks/common/useBottomSheet'; import { Props } from './BottomSheet.types'; +import { BottomSheetHeader } from './BottomSheetHeader'; import { Portal } from '../Portal'; -const Content = ({ children }: React.PropsWithChildren) =>
{children}
; - export const BottomSheet = memo(({ children, isOpen }: Props) => { const dragControls = useDragControls(); - const { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, handleClose } = - useBottomSheet(isOpen); + const { + sheetHeight, + isHidden, + isInteractionDisabled, + handleDrag, + handleDragEnd, + bottomSheetRef, + } = useBottomSheet(isOpen); return ( -
- {!isHidden && ( { onPointerDown={(e) => !isInteractionDisabled && dragControls.start(e)} exit={BOTTOM_SHEET_ANIMATION.exit} > -
- {children} + + {children} )} diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index fbd1100..688c8ca 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -2,6 +2,7 @@ import { useState, useCallback, useRef, useEffect } from 'react'; import { DragInfo } from '@/components/common/BottomSheet/BottomSheet.types'; import { INITIAL_HEIGHT, MIN_VISIBLE_HEIGHT, MAX_HEIGHT } from '@/constants/bottomSheetOptions'; +import { BOTTOM_SHEET_ANIMATION } from '@/constants/motions'; export const useBottomSheet = (isOpen: boolean) => { const [sheetHeight, setSheetHeight] = useState(INITIAL_HEIGHT); @@ -9,6 +10,7 @@ export const useBottomSheet = (isOpen: boolean) => { const [isInteractionDisabled, setIsInteractionDisabled] = useState(false); const dragOffsetRef = useRef(0); const initialPositionRef = useRef(INITIAL_HEIGHT); + const bottomSheetRef = useRef(null); useEffect(() => { if (isOpen) { @@ -20,6 +22,24 @@ export const useBottomSheet = (isOpen: boolean) => { } }, [isOpen]); + useEffect(() => { + const handleClickOutside = (event: Event) => { + if (bottomSheetRef.current && !bottomSheetRef.current.contains(event.target as Node)) { + setSheetHeight(INITIAL_HEIGHT); + } + }; + + if (isOpen) { + document.addEventListener('mousedown', handleClickOutside); + document.addEventListener('touchstart', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + document.removeEventListener('touchstart', handleClickOutside); + }; + }, [isOpen]); + const handleDrag = useCallback( (_: MouseEvent | TouchEvent | PointerEvent, info: DragInfo) => { if (isInteractionDisabled) return; @@ -40,8 +60,11 @@ export const useBottomSheet = (isOpen: boolean) => { const handleDragEnd = useCallback(() => { if (sheetHeight <= MIN_VISIBLE_HEIGHT) { - setIsHidden(true); - setIsInteractionDisabled(true); + setSheetHeight(MIN_VISIBLE_HEIGHT); + setTimeout(() => { + setIsHidden(true); + setIsInteractionDisabled(true); + }, BOTTOM_SHEET_ANIMATION.transition.duration * 1000); return; } @@ -51,26 +74,12 @@ export const useBottomSheet = (isOpen: boolean) => { } }, [sheetHeight]); - const resetSheet = useCallback(() => { - setSheetHeight(INITIAL_HEIGHT); - setIsHidden(false); - setIsInteractionDisabled(false); - dragOffsetRef.current = 0; - initialPositionRef.current = INITIAL_HEIGHT; - }, []); - - const handleClose = useCallback(() => { - if (!isInteractionDisabled) { - resetSheet(); - } - }, [isInteractionDisabled, resetSheet]); - return { sheetHeight, isHidden, isInteractionDisabled, handleDrag, handleDragEnd, - handleClose, + bottomSheetRef, }; }; From f3ff5ab178832749462583b7afff584cfe134fb8 Mon Sep 17 00:00:00 2001 From: houony Date: Fri, 15 Nov 2024 19:33:20 +0900 Subject: [PATCH 17/18] chore : remove react-use-measure, debounce --- .pnp.cjs | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/.pnp.cjs b/.pnp.cjs index 9211e06..baab6a1 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -79,7 +79,6 @@ const RAW_RUNTIME_STATE = ["react-dom", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:18.3.1"],\ ["react-error-boundary", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:4.0.13"],\ ["react-router-dom", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:6.26.2"],\ - ["react-use-measure", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:2.1.1"],\ ["storybook", "npm:8.3.5"],\ ["storybook-addon-sass-postcss", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:0.3.2"],\ ["tailwind-merge", "npm:2.5.4"],\ @@ -6582,15 +6581,6 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ - ["debounce", [\ - ["npm:1.2.1", {\ - "packageLocation": "./.yarn/cache/debounce-npm-1.2.1-b09266a260-6c9320aa09.zip/node_modules/debounce/",\ - "packageDependencies": [\ - ["debounce", "npm:1.2.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ ["debug", [\ ["npm:2.6.9", {\ "packageLocation": "./.yarn/cache/debug-npm-2.6.9-7d4cb597dc-121908fb83.zip/node_modules/debug/",\ @@ -8160,7 +8150,6 @@ const RAW_RUNTIME_STATE = ["react-dom", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:18.3.1"],\ ["react-error-boundary", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:4.0.13"],\ ["react-router-dom", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:6.26.2"],\ - ["react-use-measure", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:2.1.1"],\ ["storybook", "npm:8.3.5"],\ ["storybook-addon-sass-postcss", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:0.3.2"],\ ["tailwind-merge", "npm:2.5.4"],\ @@ -12270,33 +12259,6 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ - ["react-use-measure", [\ - ["npm:2.1.1", {\ - "packageLocation": "./.yarn/cache/react-use-measure-npm-2.1.1-7e53301142-77b035189d.zip/node_modules/react-use-measure/",\ - "packageDependencies": [\ - ["react-use-measure", "npm:2.1.1"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:2.1.1", {\ - "packageLocation": "./.yarn/__virtual__/react-use-measure-virtual-110e8e1c71/0/cache/react-use-measure-npm-2.1.1-7e53301142-77b035189d.zip/node_modules/react-use-measure/",\ - "packageDependencies": [\ - ["react-use-measure", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:2.1.1"],\ - ["@types/react", "npm:18.3.10"],\ - ["@types/react-dom", "npm:18.3.0"],\ - ["debounce", "npm:1.2.1"],\ - ["react", "npm:18.3.1"],\ - ["react-dom", "virtual:496772d107f3d2c371d44f7464355fcd2fcfd9b1940f6002dce91963b0955dbca515f91fecb79ab9255c9de181f2db37be71afcdc7b32ce3d95b36cadf9d8518#npm:18.3.1"]\ - ],\ - "packagePeers": [\ - "@types/react-dom",\ - "@types/react",\ - "react-dom",\ - "react"\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ ["read-cache", [\ ["npm:1.0.0", {\ "packageLocation": "./.yarn/cache/read-cache-npm-1.0.0-00fa89ed05-90cb275021.zip/node_modules/read-cache/",\ From a4d2f8b3122d2a0fbe3f9b49f5e8988fb05e4254 Mon Sep 17 00:00:00 2001 From: houony Date: Fri, 15 Nov 2024 19:40:19 +0900 Subject: [PATCH 18/18] refactor : remove AnimatePresence, setTimeout --- src/components/common/BottomSheet/index.tsx | 42 ++++++++++----------- src/hooks/common/useBottomSheet.tsx | 8 +--- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx index ef5ad7d..0fbedbb 100644 --- a/src/components/common/BottomSheet/index.tsx +++ b/src/components/common/BottomSheet/index.tsx @@ -1,5 +1,5 @@ import { memo } from 'react'; -import { motion, useDragControls, AnimatePresence } from 'framer-motion'; +import { motion, useDragControls } from 'framer-motion'; import { BOTTOM_SHEET_ANIMATION } from '@/constants/motions'; import { useBottomSheet } from '@/hooks/common/useBottomSheet'; @@ -23,27 +23,25 @@ export const BottomSheet = memo(({ children, isOpen }: Props) => { return ( - - {!isHidden && ( - !isInteractionDisabled && dragControls.start(e)} - exit={BOTTOM_SHEET_ANIMATION.exit} - > - - {children} - - )} - + {!isHidden && ( + !isInteractionDisabled && dragControls.start(e)} + exit={BOTTOM_SHEET_ANIMATION.exit} + > + + {children} + + )} ); }); diff --git a/src/hooks/common/useBottomSheet.tsx b/src/hooks/common/useBottomSheet.tsx index 688c8ca..fc631d3 100644 --- a/src/hooks/common/useBottomSheet.tsx +++ b/src/hooks/common/useBottomSheet.tsx @@ -2,7 +2,6 @@ import { useState, useCallback, useRef, useEffect } from 'react'; import { DragInfo } from '@/components/common/BottomSheet/BottomSheet.types'; import { INITIAL_HEIGHT, MIN_VISIBLE_HEIGHT, MAX_HEIGHT } from '@/constants/bottomSheetOptions'; -import { BOTTOM_SHEET_ANIMATION } from '@/constants/motions'; export const useBottomSheet = (isOpen: boolean) => { const [sheetHeight, setSheetHeight] = useState(INITIAL_HEIGHT); @@ -60,11 +59,8 @@ export const useBottomSheet = (isOpen: boolean) => { const handleDragEnd = useCallback(() => { if (sheetHeight <= MIN_VISIBLE_HEIGHT) { - setSheetHeight(MIN_VISIBLE_HEIGHT); - setTimeout(() => { - setIsHidden(true); - setIsInteractionDisabled(true); - }, BOTTOM_SHEET_ANIMATION.transition.duration * 1000); + setIsHidden(true); + setIsInteractionDisabled(true); return; }