yet-another-react-lightbox 2.5.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,7 @@ export type LightboxState = {
9
9
  export type LightboxStateAction = {
10
10
  increment: number;
11
11
  duration?: number;
12
+ easing?: string;
12
13
  } | undefined;
13
14
  export declare const useLightboxState: () => {
14
15
  state: LightboxState;
@@ -1,10 +1,12 @@
1
1
  import * as React from "react";
2
- export declare const useAnimation: <T>(nodeRef: React.RefObject<HTMLElement | null>, computeAnimation: (snapshot: T, rect: DOMRect, translate: {
2
+ export type ComputeAnimation<T> = (snapshot: T, rect: DOMRect, translate: {
3
3
  x: number;
4
4
  y: number;
5
5
  z: number;
6
6
  }) => {
7
7
  keyframes: Keyframe[];
8
8
  duration: number;
9
+ easing?: string;
9
10
  onfinish?: () => void;
10
- } | undefined) => (currentSnapshot: T | undefined) => void;
11
+ } | undefined;
12
+ export declare const useAnimation: <T>(nodeRef: React.RefObject<HTMLElement | null>, computeAnimation: ComputeAnimation<T>) => (currentSnapshot: T | undefined) => void;
@@ -28,10 +28,10 @@ export const useAnimation = (nodeRef, computeAnimation) => {
28
28
  useLayoutEffect(() => {
29
29
  var _a, _b, _c;
30
30
  if (nodeRef.current && snapshot.current !== undefined && !reduceMotion) {
31
- const { keyframes, duration, onfinish } = computeAnimation(snapshot.current, nodeRef.current.getBoundingClientRect(), currentTransformation(nodeRef.current)) || {};
31
+ const { keyframes, duration, easing, onfinish } = computeAnimation(snapshot.current, nodeRef.current.getBoundingClientRect(), currentTransformation(nodeRef.current)) || {};
32
32
  if (keyframes && duration) {
33
33
  (_a = animation.current) === null || _a === void 0 ? void 0 : _a.cancel();
34
- animation.current = (_c = (_b = nodeRef.current).animate) === null || _c === void 0 ? void 0 : _c.call(_b, keyframes, duration);
34
+ animation.current = (_c = (_b = nodeRef.current).animate) === null || _c === void 0 ? void 0 : _c.call(_b, keyframes, { duration, easing });
35
35
  if (animation.current) {
36
36
  animation.current.onfinish = () => {
37
37
  animation.current = undefined;
@@ -5,7 +5,6 @@ import { LightboxStateAction } from "../contexts/index.js";
5
5
  import { ACTION_CLOSE, ACTION_NEXT, ACTION_PREV, ACTION_SWIPE, YARL_EVENT_BACKDROP_CLICK } from "../consts.js";
6
6
  export type NavigationAction = {
7
7
  count?: number;
8
- animationDuration?: number;
9
8
  };
10
9
  declare module "../" {
11
10
  interface EventTypes {
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { createModule } from "../config.js";
3
- import { cleanup, clsx, cssClass, cssVar, isNumber, makeComposePrefix, makeUseContext, parseLengthPercentage, } from "../utils.js";
3
+ import { cleanup, clsx, cssClass, cssVar, getAnimationEasing, getNavigationAnimationDuration, getSwipeAnimationDuration, isNumber, makeComposePrefix, makeUseContext, parseLengthPercentage, } from "../utils.js";
4
4
  import { useAnimation, useContainerRect, useDelay, useEventCallback, useForkRef, useRTL, useSensors, } from "../hooks/index.js";
5
5
  import { useEvents, useLightboxState } from "../contexts/index.js";
6
6
  import { SwipeState, usePointerSwipe, usePreventSwipeNavigation, useWheelSwipe } from "./controller/index.js";
@@ -48,16 +48,20 @@ export const Controller = ({ children, ...props }) => {
48
48
  { transform: "translateX(0)" },
49
49
  ],
50
50
  duration: state.animation.duration,
51
+ easing: state.animation.easing,
51
52
  };
52
53
  }
53
54
  return undefined;
54
55
  });
55
56
  const swipe = useEventCallback((action) => {
56
- var _a, _b;
57
- const swipeDuration = (_a = action.animationDuration) !== null && _a !== void 0 ? _a : animation.swipe;
57
+ var _a;
58
58
  const currentSwipeOffset = action.offset || 0;
59
+ const swipeDuration = !currentSwipeOffset
60
+ ? getNavigationAnimationDuration(animation)
61
+ : getSwipeAnimationDuration(animation);
62
+ const swipeEasing = getAnimationEasing(!currentSwipeOffset ? animation.navigation : animation.swipe);
59
63
  let { direction } = action;
60
- const count = (_b = action.count) !== null && _b !== void 0 ? _b : 1;
64
+ const count = (_a = action.count) !== null && _a !== void 0 ? _a : 1;
61
65
  let newSwipeState = SwipeState.ANIMATION;
62
66
  let newSwipeAnimationDuration = swipeDuration * count;
63
67
  if (!direction) {
@@ -75,13 +79,11 @@ export const Controller = ({ children, ...props }) => {
75
79
  newSwipeAnimationDuration =
76
80
  (swipeDuration / containerWidth) * (containerWidth - Math.abs(currentSwipeOffset));
77
81
  }
82
+ direction = rtl(currentSwipeOffset) > 0 ? ACTION_PREV : ACTION_NEXT;
78
83
  }
79
84
  else {
80
85
  newSwipeAnimationDuration = swipeDuration / 2;
81
86
  }
82
- if (count !== 0) {
83
- direction = rtl(currentSwipeOffset) > 0 ? ACTION_PREV : ACTION_NEXT;
84
- }
85
87
  }
86
88
  let increment = 0;
87
89
  if (direction === ACTION_PREV) {
@@ -114,7 +116,7 @@ export const Controller = ({ children, ...props }) => {
114
116
  });
115
117
  }
116
118
  setSwipeState(newSwipeState);
117
- publish(ACTION_SWIPE, { increment, duration: newSwipeAnimationDuration });
119
+ publish(ACTION_SWIPE, { increment, duration: newSwipeAnimationDuration, easing: swipeEasing });
118
120
  });
119
121
  React.useEffect(() => {
120
122
  var _a, _b;
@@ -126,7 +128,7 @@ export const Controller = ({ children, ...props }) => {
126
128
  subscribeSensors,
127
129
  isSwipeValid,
128
130
  (containerRect === null || containerRect === void 0 ? void 0 : containerRect.width) || 0,
129
- animation.swipe,
131
+ getSwipeAnimationDuration(animation),
130
132
  () => setSwipeState(SwipeState.SWIPE),
131
133
  (offset) => setSwipeOffset(offset),
132
134
  (offset, duration) => swipe({ offset, duration, count: 1 }),
@@ -1,23 +1,20 @@
1
1
  import * as React from "react";
2
2
  import { createModule } from "../config.js";
3
3
  import { useEventCallback, useLoseFocus, useRTL, useThrottle } from "../hooks/index.js";
4
- import { cssClass, label as translateLabel } from "../utils.js";
4
+ import { cssClass, label as translateLabel, getNavigationAnimationDuration } from "../utils.js";
5
5
  import { IconButton, NextIcon, PreviousIcon } from "../components/index.js";
6
6
  import { useEvents, useLightboxState } from "../contexts/index.js";
7
7
  import { useController } from "./Controller.js";
8
8
  import { ACTION_NEXT, ACTION_PREV, EVENT_ON_KEY_DOWN, MODULE_NAVIGATION, VK_ARROW_LEFT, VK_ARROW_RIGHT, } from "../consts.js";
9
9
  export const NavigationButton = ({ labels, label, icon, renderIcon, action, onClick, disabled, }) => (React.createElement(IconButton, { label: translateLabel(labels, label), icon: icon, renderIcon: renderIcon, className: cssClass(`navigation_${action}`), disabled: disabled, onClick: onClick, ...useLoseFocus(disabled) }));
10
- export const Navigation = ({ slides, carousel: { finite }, animation: { swipe, navigation }, labels, render: { buttonPrev, buttonNext, iconPrev, iconNext }, }) => {
10
+ export const Navigation = ({ slides, carousel: { finite }, animation, labels, render: { buttonPrev, buttonNext, iconPrev, iconNext }, }) => {
11
11
  const { currentIndex } = useLightboxState().state;
12
12
  const { subscribeSensors } = useController();
13
13
  const { publish } = useEvents();
14
14
  const isRTL = useRTL();
15
15
  const prevDisabled = slides.length === 0 || (finite && currentIndex === 0);
16
16
  const nextDisabled = slides.length === 0 || (finite && currentIndex === slides.length - 1);
17
- const navigate = (action) => {
18
- publish(action, { animationDuration: navigation });
19
- };
20
- const publishThrottled = useThrottle((action) => navigate(action), (navigation !== null && navigation !== void 0 ? navigation : swipe) / 2);
17
+ const publishThrottled = useThrottle((action) => publish(action), getNavigationAnimationDuration(animation) / 2);
21
18
  const handleKeyDown = useEventCallback((event) => {
22
19
  if (event.key === VK_ARROW_LEFT && !(isRTL ? nextDisabled : prevDisabled)) {
23
20
  publishThrottled(isRTL ? ACTION_NEXT : ACTION_PREV);
@@ -28,7 +25,7 @@ export const Navigation = ({ slides, carousel: { finite }, animation: { swipe, n
28
25
  });
29
26
  React.useEffect(() => subscribeSensors(EVENT_ON_KEY_DOWN, handleKeyDown), [subscribeSensors, handleKeyDown]);
30
27
  return (React.createElement(React.Fragment, null,
31
- buttonPrev ? (buttonPrev()) : (React.createElement(NavigationButton, { label: "Previous", action: ACTION_PREV, icon: PreviousIcon, renderIcon: iconPrev, disabled: prevDisabled, labels: labels, onClick: () => navigate(ACTION_PREV) })),
32
- buttonNext ? (buttonNext()) : (React.createElement(NavigationButton, { label: "Next", action: ACTION_NEXT, icon: NextIcon, renderIcon: iconNext, disabled: nextDisabled, labels: labels, onClick: () => navigate(ACTION_NEXT) }))));
28
+ buttonPrev ? (buttonPrev()) : (React.createElement(NavigationButton, { label: "Previous", action: ACTION_PREV, icon: PreviousIcon, renderIcon: iconPrev, disabled: prevDisabled, labels: labels, onClick: () => publish(ACTION_PREV) })),
29
+ buttonNext ? (buttonNext()) : (React.createElement(NavigationButton, { label: "Next", action: ACTION_NEXT, icon: NextIcon, renderIcon: iconNext, disabled: nextDisabled, labels: labels, onClick: () => publish(ACTION_NEXT) }))));
33
30
  };
34
31
  export const NavigationModule = createModule(MODULE_NAVIGATION, Navigation);
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  import * as ReactDOM from "react-dom";
3
3
  import { LightboxDefaultProps } from "../../props.js";
4
4
  import { createModule } from "../config.js";
5
- import { clsx, composePrefix, cssClass, cssVar } from "../utils.js";
5
+ import { clsx, composePrefix, cssClass, cssVar, getAnimationEasing, getFadeAnimationDuration } from "../utils.js";
6
6
  import { useEventCallback, useMotionPreference } from "../hooks/index.js";
7
7
  import { useEvents, useTimeouts } from "../contexts/index.js";
8
8
  import { ACTION_CLOSE, CLASS_NO_SCROLL_PADDING, MODULE_PORTAL } from "../consts.js";
@@ -25,7 +25,9 @@ export const Portal = ({ children, animation, styles, className, on, close }) =>
25
25
  const cleanup = React.useRef([]);
26
26
  const { setTimeout } = useTimeouts();
27
27
  const { subscribe } = useEvents();
28
- const fadeAnimationDuration = !useMotionPreference() ? animation.fade : 0;
28
+ const reduceMotion = useMotionPreference();
29
+ const animationDuration = !reduceMotion ? getFadeAnimationDuration(animation) : 0;
30
+ const animationEasing = !reduceMotion ? getAnimationEasing(animation.fade) : undefined;
29
31
  React.useEffect(() => {
30
32
  setMounted(true);
31
33
  return () => {
@@ -41,7 +43,7 @@ export const Portal = ({ children, animation, styles, className, on, close }) =>
41
43
  var _a;
42
44
  (_a = on.exited) === null || _a === void 0 ? void 0 : _a.call(on);
43
45
  close();
44
- }, fadeAnimationDuration);
46
+ }, animationDuration);
45
47
  });
46
48
  React.useEffect(() => subscribe(ACTION_CLOSE, handleClose), [subscribe, handleClose]);
47
49
  const handleEnter = useEventCallback((node) => {
@@ -60,7 +62,7 @@ export const Portal = ({ children, animation, styles, className, on, close }) =>
60
62
  setTimeout(() => {
61
63
  var _a;
62
64
  (_a = on.entered) === null || _a === void 0 ? void 0 : _a.call(on);
63
- }, fadeAnimationDuration);
65
+ }, animationDuration);
64
66
  });
65
67
  const handleExit = useEventCallback(() => {
66
68
  cleanup.current.forEach((clean) => clean());
@@ -77,10 +79,9 @@ export const Portal = ({ children, animation, styles, className, on, close }) =>
77
79
  return mounted
78
80
  ? ReactDOM.createPortal(React.createElement("div", { ref: handleRef, className: clsx(className, cssClass("root"), cssClass(cssPrefix()), cssClass(CLASS_NO_SCROLL_PADDING), visible && cssClass(cssPrefix("open"))), role: "presentation", "aria-live": "polite", style: {
79
81
  ...(animation.fade !== LightboxDefaultProps.animation.fade
80
- ? {
81
- [cssVar("fade_animation_duration")]: `${fadeAnimationDuration}ms`,
82
- }
82
+ ? { [cssVar("fade_animation_duration")]: `${animationDuration}ms` }
83
83
  : null),
84
+ ...(animationEasing ? { [cssVar("fade_animation_timing_function")]: animationEasing } : null),
84
85
  ...styles.root,
85
86
  } }, children), document.body)
86
87
  : null;
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { Labels, Slide, SlideImage } from "../types.js";
2
+ import { AnimationSettings, AnimationSpec, Labels, Slide, SlideImage } from "../types.js";
3
3
  export declare const clsx: (...classes: (string | boolean | undefined)[]) => string;
4
4
  export declare const cssClass: (name: string) => string;
5
5
  export declare const cssVar: (name: string) => string;
@@ -23,3 +23,8 @@ export declare const parseLengthPercentage: (input: unknown) => {
23
23
  export declare const devicePixelRatio: () => number;
24
24
  export declare const getSlideIndex: (index: number, slidesCount: number) => number;
25
25
  export declare const getSlide: (slides: Slide[], index: number) => Slide;
26
+ export declare const getAnimationEasing: (animationSpec: AnimationSpec | undefined) => string | undefined;
27
+ export declare const getAnimationDuration: (animationSpec: AnimationSpec | undefined, defaultDuration: number) => number;
28
+ export declare const getFadeAnimationDuration: (animation: AnimationSettings) => number;
29
+ export declare const getSwipeAnimationDuration: (animation: AnimationSettings) => number;
30
+ export declare const getNavigationAnimationDuration: (animation: AnimationSettings) => number;
@@ -1,4 +1,5 @@
1
1
  import * as React from "react";
2
+ import { AnimationDefaultProps } from "../props.js";
2
3
  export const clsx = (...classes) => [...classes].filter((cls) => Boolean(cls)).join(" ");
3
4
  const cssPrefix = "yarl__";
4
5
  export const cssClass = (name) => `${cssPrefix}${name}`;
@@ -41,3 +42,10 @@ export const devicePixelRatio = () => {
41
42
  };
42
43
  export const getSlideIndex = (index, slidesCount) => ((index % slidesCount) + slidesCount) % slidesCount;
43
44
  export const getSlide = (slides, index) => slides[getSlideIndex(index, slides.length)];
45
+ export const getAnimationEasing = (animationSpec) => typeof animationSpec === "object" ? animationSpec.easing : undefined;
46
+ export const getAnimationDuration = (animationSpec, defaultDuration) => { var _a; return (_a = (typeof animationSpec === "object" ? animationSpec.duration : animationSpec)) !== null && _a !== void 0 ? _a : defaultDuration; };
47
+ export const getFadeAnimationDuration = (animation) => getAnimationDuration(animation.fade, AnimationDefaultProps.fade);
48
+ export const getSwipeAnimationDuration = (animation) => getAnimationDuration(animation.swipe, AnimationDefaultProps.swipe);
49
+ export const getNavigationAnimationDuration = (animation) => {
50
+ return getAnimationDuration(animation.navigation, getSwipeAnimationDuration(animation));
51
+ };
@@ -7,10 +7,8 @@ export const Description = ({ description, descriptionTextAlign, descriptionMaxL
7
7
  ...(descriptionTextAlign !== defaultCaptionsProps.descriptionTextAlign ||
8
8
  descriptionMaxLines !== defaultCaptionsProps.descriptionMaxLines
9
9
  ? {
10
- style: {
11
- [cssVar("slide_description_text_align")]: descriptionTextAlign,
12
- [cssVar("slide_description_max_lines")]: descriptionMaxLines,
13
- },
10
+ [cssVar("slide_description_text_align")]: descriptionTextAlign,
11
+ [cssVar("slide_description_max_lines")]: descriptionMaxLines,
14
12
  }
15
13
  : null),
16
14
  ...styles.captionsDescription,
@@ -5,7 +5,7 @@ import { Thumbnail } from "./Thumbnail.js";
5
5
  import { defaultThumbnailsProps } from "./Thumbnails.js";
6
6
  const isHorizontal = (position) => ["top", "bottom"].includes(position);
7
7
  const boxSize = (thumbnails, dimension, includeGap) => dimension + 2 * (thumbnails.border + thumbnails.padding) + (includeGap ? thumbnails.gap : 0);
8
- export const ThumbnailsTrack = ({ container, slides, carousel, render, thumbnails, thumbnailRect, styles, animation: { navigation }, }) => {
8
+ export const ThumbnailsTrack = ({ container, slides, carousel, render, thumbnails, thumbnailRect, styles, }) => {
9
9
  const track = React.useRef(null);
10
10
  const { globalIndex, animation } = useLightboxState().state;
11
11
  const { publish, subscribe } = useEvents();
@@ -28,6 +28,7 @@ export const ThumbnailsTrack = ({ container, slides, carousel, render, thumbnail
28
28
  { transform: "translateY(0)" },
29
29
  ],
30
30
  duration: animationDuration,
31
+ easing: animation === null || animation === void 0 ? void 0 : animation.easing,
31
32
  }));
32
33
  const handleControllerSwipe = useEventCallback(() => {
33
34
  let animationOffset;
@@ -78,10 +79,10 @@ export const ThumbnailsTrack = ({ container, slides, carousel, render, thumbnail
78
79
  }
79
80
  const handleClick = (slideIndex) => () => {
80
81
  if (slideIndex > index) {
81
- publish(ACTION_NEXT, { count: slideIndex - index, animationDuration: navigation });
82
+ publish(ACTION_NEXT, { count: slideIndex - index });
82
83
  }
83
84
  else if (slideIndex < index) {
84
- publish(ACTION_PREV, { count: index - slideIndex, animationDuration: navigation });
85
+ publish(ACTION_PREV, { count: index - slideIndex });
85
86
  }
86
87
  };
87
88
  const { width, height, border, borderRadius, padding, gap, imageFit, vignette } = thumbnails;
@@ -96,12 +96,10 @@ export const ZoomContainer = ({ slide, offset, rect, render, carousel, animation
96
96
  if (zoomAnimationStart.current && containerRef.current) {
97
97
  zoomAnimation.current = (_c = (_b = containerRef.current).animate) === null || _c === void 0 ? void 0 : _c.call(_b, [
98
98
  { transform: zoomAnimationStart.current },
99
- {
100
- transform: `scale(${zoom}) translateX(${offsetX}px) translateY(${offsetY}px)`,
101
- },
99
+ { transform: `scale(${zoom}) translateX(${offsetX}px) translateY(${offsetY}px)` },
102
100
  ], {
103
- duration: reduceMotion ? 0 : (_d = animation.zoom) !== null && _d !== void 0 ? _d : 500,
104
- easing: zoomAnimation ? "ease-out" : "ease-in-out",
101
+ duration: !reduceMotion ? (_d = animation.zoom) !== null && _d !== void 0 ? _d : 500 : 0,
102
+ easing: zoomAnimation.current ? "ease-out" : "ease-in-out",
105
103
  });
106
104
  zoomAnimationStart.current = undefined;
107
105
  if (zoomAnimation.current) {
package/dist/props.d.ts CHANGED
@@ -1,2 +1,6 @@
1
1
  import { LightboxProps } from "./types.js";
2
+ export declare const AnimationDefaultProps: {
3
+ fade: number;
4
+ swipe: number;
5
+ };
2
6
  export declare const LightboxDefaultProps: LightboxProps;
package/dist/props.js CHANGED
@@ -1,4 +1,8 @@
1
1
  import { ACTION_CLOSE, IMAGE_FIT_CONTAIN } from "./core/consts.js";
2
+ export const AnimationDefaultProps = {
3
+ fade: 330,
4
+ swipe: 500,
5
+ };
2
6
  export const LightboxDefaultProps = {
3
7
  open: false,
4
8
  close: () => { },
@@ -8,10 +12,7 @@ export const LightboxDefaultProps = {
8
12
  plugins: [],
9
13
  toolbar: { buttons: [ACTION_CLOSE] },
10
14
  labels: {},
11
- animation: {
12
- fade: 330,
13
- swipe: 500,
14
- },
15
+ animation: AnimationDefaultProps,
15
16
  carousel: {
16
17
  finite: false,
17
18
  preload: 2,
package/dist/types.d.ts CHANGED
@@ -78,13 +78,22 @@ export interface CarouselSettings {
78
78
  }
79
79
  /** Animation settings */
80
80
  export interface AnimationSettings {
81
- /** fade-in / fade-out animation duration */
82
- fade: number;
83
- /** swipe animation duration */
84
- swipe: number;
85
- /** override for `swipe` animation duration when using keyboard navigation or navigation buttons */
86
- navigation?: number;
81
+ /** fade-in / fade-out animation settings */
82
+ fade: AnimationSpec;
83
+ /** swipe animation settings */
84
+ swipe: AnimationSpec;
85
+ /** override for `swipe` animation settings when using keyboard navigation or navigation buttons */
86
+ navigation?: AnimationSpec;
87
87
  }
88
+ /** Animation duration or animation settings */
89
+ export type AnimationSpec = {
90
+ /** animation duration */
91
+ duration?: number;
92
+ /** animation easing function */
93
+ easing?: string;
94
+ }
95
+ /** animation duration */
96
+ | number;
88
97
  /** Controller settings */
89
98
  export interface ControllerSettings {
90
99
  /** if true, the lightbox captures focus when it opens */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yet-another-react-lightbox",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "Modern React lightbox component",
5
5
  "author": "Igor Danchenko",
6
6
  "license": "MIT",