yet-another-react-lightbox 1.9.6 → 1.11.1
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/dist/core/components/ImageSlide.d.ts +2 -1
- package/dist/core/components/ImageSlide.js +2 -2
- package/dist/core/hooks/index.d.ts +1 -1
- package/dist/core/hooks/index.js +1 -1
- package/dist/core/hooks/useLatest.d.ts +1 -0
- 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/Carousel.js +7 -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.js +2 -2
- package/dist/plugins/Thumbnails.js +2 -2
- package/dist/plugins/Video.js +5 -2
- package/dist/plugins/Zoom.d.ts +2 -0
- package/dist/plugins/Zoom.js +77 -61
- package/dist/styles.css +0 -7
- package/dist/types.d.ts +4 -0
- package/dist/types.js +1 -0
- package/package.json +14 -14
- package/dist/core/hooks/useEnhancedEffect.d.ts +0 -2
- package/dist/core/hooks/useEnhancedEffect.js +0 -3
|
@@ -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")) }))),
|
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;
|
|
@@ -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,
|
|
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.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/Video.js
CHANGED
|
@@ -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:
|
|
44
|
-
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) => {
|
package/dist/plugins/Zoom.d.ts
CHANGED
|
@@ -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 {
|
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, 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 [
|
|
21
|
-
const [
|
|
22
|
-
const [
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}), [
|
|
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 {
|
|
37
|
+
const { isMinZoom, isMaxZoom, isZoomSupported } = useZoom();
|
|
37
38
|
const { publish } = useEvents();
|
|
38
|
-
const disabled = !
|
|
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 {
|
|
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,30 @@ 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
|
-
|
|
176
|
+
const changeZoom = React.useCallback((value, rapid, dx, dy) => {
|
|
177
|
+
const { current } = refs;
|
|
178
|
+
const { state: { zoom }, containerRef, containerRect, maxZoom, } = current;
|
|
179
|
+
if (!containerRef.current || !containerRect)
|
|
180
|
+
return;
|
|
181
|
+
const newZoom = round(Math.min(Math.max(value + 0.001 < maxZoom ? value : maxZoom, 1), maxZoom), 5);
|
|
182
|
+
if (newZoom === zoom)
|
|
183
|
+
return;
|
|
184
|
+
if (!rapid) {
|
|
185
|
+
current.zoomAnimationStart = window.getComputedStyle(containerRef.current).transform;
|
|
186
|
+
}
|
|
187
|
+
changeOffsets(dx ? dx * (1 / zoom - 1 / newZoom) : 0, dy ? dy * (1 / zoom - 1 / newZoom) : 0, newZoom);
|
|
188
|
+
setState((prev) => ({ ...prev, zoom: newZoom }));
|
|
189
|
+
}, [changeOffsets]);
|
|
190
|
+
useLayoutEffect(() => {
|
|
180
191
|
if (refs.current.state.zoom > 1) {
|
|
192
|
+
const { maxZoom, state: { zoom: currentZoom }, } = refs.current;
|
|
193
|
+
if (currentZoom > maxZoom) {
|
|
194
|
+
changeZoom(maxZoom, true);
|
|
195
|
+
}
|
|
181
196
|
changeOffsets();
|
|
182
197
|
}
|
|
183
|
-
}, [currentControllerRect.width, currentControllerRect.height, changeOffsets]);
|
|
184
|
-
|
|
198
|
+
}, [currentControllerRect.width, currentControllerRect.height, changeOffsets, changeZoom]);
|
|
199
|
+
useLayoutEffect(() => {
|
|
185
200
|
var _a, _b;
|
|
186
201
|
const { current } = refs;
|
|
187
202
|
const { zoomAnimation, zoomAnimationStart, zoomAnimationDuration, reduceMotion, containerRef } = current;
|
|
@@ -204,13 +219,12 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
|
|
|
204
219
|
}
|
|
205
220
|
}
|
|
206
221
|
}, [state.zoom, state.offsetX, state.offsetY]);
|
|
207
|
-
|
|
222
|
+
useLayoutEffect(() => {
|
|
208
223
|
if (offset === 0) {
|
|
209
|
-
const { setMinZoom, setMaxZoom } = refs.current;
|
|
210
224
|
const resetZoom = () => {
|
|
211
225
|
setState({ zoom: 1, offsetX: 0, offsetY: 0 });
|
|
212
|
-
|
|
213
|
-
|
|
226
|
+
setIsMinZoom(true);
|
|
227
|
+
setIsMaxZoom(false);
|
|
214
228
|
};
|
|
215
229
|
resetZoom();
|
|
216
230
|
return () => {
|
|
@@ -218,28 +232,19 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
|
|
|
218
232
|
};
|
|
219
233
|
}
|
|
220
234
|
return () => { };
|
|
221
|
-
}, [offset]);
|
|
222
|
-
|
|
235
|
+
}, [offset, setIsMinZoom, setIsMaxZoom]);
|
|
236
|
+
useLayoutEffect(() => {
|
|
223
237
|
if (offset === 0) {
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (!containerRef.current || !containerRect)
|
|
233
|
-
return;
|
|
234
|
-
const newZoom = round(Math.min(Math.max(value + 0.001 < maxZoom ? value : maxZoom, 1), maxZoom), 5);
|
|
235
|
-
if (newZoom === zoom)
|
|
236
|
-
return;
|
|
237
|
-
if (!rapid) {
|
|
238
|
-
current.zoomAnimationStart = window.getComputedStyle(containerRef.current).transform;
|
|
238
|
+
const newMinZoom = state.zoom <= 1;
|
|
239
|
+
if (newMinZoom !== isMinZoom) {
|
|
240
|
+
setIsMinZoom(newMinZoom);
|
|
241
|
+
}
|
|
242
|
+
const newMaxZoom = state.zoom >= currentMaxZoom;
|
|
243
|
+
if (newMaxZoom !== isMaxZoom) {
|
|
244
|
+
setIsMaxZoom(newMaxZoom);
|
|
245
|
+
}
|
|
239
246
|
}
|
|
240
|
-
|
|
241
|
-
setState((prev) => ({ ...prev, zoom: newZoom }));
|
|
242
|
-
}, [changeOffsets]);
|
|
247
|
+
}, [offset, state.zoom, currentMaxZoom, isMinZoom, isMaxZoom, setIsMinZoom, setIsMaxZoom]);
|
|
243
248
|
const translateCoordinates = React.useCallback((event) => {
|
|
244
249
|
const { controllerRef } = refs.current;
|
|
245
250
|
if (controllerRef.current) {
|
|
@@ -252,9 +257,13 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
|
|
|
252
257
|
}, []);
|
|
253
258
|
const onKeyDown = React.useCallback((event) => {
|
|
254
259
|
const { state: { zoom }, zoomProps: { keyboardMoveDistance, zoomInMultiplier }, } = refs.current;
|
|
260
|
+
const preventDefault = () => {
|
|
261
|
+
event.preventDefault();
|
|
262
|
+
event.stopPropagation();
|
|
263
|
+
};
|
|
255
264
|
if (zoom > 1) {
|
|
256
265
|
const move = (deltaX, deltaY) => {
|
|
257
|
-
|
|
266
|
+
preventDefault();
|
|
258
267
|
changeOffsets(deltaX, deltaY);
|
|
259
268
|
};
|
|
260
269
|
if (event.key === "ArrowDown") {
|
|
@@ -270,28 +279,35 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
|
|
|
270
279
|
move(keyboardMoveDistance, 0);
|
|
271
280
|
}
|
|
272
281
|
}
|
|
273
|
-
|
|
274
|
-
|
|
282
|
+
const handleChangeZoom = (zoomValue) => {
|
|
283
|
+
preventDefault();
|
|
284
|
+
changeZoom(zoomValue);
|
|
285
|
+
};
|
|
286
|
+
const hasMeta = () => event.getModifierState("Meta") || event.getModifierState("OS");
|
|
287
|
+
if (event.key === "+" || (event.key === "=" && hasMeta())) {
|
|
288
|
+
handleChangeZoom(zoom * zoomInMultiplier);
|
|
275
289
|
}
|
|
276
|
-
else if (event.key === "-" || (event.key === "_" &&
|
|
277
|
-
|
|
290
|
+
else if (event.key === "-" || (event.key === "_" && hasMeta())) {
|
|
291
|
+
handleChangeZoom(zoom / zoomInMultiplier);
|
|
278
292
|
}
|
|
279
|
-
else if (event.key === "0" &&
|
|
280
|
-
|
|
293
|
+
else if (event.key === "0" && hasMeta()) {
|
|
294
|
+
handleChangeZoom(1);
|
|
281
295
|
}
|
|
282
296
|
}, [changeZoom, changeOffsets]);
|
|
283
297
|
const onWheel = React.useCallback((event) => {
|
|
284
|
-
const { state: { zoom }, zoomProps: { wheelZoomDistanceFactor }, } = refs.current;
|
|
285
|
-
if (event.ctrlKey) {
|
|
286
|
-
event.stopPropagation();
|
|
298
|
+
const { state: { zoom }, zoomProps: { wheelZoomDistanceFactor, scrollToZoom }, } = refs.current;
|
|
299
|
+
if (event.ctrlKey || scrollToZoom) {
|
|
287
300
|
if (Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
|
|
301
|
+
event.stopPropagation();
|
|
288
302
|
changeZoom(zoom * (1 - event.deltaY / wheelZoomDistanceFactor), true, ...translateCoordinates(event));
|
|
303
|
+
return;
|
|
289
304
|
}
|
|
290
|
-
return;
|
|
291
305
|
}
|
|
292
306
|
if (zoom > 1) {
|
|
293
307
|
event.stopPropagation();
|
|
294
|
-
|
|
308
|
+
if (!scrollToZoom) {
|
|
309
|
+
changeOffsets(event.deltaX, event.deltaY);
|
|
310
|
+
}
|
|
295
311
|
}
|
|
296
312
|
}, [changeZoom, changeOffsets, translateCoordinates]);
|
|
297
313
|
const clearPointer = React.useCallback((event) => {
|
|
@@ -381,14 +397,14 @@ const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom:
|
|
|
381
397
|
};
|
|
382
398
|
const ZoomWrapper = ({ slide, offset, rect, render, carousel, animation, zoom }) => {
|
|
383
399
|
var _a;
|
|
384
|
-
const {
|
|
400
|
+
const { setIsZoomSupported, isZoomSupported } = useZoom();
|
|
385
401
|
const imageSlide = !("type" in slide);
|
|
386
402
|
const zoomSupported = imageSlide && ("srcSet" in slide || ("width" in slide && "height" in slide));
|
|
387
403
|
React.useEffect(() => {
|
|
388
|
-
if (offset === 0) {
|
|
389
|
-
|
|
404
|
+
if (offset === 0 && zoomSupported !== isZoomSupported) {
|
|
405
|
+
setIsZoomSupported(zoomSupported);
|
|
390
406
|
}
|
|
391
|
-
}, [offset, zoomSupported,
|
|
407
|
+
}, [offset, zoomSupported, isZoomSupported, setIsZoomSupported]);
|
|
392
408
|
if (zoomSupported) {
|
|
393
409
|
return (React.createElement(ZoomContainer, { slide: slide, offset: offset, rect: rect, render: render, carousel: carousel, animation: animation, zoom: zoom }));
|
|
394
410
|
}
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yet-another-react-lightbox",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "Modern React lightbox component",
|
|
5
5
|
"author": "Igor Danchenko",
|
|
6
6
|
"license": "MIT",
|
|
@@ -96,25 +96,25 @@
|
|
|
96
96
|
"@semantic-release/github": "^8.0.5",
|
|
97
97
|
"@testing-library/jest-dom": "^5.16.4",
|
|
98
98
|
"@testing-library/react": "^13.3.0",
|
|
99
|
-
"@testing-library/user-event": "^14.
|
|
100
|
-
"@types/jest": "^28.1.
|
|
101
|
-
"@types/react": "^18.0.
|
|
102
|
-
"@types/react-dom": "^18.0.
|
|
103
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
104
|
-
"@typescript-eslint/parser": "^5.
|
|
105
|
-
"autoprefixer": "^10.4.
|
|
106
|
-
"eslint": "^8.
|
|
99
|
+
"@testing-library/user-event": "^14.3.0",
|
|
100
|
+
"@types/jest": "^28.1.6",
|
|
101
|
+
"@types/react": "^18.0.15",
|
|
102
|
+
"@types/react-dom": "^18.0.6",
|
|
103
|
+
"@typescript-eslint/eslint-plugin": "^5.31.0",
|
|
104
|
+
"@typescript-eslint/parser": "^5.31.0",
|
|
105
|
+
"autoprefixer": "^10.4.8",
|
|
106
|
+
"eslint": "^8.20.0",
|
|
107
107
|
"eslint-config-airbnb": "^19.0.4",
|
|
108
108
|
"eslint-config-airbnb-typescript": "^17.0.0",
|
|
109
109
|
"eslint-config-prettier": "^8.5.0",
|
|
110
110
|
"eslint-plugin-import": "^2.26.0",
|
|
111
|
-
"eslint-plugin-jsx-a11y": "^6.6.
|
|
111
|
+
"eslint-plugin-jsx-a11y": "^6.6.1",
|
|
112
112
|
"eslint-plugin-prettier": "^4.2.1",
|
|
113
113
|
"eslint-plugin-react": "^7.30.1",
|
|
114
114
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
115
115
|
"husky": "^8.0.1",
|
|
116
|
-
"jest": "^28.1.
|
|
117
|
-
"jest-environment-jsdom": "^28.1.
|
|
116
|
+
"jest": "^28.1.3",
|
|
117
|
+
"jest-environment-jsdom": "^28.1.3",
|
|
118
118
|
"lint-staged": "^13.0.3",
|
|
119
119
|
"npm-run-all": "^4.1.5",
|
|
120
120
|
"postcss": "^8.4.14",
|
|
@@ -123,8 +123,8 @@
|
|
|
123
123
|
"react": "^18.2.0",
|
|
124
124
|
"react-dom": "^18.2.0",
|
|
125
125
|
"rimraf": "^3.0.2",
|
|
126
|
-
"sass": "^1.
|
|
127
|
-
"ts-jest": "^28.0.
|
|
126
|
+
"sass": "^1.54.0",
|
|
127
|
+
"ts-jest": "^28.0.7",
|
|
128
128
|
"typescript": "^4.7.4"
|
|
129
129
|
},
|
|
130
130
|
"keywords": [
|