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 +51 -49
- package/dist/core/hooks/index.d.ts +1 -1
- package/dist/core/hooks/index.js +1 -1
- package/dist/core/hooks/useLayoutEffect.d.ts +2 -0
- package/dist/core/hooks/useLayoutEffect.js +3 -0
- package/dist/core/hooks/useMotionPreference.js +5 -3
- package/dist/core/hooks/useRTL.js +2 -2
- package/dist/core/modules/Controller.js +4 -4
- package/dist/core/modules/NoScroll.js +39 -12
- package/dist/core/modules/Portal.js +59 -32
- package/dist/plugins/Inline.d.ts +1 -1
- package/dist/plugins/Inline.js +2 -2
- package/dist/plugins/Thumbnails.js +2 -2
- package/dist/plugins/Zoom.js +34 -34
- package/dist/plugins/captions.css +4 -0
- package/dist/styles.css +0 -7
- package/dist/types.d.ts +2 -0
- package/dist/types.js +1 -0
- package/package.json +15 -13
- package/dist/core/hooks/useEnhancedEffect.d.ts +0 -2
- package/dist/core/hooks/useEnhancedEffect.js +0 -3
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
|
+

|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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;
|
package/dist/core/hooks/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useLayoutEffect } from "./useLayoutEffect.js";
|
|
3
3
|
export const useMotionPreference = () => {
|
|
4
4
|
const [reduceMotion, setReduceMotion] = React.useState(false);
|
|
5
|
-
|
|
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 {
|
|
2
|
+
import { useLayoutEffect } from "./useLayoutEffect.js";
|
|
3
3
|
export const useRTL = () => {
|
|
4
4
|
const [isRTL, setIsRTL] = React.useState(false);
|
|
5
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3
|
+
import { cssClass } from "../utils.js";
|
|
4
|
+
import { useLayoutEffect, useRTL } from "../hooks/index.js";
|
|
4
5
|
const noScroll = cssClass("no_scroll");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
41
|
+
body.classList.add(noScroll);
|
|
15
42
|
return () => {
|
|
16
|
-
|
|
17
|
-
|
|
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/
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
},
|
|
31
|
-
}), [
|
|
32
|
-
React.
|
|
33
|
-
var _a, _b;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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);
|
package/dist/plugins/Inline.d.ts
CHANGED
|
@@ -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
|
|
5
|
+
/** HTML div element attributes to be passed to the Inline plugin container */
|
|
6
6
|
inline?: React.HTMLAttributes<HTMLDivElement>;
|
|
7
7
|
}
|
|
8
8
|
}
|
package/dist/plugins/Inline.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
98
|
+
useLayoutEffect(() => {
|
|
99
99
|
var _a, _b, _c, _d;
|
|
100
100
|
if (track.current && state.offset) {
|
|
101
101
|
const { current } = refs;
|
package/dist/plugins/Zoom.js
CHANGED
|
@@ -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,
|
|
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 [
|
|
21
|
-
const [
|
|
22
|
-
const [
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}), [
|
|
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 {
|
|
36
|
+
const { isMinZoom, isMaxZoom, isZoomSupported } = useZoom();
|
|
37
37
|
const { publish } = useEvents();
|
|
38
|
-
const disabled = !
|
|
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 {
|
|
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
|
-
|
|
175
|
+
useLayoutEffect(() => {
|
|
180
176
|
if (refs.current.state.zoom > 1) {
|
|
181
177
|
changeOffsets();
|
|
182
178
|
}
|
|
183
179
|
}, [currentControllerRect.width, currentControllerRect.height, changeOffsets]);
|
|
184
|
-
|
|
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
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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
|
-
|
|
216
|
+
}, [offset, setIsMinZoom, setIsMaxZoom]);
|
|
217
|
+
useLayoutEffect(() => {
|
|
223
218
|
if (offset === 0) {
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
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 {
|
|
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
|
-
|
|
388
|
+
if (offset === 0 && zoomSupported !== isZoomSupported) {
|
|
389
|
+
setIsZoomSupported(zoomSupported);
|
|
390
390
|
}
|
|
391
|
-
}, [offset, zoomSupported,
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yet-another-react-lightbox",
|
|
3
|
-
"version": "1.
|
|
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":
|
|
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.
|
|
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.
|
|
98
|
-
"@types/jest": "^28.1.
|
|
99
|
-
"@types/react": "^18.0.
|
|
100
|
-
"@types/react-dom": "^18.0.
|
|
101
|
-
"@typescript-eslint/eslint-plugin": "^5.30.
|
|
102
|
-
"@typescript-eslint/parser": "^5.30.
|
|
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.
|
|
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.
|
|
115
|
-
"jest-environment-jsdom": "^28.1.
|
|
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.
|
|
127
|
+
"ts-jest": "^28.0.6",
|
|
126
128
|
"typescript": "^4.7.4"
|
|
127
129
|
},
|
|
128
130
|
"keywords": [
|