yet-another-react-lightbox 2.0.0-rc.2 → 2.0.0-rc.5
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 +4 -2
- package/dist/core/components/ImageSlide.js +5 -4
- package/dist/core/modules/Controller.js +15 -15
- package/dist/core/modules/controller/usePointerSwipe.d.ts +1 -2
- package/dist/core/modules/controller/usePointerSwipe.js +19 -19
- package/dist/core/modules/controller/useWheelSwipe.d.ts +1 -1
- package/dist/core/modules/controller/useWheelSwipe.js +27 -29
- package/dist/core/utils.d.ts +1 -0
- package/dist/core/utils.js +3 -0
- package/dist/plugins/zoom/ResponsiveImage.d.ts +12 -0
- package/dist/plugins/zoom/ResponsiveImage.js +48 -0
- package/dist/plugins/zoom/Zoom.js +8 -4
- package/dist/plugins/zoom/ZoomContainer.d.ts +1 -1
- package/dist/plugins/zoom/ZoomContainer.js +15 -4
- package/dist/plugins/zoom/ZoomContext.d.ts +0 -1
- package/dist/plugins/zoom/ZoomContext.js +5 -3
- package/dist/styles.css +4 -0
- package/dist/types.d.ts +11 -8
- package/package.json +1 -1
- package/dist/core/modules/controller/useOffset.d.ts +0 -2
- package/dist/core/modules/controller/useOffset.js +0 -10
- package/dist/plugins/zoom/ZoomWrapper.d.ts +0 -3
- package/dist/plugins/zoom/ZoomWrapper.js +0 -26
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
import { ImageFit, Render, SlideImage } from "../../types.js";
|
|
3
3
|
import { ContainerRect } from "../hooks/index.js";
|
|
4
4
|
export declare type ImageSlideProps = {
|
|
@@ -8,5 +8,7 @@ export declare type ImageSlideProps = {
|
|
|
8
8
|
rect?: ContainerRect;
|
|
9
9
|
imageFit?: ImageFit;
|
|
10
10
|
onClick?: () => void;
|
|
11
|
+
onLoad?: (image: HTMLImageElement) => void;
|
|
12
|
+
style?: React.CSSProperties;
|
|
11
13
|
};
|
|
12
|
-
export declare const ImageSlide: ({ slide: image, offset, render, rect, imageFit, onClick }: ImageSlideProps) => JSX.Element;
|
|
14
|
+
export declare const ImageSlide: ({ slide: image, offset, render, rect, imageFit, onClick, onLoad, style, }: ImageSlideProps) => JSX.Element;
|
|
@@ -6,7 +6,7 @@ import { ErrorIcon, LoadingIcon } from "./Icons.js";
|
|
|
6
6
|
import { activeSlideStatus, ELEMENT_ICON, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, SLIDE_STATUS_PLACEHOLDER, } from "../consts.js";
|
|
7
7
|
const slidePrefix = makeComposePrefix("slide");
|
|
8
8
|
const slideImagePrefix = makeComposePrefix("slide_image");
|
|
9
|
-
export const ImageSlide = ({ slide: image, offset, render, rect, imageFit, onClick }) => {
|
|
9
|
+
export const ImageSlide = ({ slide: image, offset, render, rect, imageFit, onClick, onLoad, style, }) => {
|
|
10
10
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
11
11
|
const [status, setStatus] = React.useState(SLIDE_STATUS_LOADING);
|
|
12
12
|
const { publish } = useEvents();
|
|
@@ -27,6 +27,7 @@ export const ImageSlide = ({ slide: image, offset, render, rect, imageFit, onCli
|
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
29
|
setStatus(SLIDE_STATUS_COMPLETE);
|
|
30
|
+
onLoad === null || onLoad === void 0 ? void 0 : onLoad(img);
|
|
30
31
|
});
|
|
31
32
|
});
|
|
32
33
|
const setImageRef = React.useCallback((img) => {
|
|
@@ -35,7 +36,7 @@ export const ImageSlide = ({ slide: image, offset, render, rect, imageFit, onCli
|
|
|
35
36
|
handleLoading(img);
|
|
36
37
|
}
|
|
37
38
|
}, [handleLoading]);
|
|
38
|
-
const
|
|
39
|
+
const handleOnLoad = React.useCallback((event) => {
|
|
39
40
|
handleLoading(event.currentTarget);
|
|
40
41
|
}, [handleLoading]);
|
|
41
42
|
const onError = React.useCallback(() => {
|
|
@@ -45,7 +46,7 @@ export const ImageSlide = ({ slide: image, offset, render, rect, imageFit, onCli
|
|
|
45
46
|
const nonInfinite = (value, fallback) => (Number.isFinite(value) ? value : fallback);
|
|
46
47
|
const maxWidth = nonInfinite(Math.max(...((_b = (_a = image.srcSet) === null || _a === void 0 ? void 0 : _a.map((x) => x.width)) !== null && _b !== void 0 ? _b : []).concat(image.width ? [image.width] : [])), ((_c = imageRef.current) === null || _c === void 0 ? void 0 : _c.naturalWidth) || 0);
|
|
47
48
|
const maxHeight = nonInfinite(Math.max(...((_e = (_d = image.srcSet) === null || _d === void 0 ? void 0 : _d.map((x) => x.height)) !== null && _e !== void 0 ? _e : []).concat(image.height ? [image.height] : [])), ((_f = imageRef.current) === null || _f === void 0 ? void 0 : _f.naturalHeight) || 0);
|
|
48
|
-
const
|
|
49
|
+
const defaultStyle = maxWidth && maxHeight
|
|
49
50
|
? {
|
|
50
51
|
maxWidth: `min(${maxWidth}px, 100%)`,
|
|
51
52
|
maxHeight: `min(${maxHeight}px, 100%)`,
|
|
@@ -58,7 +59,7 @@ export const ImageSlide = ({ slide: image, offset, render, rect, imageFit, onCli
|
|
|
58
59
|
const estimateActualWidth = () => rect && !cover && image.width && image.height ? (rect.height / image.height) * image.width : Number.MAX_VALUE;
|
|
59
60
|
const sizes = srcSet && rect && hasWindow() ? `${Math.round(Math.min(estimateActualWidth(), rect.width))}px` : undefined;
|
|
60
61
|
return (React.createElement(React.Fragment, null,
|
|
61
|
-
React.createElement("img", { ref: setImageRef, onLoad:
|
|
62
|
+
React.createElement("img", { ref: setImageRef, onLoad: handleOnLoad, onError: onError, onClick: onClick, className: clsx(cssClass(slideImagePrefix()), cover && cssClass(slideImagePrefix("cover")), status !== SLIDE_STATUS_COMPLETE && cssClass(slideImagePrefix("loading"))), draggable: false, alt: image.alt, style: { ...defaultStyle, ...style }, sizes: sizes, srcSet: srcSet, src: image.src }),
|
|
62
63
|
status !== SLIDE_STATUS_COMPLETE && (React.createElement("div", { className: cssClass(slidePrefix(SLIDE_STATUS_PLACEHOLDER)) },
|
|
63
64
|
status === SLIDE_STATUS_LOADING &&
|
|
64
65
|
((render === null || render === void 0 ? void 0 : render.iconLoading) ? (render.iconLoading()) : (React.createElement(LoadingIcon, { className: clsx(cssClass(ELEMENT_ICON), cssClass(slidePrefix(SLIDE_STATUS_LOADING))) }))),
|
|
@@ -12,7 +12,7 @@ export const Controller = ({ children, ...props }) => {
|
|
|
12
12
|
const { carousel, slides, animation, controller, on, styles } = props;
|
|
13
13
|
const { state, dispatch } = useLightboxState();
|
|
14
14
|
const [swipeState, setSwipeState] = React.useState(SwipeState.NONE);
|
|
15
|
-
const
|
|
15
|
+
const swipeOffset = React.useRef(0);
|
|
16
16
|
const swipeAnimationReset = React.useRef();
|
|
17
17
|
const { registerSensors, subscribeSensors } = useSensors();
|
|
18
18
|
const { subscribe, publish } = useEvents();
|
|
@@ -29,6 +29,11 @@ export const Controller = ({ children, ...props }) => {
|
|
|
29
29
|
const isSwipeValid = useEventCallback((offset) => !(carousel.finite &&
|
|
30
30
|
((rtl(offset) > 0 && state.currentIndex === 0) ||
|
|
31
31
|
(rtl(offset) < 0 && state.currentIndex === slides.length - 1))));
|
|
32
|
+
const setSwipeOffset = React.useCallback((offset) => {
|
|
33
|
+
var _a;
|
|
34
|
+
swipeOffset.current = offset;
|
|
35
|
+
(_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.style.setProperty(cssVar("swipe_offset"), `${Math.round(offset)}px`);
|
|
36
|
+
}, [containerRef]);
|
|
32
37
|
const swipe = useEventCallback((action) => {
|
|
33
38
|
var _a;
|
|
34
39
|
const swipeDuration = animation.swipe;
|
|
@@ -79,6 +84,12 @@ export const Controller = ({ children, ...props }) => {
|
|
|
79
84
|
newSwipeAnimationDuration = swipeDuration;
|
|
80
85
|
}
|
|
81
86
|
}
|
|
87
|
+
if (carouselRef.current) {
|
|
88
|
+
carouselSwipeAnimation.current = {
|
|
89
|
+
rect: carouselRef.current.getBoundingClientRect(),
|
|
90
|
+
index: state.globalIndex,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
82
93
|
newSwipeAnimationDuration = Math.round(newSwipeAnimationDuration);
|
|
83
94
|
clearTimeout(swipeAnimationReset.current);
|
|
84
95
|
if (newSwipeState) {
|
|
@@ -90,12 +101,6 @@ export const Controller = ({ children, ...props }) => {
|
|
|
90
101
|
}, newSwipeAnimationDuration);
|
|
91
102
|
swipeAnimationReset.current = timeoutId;
|
|
92
103
|
}
|
|
93
|
-
if (carouselRef.current) {
|
|
94
|
-
carouselSwipeAnimation.current = {
|
|
95
|
-
rect: carouselRef.current.getBoundingClientRect(),
|
|
96
|
-
index: state.globalIndex,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
104
|
setSwipeState(newSwipeState);
|
|
100
105
|
dispatch({ increment, animationDuration: newSwipeAnimationDuration });
|
|
101
106
|
});
|
|
@@ -125,22 +130,17 @@ export const Controller = ({ children, ...props }) => {
|
|
|
125
130
|
});
|
|
126
131
|
useLayoutEffect(animateCarouselSwipe);
|
|
127
132
|
const swipeParams = [
|
|
128
|
-
swipeState,
|
|
129
133
|
subscribeSensors,
|
|
130
134
|
isSwipeValid,
|
|
131
135
|
(containerRect === null || containerRect === void 0 ? void 0 : containerRect.width) || 0,
|
|
132
136
|
animation.swipe,
|
|
133
137
|
() => setSwipeState(SwipeState.SWIPE),
|
|
138
|
+
(offset) => setSwipeOffset(offset),
|
|
134
139
|
(offset, duration) => swipe({ offset, duration, count: 1 }),
|
|
135
140
|
(offset) => swipe({ offset, count: 0 }),
|
|
136
|
-
(offset) => {
|
|
137
|
-
if (swipeState === SwipeState.SWIPE) {
|
|
138
|
-
setSwipeOffset(offset);
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
141
|
];
|
|
142
142
|
usePointerSwipe(...swipeParams);
|
|
143
|
-
useWheelSwipe(...swipeParams);
|
|
143
|
+
useWheelSwipe(swipeState, ...swipeParams);
|
|
144
144
|
const focusOnMount = useEventCallback(() => {
|
|
145
145
|
var _a;
|
|
146
146
|
if (controller.focus) {
|
|
@@ -180,7 +180,7 @@ export const Controller = ({ children, ...props }) => {
|
|
|
180
180
|
}), [getLightboxProps, subscribeSensors, transferFocus, containerRect, containerRef, setCarouselRef]);
|
|
181
181
|
return (React.createElement("div", { ref: handleContainerRef, className: clsx(cssClass(cssContainerPrefix()), cssClass(CLASS_FLEX_CENTER)), style: {
|
|
182
182
|
...(swipeState === SwipeState.SWIPE
|
|
183
|
-
? { [cssVar("swipe_offset")]: `${Math.round(swipeOffset)}px` }
|
|
183
|
+
? { [cssVar("swipe_offset")]: `${Math.round(swipeOffset.current)}px` }
|
|
184
184
|
: null),
|
|
185
185
|
...(controller.touchAction !== "none" ? {} : null),
|
|
186
186
|
...styles.container,
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { UseSensors } from "../../hooks/useSensors.js";
|
|
2
|
-
|
|
3
|
-
export declare const usePointerSwipe: <T extends Element = Element>(swipeState: SwipeState, subscribeSensors: import("../../hooks/useSensors.js").SubscribeSensors<T>, isSwipeValid: (offset: number) => boolean, containerWidth: number, swipeAnimationDuration: number, onSwipeStart: () => void, onSwipeFinish: (offset: number, duration: number) => void, onSwipeCancel: (offset: number) => void, onChange: (offset: number) => void) => void;
|
|
2
|
+
export declare const usePointerSwipe: <T extends Element = Element>(subscribeSensors: import("../../hooks/useSensors.js").SubscribeSensors<T>, isSwipeValid: (offset: number) => boolean, containerWidth: number, swipeAnimationDuration: number, onSwipeStart: () => void, onSwipeProgress: (offset: number) => void, onSwipeFinish: (offset: number, duration: number) => void, onSwipeCancel: (offset: number) => void) => void;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { cleanup } from "../../utils.js";
|
|
3
3
|
import { useEventCallback } from "../../hooks/useEventCallback.js";
|
|
4
|
-
import { SwipeState } from "./index.js";
|
|
5
|
-
import { useOffset } from "./useOffset.js";
|
|
6
4
|
import { EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, } from "../../consts.js";
|
|
7
|
-
export const usePointerSwipe = (
|
|
8
|
-
const
|
|
5
|
+
export const usePointerSwipe = (subscribeSensors, isSwipeValid, containerWidth, swipeAnimationDuration, onSwipeStart, onSwipeProgress, onSwipeFinish, onSwipeCancel) => {
|
|
6
|
+
const offset = React.useRef(0);
|
|
9
7
|
const pointers = React.useRef([]);
|
|
10
8
|
const activePointer = React.useRef();
|
|
11
9
|
const startTime = React.useRef(0);
|
|
@@ -24,18 +22,18 @@ export const usePointerSwipe = (swipeState, subscribeSensors, isSwipeValid, cont
|
|
|
24
22
|
addPointer(event);
|
|
25
23
|
});
|
|
26
24
|
const onPointerUp = useEventCallback((event) => {
|
|
27
|
-
if (
|
|
28
|
-
pointers.current.find((x) => x.pointerId === event.pointerId) &&
|
|
25
|
+
if (pointers.current.find((x) => x.pointerId === event.pointerId) &&
|
|
29
26
|
activePointer.current === event.pointerId) {
|
|
30
27
|
const duration = Date.now() - startTime.current;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
const currentOffset = offset.current;
|
|
29
|
+
if (Math.abs(currentOffset) > 0.3 * containerWidth ||
|
|
30
|
+
(Math.abs(currentOffset) > 5 && duration < swipeAnimationDuration)) {
|
|
31
|
+
onSwipeFinish(currentOffset, duration);
|
|
34
32
|
}
|
|
35
33
|
else {
|
|
36
|
-
onSwipeCancel(
|
|
34
|
+
onSwipeCancel(currentOffset);
|
|
37
35
|
}
|
|
38
|
-
|
|
36
|
+
offset.current = 0;
|
|
39
37
|
}
|
|
40
38
|
clearPointer(event);
|
|
41
39
|
});
|
|
@@ -44,16 +42,18 @@ export const usePointerSwipe = (swipeState, subscribeSensors, isSwipeValid, cont
|
|
|
44
42
|
if (pointer) {
|
|
45
43
|
const deltaX = event.clientX - pointer.clientX;
|
|
46
44
|
const deltaY = event.clientY - pointer.clientY;
|
|
47
|
-
if (!
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
if (!activePointer.current &&
|
|
46
|
+
isSwipeValid(deltaX) &&
|
|
47
|
+
Math.abs(deltaX) > Math.abs(deltaY) &&
|
|
48
|
+
Math.abs(deltaX) > 30) {
|
|
49
|
+
addPointer(event);
|
|
50
|
+
activePointer.current = event.pointerId;
|
|
51
|
+
startTime.current = Date.now();
|
|
52
|
+
onSwipeStart();
|
|
54
53
|
}
|
|
55
54
|
else if (activePointer.current === event.pointerId) {
|
|
56
|
-
|
|
55
|
+
offset.current = deltaX;
|
|
56
|
+
onSwipeProgress(deltaX);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { UseSensors } from "../../hooks/index.js";
|
|
2
2
|
import { SwipeState } from "./index.js";
|
|
3
|
-
export declare const useWheelSwipe: <T extends Element = Element>(swipeState: SwipeState
|
|
3
|
+
export declare const useWheelSwipe: <T extends Element = Element>(swipeState: SwipeState, subscribeSensors: import("../../hooks/useSensors.js").SubscribeSensors<T>, isSwipeValid: (offset: number) => boolean, containerWidth: number, swipeAnimationDuration: number, onSwipeStart: () => void, onSwipeProgress: (offset: number) => void, onSwipeFinish: (offset: number, duration: number) => void, onSwipeCancel: (offset: number) => void) => void;
|
|
@@ -2,33 +2,30 @@ import * as React from "react";
|
|
|
2
2
|
import { useEventCallback } from "../../hooks/index.js";
|
|
3
3
|
import { useTimeouts } from "../../contexts/index.js";
|
|
4
4
|
import { SwipeState } from "./index.js";
|
|
5
|
-
import { useOffset } from "./useOffset.js";
|
|
6
5
|
import { EVENT_ON_WHEEL } from "../../consts.js";
|
|
7
|
-
export const useWheelSwipe = (swipeState, subscribeSensors, isSwipeValid, containerWidth, swipeAnimationDuration, onSwipeStart, onSwipeFinish, onSwipeCancel
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
6
|
+
export const useWheelSwipe = (swipeState, subscribeSensors, isSwipeValid, containerWidth, swipeAnimationDuration, onSwipeStart, onSwipeProgress, onSwipeFinish, onSwipeCancel) => {
|
|
7
|
+
const offset = React.useRef(0);
|
|
8
|
+
const intent = React.useRef(0);
|
|
9
|
+
const intentCleanup = React.useRef();
|
|
10
|
+
const resetCleanup = React.useRef();
|
|
12
11
|
const wheelResidualMomentum = React.useRef(0);
|
|
13
12
|
const startTime = React.useRef(0);
|
|
14
13
|
const { setTimeout, clearTimeout } = useTimeouts();
|
|
15
14
|
const cancelSwipeIntentCleanup = React.useCallback(() => {
|
|
16
|
-
if (
|
|
17
|
-
clearTimeout(
|
|
18
|
-
|
|
15
|
+
if (intentCleanup.current) {
|
|
16
|
+
clearTimeout(intentCleanup.current);
|
|
17
|
+
intentCleanup.current = undefined;
|
|
19
18
|
}
|
|
20
19
|
}, [clearTimeout]);
|
|
21
20
|
const cancelSwipeResetCleanup = React.useCallback(() => {
|
|
22
|
-
if (
|
|
23
|
-
clearTimeout(
|
|
24
|
-
|
|
21
|
+
if (resetCleanup.current) {
|
|
22
|
+
clearTimeout(resetCleanup.current);
|
|
23
|
+
resetCleanup.current = undefined;
|
|
25
24
|
}
|
|
26
25
|
}, [clearTimeout]);
|
|
27
26
|
const handleCleanup = useEventCallback(() => {
|
|
28
27
|
if (swipeState !== SwipeState.SWIPE) {
|
|
29
|
-
|
|
30
|
-
setOffset(0);
|
|
31
|
-
}
|
|
28
|
+
offset.current = 0;
|
|
32
29
|
startTime.current = 0;
|
|
33
30
|
cancelSwipeIntentCleanup();
|
|
34
31
|
cancelSwipeResetCleanup();
|
|
@@ -36,9 +33,9 @@ export const useWheelSwipe = (swipeState, subscribeSensors, isSwipeValid, contai
|
|
|
36
33
|
});
|
|
37
34
|
React.useEffect(handleCleanup, [swipeState, handleCleanup]);
|
|
38
35
|
const handleCancelSwipe = useEventCallback((currentSwipeOffset) => {
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
onSwipeCancel(offset);
|
|
36
|
+
resetCleanup.current = undefined;
|
|
37
|
+
if (offset.current === currentSwipeOffset) {
|
|
38
|
+
onSwipeCancel(offset.current);
|
|
42
39
|
}
|
|
43
40
|
});
|
|
44
41
|
const onWheel = useEventCallback((event) => {
|
|
@@ -56,35 +53,36 @@ export const useWheelSwipe = (swipeState, subscribeSensors, isSwipeValid, contai
|
|
|
56
53
|
if (!isSwipeValid(-event.deltaX)) {
|
|
57
54
|
return;
|
|
58
55
|
}
|
|
59
|
-
|
|
56
|
+
intent.current += event.deltaX;
|
|
60
57
|
cancelSwipeIntentCleanup();
|
|
61
|
-
if (Math.abs(
|
|
62
|
-
|
|
58
|
+
if (Math.abs(intent.current) > 30) {
|
|
59
|
+
intent.current = 0;
|
|
63
60
|
wheelResidualMomentum.current = 0;
|
|
64
61
|
startTime.current = Date.now();
|
|
65
62
|
onSwipeStart();
|
|
66
63
|
}
|
|
67
64
|
else {
|
|
68
|
-
const currentSwipeIntent =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (currentSwipeIntent ===
|
|
72
|
-
|
|
65
|
+
const currentSwipeIntent = intent.current;
|
|
66
|
+
intentCleanup.current = setTimeout(() => {
|
|
67
|
+
intentCleanup.current = undefined;
|
|
68
|
+
if (currentSwipeIntent === intent.current) {
|
|
69
|
+
intent.current = 0;
|
|
73
70
|
}
|
|
74
71
|
}, swipeAnimationDuration);
|
|
75
72
|
}
|
|
76
73
|
}
|
|
77
74
|
else if (swipeState === SwipeState.SWIPE) {
|
|
78
|
-
let newSwipeOffset = offset - event.deltaX;
|
|
75
|
+
let newSwipeOffset = offset.current - event.deltaX;
|
|
79
76
|
newSwipeOffset = Math.min(Math.abs(newSwipeOffset), containerWidth) * Math.sign(newSwipeOffset);
|
|
80
|
-
|
|
77
|
+
offset.current = newSwipeOffset;
|
|
78
|
+
onSwipeProgress(newSwipeOffset);
|
|
81
79
|
cancelSwipeResetCleanup();
|
|
82
80
|
if (Math.abs(newSwipeOffset) > 0.2 * containerWidth) {
|
|
83
81
|
wheelResidualMomentum.current = event.deltaX;
|
|
84
82
|
onSwipeFinish(newSwipeOffset, Date.now() - startTime.current);
|
|
85
83
|
return;
|
|
86
84
|
}
|
|
87
|
-
|
|
85
|
+
resetCleanup.current = setTimeout(() => handleCancelSwipe(newSwipeOffset), 2 * swipeAnimationDuration);
|
|
88
86
|
}
|
|
89
87
|
else {
|
|
90
88
|
wheelResidualMomentum.current = event.deltaX;
|
package/dist/core/utils.d.ts
CHANGED
package/dist/core/utils.js
CHANGED
|
@@ -36,3 +36,6 @@ export const parseLengthPercentage = (input) => {
|
|
|
36
36
|
}
|
|
37
37
|
return { pixel: 0 };
|
|
38
38
|
};
|
|
39
|
+
export const devicePixelRatio = () => {
|
|
40
|
+
return (typeof window !== "undefined" ? window === null || window === void 0 ? void 0 : window.devicePixelRatio : undefined) || 1;
|
|
41
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { ImageSlideProps } from "../../core/index.js";
|
|
3
|
+
import { ImageSource, SlideImage } from "../../types.js";
|
|
4
|
+
declare type ResponsiveImageSlide = Omit<SlideImage, "srcSet"> & {
|
|
5
|
+
srcSet: [ImageSource, ...ImageSource[]];
|
|
6
|
+
};
|
|
7
|
+
export declare const isResponsiveImageSlide: (slide: SlideImage) => slide is ResponsiveImageSlide;
|
|
8
|
+
declare type ResponsiveImageProps = Omit<ImageSlideProps, "slide" | "rect"> & Required<Pick<ImageSlideProps, "rect">> & {
|
|
9
|
+
slide: ResponsiveImageSlide;
|
|
10
|
+
};
|
|
11
|
+
export declare const ResponsiveImage: React.FC<ResponsiveImageProps>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { devicePixelRatio, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, ImageSlide, useEventCallback, useLayoutEffect, } from "../../core/index.js";
|
|
3
|
+
export const isResponsiveImageSlide = (slide) => { var _a; return (((_a = slide.srcSet) === null || _a === void 0 ? void 0 : _a.length) || 0) > 0; };
|
|
4
|
+
export const ResponsiveImage = (props) => {
|
|
5
|
+
var _a, _b;
|
|
6
|
+
const [state, setState] = React.useState({});
|
|
7
|
+
const { current, preload } = state;
|
|
8
|
+
const { slide: image, rect, imageFit, render } = props;
|
|
9
|
+
const srcSet = image.srcSet.sort((a, b) => a.width - b.width);
|
|
10
|
+
const width = (_a = image.width) !== null && _a !== void 0 ? _a : srcSet[srcSet.length - 1].width;
|
|
11
|
+
const height = (_b = image.height) !== null && _b !== void 0 ? _b : srcSet[srcSet.length - 1].height;
|
|
12
|
+
const cover = image.imageFit === IMAGE_FIT_COVER || (image.imageFit !== IMAGE_FIT_CONTAIN && imageFit === IMAGE_FIT_COVER);
|
|
13
|
+
const maxWidth = Math.max(...srcSet.map((x) => x.width));
|
|
14
|
+
const targetWidth = Math.min((cover ? Math.max : Math.min)(rect.width, width * (rect.height / height)), maxWidth);
|
|
15
|
+
const pixelDensity = devicePixelRatio();
|
|
16
|
+
const handleSourceChange = useEventCallback(() => {
|
|
17
|
+
var _a;
|
|
18
|
+
const targetSource = (_a = srcSet.find((x) => x.width >= targetWidth * pixelDensity)) !== null && _a !== void 0 ? _a : srcSet[srcSet.length - 1];
|
|
19
|
+
if (!current) {
|
|
20
|
+
setState((prev) => ({ ...prev, current: targetSource.src }));
|
|
21
|
+
}
|
|
22
|
+
else if (srcSet.findIndex((x) => x.src === current) < srcSet.findIndex((x) => x === targetSource)) {
|
|
23
|
+
setState((prev) => ({ ...prev, preload: targetSource.src }));
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
useLayoutEffect(handleSourceChange, [rect === null || rect === void 0 ? void 0 : rect.width, rect === null || rect === void 0 ? void 0 : rect.height, pixelDensity, handleSourceChange]);
|
|
27
|
+
const handlePreload = useEventCallback((currentPreload) => {
|
|
28
|
+
if (currentPreload === preload) {
|
|
29
|
+
setState((prev) => ({ ...prev, current: preload, preload: undefined }));
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
const style = {
|
|
33
|
+
"-webkit-transform": "translateZ(0)",
|
|
34
|
+
...(rect.width / rect.height < width / height
|
|
35
|
+
? {
|
|
36
|
+
width: "100%",
|
|
37
|
+
height: "auto",
|
|
38
|
+
}
|
|
39
|
+
: { width: "auto", height: "100%" }),
|
|
40
|
+
};
|
|
41
|
+
return (React.createElement(React.Fragment, null,
|
|
42
|
+
preload && preload !== current && (React.createElement(ImageSlide, { key: preload, ...props, slide: { ...image, src: preload, srcSet: undefined }, style: { position: "absolute", visibility: "hidden", ...style }, onLoad: () => handlePreload(preload), render: {
|
|
43
|
+
...render,
|
|
44
|
+
iconLoading: () => null,
|
|
45
|
+
iconError: () => null,
|
|
46
|
+
} })),
|
|
47
|
+
current && (React.createElement(ImageSlide, { key: current, ...props, slide: { ...image, src: current, srcSet: undefined }, style: style }))));
|
|
48
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { createModule, MODULE_CONTROLLER, PLUGIN_ZOOM } from "../../core/index.js";
|
|
2
|
+
import { createModule, isImageSlide, MODULE_CONTROLLER, PLUGIN_ZOOM } from "../../core/index.js";
|
|
3
3
|
import { ZoomContextProvider } from "./ZoomContext.js";
|
|
4
4
|
import { ZoomButtonsGroup } from "./ZoomButtonsGroup.js";
|
|
5
|
-
import {
|
|
5
|
+
import { ZoomContainer } from "./ZoomContainer.js";
|
|
6
6
|
export const defaultZoomProps = {
|
|
7
7
|
maxZoomPixelRatio: 1,
|
|
8
8
|
zoomInMultiplier: 2,
|
|
@@ -15,14 +15,17 @@ export const defaultZoomProps = {
|
|
|
15
15
|
scrollToZoom: false,
|
|
16
16
|
};
|
|
17
17
|
export const Zoom = ({ augment, append }) => {
|
|
18
|
-
augment(({ toolbar: { buttons, ...restToolbar }, render, carousel, animation, zoom, ...restProps }) => ({
|
|
18
|
+
augment(({ toolbar: { buttons, ...restToolbar }, render, carousel, animation, zoom, on, ...restProps }) => ({
|
|
19
19
|
toolbar: {
|
|
20
20
|
buttons: [React.createElement(ZoomButtonsGroup, { key: PLUGIN_ZOOM, labels: restProps.labels, render: render }), ...buttons],
|
|
21
21
|
...restToolbar,
|
|
22
22
|
},
|
|
23
23
|
render: {
|
|
24
24
|
...render,
|
|
25
|
-
slide: (slide, offset, rect) =>
|
|
25
|
+
slide: (slide, offset, rect) => {
|
|
26
|
+
var _a;
|
|
27
|
+
return isImageSlide(slide) ? (React.createElement(ZoomContainer, { slide: slide, offset: offset, rect: rect, render: render, carousel: carousel, animation: animation, zoom: zoom, on: on })) : ((_a = render.slide) === null || _a === void 0 ? void 0 : _a.call(render, slide, offset, rect));
|
|
28
|
+
},
|
|
26
29
|
},
|
|
27
30
|
zoom: {
|
|
28
31
|
...defaultZoomProps,
|
|
@@ -30,6 +33,7 @@ export const Zoom = ({ augment, append }) => {
|
|
|
30
33
|
},
|
|
31
34
|
carousel,
|
|
32
35
|
animation,
|
|
36
|
+
on,
|
|
33
37
|
...restProps,
|
|
34
38
|
}));
|
|
35
39
|
append(MODULE_CONTROLLER, createModule(PLUGIN_ZOOM, ZoomContextProvider));
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { ContainerRect } from "../../core/index.js";
|
|
3
3
|
import { LightboxProps, Slide } from "../../types.js";
|
|
4
4
|
/** Zoom container */
|
|
5
|
-
export declare const ZoomContainer: React.FC<Pick<LightboxProps, "render" | "carousel" | "zoom" | "animation"> & {
|
|
5
|
+
export declare const ZoomContainer: React.FC<Pick<LightboxProps, "render" | "carousel" | "zoom" | "animation" | "on"> & {
|
|
6
6
|
slide: Slide;
|
|
7
7
|
offset: number;
|
|
8
8
|
rect: ContainerRect;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { CLASS_FLEX_CENTER, CLASS_FULLSIZE, cleanup, clsx, cssClass, EVENT_ON_KEY_DOWN, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, ImageSlide, isImageSlide, round, useContainerRect, useController, useEventCallback, useEvents, useLayoutEffect, useMotionPreference, } from "../../core/index.js";
|
|
2
|
+
import { CLASS_FLEX_CENTER, CLASS_FULLSIZE, cleanup, clsx, cssClass, EVENT_ON_KEY_DOWN, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, ImageSlide, isImageSlide, round, useContainerRect, useController, useEventCallback, useEvents, useLayoutEffect, useLightboxState, useMotionPreference, } from "../../core/index.js";
|
|
3
3
|
import { useZoom } from "./ZoomContext.js";
|
|
4
4
|
import { defaultZoomProps } from "./Zoom.js";
|
|
5
5
|
import { ACTION_ZOOM_IN, ACTION_ZOOM_OUT } from "./index.js";
|
|
6
|
+
import { isResponsiveImageSlide, ResponsiveImage } from "./ResponsiveImage.js";
|
|
6
7
|
const getSlideRects = (slide, cover, maxZoomPixelRatio, rect) => {
|
|
7
8
|
var _a, _b;
|
|
8
9
|
let slideRect = { width: 0, height: 0 };
|
|
@@ -35,12 +36,14 @@ const getSlideRects = (slide, cover, maxZoomPixelRatio, rect) => {
|
|
|
35
36
|
return { slideRect, maxSlideRect };
|
|
36
37
|
};
|
|
37
38
|
const distance = (pointerA, pointerB) => ((pointerA.clientX - pointerB.clientX) ** 2 + (pointerA.clientY - pointerB.clientY) ** 2) ** 0.5;
|
|
38
|
-
export const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom: originalZoomProps }) => {
|
|
39
|
+
export const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom: originalZoomProps, on }) => {
|
|
39
40
|
var _a;
|
|
40
41
|
const zoomProps = { ...defaultZoomProps, ...originalZoomProps };
|
|
42
|
+
const { state: { currentIndex }, } = useLightboxState();
|
|
41
43
|
const [zoom, setZoom] = React.useState(1);
|
|
42
44
|
const [offsetX, setOffsetX] = React.useState(0);
|
|
43
45
|
const [offsetY, setOffsetY] = React.useState(0);
|
|
46
|
+
const [imageDimensions, setImageDimensions] = React.useState();
|
|
44
47
|
const activePointers = React.useRef([]);
|
|
45
48
|
const lastPointerDown = React.useRef(0);
|
|
46
49
|
const zoomAnimation = React.useRef();
|
|
@@ -51,7 +54,7 @@ export const ZoomContainer = ({ slide, offset, rect, render, carousel, animation
|
|
|
51
54
|
const { subscribeSensors, containerRef: controllerRef, containerRect: controllerRect } = useController();
|
|
52
55
|
const { subscribe } = useEvents();
|
|
53
56
|
const reduceMotion = useMotionPreference();
|
|
54
|
-
const { slideRect, maxSlideRect: currentMaxSlideRect } = getSlideRects(slide, carousel.imageFit === "cover" || ("imageFit" in slide && slide.imageFit === "cover"), zoomProps.maxZoomPixelRatio, containerRect);
|
|
57
|
+
const { slideRect, maxSlideRect: currentMaxSlideRect } = getSlideRects({ ...slide, ...imageDimensions }, carousel.imageFit === "cover" || ("imageFit" in slide && slide.imageFit === "cover"), zoomProps.maxZoomPixelRatio, containerRect);
|
|
55
58
|
const maxZoom = slideRect.width ? Math.max(round(currentMaxSlideRect.width / slideRect.width, 5), 1) : 1;
|
|
56
59
|
const changeOffsets = useEventCallback((dx, dy, targetZoom) => {
|
|
57
60
|
const newZoom = targetZoom || zoom;
|
|
@@ -295,7 +298,15 @@ export const ZoomContainer = ({ slide, offset, rect, render, carousel, animation
|
|
|
295
298
|
: rect;
|
|
296
299
|
let rendered = (_a = render.slide) === null || _a === void 0 ? void 0 : _a.call(render, slide, offset, scaledRect);
|
|
297
300
|
if (!rendered && isImageSlide(slide)) {
|
|
298
|
-
|
|
301
|
+
const slideProps = {
|
|
302
|
+
slide,
|
|
303
|
+
offset,
|
|
304
|
+
rect,
|
|
305
|
+
render,
|
|
306
|
+
imageFit: carousel.imageFit,
|
|
307
|
+
onClick: offset === 0 ? () => { var _a; return (_a = on.click) === null || _a === void 0 ? void 0 : _a.call(on, currentIndex); } : undefined,
|
|
308
|
+
};
|
|
309
|
+
rendered = isResponsiveImageSlide(slide) ? (React.createElement(ResponsiveImage, { ...slideProps, slide: slide, rect: scaledRect })) : (React.createElement(ImageSlide, { onLoad: (img) => setImageDimensions({ width: img.naturalWidth, height: img.naturalHeight }), ...slideProps }));
|
|
299
310
|
}
|
|
300
311
|
return rendered ? (React.createElement("div", { ref: setContainerRef, className: clsx(cssClass(CLASS_FULLSIZE), cssClass(CLASS_FLEX_CENTER)), ...(offset === 0
|
|
301
312
|
? { style: { transform: `scale(${zoom}) translateX(${offsetX}px) translateY(${offsetY}px)` } }
|
|
@@ -5,7 +5,6 @@ declare type ZoomContextType = {
|
|
|
5
5
|
isZoomSupported: boolean;
|
|
6
6
|
setIsMinZoom: (value: boolean) => void;
|
|
7
7
|
setIsMaxZoom: (value: boolean) => void;
|
|
8
|
-
setIsZoomSupported: (value: boolean) => void;
|
|
9
8
|
};
|
|
10
9
|
export declare const useZoom: () => ZoomContextType;
|
|
11
10
|
export declare const ZoomContextProvider: Component;
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { makeUseContext } from "../../core/index.js";
|
|
2
|
+
import { isImageSlide, makeUseContext, useEventCallback, useLayoutEffect, useLightboxState } from "../../core/index.js";
|
|
3
3
|
const ZoomContext = React.createContext(null);
|
|
4
4
|
export const useZoom = makeUseContext("useZoom", "ZoomContext", ZoomContext);
|
|
5
|
-
export const ZoomContextProvider = ({ children }) => {
|
|
5
|
+
export const ZoomContextProvider = ({ slides, children }) => {
|
|
6
6
|
const [isMinZoom, setIsMinZoom] = React.useState(false);
|
|
7
7
|
const [isMaxZoom, setIsMaxZoom] = React.useState(false);
|
|
8
8
|
const [isZoomSupported, setIsZoomSupported] = React.useState(false);
|
|
9
|
+
const { state: { currentIndex }, } = useLightboxState();
|
|
10
|
+
const updateZoomSupported = useEventCallback(() => setIsZoomSupported(isImageSlide(slides[currentIndex])));
|
|
11
|
+
useLayoutEffect(updateZoomSupported, [currentIndex, updateZoomSupported]);
|
|
9
12
|
const context = React.useMemo(() => ({
|
|
10
13
|
isMinZoom,
|
|
11
14
|
isMaxZoom,
|
|
12
15
|
isZoomSupported,
|
|
13
16
|
setIsMinZoom,
|
|
14
17
|
setIsMaxZoom,
|
|
15
|
-
setIsZoomSupported,
|
|
16
18
|
}), [isMinZoom, isMaxZoom, isZoomSupported]);
|
|
17
19
|
return React.createElement(ZoomContext.Provider, { value: context }, children);
|
|
18
20
|
};
|
package/dist/styles.css
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
-moz-user-select: none;
|
|
27
27
|
user-select: none;
|
|
28
28
|
touch-action: var(--yarl__controller_touch_action, none);
|
|
29
|
+
overscroll-behavior: var(--yarl__controller_overscroll-behavior, contain);
|
|
29
30
|
}
|
|
30
31
|
.yarl__carousel {
|
|
31
32
|
display: flex;
|
|
@@ -56,8 +57,11 @@
|
|
|
56
57
|
--yarl__direction: -1;
|
|
57
58
|
}
|
|
58
59
|
.yarl__slide_image {
|
|
60
|
+
max-width: 100%;
|
|
61
|
+
max-height: 100%;
|
|
59
62
|
-o-object-fit: contain;
|
|
60
63
|
object-fit: contain;
|
|
64
|
+
pointer-events: none;
|
|
61
65
|
-moz-user-select: none;
|
|
62
66
|
user-select: none;
|
|
63
67
|
-webkit-user-select: none;
|
package/dist/types.d.ts
CHANGED
|
@@ -2,6 +2,16 @@ import * as React from "react";
|
|
|
2
2
|
import { ContainerRect } from "./core/hooks/useContainerRect.js";
|
|
3
3
|
/** Image fit setting */
|
|
4
4
|
export declare type ImageFit = "contain" | "cover";
|
|
5
|
+
/** Image source */
|
|
6
|
+
export interface ImageSource {
|
|
7
|
+
/** image URL */
|
|
8
|
+
src: string;
|
|
9
|
+
/** image width in pixels */
|
|
10
|
+
width: number;
|
|
11
|
+
/** image height in pixels */
|
|
12
|
+
height: number;
|
|
13
|
+
}
|
|
14
|
+
/** Generic slide */
|
|
5
15
|
export interface GenericSlide {
|
|
6
16
|
}
|
|
7
17
|
/** Image slide properties */
|
|
@@ -19,14 +29,7 @@ export interface SlideImage extends GenericSlide {
|
|
|
19
29
|
/** `object-fit` setting */
|
|
20
30
|
imageFit?: ImageFit;
|
|
21
31
|
/** alternative images to be passed to the 'srcSet' */
|
|
22
|
-
srcSet?:
|
|
23
|
-
/** image URL */
|
|
24
|
-
src: string;
|
|
25
|
-
/** image width in pixels */
|
|
26
|
-
width: number;
|
|
27
|
-
/** image height in pixels */
|
|
28
|
-
height: number;
|
|
29
|
-
}[];
|
|
32
|
+
srcSet?: ImageSource[];
|
|
30
33
|
}
|
|
31
34
|
/** Supported slide types */
|
|
32
35
|
export interface SlideTypes {
|
package/package.json
CHANGED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { useEventCallback } from "../../hooks/useEventCallback.js";
|
|
3
|
-
export const useOffset = (onChange) => {
|
|
4
|
-
const [offset, setOffset] = React.useState(0);
|
|
5
|
-
const handleOnChange = useEventCallback(() => {
|
|
6
|
-
onChange(offset);
|
|
7
|
-
});
|
|
8
|
-
React.useEffect(handleOnChange, [offset, handleOnChange]);
|
|
9
|
-
return [offset, setOffset];
|
|
10
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { ImageSlide, isImageSlide, useEventCallback } from "../../core/index.js";
|
|
3
|
-
import { ZoomContainer } from "./ZoomContainer.js";
|
|
4
|
-
import { useZoom } from "./ZoomContext.js";
|
|
5
|
-
export const ZoomWrapper = ({ slide, offset, rect, render, carousel, animation, zoom }) => {
|
|
6
|
-
var _a;
|
|
7
|
-
const { setIsZoomSupported, isZoomSupported } = useZoom();
|
|
8
|
-
const zoomSupported = isImageSlide(slide) && (Boolean(slide.srcSet) || Boolean(slide.width && slide.height));
|
|
9
|
-
const updateIsZoomSupported = useEventCallback(() => {
|
|
10
|
-
if (offset === 0 && zoomSupported !== isZoomSupported) {
|
|
11
|
-
setIsZoomSupported(zoomSupported);
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
React.useEffect(updateIsZoomSupported, [offset, updateIsZoomSupported]);
|
|
15
|
-
if (zoomSupported) {
|
|
16
|
-
return (React.createElement(ZoomContainer, { slide: slide, offset: offset, rect: rect, render: render, carousel: carousel, animation: animation, zoom: zoom }));
|
|
17
|
-
}
|
|
18
|
-
const rendered = (_a = render.slide) === null || _a === void 0 ? void 0 : _a.call(render, slide, offset, rect);
|
|
19
|
-
if (rendered) {
|
|
20
|
-
return React.createElement(React.Fragment, null, rendered);
|
|
21
|
-
}
|
|
22
|
-
if (isImageSlide(slide)) {
|
|
23
|
-
return React.createElement(ImageSlide, { slide: slide, offset: offset, rect: rect, render: render, imageFit: carousel.imageFit });
|
|
24
|
-
}
|
|
25
|
-
return null;
|
|
26
|
-
};
|