From 319fb77103f60620fc766ee96f9928c618848857 Mon Sep 17 00:00:00 2001 From: Marukome0743 Date: Mon, 27 Jan 2025 11:08:00 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8feat:=20swipe=20image=20gallery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/@modal/(.)history/modal.tsx | 128 +++++++++++++++++++++++++++++--- 1 file changed, 117 insertions(+), 11 deletions(-) diff --git a/app/@modal/(.)history/modal.tsx b/app/@modal/(.)history/modal.tsx index 75e2013e..5f1da814 100644 --- a/app/@modal/(.)history/modal.tsx +++ b/app/@modal/(.)history/modal.tsx @@ -1,13 +1,24 @@ "use client" -import { XMarkIcon } from "@heroicons/react/24/solid" -import { useRouter } from "next/navigation" +import type { Picture } from "@/app/interfaces/picture" +import type { EventDate } from "@/app/interfaces/schedule" +import { HIROSHIMA_HISTORY, KANTO, KANTO_HISTORY } from "@/app/lib/constant" +import { + ChevronLeftIcon, + ChevronRightIcon, + XMarkIcon, +} from "@heroicons/react/24/solid" +import { SlashIcon } from "@heroicons/react/24/solid" +import { usePathname, useRouter } from "next/navigation" import { type JSX, + type MouseEvent, type ReactNode, type RefObject, + type TouchEvent, useEffect, useRef, + useState, } from "react" import styles from "./modal.module.css" @@ -15,8 +26,24 @@ export function Modal({ children, }: Readonly<{ children: ReactNode }>): JSX.Element { const router = useRouter() + const pathname: string = usePathname() + const pathParts: string[] = pathname.split("/") + const history: EventDate[] = + `/${pathParts[3]}` === KANTO.pathname ? KANTO_HISTORY : HIROSHIMA_HISTORY + const eventDate: EventDate = history.find( + (history) => history.date === pathParts[4], + ) as EventDate + const picture: Picture = eventDate.pictures.find( + (picture) => picture.src.split("/")[5].split(".")[0] === pathParts[5], + ) as Picture + const indexOfPicture: number = eventDate.pictures.indexOf(picture) const dialogRef: RefObject = useRef(null) + const [touchState, setTouchState] = useState<{ + touchStart: number + touchEnd: number + }>({ touchStart: 0, touchEnd: 0 }) + const minSwipeDistance = 50 useEffect(() => { if (!dialogRef.current?.open) { @@ -24,13 +51,65 @@ export function Modal({ } }) + function onTouchStart(e: MouseEvent | TouchEvent) { + setTouchState({ + touchStart: "targetTouches" in e ? e.targetTouches[0].clientX : e.clientX, + touchEnd: 0, + }) + } + + function onTouchMove(e: MouseEvent | TouchEvent) { + setTouchState((prev) => ({ + ...prev, + touchEnd: "targetTouches" in e ? e.targetTouches[0].clientX : e.clientX, + })) + } + + function onTouchEnd() { + if (touchState.touchStart === 0 || touchState.touchEnd === 0) { + return + } + + const distance: number = touchState.touchStart - touchState.touchEnd + const isLeftSwipe: boolean = minSwipeDistance < distance + const isRightSwipe: boolean = distance < -minSwipeDistance + + if (!(isLeftSwipe || isRightSwipe)) { + return + } + + if (isLeftSwipe && indexOfPicture < eventDate.pictures.length - 1) { + MovePage("next") + } + if (isRightSwipe && indexOfPicture > 0) { + MovePage("prev") + } + } + + function MovePage(direction: "next" | "prev"): void { + const newPicture: Picture = + direction === "next" + ? eventDate.pictures[indexOfPicture + 1] + : eventDate.pictures[indexOfPicture - 1] + pathParts[pathParts.length - 1] = newPicture.src.split("/")[5].split(".")[0] + router.replace(pathParts.join("/")) + } + return ( -
+
{children} - +
+ + {indexOfPicture + 1} + + {eventDate.pictures.length} + +
+ + +
+ +