yet-another-react-lightbox 1.9.4 → 1.10.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.
package/README.md CHANGED
@@ -20,6 +20,8 @@ Modern React lightbox component. Performant, easy to use, customizable and exten
20
20
  - **TypeScript:** type definitions come built-in in the package
21
21
  - **RTL:** compatible with RTL layout
22
22
 
23
+ ![Yet Another React Lightbox | Example](https://yet-another-react-lightbox.com/images/example.jpg)
24
+
23
25
  ## Documentation
24
26
 
25
27
  [https://yet-another-react-lightbox.com/documentation](https://yet-another-react-lightbox.com/documentation)
@@ -48,25 +50,25 @@ import Lightbox from "yet-another-react-lightbox";
48
50
  import "yet-another-react-lightbox/styles.css";
49
51
 
50
52
  const App = () => {
51
- const [open, setOpen] = React.useState(false);
52
-
53
- return (
54
- <>
55
- <button type="button" onClick={() => setOpen(true)}>
56
- Open Lightbox
57
- </button>
58
-
59
- <Lightbox
60
- open={open}
61
- close={() => setOpen(false)}
62
- slides={[
63
- { src: "/image1.jpg" },
64
- { src: "/image2.jpg" },
65
- { src: "/image3.jpg" },
66
- ]}
67
- />
68
- </>
69
- );
53
+ const [open, setOpen] = React.useState(false);
54
+
55
+ return (
56
+ <>
57
+ <button type="button" onClick={() => setOpen(true)}>
58
+ Open Lightbox
59
+ </button>
60
+
61
+ <Lightbox
62
+ open={open}
63
+ close={() => setOpen(false)}
64
+ slides={[
65
+ { src: "/image1.jpg" },
66
+ { src: "/image2.jpg" },
67
+ { src: "/image3.jpg" },
68
+ ]}
69
+ />
70
+ </>
71
+ );
70
72
  };
71
73
 
72
74
  export default App;
@@ -85,36 +87,36 @@ import Lightbox from "yet-another-react-lightbox";
85
87
  import "yet-another-react-lightbox/styles.css";
86
88
 
87
89
  const App = () => {
88
- const [open, setOpen] = React.useState(false);
89
-
90
- return (
91
- <>
92
- <button type="button" onClick={() => setOpen(true)}>
93
- Open Lightbox
94
- </button>
95
-
96
- <Lightbox
97
- open={open}
98
- close={() => setOpen(false)}
99
- slides={[
100
- {
101
- src: "/image1x3840.jpg",
102
- alt: "image 1",
103
- width: 3840,
104
- height: 2560,
105
- srcSet: [
106
- { src: "/image1x320.jpg", width: 320, height: 213 },
107
- { src: "/image1x640.jpg", width: 640, height: 427 },
108
- { src: "/image1x1200.jpg", width: 1200, height: 800 },
109
- { src: "/image1x2048.jpg", width: 2048, height: 1365 },
110
- { src: "/image1x3840.jpg", width: 3840, height: 2560 },
111
- ]
112
- },
113
- // ...
114
- ]}
115
- />
116
- </>
117
- );
90
+ const [open, setOpen] = React.useState(false);
91
+
92
+ return (
93
+ <>
94
+ <button type="button" onClick={() => setOpen(true)}>
95
+ Open Lightbox
96
+ </button>
97
+
98
+ <Lightbox
99
+ open={open}
100
+ close={() => setOpen(false)}
101
+ slides={[
102
+ {
103
+ src: "/image1x3840.jpg",
104
+ alt: "image 1",
105
+ width: 3840,
106
+ height: 2560,
107
+ srcSet: [
108
+ { src: "/image1x320.jpg", width: 320, height: 213 },
109
+ { src: "/image1x640.jpg", width: 640, height: 427 },
110
+ { src: "/image1x1200.jpg", width: 1200, height: 800 },
111
+ { src: "/image1x2048.jpg", width: 2048, height: 1365 },
112
+ { src: "/image1x3840.jpg", width: 3840, height: 2560 },
113
+ ]
114
+ },
115
+ // ...
116
+ ]}
117
+ />
118
+ </>
119
+ );
118
120
  };
119
121
 
120
122
  export default App;
@@ -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";
@@ -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;
@@ -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);
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  import { Component, Plugin } from "../types.js";
3
3
  declare module "../types.js" {
4
4
  interface LightboxProps {
5
- /** HTML div element attributes to be passed to the inline plugin container */
5
+ /** HTML div element attributes to be passed to the Inline plugin container */
6
6
  inline?: React.HTMLAttributes<HTMLDivElement>;
7
7
  }
8
8
  }
@@ -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;
@@ -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, useLayoutEffect, useEvents, useMotionPreference, } from "../core/index.js";
3
3
  const defaultZoomProps = {
4
4
  maxZoomPixelRatio: 1,
5
5
  zoomInMultiplier: 2,
@@ -17,25 +17,25 @@ const ZoomOutIcon = createIcon("ZoomOut", React.createElement("path", { d: "M15.
17
17
  const ZoomContext = React.createContext(null);
18
18
  const useZoom = makeUseContext("useZoom", "ZoomContext", ZoomContext);
19
19
  const ZoomContextProvider = ({ children }) => {
20
- const [minZoom, setMinZoom] = React.useState(false);
21
- const [maxZoom, setMaxZoom] = React.useState(false);
22
- const [zoomSupported, setZoomSupported] = React.useState(false);
20
+ const [isMinZoom, setIsMinZoom] = React.useState(false);
21
+ const [isMaxZoom, setIsMaxZoom] = React.useState(false);
22
+ const [isZoomSupported, setIsZoomSupported] = React.useState(false);
23
23
  const context = React.useMemo(() => ({
24
- minZoom,
25
- maxZoom,
26
- zoomSupported,
27
- setMinZoom,
28
- setMaxZoom,
29
- setZoomSupported,
30
- }), [minZoom, maxZoom, zoomSupported]);
24
+ isMinZoom,
25
+ isMaxZoom,
26
+ isZoomSupported,
27
+ setIsMinZoom,
28
+ setIsMaxZoom,
29
+ setIsZoomSupported,
30
+ }), [isMinZoom, isMaxZoom, isZoomSupported]);
31
31
  return React.createElement(ZoomContext.Provider, { value: context }, children);
32
32
  };
33
33
  const ZoomButton = React.forwardRef(({ labels, render, zoomIn, onLoseFocus }, ref) => {
34
34
  const wasEnabled = React.useRef(false);
35
35
  const wasFocused = React.useRef(false);
36
- const { zoomSupported, minZoom, maxZoom } = useZoom();
36
+ const { isMinZoom, isMaxZoom, isZoomSupported } = useZoom();
37
37
  const { publish } = useEvents();
38
- const disabled = !zoomSupported || (zoomIn ? maxZoom : minZoom);
38
+ const disabled = !isZoomSupported || (zoomIn ? isMaxZoom : isMinZoom);
39
39
  const onClick = () => publish(zoomIn ? "zoom-in" : "zoom-out");
40
40
  const onFocus = React.useCallback(() => {
41
41
  wasFocused.current = true;
@@ -126,7 +126,7 @@ const distance = (pointerA, pointerB) => ((pointerA.clientX - pointerB.clientX)
126
126
  const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom: originalZoomProps }) => {
127
127
  var _a;
128
128
  const zoomProps = { ...defaultZoomProps, ...originalZoomProps };
129
- const { setMinZoom: currentSetMinZoom, setMaxZoom: currentSetMaxZoom } = useZoom();
129
+ const { isMinZoom, isMaxZoom, setIsMinZoom, setIsMaxZoom } = useZoom();
130
130
  const { setContainerRef, containerRef: currentContainerRef, containerRect: currentContainerRect, } = useContainerRect();
131
131
  const { subscribeSensors, containerRef: currentControllerRef, containerRect: currentControllerRect, } = useController();
132
132
  const { subscribe } = useEvents();
@@ -145,8 +145,6 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
145
145
  controllerRect: currentControllerRect,
146
146
  maxZoom: currentMaxZoom,
147
147
  reduceMotion: currentReduceMotion,
148
- setMinZoom: currentSetMinZoom,
149
- setMaxZoom: currentSetMaxZoom,
150
148
  activePointers: [],
151
149
  lastPointerDown: 0,
152
150
  zoomProps,
@@ -159,8 +157,6 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
159
157
  refs.current.controllerRect = currentControllerRect;
160
158
  refs.current.maxZoom = currentMaxZoom;
161
159
  refs.current.reduceMotion = currentReduceMotion;
162
- refs.current.setMinZoom = currentSetMinZoom;
163
- refs.current.setMaxZoom = currentSetMaxZoom;
164
160
  refs.current.zoomAnimationDuration = animation.zoom;
165
161
  refs.current.zoomProps = zoomProps;
166
162
  const changeOffsets = React.useCallback((dx, dy, newZoom) => {
@@ -176,12 +172,12 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
176
172
  offsetY: Math.min(Math.abs(newOffsetY), Math.max(maxOffsetY, 0)) * Math.sign(newOffsetY),
177
173
  }));
178
174
  }, []);
179
- useEnhancedEffect(() => {
175
+ useLayoutEffect(() => {
180
176
  if (refs.current.state.zoom > 1) {
181
177
  changeOffsets();
182
178
  }
183
179
  }, [currentControllerRect.width, currentControllerRect.height, changeOffsets]);
184
- useEnhancedEffect(() => {
180
+ useLayoutEffect(() => {
185
181
  var _a, _b;
186
182
  const { current } = refs;
187
183
  const { zoomAnimation, zoomAnimationStart, zoomAnimationDuration, reduceMotion, containerRef } = current;
@@ -204,13 +200,12 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
204
200
  }
205
201
  }
206
202
  }, [state.zoom, state.offsetX, state.offsetY]);
207
- useEnhancedEffect(() => {
203
+ useLayoutEffect(() => {
208
204
  if (offset === 0) {
209
- const { setMinZoom, setMaxZoom } = refs.current;
210
205
  const resetZoom = () => {
211
206
  setState({ zoom: 1, offsetX: 0, offsetY: 0 });
212
- setMinZoom(true);
213
- setMaxZoom(false);
207
+ setIsMinZoom(true);
208
+ setIsMaxZoom(false);
214
209
  };
215
210
  resetZoom();
216
211
  return () => {
@@ -218,14 +213,19 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
218
213
  };
219
214
  }
220
215
  return () => { };
221
- }, [offset]);
222
- useEnhancedEffect(() => {
216
+ }, [offset, setIsMinZoom, setIsMaxZoom]);
217
+ useLayoutEffect(() => {
223
218
  if (offset === 0) {
224
- const { setMinZoom, setMaxZoom } = refs.current;
225
- setMinZoom(state.zoom <= 1);
226
- setMaxZoom(state.zoom >= currentMaxZoom);
219
+ const newMinZoom = state.zoom <= 1;
220
+ if (newMinZoom !== isMinZoom) {
221
+ setIsMinZoom(newMinZoom);
222
+ }
223
+ const newMaxZoom = state.zoom >= currentMaxZoom;
224
+ if (newMaxZoom !== isMaxZoom) {
225
+ setIsMaxZoom(newMaxZoom);
226
+ }
227
227
  }
228
- }, [offset, state.zoom, currentMaxZoom]);
228
+ }, [offset, state.zoom, currentMaxZoom, isMinZoom, isMaxZoom, setIsMinZoom, setIsMaxZoom]);
229
229
  const changeZoom = React.useCallback((value, rapid, dx, dy) => {
230
230
  const { current } = refs;
231
231
  const { state: { zoom }, containerRef, containerRect, maxZoom, } = current;
@@ -381,14 +381,14 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
381
381
  };
382
382
  const ZoomWrapper = ({ slide, offset, rect, render, carousel, animation, zoom }) => {
383
383
  var _a;
384
- const { setZoomSupported } = useZoom();
384
+ const { setIsZoomSupported, isZoomSupported } = useZoom();
385
385
  const imageSlide = !("type" in slide);
386
386
  const zoomSupported = imageSlide && ("srcSet" in slide || ("width" in slide && "height" in slide));
387
387
  React.useEffect(() => {
388
- if (offset === 0) {
389
- setZoomSupported(zoomSupported);
388
+ if (offset === 0 && zoomSupported !== isZoomSupported) {
389
+ setIsZoomSupported(zoomSupported);
390
390
  }
391
- }, [offset, zoomSupported, setZoomSupported]);
391
+ }, [offset, zoomSupported, isZoomSupported, setIsZoomSupported]);
392
392
  if (zoomSupported) {
393
393
  return (React.createElement(ZoomContainer, { slide: slide, offset: offset, rect: rect, render: render, carousel: carousel, animation: animation, zoom: zoom }));
394
394
  }
@@ -14,6 +14,8 @@
14
14
  top: 0;
15
15
  padding: 16px;
16
16
  background: rgba(0, 0, 0, 0.5);
17
+ -webkit-transform: translateZ(0);
18
+ transform: translateZ(0);
17
19
  }
18
20
  .yarl__slide_description {
19
21
  font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
@@ -35,4 +37,6 @@
35
37
  bottom: 0;
36
38
  padding: 16px;
37
39
  background: rgba(0, 0, 0, 0.5);
40
+ -webkit-transform: translateZ(0);
41
+ transform: translateZ(0);
38
42
  }
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 {
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.4",
3
+ "version": "1.10.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;