yet-another-react-lightbox 1.9.5 → 1.11.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.
@@ -7,5 +7,6 @@ export declare type ImageSlideProps = {
7
7
  render?: Render;
8
8
  rect?: ContainerRect;
9
9
  imageFit?: ImageFit;
10
+ onClick?: () => void;
10
11
  };
11
- export declare const ImageSlide: ({ slide: image, offset, render, rect, imageFit }: ImageSlideProps) => JSX.Element;
12
+ export declare const ImageSlide: ({ slide: image, offset, render, rect, imageFit, onClick }: ImageSlideProps) => JSX.Element;
@@ -4,7 +4,7 @@ import { useLatest } from "../hooks/index.js";
4
4
  import { useEvents } from "../contexts/index.js";
5
5
  import { ErrorIcon, LoadingIcon } from "./Icons.js";
6
6
  import { activeSlideStatus, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, } from "../consts.js";
7
- export const ImageSlide = ({ slide: image, offset, render, rect, imageFit }) => {
7
+ export const ImageSlide = ({ slide: image, offset, render, rect, imageFit, onClick }) => {
8
8
  var _a, _b, _c, _d, _e, _f, _g;
9
9
  const [status, setStatus] = React.useState(SLIDE_STATUS_LOADING);
10
10
  const latestStatus = useLatest(status);
@@ -61,7 +61,7 @@ export const ImageSlide = ({ slide: image, offset, render, rect, imageFit }) =>
61
61
  ? `${Math.ceil((Math.min(estimateActualWidth(), rect.width) / window.innerWidth) * 100)}vw`
62
62
  : undefined;
63
63
  return (React.createElement(React.Fragment, null,
64
- React.createElement("img", { ref: setImageRef, onLoad: onLoad, onError: onError, className: clsx(cssClass("slide_image"), cssClass("fullsize"), cover && cssClass("slide_image_cover"), status !== SLIDE_STATUS_COMPLETE && cssClass("slide_image_loading")), draggable: false, alt: image.alt, style: style, sizes: sizes, srcSet: srcSet, src: image.src }),
64
+ React.createElement("img", { ref: setImageRef, onLoad: onLoad, onError: onError, onClick: onClick, className: clsx(cssClass("slide_image"), cssClass("fullsize"), cover && cssClass("slide_image_cover"), status !== SLIDE_STATUS_COMPLETE && cssClass("slide_image_loading")), draggable: false, alt: image.alt, style: style, sizes: sizes, srcSet: srcSet, src: image.src }),
65
65
  status !== SLIDE_STATUS_COMPLETE && (React.createElement("div", { className: cssClass("slide_placeholder") },
66
66
  status === SLIDE_STATUS_LOADING &&
67
67
  ((render === null || render === void 0 ? void 0 : render.iconLoading) ? (render.iconLoading()) : (React.createElement(LoadingIcon, { className: clsx(cssClass("icon"), cssClass("slide_loading")) }))),
@@ -1,5 +1,5 @@
1
1
  export * from "./useContainerRect.js";
2
- export * from "./useEnhancedEffect.js";
2
+ export * from "./useLayoutEffect.js";
3
3
  export * from "./useLatest.js";
4
4
  export * from "./useMotionPreference.js";
5
5
  export * from "./useRTL.js";
@@ -1,5 +1,5 @@
1
1
  export * from "./useContainerRect.js";
2
- export * from "./useEnhancedEffect.js";
2
+ export * from "./useLayoutEffect.js";
3
3
  export * from "./useLatest.js";
4
4
  export * from "./useMotionPreference.js";
5
5
  export * from "./useRTL.js";
@@ -1,2 +1,3 @@
1
1
  import * as React from "react";
2
+ /** @deprecated migrate to useEventCallback */
2
3
  export declare const useLatest: <T>(value: T) => React.MutableRefObject<T>;
@@ -0,0 +1,2 @@
1
+ import * as React from "react";
2
+ export declare const useLayoutEffect: typeof React.useLayoutEffect;
@@ -0,0 +1,3 @@
1
+ import * as React from "react";
2
+ import { hasWindow } from "../utils.js";
3
+ export const useLayoutEffect = hasWindow() ? React.useLayoutEffect : React.useEffect;
@@ -1,12 +1,14 @@
1
1
  import * as React from "react";
2
- import { useEnhancedEffect } from "./useEnhancedEffect.js";
2
+ import { useLayoutEffect } from "./useLayoutEffect.js";
3
3
  export const useMotionPreference = () => {
4
4
  const [reduceMotion, setReduceMotion] = React.useState(false);
5
- useEnhancedEffect(() => {
5
+ useLayoutEffect(() => {
6
6
  var _a;
7
7
  const mediaQuery = (_a = window.matchMedia) === null || _a === void 0 ? void 0 : _a.call(window, "(prefers-reduced-motion: reduce)");
8
- mediaQuery === null || mediaQuery === void 0 ? void 0 : mediaQuery.addEventListener("change", () => setReduceMotion(mediaQuery.matches));
9
8
  setReduceMotion(mediaQuery === null || mediaQuery === void 0 ? void 0 : mediaQuery.matches);
9
+ const listener = () => setReduceMotion(mediaQuery.matches);
10
+ mediaQuery === null || mediaQuery === void 0 ? void 0 : mediaQuery.addEventListener("change", listener);
11
+ return () => mediaQuery === null || mediaQuery === void 0 ? void 0 : mediaQuery.removeEventListener("change", listener);
10
12
  }, []);
11
13
  return reduceMotion;
12
14
  };
@@ -1,8 +1,8 @@
1
1
  import * as React from "react";
2
- import { useEnhancedEffect } from "./useEnhancedEffect.js";
2
+ import { useLayoutEffect } from "./useLayoutEffect.js";
3
3
  export const useRTL = () => {
4
4
  const [isRTL, setIsRTL] = React.useState(false);
5
- useEnhancedEffect(() => {
5
+ useLayoutEffect(() => {
6
6
  setIsRTL(window.getComputedStyle(window.document.documentElement).direction === "rtl");
7
7
  }, []);
8
8
  return isRTL;
@@ -7,13 +7,18 @@ import { ImageSlide } from "../components/index.js";
7
7
  import { useController } from "./Controller.js";
8
8
  const CarouselSlide = ({ slide, offset }) => {
9
9
  const { setContainerRef, containerRect } = useContainerRect();
10
- const { latestProps } = useController();
10
+ const { latestProps, currentIndex } = useController();
11
11
  const { render } = latestProps.current;
12
12
  const renderSlide = (rect) => {
13
13
  var _a, _b, _c, _d;
14
14
  let rendered = (_a = render.slide) === null || _a === void 0 ? void 0 : _a.call(render, slide, offset, rect);
15
15
  if (!rendered && "src" in slide) {
16
- rendered = (React.createElement(ImageSlide, { slide: slide, offset: offset, render: render, rect: rect, imageFit: latestProps.current.carousel.imageFit }));
16
+ rendered = (React.createElement(ImageSlide, { slide: slide, offset: offset, render: render, rect: rect, imageFit: latestProps.current.carousel.imageFit, onClick: latestProps.current.on.click && offset === 0
17
+ ? () => {
18
+ var _a, _b;
19
+ (_b = (_a = latestProps.current.on).click) === null || _b === void 0 ? void 0 : _b.call(_a, currentIndex);
20
+ }
21
+ : undefined }));
17
22
  }
18
23
  return rendered ? (React.createElement(React.Fragment, null, (_b = render.slideHeader) === null || _b === void 0 ? void 0 :
19
24
  _b.call(render, slide),
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  import { LightboxDefaultProps } from "../../types.js";
3
3
  import { cleanup, clsx, cssClass, cssVar, makeUseContext } from "../utils.js";
4
4
  import { createModule } from "../config.js";
5
- import { useContainerRect, useEnhancedEffect, useLatest, useRTL, useSensors, } from "../hooks/index.js";
5
+ import { useContainerRect, useLatest, useLayoutEffect, useRTL, useSensors, } from "../hooks/index.js";
6
6
  import { useEvents, useTimeouts } from "../contexts/index.js";
7
7
  const SWIPE_OFFSET_THRESHOLD = 30;
8
8
  const ControllerContext = React.createContext(null);
@@ -29,7 +29,7 @@ export const Controller = ({ children, ...props }) => {
29
29
  });
30
30
  refs.current.state = state;
31
31
  refs.current.props = props;
32
- useEnhancedEffect(() => {
32
+ useLayoutEffect(() => {
33
33
  const preventDefault = (event) => {
34
34
  if (Math.abs(event.deltaX) > Math.abs(event.deltaY) || event.ctrlKey) {
35
35
  event.preventDefault();
@@ -65,7 +65,7 @@ export const Controller = ({ children, ...props }) => {
65
65
  (_b = containerRef.current) === null || _b === void 0 ? void 0 : _b.style.removeProperty(offsetVar);
66
66
  }
67
67
  }, [containerRef]);
68
- useEnhancedEffect(() => {
68
+ useLayoutEffect(() => {
69
69
  updateSwipeOffset();
70
70
  });
71
71
  const rerender = React.useCallback(() => {
@@ -298,6 +298,6 @@ export const Controller = ({ children, ...props }) => {
298
298
  [cssVar("controller_touch_action")]: props.controller.touchAction,
299
299
  }
300
300
  : null),
301
- }, role: "presentation", "aria-live": "polite", tabIndex: -1, ...registerSensors }, containerRect && (React.createElement(ControllerContext.Provider, { value: context }, children))));
301
+ }, ...(props.controller.aria ? { role: "presentation", "aria-live": "polite" } : null), tabIndex: -1, ...registerSensors }, containerRect && (React.createElement(ControllerContext.Provider, { value: context }, children))));
302
302
  };
303
303
  export const ControllerModule = createModule("controller", Controller);
@@ -1,22 +1,49 @@
1
1
  import * as React from "react";
2
2
  import { createModule } from "../config.js";
3
- import { cssClass, cssVar } from "../utils.js";
3
+ import { cssClass } from "../utils.js";
4
+ import { useLayoutEffect, useRTL } from "../hooks/index.js";
4
5
  const noScroll = cssClass("no_scroll");
5
- const padScrollbar = cssClass("pad_scrollbar");
6
- const scrollbarPadding = cssVar("scrollbar_padding");
6
+ const noScrollPadding = cssClass("no_scroll_padding");
7
+ const isHTMLElement = (element) => "style" in element;
8
+ const padScrollbar = (element, padding, rtl) => {
9
+ const styles = window.getComputedStyle(element);
10
+ const property = rtl ? "padding-left" : "padding-right";
11
+ const computedValue = rtl ? styles.paddingLeft : styles.paddingRight;
12
+ const originalValue = element.style.getPropertyValue(property);
13
+ element.style.setProperty(property, `${(parseInt(computedValue, 10) || 0) + padding}px`);
14
+ return () => {
15
+ if (originalValue) {
16
+ element.style.setProperty(property, originalValue);
17
+ }
18
+ else {
19
+ element.style.removeProperty(property);
20
+ }
21
+ };
22
+ };
7
23
  export const NoScroll = ({ children }) => {
8
- React.useEffect(() => {
9
- const scrollbarWidth = Math.round(window.innerWidth - document.documentElement.clientWidth);
10
- if (scrollbarWidth > 1) {
11
- document.body.style.setProperty(scrollbarPadding, `${scrollbarWidth}px`);
12
- document.body.classList.add(padScrollbar);
24
+ const rtl = useRTL();
25
+ useLayoutEffect(() => {
26
+ const cleanup = [];
27
+ const { body, documentElement } = document;
28
+ const scrollbar = Math.round(window.innerWidth - documentElement.clientWidth);
29
+ if (scrollbar > 0) {
30
+ cleanup.push(padScrollbar(body, scrollbar, rtl));
31
+ const elements = body.getElementsByTagName("*");
32
+ for (let i = 0; i < elements.length; i += 1) {
33
+ const element = elements[i];
34
+ if (isHTMLElement(element) &&
35
+ window.getComputedStyle(element).getPropertyValue("position") === "fixed" &&
36
+ !element.classList.contains(noScrollPadding)) {
37
+ cleanup.push(padScrollbar(element, scrollbar, rtl));
38
+ }
39
+ }
13
40
  }
14
- document.body.classList.add(noScroll);
41
+ body.classList.add(noScroll);
15
42
  return () => {
16
- document.body.style.removeProperty(scrollbarPadding);
17
- document.body.classList.remove(noScroll, padScrollbar);
43
+ body.classList.remove(noScroll);
44
+ cleanup.forEach((clean) => clean());
18
45
  };
19
- });
46
+ }, [rtl]);
20
47
  return React.createElement(React.Fragment, null, children);
21
48
  };
22
49
  export const NoScrollModule = createModule("no-scroll", NoScroll);
@@ -2,50 +2,77 @@ import * as React from "react";
2
2
  import * as ReactDOM from "react-dom";
3
3
  import { LightboxDefaultProps } from "../../types.js";
4
4
  import { createModule } from "../config.js";
5
- import { cssClass, cssVar } from "../utils.js";
6
- import { useLatest } from "../hooks/useLatest.js";
5
+ import { clsx, cssClass, cssVar } from "../utils.js";
6
+ import { useLatest, useMotionPreference } from "../hooks/index.js";
7
7
  import { useEvents, useTimeouts } from "../contexts/index.js";
8
+ const setAttribute = (element, attribute, value) => {
9
+ const previousValue = element.getAttribute(attribute);
10
+ element.setAttribute(attribute, value);
11
+ return () => {
12
+ if (previousValue) {
13
+ element.setAttribute(attribute, previousValue);
14
+ }
15
+ else {
16
+ element.removeAttribute(attribute);
17
+ }
18
+ };
19
+ };
8
20
  export const Portal = ({ children, ...props }) => {
9
21
  const [mounted, setMounted] = React.useState(false);
22
+ const [visible, setVisible] = React.useState(false);
23
+ const cleanup = React.useRef([]);
10
24
  const latestProps = useLatest(props);
25
+ const latestAnimationDuration = useLatest(!useMotionPreference() ? props.animation.fade : 0);
11
26
  const { setTimeout } = useTimeouts();
12
27
  const { subscribe } = useEvents();
13
- const [portal] = React.useState(() => {
14
- const div = document.createElement("div");
15
- div.className = cssClass("portal");
16
- const fadeAnimation = latestProps.current.animation.fade;
17
- if (fadeAnimation !== LightboxDefaultProps.animation.fade) {
18
- div.style.setProperty(cssVar("fade_animation_duration"), `${Math.round(fadeAnimation)}ms`);
19
- }
20
- return div;
21
- });
28
+ React.useEffect(() => {
29
+ setMounted(true);
30
+ return () => {
31
+ setMounted(false);
32
+ };
33
+ }, []);
22
34
  React.useEffect(() => subscribe("close", () => {
23
35
  var _a, _b;
36
+ setVisible(false);
24
37
  (_b = (_a = latestProps.current.on).exiting) === null || _b === void 0 ? void 0 : _b.call(_a);
25
- portal.classList.remove(cssClass("portal_open"));
26
38
  setTimeout(() => {
27
39
  var _a, _b;
28
40
  (_b = (_a = latestProps.current.on).exited) === null || _b === void 0 ? void 0 : _b.call(_a);
29
41
  latestProps.current.close();
30
- }, latestProps.current.animation.fade);
31
- }), [portal, setTimeout, subscribe, latestProps]);
32
- React.useEffect(() => {
33
- var _a, _b;
34
- document.body.appendChild(portal);
35
- setMounted(true);
36
- (_b = (_a = latestProps.current.on).entering) === null || _b === void 0 ? void 0 : _b.call(_a);
37
- setTimeout(() => {
38
- portal.classList.add(cssClass("portal_open"));
39
- }, 0);
40
- setTimeout(() => {
41
- var _a, _b;
42
- (_b = (_a = latestProps.current.on).entered) === null || _b === void 0 ? void 0 : _b.call(_a);
43
- }, latestProps.current.animation.fade);
44
- return () => {
45
- document.body.removeChild(portal);
46
- setMounted(false);
47
- };
48
- }, [portal, setTimeout, latestProps]);
49
- return ReactDOM.createPortal(mounted ? children : null, portal);
42
+ }, latestAnimationDuration.current);
43
+ }), [setTimeout, subscribe, latestProps, latestAnimationDuration]);
44
+ const handlePortalRef = React.useCallback((node) => {
45
+ var _a, _b, _c, _d;
46
+ if (node) {
47
+ node.getBoundingClientRect();
48
+ setVisible(true);
49
+ (_b = (_a = latestProps.current.on).entering) === null || _b === void 0 ? void 0 : _b.call(_a);
50
+ const elements = (_d = (_c = node.parentNode) === null || _c === void 0 ? void 0 : _c.children) !== null && _d !== void 0 ? _d : [];
51
+ for (let i = 0; i < elements.length; i += 1) {
52
+ const element = elements[i];
53
+ if (["TEMPLATE", "SCRIPT", "STYLE"].indexOf(element.tagName) === -1 && element !== node) {
54
+ cleanup.current.push(setAttribute(element, "inert", "true"));
55
+ cleanup.current.push(setAttribute(element, "aria-hidden", "true"));
56
+ }
57
+ }
58
+ setTimeout(() => {
59
+ var _a, _b;
60
+ (_b = (_a = latestProps.current.on).entered) === null || _b === void 0 ? void 0 : _b.call(_a);
61
+ }, latestAnimationDuration.current);
62
+ }
63
+ else {
64
+ cleanup.current.forEach((clean) => clean());
65
+ cleanup.current = [];
66
+ }
67
+ }, [setTimeout, latestProps, latestAnimationDuration]);
68
+ return mounted
69
+ ? ReactDOM.createPortal(React.createElement("div", { ref: handlePortalRef, className: clsx(cssClass("portal"), cssClass("no_scroll_padding"), visible && cssClass("portal_open")), role: "presentation", "aria-live": "polite", ...(props.animation.fade !== LightboxDefaultProps.animation.fade
70
+ ? {
71
+ style: {
72
+ [cssVar("fade_animation_duration")]: `${Math.round(props.animation.fade)}ms`,
73
+ },
74
+ }
75
+ : null) }, children), document.body)
76
+ : null;
50
77
  };
51
78
  export const PortalModule = createModule("portal", Portal);
@@ -3,7 +3,7 @@ import { createModule } from "../core/index.js";
3
3
  export const InlineContainer = ({ inline, children }) => React.createElement("div", { ...inline }, children);
4
4
  export const InlineModule = createModule("inline", InlineContainer);
5
5
  export const Inline = ({ augment, replace, remove }) => {
6
- augment(({ toolbar: { buttons, ...restToolbar }, open, close, controller: { focus, touchAction, ...restController }, ...restProps }) => ({
6
+ augment(({ toolbar: { buttons, ...restToolbar }, open, close, controller: { focus, aria, touchAction, ...restController }, ...restProps }) => ({
7
7
  open: true,
8
8
  close: () => { },
9
9
  toolbar: {
@@ -11,7 +11,7 @@ export const Inline = ({ augment, replace, remove }) => {
11
11
  ...restToolbar,
12
12
  },
13
13
  inline: { style: { width: "100%", height: "100%" } },
14
- controller: { focus: false, touchAction: "pan-y", ...restController },
14
+ controller: { focus: false, aria: true, touchAction: "pan-y", ...restController },
15
15
  ...restProps,
16
16
  }));
17
17
  remove("no-scroll");
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { clsx, createIcon, createModule, cssClass, cssVar, ImageSlide, useEnhancedEffect, useEvents, useLatest, useMotionPreference, useRTL, } from "../core/index.js";
2
+ import { clsx, createIcon, createModule, cssClass, cssVar, ImageSlide, useLayoutEffect, useEvents, useLatest, useMotionPreference, useRTL, } from "../core/index.js";
3
3
  const defaultThumbnailsProps = {
4
4
  position: "bottom",
5
5
  width: 120,
@@ -95,7 +95,7 @@ export const ThumbnailsTrack = ({ container, startingIndex, slides, carousel, an
95
95
  });
96
96
  }
97
97
  }), [container, subscribe]);
98
- useEnhancedEffect(() => {
98
+ useLayoutEffect(() => {
99
99
  var _a, _b, _c, _d;
100
100
  if (track.current && state.offset) {
101
101
  const { current } = refs;
@@ -39,9 +39,12 @@ export const VideoSlide = ({ slide, offset }) => {
39
39
  if (!width || !height || !containerRect)
40
40
  return null;
41
41
  const widthBound = width / height > containerRect.width / containerRect.height;
42
+ const elementWidth = widthBound ? containerRect.width : Math.round((containerRect.height / height) * width);
43
+ const elementHeight = !widthBound ? containerRect.height : Math.round((containerRect.width / width) * height);
42
44
  return {
43
- width: widthBound ? containerRect.width : Math.round((containerRect.height / height) * width),
44
- height: !widthBound ? containerRect.height : Math.round((containerRect.width / width) * height),
45
+ width: elementWidth,
46
+ height: elementHeight,
47
+ style: { width: elementWidth, height: elementHeight, maxWidth: "100%", maxHeight: "100%" },
45
48
  };
46
49
  };
47
50
  const resolveBoolean = (attr) => {
@@ -28,6 +28,8 @@ declare module "../types.js" {
28
28
  wheelZoomDistanceFactor?: number;
29
29
  /** pinch zoom distance factor */
30
30
  pinchZoomDistanceFactor?: number;
31
+ /** if `true`, enables image zoom via scroll gestures for mouse and trackpad users */
32
+ scrollToZoom?: boolean;
31
33
  };
32
34
  }
33
35
  interface AnimationSettings {
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { cleanup, clsx, createIcon, createModule, cssClass, IconButton, ImageSlide, label, makeUseContext, round, useContainerRect, useController, useEnhancedEffect, useEvents, useMotionPreference, } from "../core/index.js";
2
+ import { cleanup, clsx, createIcon, createModule, cssClass, IconButton, ImageSlide, label, makeUseContext, round, useContainerRect, useController, useEvents, useLayoutEffect, useMotionPreference, } from "../core/index.js";
3
3
  const defaultZoomProps = {
4
4
  maxZoomPixelRatio: 1,
5
5
  zoomInMultiplier: 2,
@@ -9,6 +9,7 @@ const defaultZoomProps = {
9
9
  keyboardMoveDistance: 50,
10
10
  wheelZoomDistanceFactor: 100,
11
11
  pinchZoomDistanceFactor: 100,
12
+ scrollToZoom: false,
12
13
  };
13
14
  const ZoomInIcon = createIcon("ZoomIn", React.createElement(React.Fragment, null,
14
15
  React.createElement("path", { d: "M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" }),
@@ -17,25 +18,25 @@ const ZoomOutIcon = createIcon("ZoomOut", React.createElement("path", { d: "M15.
17
18
  const ZoomContext = React.createContext(null);
18
19
  const useZoom = makeUseContext("useZoom", "ZoomContext", ZoomContext);
19
20
  const ZoomContextProvider = ({ children }) => {
20
- const [minZoom, setMinZoom] = React.useState(false);
21
- const [maxZoom, setMaxZoom] = React.useState(false);
22
- const [zoomSupported, setZoomSupported] = React.useState(false);
21
+ const [isMinZoom, setIsMinZoom] = React.useState(false);
22
+ const [isMaxZoom, setIsMaxZoom] = React.useState(false);
23
+ const [isZoomSupported, setIsZoomSupported] = React.useState(false);
23
24
  const context = React.useMemo(() => ({
24
- minZoom,
25
- maxZoom,
26
- zoomSupported,
27
- setMinZoom,
28
- setMaxZoom,
29
- setZoomSupported,
30
- }), [minZoom, maxZoom, zoomSupported]);
25
+ isMinZoom,
26
+ isMaxZoom,
27
+ isZoomSupported,
28
+ setIsMinZoom,
29
+ setIsMaxZoom,
30
+ setIsZoomSupported,
31
+ }), [isMinZoom, isMaxZoom, isZoomSupported]);
31
32
  return React.createElement(ZoomContext.Provider, { value: context }, children);
32
33
  };
33
34
  const ZoomButton = React.forwardRef(({ labels, render, zoomIn, onLoseFocus }, ref) => {
34
35
  const wasEnabled = React.useRef(false);
35
36
  const wasFocused = React.useRef(false);
36
- const { zoomSupported, minZoom, maxZoom } = useZoom();
37
+ const { isMinZoom, isMaxZoom, isZoomSupported } = useZoom();
37
38
  const { publish } = useEvents();
38
- const disabled = !zoomSupported || (zoomIn ? maxZoom : minZoom);
39
+ const disabled = !isZoomSupported || (zoomIn ? isMaxZoom : isMinZoom);
39
40
  const onClick = () => publish(zoomIn ? "zoom-in" : "zoom-out");
40
41
  const onFocus = React.useCallback(() => {
41
42
  wasFocused.current = true;
@@ -126,7 +127,7 @@ const distance = (pointerA, pointerB) => ((pointerA.clientX - pointerB.clientX)
126
127
  const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom: originalZoomProps }) => {
127
128
  var _a;
128
129
  const zoomProps = { ...defaultZoomProps, ...originalZoomProps };
129
- const { setMinZoom: currentSetMinZoom, setMaxZoom: currentSetMaxZoom } = useZoom();
130
+ const { isMinZoom, isMaxZoom, setIsMinZoom, setIsMaxZoom } = useZoom();
130
131
  const { setContainerRef, containerRef: currentContainerRef, containerRect: currentContainerRect, } = useContainerRect();
131
132
  const { subscribeSensors, containerRef: currentControllerRef, containerRect: currentControllerRect, } = useController();
132
133
  const { subscribe } = useEvents();
@@ -145,8 +146,6 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
145
146
  controllerRect: currentControllerRect,
146
147
  maxZoom: currentMaxZoom,
147
148
  reduceMotion: currentReduceMotion,
148
- setMinZoom: currentSetMinZoom,
149
- setMaxZoom: currentSetMaxZoom,
150
149
  activePointers: [],
151
150
  lastPointerDown: 0,
152
151
  zoomProps,
@@ -159,8 +158,6 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
159
158
  refs.current.controllerRect = currentControllerRect;
160
159
  refs.current.maxZoom = currentMaxZoom;
161
160
  refs.current.reduceMotion = currentReduceMotion;
162
- refs.current.setMinZoom = currentSetMinZoom;
163
- refs.current.setMaxZoom = currentSetMaxZoom;
164
161
  refs.current.zoomAnimationDuration = animation.zoom;
165
162
  refs.current.zoomProps = zoomProps;
166
163
  const changeOffsets = React.useCallback((dx, dy, newZoom) => {
@@ -176,12 +173,12 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
176
173
  offsetY: Math.min(Math.abs(newOffsetY), Math.max(maxOffsetY, 0)) * Math.sign(newOffsetY),
177
174
  }));
178
175
  }, []);
179
- useEnhancedEffect(() => {
176
+ useLayoutEffect(() => {
180
177
  if (refs.current.state.zoom > 1) {
181
178
  changeOffsets();
182
179
  }
183
180
  }, [currentControllerRect.width, currentControllerRect.height, changeOffsets]);
184
- useEnhancedEffect(() => {
181
+ useLayoutEffect(() => {
185
182
  var _a, _b;
186
183
  const { current } = refs;
187
184
  const { zoomAnimation, zoomAnimationStart, zoomAnimationDuration, reduceMotion, containerRef } = current;
@@ -204,13 +201,12 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
204
201
  }
205
202
  }
206
203
  }, [state.zoom, state.offsetX, state.offsetY]);
207
- useEnhancedEffect(() => {
204
+ useLayoutEffect(() => {
208
205
  if (offset === 0) {
209
- const { setMinZoom, setMaxZoom } = refs.current;
210
206
  const resetZoom = () => {
211
207
  setState({ zoom: 1, offsetX: 0, offsetY: 0 });
212
- setMinZoom(true);
213
- setMaxZoom(false);
208
+ setIsMinZoom(true);
209
+ setIsMaxZoom(false);
214
210
  };
215
211
  resetZoom();
216
212
  return () => {
@@ -218,14 +214,19 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
218
214
  };
219
215
  }
220
216
  return () => { };
221
- }, [offset]);
222
- useEnhancedEffect(() => {
217
+ }, [offset, setIsMinZoom, setIsMaxZoom]);
218
+ useLayoutEffect(() => {
223
219
  if (offset === 0) {
224
- const { setMinZoom, setMaxZoom } = refs.current;
225
- setMinZoom(state.zoom <= 1);
226
- setMaxZoom(state.zoom >= currentMaxZoom);
220
+ const newMinZoom = state.zoom <= 1;
221
+ if (newMinZoom !== isMinZoom) {
222
+ setIsMinZoom(newMinZoom);
223
+ }
224
+ const newMaxZoom = state.zoom >= currentMaxZoom;
225
+ if (newMaxZoom !== isMaxZoom) {
226
+ setIsMaxZoom(newMaxZoom);
227
+ }
227
228
  }
228
- }, [offset, state.zoom, currentMaxZoom]);
229
+ }, [offset, state.zoom, currentMaxZoom, isMinZoom, isMaxZoom, setIsMinZoom, setIsMaxZoom]);
229
230
  const changeZoom = React.useCallback((value, rapid, dx, dy) => {
230
231
  const { current } = refs;
231
232
  const { state: { zoom }, containerRef, containerRect, maxZoom, } = current;
@@ -281,17 +282,19 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
281
282
  }
282
283
  }, [changeZoom, changeOffsets]);
283
284
  const onWheel = React.useCallback((event) => {
284
- const { state: { zoom }, zoomProps: { wheelZoomDistanceFactor }, } = refs.current;
285
- if (event.ctrlKey) {
286
- event.stopPropagation();
285
+ const { state: { zoom }, zoomProps: { wheelZoomDistanceFactor, scrollToZoom }, } = refs.current;
286
+ if (event.ctrlKey || scrollToZoom) {
287
287
  if (Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
288
+ event.stopPropagation();
288
289
  changeZoom(zoom * (1 - event.deltaY / wheelZoomDistanceFactor), true, ...translateCoordinates(event));
290
+ return;
289
291
  }
290
- return;
291
292
  }
292
293
  if (zoom > 1) {
293
294
  event.stopPropagation();
294
- changeOffsets(event.deltaX, event.deltaY);
295
+ if (!scrollToZoom) {
296
+ changeOffsets(event.deltaX, event.deltaY);
297
+ }
295
298
  }
296
299
  }, [changeZoom, changeOffsets, translateCoordinates]);
297
300
  const clearPointer = React.useCallback((event) => {
@@ -381,14 +384,14 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
381
384
  };
382
385
  const ZoomWrapper = ({ slide, offset, rect, render, carousel, animation, zoom }) => {
383
386
  var _a;
384
- const { setZoomSupported } = useZoom();
387
+ const { setIsZoomSupported, isZoomSupported } = useZoom();
385
388
  const imageSlide = !("type" in slide);
386
389
  const zoomSupported = imageSlide && ("srcSet" in slide || ("width" in slide && "height" in slide));
387
390
  React.useEffect(() => {
388
- if (offset === 0) {
389
- setZoomSupported(zoomSupported);
391
+ if (offset === 0 && zoomSupported !== isZoomSupported) {
392
+ setIsZoomSupported(zoomSupported);
390
393
  }
391
- }, [offset, zoomSupported, setZoomSupported]);
394
+ }, [offset, zoomSupported, isZoomSupported, setIsZoomSupported]);
392
395
  if (zoomSupported) {
393
396
  return (React.createElement(ZoomContainer, { slide: slide, offset: offset, rect: rect, render: render, carousel: carousel, animation: animation, zoom: zoom }));
394
397
  }
package/dist/styles.css CHANGED
@@ -214,13 +214,6 @@
214
214
  overflow: hidden;
215
215
  overscroll-behavior: none;
216
216
  }
217
- .yarl__pad_scrollbar {
218
- padding-right: var(--yarl__scrollbar_padding, 17px);
219
- }
220
- [dir=rtl] .yarl__pad_scrollbar {
221
- padding-right: unset;
222
- padding-left: var(--yarl__scrollbar_padding, 17px);
223
- }
224
217
 
225
218
  @-webkit-keyframes yarl__delayed_fadein {
226
219
  0% {
package/dist/types.d.ts CHANGED
@@ -59,6 +59,8 @@ export interface ControllerSettings {
59
59
  focus: boolean;
60
60
  /** controller `touch-action` */
61
61
  touchAction: "none" | "pan-y";
62
+ /** if `true`, set ARIA attributes on the controller div */
63
+ aria: boolean;
62
64
  }
63
65
  /** Custom render functions. */
64
66
  export interface Render {
@@ -97,6 +99,8 @@ export interface Render {
97
99
  export interface Callbacks {
98
100
  /** a callback called when a slide becomes active */
99
101
  view?: (index: number) => void;
102
+ /** a callback called when a slide is clicked */
103
+ click?: (index: number) => void;
100
104
  /** a callback called when the portal starts opening */
101
105
  entering?: () => void;
102
106
  /** a callback called when the portal opens */
package/dist/types.js CHANGED
@@ -20,6 +20,7 @@ export const LightboxDefaultProps = {
20
20
  },
21
21
  controller: {
22
22
  focus: true,
23
+ aria: false,
23
24
  touchAction: "none",
24
25
  },
25
26
  on: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yet-another-react-lightbox",
3
- "version": "1.9.5",
3
+ "version": "1.11.0",
4
4
  "description": "Modern React lightbox component",
5
5
  "author": "Igor Danchenko",
6
6
  "license": "MIT",
@@ -59,7 +59,9 @@
59
59
  "files": [
60
60
  "dist"
61
61
  ],
62
- "sideEffects": false,
62
+ "sideEffects": [
63
+ "*.css"
64
+ ],
63
65
  "homepage": "https://yet-another-react-lightbox.com",
64
66
  "repository": {
65
67
  "type": "git",
@@ -91,17 +93,17 @@
91
93
  "@commitlint/cli": "^17.0.3",
92
94
  "@commitlint/config-conventional": "^17.0.3",
93
95
  "@semantic-release/changelog": "^6.0.1",
94
- "@semantic-release/github": "^8.0.4",
96
+ "@semantic-release/github": "^8.0.5",
95
97
  "@testing-library/jest-dom": "^5.16.4",
96
98
  "@testing-library/react": "^13.3.0",
97
- "@testing-library/user-event": "^14.2.1",
98
- "@types/jest": "^28.1.3",
99
- "@types/react": "^18.0.14",
100
- "@types/react-dom": "^18.0.5",
101
- "@typescript-eslint/eslint-plugin": "^5.30.0",
102
- "@typescript-eslint/parser": "^5.30.0",
99
+ "@testing-library/user-event": "^14.2.3",
100
+ "@types/jest": "^28.1.5",
101
+ "@types/react": "^18.0.15",
102
+ "@types/react-dom": "^18.0.6",
103
+ "@typescript-eslint/eslint-plugin": "^5.30.6",
104
+ "@typescript-eslint/parser": "^5.30.6",
103
105
  "autoprefixer": "^10.4.7",
104
- "eslint": "^8.18.0",
106
+ "eslint": "^8.19.0",
105
107
  "eslint-config-airbnb": "^19.0.4",
106
108
  "eslint-config-airbnb-typescript": "^17.0.0",
107
109
  "eslint-config-prettier": "^8.5.0",
@@ -111,8 +113,8 @@
111
113
  "eslint-plugin-react": "^7.30.1",
112
114
  "eslint-plugin-react-hooks": "^4.6.0",
113
115
  "husky": "^8.0.1",
114
- "jest": "^28.1.2",
115
- "jest-environment-jsdom": "^28.1.2",
116
+ "jest": "^28.1.3",
117
+ "jest-environment-jsdom": "^28.1.3",
116
118
  "lint-staged": "^13.0.3",
117
119
  "npm-run-all": "^4.1.5",
118
120
  "postcss": "^8.4.14",
@@ -122,7 +124,7 @@
122
124
  "react-dom": "^18.2.0",
123
125
  "rimraf": "^3.0.2",
124
126
  "sass": "^1.53.0",
125
- "ts-jest": "^28.0.5",
127
+ "ts-jest": "^28.0.6",
126
128
  "typescript": "^4.7.4"
127
129
  },
128
130
  "keywords": [
@@ -1,2 +0,0 @@
1
- import * as React from "react";
2
- export declare const useEnhancedEffect: typeof React.useLayoutEffect;
@@ -1,3 +0,0 @@
1
- import * as React from "react";
2
- import { hasWindow } from "../utils.js";
3
- export const useEnhancedEffect = hasWindow() ? React.useLayoutEffect : React.useEffect;