Skip to content

Commit 3ab1826

Browse files
fix: mouse pointer backdrop click (fix #357)
* fix: mouse pointer backdrop click * refactor: code cleanup --------- Co-authored-by: Igor Danchenko <[email protected]>
1 parent 1c69812 commit 3ab1826

File tree

4 files changed

+48
-44
lines changed

4 files changed

+48
-44
lines changed

src/consts.ts

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const CLASS_FULLSIZE = "fullsize";
3939
export const CLASS_FLEX_CENTER = "flex_center";
4040
export const CLASS_NO_SCROLL = "no_scroll";
4141
export const CLASS_NO_SCROLL_PADDING = "no_scroll_padding";
42+
export const CLASS_SLIDE = "slide";
4243
export const CLASS_SLIDE_WRAPPER = "slide_wrapper";
4344
export const CLASS_SLIDE_WRAPPER_INTERACTIVE = "slide_wrapper_interactive";
4445

src/modules/Carousel.tsx

+4-23
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ import {
1818
import { ImageSlide } from "../components/index.js";
1919
import { useController } from "./Controller/index.js";
2020
import { useDocumentContext, useLightboxProps, useLightboxState } from "../contexts/index.js";
21-
import { CLASS_FLEX_CENTER, CLASS_SLIDE_WRAPPER, MODULE_CAROUSEL } from "../consts.js";
21+
import { CLASS_FLEX_CENTER, CLASS_SLIDE, MODULE_CAROUSEL } from "../consts.js";
2222

2323
function cssPrefix(value?: string) {
2424
return composePrefix(MODULE_CAROUSEL, value);
2525
}
2626

2727
function cssSlidePrefix(value?: string) {
28-
return composePrefix("slide", value);
28+
return composePrefix(CLASS_SLIDE, value);
2929
}
3030

3131
type CarouselSlideProps = {
@@ -37,12 +37,11 @@ function CarouselSlide({ slide, offset }: CarouselSlideProps) {
3737
const containerRef = React.useRef<HTMLDivElement>(null);
3838

3939
const { currentIndex } = useLightboxState();
40-
const { slideRect, close, focus } = useController();
40+
const { slideRect, focus } = useController();
4141
const {
4242
render,
4343
carousel: { imageFit, imageProps },
4444
on: { click: onClick },
45-
controller: { closeOnBackdropClick },
4645
styles: { slide: style },
4746
} = useLightboxProps();
4847
const { getOwnerDocument } = useDocumentContext();
@@ -81,24 +80,7 @@ function CarouselSlide({ slide, offset }: CarouselSlideProps) {
8180
) : null;
8281
};
8382

84-
const handleBackdropClick: React.MouseEventHandler = (event) => {
85-
const container = containerRef.current;
86-
const target = event.target instanceof HTMLElement ? event.target : undefined;
87-
if (
88-
closeOnBackdropClick &&
89-
target &&
90-
container &&
91-
(target === container ||
92-
// detect Zoom and Video wrappers
93-
(Array.from(container.children).find((x) => x === target) &&
94-
target.classList.contains(cssClass(CLASS_SLIDE_WRAPPER))))
95-
) {
96-
close();
97-
}
98-
};
99-
10083
return (
101-
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
10284
<div
10385
ref={containerRef}
10486
className={clsx(
@@ -107,7 +89,6 @@ function CarouselSlide({ slide, offset }: CarouselSlideProps) {
10789
cssClass(CLASS_FLEX_CENTER),
10890
)}
10991
{...makeInertWhen(offscreen)}
110-
onClick={handleBackdropClick}
11192
style={style}
11293
role="region"
11394
aria-roledescription="slide"
@@ -119,7 +100,7 @@ function CarouselSlide({ slide, offset }: CarouselSlideProps) {
119100

120101
function Placeholder() {
121102
const style = useLightboxProps().styles.slide;
122-
return <div className={cssClass("slide")} style={style} />;
103+
return <div className={cssClass(CLASS_SLIDE)} style={style} />;
123104
}
124105

125106
export function Carousel({ carousel }: ComponentProps) {

src/modules/Controller/Controller.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ export function Controller({ children, ...props }: ComponentProps) {
322322
(offset: number) => pull(offset, true),
323323
] as const;
324324

325-
usePointerSwipe(controller, ...swipeParams, closeOnPullUp, closeOnPullDown, ...pullParams);
325+
usePointerSwipe(controller, ...swipeParams, closeOnPullUp, closeOnPullDown, ...pullParams, close);
326326

327327
useWheelSwipe(swipeState, ...swipeParams);
328328

src/modules/Controller/usePointerSwipe.ts

+42-20
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { ControllerSettings } from "../../types.js";
44
import { UseSensors } from "../../hooks/useSensors.js";
55
import { useEventCallback } from "../../hooks/useEventCallback.js";
66
import { usePointerEvents } from "../../hooks/usePointerEvents.js";
7+
import { cssClass } from "../../utils.js";
8+
import { CLASS_SLIDE, CLASS_SLIDE_WRAPPER } from "../../consts.js";
79

810
enum Gesture {
911
NONE,
@@ -14,7 +16,7 @@ enum Gesture {
1416
const SWIPE_THRESHOLD = 30;
1517

1618
export function usePointerSwipe<T extends Element = Element>(
17-
{ disableSwipeNavigation }: ControllerSettings,
19+
{ disableSwipeNavigation, closeOnBackdropClick }: ControllerSettings,
1820
subscribeSensors: UseSensors<T>["subscribeSensors"],
1921
isSwipeValid: (offset: number) => boolean,
2022
containerWidth: number,
@@ -29,6 +31,7 @@ export function usePointerSwipe<T extends Element = Element>(
2931
onPullProgress: (offset: number) => void,
3032
onPullFinish: (offset: number, duration: number) => void,
3133
onPullCancel: (offset: number) => void,
34+
onClose: () => void,
3235
) {
3336
const offset = React.useRef<number>(0);
3437
const pointers = React.useRef<React.PointerEvent[]>([]);
@@ -59,6 +62,11 @@ export function usePointerSwipe<T extends Element = Element>(
5962
[clearPointer],
6063
);
6164

65+
const lookupPointer = React.useCallback(
66+
(event: React.PointerEvent) => pointers.current.find(({ pointerId }) => event.pointerId === pointerId),
67+
[],
68+
);
69+
6270
const onPointerDown = useEventCallback((event: React.PointerEvent) => {
6371
addPointer(event);
6472
});
@@ -67,36 +75,50 @@ export function usePointerSwipe<T extends Element = Element>(
6775
(pullDownEnabled && value > threshold) || (pullUpEnabled && value < -threshold);
6876

6977
const onPointerUp = useEventCallback((event: React.PointerEvent) => {
70-
if (pointers.current.find((x) => x.pointerId === event.pointerId) && activePointer.current === event.pointerId) {
71-
const duration = Date.now() - startTime.current;
72-
const currentOffset = offset.current;
78+
const pointer = lookupPointer(event);
79+
if (pointer) {
80+
if (activePointer.current === event.pointerId) {
81+
const duration = Date.now() - startTime.current;
82+
const currentOffset = offset.current;
83+
84+
if (gesture.current === Gesture.SWIPE) {
85+
if (
86+
Math.abs(currentOffset) > 0.3 * containerWidth ||
87+
(Math.abs(currentOffset) > 5 && duration < swipeAnimationDuration)
88+
) {
89+
onSwipeFinish(currentOffset, duration);
90+
} else {
91+
onSwipeCancel(currentOffset);
92+
}
93+
} else if (gesture.current === Gesture.PULL) {
94+
if (exceedsPullThreshold(currentOffset, 2 * SWIPE_THRESHOLD)) {
95+
onPullFinish(currentOffset, duration);
96+
} else {
97+
onPullCancel(currentOffset);
98+
}
99+
}
73100

74-
if (gesture.current === Gesture.SWIPE) {
101+
offset.current = 0;
102+
gesture.current = Gesture.NONE;
103+
} else {
104+
// Handle click events
105+
const { target } = event;
75106
if (
76-
Math.abs(currentOffset) > 0.3 * containerWidth ||
77-
(Math.abs(currentOffset) > 5 && duration < swipeAnimationDuration)
107+
closeOnBackdropClick &&
108+
target instanceof HTMLElement &&
109+
target === pointer.target &&
110+
(target.classList.contains(cssClass(CLASS_SLIDE)) || target.classList.contains(cssClass(CLASS_SLIDE_WRAPPER)))
78111
) {
79-
onSwipeFinish(currentOffset, duration);
80-
} else {
81-
onSwipeCancel(currentOffset);
82-
}
83-
} else if (gesture.current === Gesture.PULL) {
84-
if (exceedsPullThreshold(currentOffset, 2 * SWIPE_THRESHOLD)) {
85-
onPullFinish(currentOffset, duration);
86-
} else {
87-
onPullCancel(currentOffset);
112+
onClose();
88113
}
89114
}
90-
91-
offset.current = 0;
92-
gesture.current = Gesture.NONE;
93115
}
94116

95117
clearPointer(event);
96118
});
97119

98120
const onPointerMove = useEventCallback((event: React.PointerEvent) => {
99-
const pointer = pointers.current.find((p) => p.pointerId === event.pointerId);
121+
const pointer = lookupPointer(event);
100122
if (pointer) {
101123
const isCurrentPointer = activePointer.current === event.pointerId;
102124

0 commit comments

Comments
 (0)