From a053cf2d71cd99618d150f1034659bd56642a56e Mon Sep 17 00:00:00 2001 From: gahusb Date: Thu, 23 Apr 2026 14:33:15 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20react-swipeable=20=EC=84=A4=EC=B9=98=20?= =?UTF-8?q?+=20useIsMobile/useSwipe=20=ED=9B=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- package-lock.json | 10 ++++++++++ package.json | 1 + src/hooks/useIsMobile.js | 18 ++++++++++++++++++ src/hooks/useSwipe.js | 13 +++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 src/hooks/useIsMobile.js create mode 100644 src/hooks/useSwipe.js diff --git a/package-lock.json b/package-lock.json index 1e8d924..3911dd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "react-dom": "^18.2.0", "react-leaflet": "^4.2.1", "react-router-dom": "^6.30.3", + "react-swipeable": "^7.0.2", "recharts": "^3.7.0", "three": "^0.182.0" }, @@ -3088,6 +3089,15 @@ "react-dom": ">=16.8" } }, + "node_modules/react-swipeable": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-7.0.2.tgz", + "integrity": "sha512-v1Qx1l+aC2fdxKa9aKJiaU/ZxmJ5o98RMoFwUqAAzVWUcxgfHFXDDruCKXhw6zIYXm6V64JiHgP9f6mlME5l8w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/recharts": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.7.0.tgz", diff --git a/package.json b/package.json index 7c239e9..83c71db 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "react-dom": "^18.2.0", "react-leaflet": "^4.2.1", "react-router-dom": "^6.30.3", + "react-swipeable": "^7.0.2", "recharts": "^3.7.0", "three": "^0.182.0" }, diff --git a/src/hooks/useIsMobile.js b/src/hooks/useIsMobile.js new file mode 100644 index 0000000..5d6910a --- /dev/null +++ b/src/hooks/useIsMobile.js @@ -0,0 +1,18 @@ +import { useState, useEffect } from 'react'; + +const MOBILE_BREAKPOINT = 768; + +export function useIsMobile() { + const [isMobile, setIsMobile] = useState( + () => window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`).matches + ); + + useEffect(() => { + const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`); + const handler = (e) => setIsMobile(e.matches); + mql.addEventListener('change', handler); + return () => mql.removeEventListener('change', handler); + }, []); + + return isMobile; +} diff --git a/src/hooks/useSwipe.js b/src/hooks/useSwipe.js new file mode 100644 index 0000000..cd8ac9d --- /dev/null +++ b/src/hooks/useSwipe.js @@ -0,0 +1,13 @@ +import { useSwipeable } from 'react-swipeable'; + +export function useSwipe({ onSwipedLeft, onSwipedRight, threshold = 50 } = {}) { + const handlers = useSwipeable({ + onSwipedLeft, + onSwipedRight, + delta: threshold, + trackMouse: false, + preventScrollOnSwipe: true, + }); + + return handlers; +}