diff --git a/src/consts.ts b/src/consts.ts index 4780626..a89e306 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -39,6 +39,7 @@ export const CLASS_FULLSIZE = "fullsize"; export const CLASS_FLEX_CENTER = "flex_center"; export const CLASS_NO_SCROLL = "no_scroll"; export const CLASS_NO_SCROLL_PADDING = "no_scroll_padding"; +export const CLASS_SLIDE = "slide"; export const CLASS_SLIDE_WRAPPER = "slide_wrapper"; export const CLASS_SLIDE_WRAPPER_INTERACTIVE = "slide_wrapper_interactive"; diff --git a/src/modules/Carousel.tsx b/src/modules/Carousel.tsx index 99e0007..c021093 100644 --- a/src/modules/Carousel.tsx +++ b/src/modules/Carousel.tsx @@ -18,14 +18,14 @@ import { import { ImageSlide } from "../components/index.js"; import { useController } from "./Controller/index.js"; import { useDocumentContext, useLightboxProps, useLightboxState } from "../contexts/index.js"; -import { CLASS_FLEX_CENTER, CLASS_SLIDE_WRAPPER, MODULE_CAROUSEL } from "../consts.js"; +import { CLASS_FLEX_CENTER, CLASS_SLIDE, MODULE_CAROUSEL } from "../consts.js"; function cssPrefix(value?: string) { return composePrefix(MODULE_CAROUSEL, value); } function cssSlidePrefix(value?: string) { - return composePrefix("slide", value); + return composePrefix(CLASS_SLIDE, value); } type CarouselSlideProps = { @@ -37,12 +37,11 @@ function CarouselSlide({ slide, offset }: CarouselSlideProps) { const containerRef = React.useRef(null); const { currentIndex } = useLightboxState(); - const { slideRect, close, focus } = useController(); + const { slideRect, focus } = useController(); const { render, carousel: { imageFit, imageProps }, on: { click: onClick }, - controller: { closeOnBackdropClick }, styles: { slide: style }, } = useLightboxProps(); const { getOwnerDocument } = useDocumentContext(); @@ -81,24 +80,7 @@ function CarouselSlide({ slide, offset }: CarouselSlideProps) { ) : null; }; - const handleBackdropClick: React.MouseEventHandler = (event) => { - const container = containerRef.current; - const target = event.target instanceof HTMLElement ? event.target : undefined; - if ( - closeOnBackdropClick && - target && - container && - (target === container || - // detect Zoom and Video wrappers - (Array.from(container.children).find((x) => x === target) && - target.classList.contains(cssClass(CLASS_SLIDE_WRAPPER)))) - ) { - close(); - } - }; - return ( - // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
; + return
; } export function Carousel({ carousel }: ComponentProps) { diff --git a/src/modules/Controller/Controller.tsx b/src/modules/Controller/Controller.tsx index 48b26e2..2239010 100644 --- a/src/modules/Controller/Controller.tsx +++ b/src/modules/Controller/Controller.tsx @@ -322,7 +322,7 @@ export function Controller({ children, ...props }: ComponentProps) { (offset: number) => pull(offset, true), ] as const; - usePointerSwipe(controller, ...swipeParams, closeOnPullUp, closeOnPullDown, ...pullParams); + usePointerSwipe(controller, ...swipeParams, closeOnPullUp, closeOnPullDown, ...pullParams, close); useWheelSwipe(swipeState, ...swipeParams); diff --git a/src/modules/Controller/usePointerSwipe.ts b/src/modules/Controller/usePointerSwipe.ts index 3812e1b..da96c54 100644 --- a/src/modules/Controller/usePointerSwipe.ts +++ b/src/modules/Controller/usePointerSwipe.ts @@ -4,6 +4,8 @@ import { ControllerSettings } from "../../types.js"; import { UseSensors } from "../../hooks/useSensors.js"; import { useEventCallback } from "../../hooks/useEventCallback.js"; import { usePointerEvents } from "../../hooks/usePointerEvents.js"; +import { cssClass } from "../../utils.js"; +import { CLASS_SLIDE, CLASS_SLIDE_WRAPPER } from "../../consts.js"; enum Gesture { NONE, @@ -14,7 +16,7 @@ enum Gesture { const SWIPE_THRESHOLD = 30; export function usePointerSwipe( - { disableSwipeNavigation }: ControllerSettings, + { disableSwipeNavigation, closeOnBackdropClick }: ControllerSettings, subscribeSensors: UseSensors["subscribeSensors"], isSwipeValid: (offset: number) => boolean, containerWidth: number, @@ -29,6 +31,7 @@ export function usePointerSwipe( onPullProgress: (offset: number) => void, onPullFinish: (offset: number, duration: number) => void, onPullCancel: (offset: number) => void, + onClose: () => void, ) { const offset = React.useRef(0); const pointers = React.useRef([]); @@ -59,6 +62,11 @@ export function usePointerSwipe( [clearPointer], ); + const lookupPointer = React.useCallback( + (event: React.PointerEvent) => pointers.current.find(({ pointerId }) => event.pointerId === pointerId), + [], + ); + const onPointerDown = useEventCallback((event: React.PointerEvent) => { addPointer(event); }); @@ -67,36 +75,50 @@ export function usePointerSwipe( (pullDownEnabled && value > threshold) || (pullUpEnabled && value < -threshold); const onPointerUp = useEventCallback((event: React.PointerEvent) => { - if (pointers.current.find((x) => x.pointerId === event.pointerId) && activePointer.current === event.pointerId) { - const duration = Date.now() - startTime.current; - const currentOffset = offset.current; + const pointer = lookupPointer(event); + if (pointer) { + if (activePointer.current === event.pointerId) { + const duration = Date.now() - startTime.current; + const currentOffset = offset.current; + + if (gesture.current === Gesture.SWIPE) { + if ( + Math.abs(currentOffset) > 0.3 * containerWidth || + (Math.abs(currentOffset) > 5 && duration < swipeAnimationDuration) + ) { + onSwipeFinish(currentOffset, duration); + } else { + onSwipeCancel(currentOffset); + } + } else if (gesture.current === Gesture.PULL) { + if (exceedsPullThreshold(currentOffset, 2 * SWIPE_THRESHOLD)) { + onPullFinish(currentOffset, duration); + } else { + onPullCancel(currentOffset); + } + } - if (gesture.current === Gesture.SWIPE) { + offset.current = 0; + gesture.current = Gesture.NONE; + } else { + // Handle click events + const { target } = event; if ( - Math.abs(currentOffset) > 0.3 * containerWidth || - (Math.abs(currentOffset) > 5 && duration < swipeAnimationDuration) + closeOnBackdropClick && + target instanceof HTMLElement && + target === pointer.target && + (target.classList.contains(cssClass(CLASS_SLIDE)) || target.classList.contains(cssClass(CLASS_SLIDE_WRAPPER))) ) { - onSwipeFinish(currentOffset, duration); - } else { - onSwipeCancel(currentOffset); - } - } else if (gesture.current === Gesture.PULL) { - if (exceedsPullThreshold(currentOffset, 2 * SWIPE_THRESHOLD)) { - onPullFinish(currentOffset, duration); - } else { - onPullCancel(currentOffset); + onClose(); } } - - offset.current = 0; - gesture.current = Gesture.NONE; } clearPointer(event); }); const onPointerMove = useEventCallback((event: React.PointerEvent) => { - const pointer = pointers.current.find((p) => p.pointerId === event.pointerId); + const pointer = lookupPointer(event); if (pointer) { const isCurrentPointer = activePointer.current === event.pointerId;