Skip to content

Commit

Permalink
feat: add closeOnPullUp controller option
Browse files Browse the repository at this point in the history
  • Loading branch information
igordanchenko committed Jan 18, 2024
1 parent 5f4c315 commit 98e095a
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 48 deletions.
2 changes: 2 additions & 0 deletions docs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ import "yet-another-react-lightbox/styles.css";
&nbsp;&nbsp;focus?: boolean;<br />
&nbsp;&nbsp;aria?: boolean;<br />
&nbsp;&nbsp;touchAction?: "none" | "pan-y";<br />
&nbsp;&nbsp;closeOnPullUp?: boolean;<br />
&nbsp;&nbsp;closeOnPullDown?: boolean;<br />
&nbsp;&nbsp;closeOnBackdropClick?: boolean;<br />
&#125;
Expand All @@ -190,6 +191,7 @@ import "yet-another-react-lightbox/styles.css";
<li>`focus` - deprecated, for internal use only</li>
<li>`aria` - if `true`, set ARIA attributes on the controller div</li>
<li>`touchAction` - controller touch-action setting</li>
<li>`closeOnPullUp` - if `true`, close the lightbox on pull-up gesture</li>
<li>`closeOnPullDown` - if `true`, close the lightbox on pull-down gesture</li>
<li>`closeOnBackdropClick` - if `true`, close the lightbox when the backdrop is clicked</li>
</ul>
Expand Down
67 changes: 36 additions & 31 deletions src/modules/Controller/Controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ export function Controller({ children, ...props }: ComponentProps) {

const [swipeState, setSwipeState] = React.useState(SwipeState.NONE);
const swipeOffset = React.useRef(0);
const pullDownOffset = React.useRef(0);
const pullDownOpacity = React.useRef(1);
const pullOffset = React.useRef(0);
const pullOpacity = React.useRef(1);

const { registerSensors, subscribeSensors } = useSensors<HTMLDivElement>();
const { subscribe, publish } = useEvents();

const cleanupAnimationIncrement = useDelay();
const cleanupSwipeOffset = useDelay();
const cleanupPullDownOffset = useDelay();
const cleanupPullOffset = useDelay();

const { containerRef, setContainerRef, containerRect } = useContainerRect<HTMLDivElement>();
const handleContainerRef = useForkRef(usePreventSwipeNavigation(), setContainerRef);
Expand Down Expand Up @@ -107,21 +107,26 @@ export function Controller({ children, ...props }: ComponentProps) {
containerRef.current?.style.setProperty(cssVar("swipe_offset"), `${Math.round(offset)}px`);
};

const { closeOnPullDown } = controller;
const { closeOnPullUp, closeOnPullDown } = controller;

const setPullDownOffset = (offset: number) => {
pullDownOffset.current = offset;
pullDownOpacity.current = (() => {
const setPullOffset = (offset: number) => {
pullOffset.current = offset;
pullOpacity.current = (() => {
const threshold = 60;
const minOpacity = 0.5;
return Math.min(Math.max(round(1 - (offset / threshold) * (1 - minOpacity), 2), minOpacity), 1);
const offsetValue = (() => {
if (closeOnPullDown && offset > 0) return offset;
if (closeOnPullUp && offset < 0) return -offset;
return 0;
})();
return Math.min(Math.max(round(1 - (offsetValue / threshold) * (1 - minOpacity), 2), minOpacity), 1);
})();

containerRef.current?.style.setProperty(cssVar("pull_down_offset"), `${Math.round(offset)}px`);
containerRef.current?.style.setProperty(cssVar("pull_down_opacity"), `${pullDownOpacity.current}`);
containerRef.current?.style.setProperty(cssVar("pull_offset"), `${Math.round(offset)}px`);
containerRef.current?.style.setProperty(cssVar("pull_opacity"), `${pullOpacity.current}`);
};

const { prepareAnimation: preparePullDownAnimation } = useAnimation<{
const { prepareAnimation: preparePullAnimation } = useAnimation<{
rect: DOMRect;
opacity: number;
duration: number;
Expand All @@ -142,24 +147,24 @@ export function Controller({ children, ...props }: ComponentProps) {
return undefined;
});

const pullDown = (offset: number, cancel?: boolean) => {
if (closeOnPullDown) {
setPullDownOffset(offset);
const pull = (offset: number, cancel?: boolean) => {
if (closeOnPullUp || closeOnPullDown) {
setPullOffset(offset);

let duration = 0;

if (carouselRef.current) {
duration = animation.fade * (cancel ? 2 : 1);

preparePullDownAnimation({
preparePullAnimation({
rect: carouselRef.current.getBoundingClientRect(),
opacity: pullDownOpacity.current,
opacity: pullOpacity.current,
duration,
});
}

cleanupPullDownOffset(() => {
setPullDownOffset(0);
cleanupPullOffset(() => {
setPullOffset(0);
setSwipeState(SwipeState.NONE);
}, duration);

Expand Down Expand Up @@ -298,22 +303,22 @@ export function Controller({ children, ...props }: ComponentProps) {
(offset: number) => swipe({ offset, count: 0 }),
] as const;

const pullDownParams = [
// onPullDownStart
const pullParams = [
// onPullStart
() => {
if (closeOnPullDown) {
setSwipeState(SwipeState.PULL_DOWN);
setSwipeState(SwipeState.PULL);
}
},
// onPullDownProgress
(offset: number) => setPullDownOffset(offset),
// onPullDownFinish
(offset: number) => pullDown(offset),
// onPullDownCancel
(offset: number) => pullDown(offset, true),
// onPullProgress
(offset: number) => setPullOffset(offset),
// onPullFinish
(offset: number) => pull(offset),
// onPullCancel
(offset: number) => pull(offset, true),
] as const;

usePointerSwipe(...swipeParams, closeOnPullDown, ...pullDownParams);
usePointerSwipe(...swipeParams, closeOnPullUp, closeOnPullDown, ...pullParams);

useWheelSwipe(swipeState, ...swipeParams);

Expand Down Expand Up @@ -395,10 +400,10 @@ export function Controller({ children, ...props }: ComponentProps) {
...(swipeState === SwipeState.SWIPE
? { [cssVar("swipe_offset")]: `${Math.round(swipeOffset.current)}px` }
: null),
...(swipeState === SwipeState.PULL_DOWN
...(swipeState === SwipeState.PULL
? {
[cssVar("pull_down_offset")]: `${Math.round(pullDownOffset.current)}px`,
[cssVar("pull_down_opacity")]: `${pullDownOpacity.current}`,
[cssVar("pull_offset")]: `${Math.round(pullOffset.current)}px`,
[cssVar("pull_opacity")]: `${pullOpacity.current}`,
}
: null),
...(controller.touchAction !== "none" ? { [cssVar("controller_touch_action")]: controller.touchAction } : null),
Expand Down
2 changes: 1 addition & 1 deletion src/modules/Controller/SwipeState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export enum SwipeState {
NONE,
SWIPE,
PULL_DOWN,
PULL,
ANIMATION,
}
32 changes: 18 additions & 14 deletions src/modules/Controller/usePointerSwipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { usePointerEvents } from "../../hooks/usePointerEvents.js";
enum Gesture {
NONE,
SWIPE,
PULL_DOWN,
PULL,
}

const SWIPE_THRESHOLD = 30;
Expand All @@ -21,11 +21,12 @@ export function usePointerSwipe<T extends Element = Element>(
onSwipeProgress: (offset: number) => void,
onSwipeFinish: (offset: number, duration: number) => void,
onSwipeCancel: (offset: number) => void,
pullUpEnabled: boolean,
pullDownEnabled: boolean,
onPullDownStart: () => void,
onPullDownProgress: (offset: number) => void,
onPullDownFinish: (offset: number, duration: number) => void,
onPullDownCancel: (offset: number) => void,
onPullStart: () => void,
onPullProgress: (offset: number) => void,
onPullFinish: (offset: number, duration: number) => void,
onPullCancel: (offset: number) => void,
) {
const offset = React.useRef<number>(0);
const pointers = React.useRef<React.PointerEvent[]>([]);
Expand Down Expand Up @@ -60,6 +61,9 @@ export function usePointerSwipe<T extends Element = Element>(
addPointer(event);
});

const exceedsPullThreshold = (value: number, threshold: number) =>
(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;
Expand All @@ -74,11 +78,11 @@ export function usePointerSwipe<T extends Element = Element>(
} else {
onSwipeCancel(currentOffset);
}
} else if (gesture.current === Gesture.PULL_DOWN) {
if (currentOffset > 2 * SWIPE_THRESHOLD) {
onPullDownFinish(currentOffset, duration);
} else if (gesture.current === Gesture.PULL) {
if (exceedsPullThreshold(currentOffset, 2 * SWIPE_THRESHOLD)) {
onPullFinish(currentOffset, duration);
} else {
onPullDownCancel(currentOffset);
onPullCancel(currentOffset);
}
}

Expand Down Expand Up @@ -124,18 +128,18 @@ export function usePointerSwipe<T extends Element = Element>(
// start swipe gesture
startGesture(Gesture.SWIPE);
onSwipeStart();
} else if (pullDownEnabled && Math.abs(deltaY) > Math.abs(deltaX) && deltaY > SWIPE_THRESHOLD) {
} else if (Math.abs(deltaY) > Math.abs(deltaX) && exceedsPullThreshold(deltaY, SWIPE_THRESHOLD)) {
// start pull-down gesture
startGesture(Gesture.PULL_DOWN);
onPullDownStart();
startGesture(Gesture.PULL);
onPullStart();
}
} else if (isCurrentPointer) {
if (gesture.current === Gesture.SWIPE) {
offset.current = deltaX;
onSwipeProgress(deltaX);
} else if (gesture.current === Gesture.PULL_DOWN) {
} else if (gesture.current === Gesture.PULL) {
offset.current = deltaY;
onPullDownProgress(deltaY);
onPullProgress(deltaY);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const LightboxDefaultProps: LightboxProps = {
focus: true,
aria: false,
touchAction: "none",
closeOnPullUp: false,
closeOnPullDown: false,
closeOnBackdropClick: false,
},
Expand Down
4 changes: 2 additions & 2 deletions src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
100% + (var(--yarl__carousel_slides_count) - 1) *
(100% + var(--yarl__carousel_spacing_px, 0) * 1px + var(--yarl__carousel_spacing_percent, 0) * 1%)
);
transform: translate(var(--yarl__swipe_offset, 0px), var(--yarl__pull_down_offset, 0px));
opacity: var(--yarl__pull_down_opacity, 1);
transform: translate(var(--yarl__swipe_offset, 0px), var(--yarl__pull_offset, 0px));
opacity: var(--yarl__pull_opacity, 1);

&_with_slides {
column-gap: calc(
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ export interface ControllerSettings {
touchAction: "none" | "pan-y";
/** if `true`, set ARIA attributes on the controller div */
aria: boolean;
/** if `true`, close the lightbox on pull-up gesture */
closeOnPullUp: boolean;
/** if `true`, close the lightbox on pull-down gesture */
closeOnPullDown: boolean;
/** if `true`, close the lightbox when the backdrop is clicked */
Expand Down

0 comments on commit 98e095a

Please sign in to comment.