yet-another-react-lightbox 1.8.0 → 1.9.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/README.md +10 -7
- package/dist/core/components/IconButton.d.ts +1 -1
- package/dist/core/components/IconButton.js +2 -1
- package/dist/core/components/ImageSlide.js +23 -27
- package/dist/core/contexts/Timeouts.js +2 -2
- package/dist/core/hooks/index.d.ts +2 -0
- package/dist/core/hooks/index.js +2 -0
- package/dist/core/hooks/useMotionPreference.d.ts +1 -0
- package/dist/core/hooks/useMotionPreference.js +12 -0
- package/dist/core/hooks/useRTL.d.ts +1 -0
- package/dist/core/hooks/useRTL.js +9 -0
- package/dist/core/hooks/useSensors.js +5 -2
- package/dist/core/modules/Controller.d.ts +4 -2
- package/dist/core/modules/Controller.js +20 -16
- package/dist/core/modules/Navigation.js +8 -6
- package/dist/core/utils.d.ts +2 -1
- package/dist/core/utils.js +5 -1
- package/dist/plugins/Captions.js +2 -2
- package/dist/plugins/Fullscreen.d.ts +2 -3
- package/dist/plugins/Thumbnails.d.ts +7 -6
- package/dist/plugins/Thumbnails.js +30 -33
- package/dist/plugins/Zoom.d.ts +51 -0
- package/dist/plugins/Zoom.js +423 -0
- package/dist/plugins/captions.css +0 -1
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/thumbnails.css +0 -1
- package/dist/styles.css +1 -3
- package/dist/types.d.ts +11 -1
- package/package.json +17 -13
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { LightboxProps, Plugin } from "../types.js";
|
|
3
|
+
/** Custom zoom button render function */
|
|
4
|
+
declare type RenderZoomButton = ({ ref, labels, disabled, onClick, onFocus, onBlur, }: Pick<LightboxProps, "labels"> & {
|
|
5
|
+
ref: React.ForwardedRef<HTMLButtonElement>;
|
|
6
|
+
disabled: boolean;
|
|
7
|
+
onClick: () => void;
|
|
8
|
+
onFocus: () => void;
|
|
9
|
+
onBlur: () => void;
|
|
10
|
+
}) => React.ReactNode;
|
|
11
|
+
declare module "../types.js" {
|
|
12
|
+
interface LightboxProps {
|
|
13
|
+
/** Zoom plugin settings */
|
|
14
|
+
zoom?: {
|
|
15
|
+
/** ratio of image pixels to physical pixels at maximum zoom level */
|
|
16
|
+
maxZoomPixelRatio?: number;
|
|
17
|
+
/** zoom-in multiplier */
|
|
18
|
+
zoomInMultiplier?: number;
|
|
19
|
+
/** double-tap maximum time delay */
|
|
20
|
+
doubleTapDelay?: number;
|
|
21
|
+
/** double-click maximum time delay */
|
|
22
|
+
doubleClickDelay?: number;
|
|
23
|
+
/** maximum number of zoom-in stops via double-click or double-tap */
|
|
24
|
+
doubleClickMaxStops?: number;
|
|
25
|
+
/** keyboard move distance */
|
|
26
|
+
keyboardMoveDistance?: number;
|
|
27
|
+
/** wheel zoom distance factor */
|
|
28
|
+
wheelZoomDistanceFactor?: number;
|
|
29
|
+
/** pinch zoom distance factor */
|
|
30
|
+
pinchZoomDistanceFactor?: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
interface AnimationSettings {
|
|
34
|
+
/** zoom animation duration */
|
|
35
|
+
zoom?: number;
|
|
36
|
+
}
|
|
37
|
+
interface Render {
|
|
38
|
+
/** render custom Zoom in button */
|
|
39
|
+
buttonZoomIn?: RenderZoomButton;
|
|
40
|
+
/** render custom Zoom in button */
|
|
41
|
+
buttonZoomOut?: RenderZoomButton;
|
|
42
|
+
/** render custom Zoom in icon */
|
|
43
|
+
iconZoomIn?: () => React.ReactNode;
|
|
44
|
+
/** render custom Zoom out icon */
|
|
45
|
+
iconZoomOut?: () => React.ReactNode;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export declare const ZoomModule: import("../types.js").Module;
|
|
49
|
+
/** Zoom plugin */
|
|
50
|
+
export declare const Zoom: Plugin;
|
|
51
|
+
export default Zoom;
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { adjustDevicePixelRatio, cleanup, clsx, createIcon, createModule, cssClass, IconButton, ImageSlide, label, makeUseContext, round, useContainerRect, useController, useEnhancedEffect, useEvents, useMotionPreference, } from "../core/index.js";
|
|
3
|
+
const defaultZoomProps = {
|
|
4
|
+
maxZoomPixelRatio: 1,
|
|
5
|
+
zoomInMultiplier: 2,
|
|
6
|
+
doubleTapDelay: 300,
|
|
7
|
+
doubleClickDelay: 500,
|
|
8
|
+
doubleClickMaxStops: 2,
|
|
9
|
+
keyboardMoveDistance: 50,
|
|
10
|
+
wheelZoomDistanceFactor: 100,
|
|
11
|
+
pinchZoomDistanceFactor: 100,
|
|
12
|
+
};
|
|
13
|
+
const ZoomInIcon = createIcon("ZoomIn", React.createElement(React.Fragment, null,
|
|
14
|
+
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" }),
|
|
15
|
+
React.createElement("path", { d: "M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z" })));
|
|
16
|
+
const ZoomOutIcon = createIcon("ZoomOut", 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 14zM7 9h5v1H7z" }));
|
|
17
|
+
const ZoomContext = React.createContext(null);
|
|
18
|
+
const useZoom = makeUseContext("useZoom", "ZoomContext", ZoomContext);
|
|
19
|
+
const ZoomContextProvider = ({ children }) => {
|
|
20
|
+
const [minZoom, setMinZoom] = React.useState(false);
|
|
21
|
+
const [maxZoom, setMaxZoom] = React.useState(false);
|
|
22
|
+
const [zoomSupported, setZoomSupported] = React.useState(false);
|
|
23
|
+
const context = React.useMemo(() => ({
|
|
24
|
+
minZoom,
|
|
25
|
+
maxZoom,
|
|
26
|
+
zoomSupported,
|
|
27
|
+
setMinZoom,
|
|
28
|
+
setMaxZoom,
|
|
29
|
+
setZoomSupported,
|
|
30
|
+
}), [minZoom, maxZoom, zoomSupported]);
|
|
31
|
+
return React.createElement(ZoomContext.Provider, { value: context }, children);
|
|
32
|
+
};
|
|
33
|
+
const ZoomButton = React.forwardRef(({ labels, render, zoomIn, onLoseFocus }, ref) => {
|
|
34
|
+
const wasEnabled = React.useRef(false);
|
|
35
|
+
const wasFocused = React.useRef(false);
|
|
36
|
+
const { zoomSupported, minZoom, maxZoom } = useZoom();
|
|
37
|
+
const { publish } = useEvents();
|
|
38
|
+
const disabled = !zoomSupported || (zoomIn ? maxZoom : minZoom);
|
|
39
|
+
const onClick = () => publish(zoomIn ? "zoom-in" : "zoom-out");
|
|
40
|
+
const onFocus = React.useCallback(() => {
|
|
41
|
+
wasFocused.current = true;
|
|
42
|
+
}, []);
|
|
43
|
+
const onBlur = React.useCallback(() => {
|
|
44
|
+
wasFocused.current = false;
|
|
45
|
+
}, []);
|
|
46
|
+
React.useEffect(() => {
|
|
47
|
+
if (disabled && wasEnabled.current && wasFocused.current) {
|
|
48
|
+
onLoseFocus();
|
|
49
|
+
}
|
|
50
|
+
if (!disabled) {
|
|
51
|
+
wasEnabled.current = true;
|
|
52
|
+
}
|
|
53
|
+
}, [disabled, onLoseFocus]);
|
|
54
|
+
if (zoomIn && render.buttonZoomIn)
|
|
55
|
+
return (React.createElement(React.Fragment, null, render.buttonZoomIn({
|
|
56
|
+
ref,
|
|
57
|
+
labels,
|
|
58
|
+
disabled,
|
|
59
|
+
onClick,
|
|
60
|
+
onFocus,
|
|
61
|
+
onBlur,
|
|
62
|
+
})));
|
|
63
|
+
if (zoomIn && render.buttonZoomOut)
|
|
64
|
+
return (React.createElement(React.Fragment, null, render.buttonZoomOut({
|
|
65
|
+
ref,
|
|
66
|
+
labels,
|
|
67
|
+
disabled,
|
|
68
|
+
onClick,
|
|
69
|
+
onFocus,
|
|
70
|
+
onBlur,
|
|
71
|
+
})));
|
|
72
|
+
return (React.createElement(IconButton, { ref: ref, label: label(labels, zoomIn ? "Zoom in" : "Zoom out"), icon: zoomIn ? ZoomInIcon : ZoomOutIcon, renderIcon: zoomIn ? render.iconZoomIn : render.iconZoomOut, disabled: disabled, onClick: onClick, onFocus: onFocus, onBlur: onBlur }));
|
|
73
|
+
});
|
|
74
|
+
ZoomButton.displayName = "ZoomButton";
|
|
75
|
+
const ZoomButtonsGroup = ({ labels, render }) => {
|
|
76
|
+
const zoomInRef = React.useRef(null);
|
|
77
|
+
const zoomOutRef = React.useRef(null);
|
|
78
|
+
const { transferFocus } = useController();
|
|
79
|
+
const focusSibling = React.useCallback((sibling) => {
|
|
80
|
+
var _a, _b;
|
|
81
|
+
if (!((_a = sibling.current) === null || _a === void 0 ? void 0 : _a.disabled)) {
|
|
82
|
+
(_b = sibling.current) === null || _b === void 0 ? void 0 : _b.focus();
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
transferFocus();
|
|
86
|
+
}
|
|
87
|
+
}, [transferFocus]);
|
|
88
|
+
const focusZoomIn = React.useCallback(() => focusSibling(zoomInRef), [focusSibling]);
|
|
89
|
+
const focusZoomOut = React.useCallback(() => focusSibling(zoomOutRef), [focusSibling]);
|
|
90
|
+
return (React.createElement(React.Fragment, null,
|
|
91
|
+
React.createElement(ZoomButton, { ref: zoomInRef, key: "zoomIn", zoomIn: true, labels: labels, render: render, onLoseFocus: focusZoomOut }),
|
|
92
|
+
React.createElement(ZoomButton, { ref: zoomOutRef, key: "zoomOut", labels: labels, render: render, onLoseFocus: focusZoomIn })));
|
|
93
|
+
};
|
|
94
|
+
const getSlideRects = (slide, cover, maxZoomPixelRatio, rect) => {
|
|
95
|
+
var _a, _b, _c;
|
|
96
|
+
let slideRect = { width: 0, height: 0 };
|
|
97
|
+
let maxSlideRect = { width: 0, height: 0 };
|
|
98
|
+
if (rect && !("type" in slide) && "src" in slide) {
|
|
99
|
+
const width = Math.max(...(((_a = slide.srcSet) === null || _a === void 0 ? void 0 : _a.map((x) => x.width)) || []).concat(slide.width ? [slide.width] : []));
|
|
100
|
+
const height = Math.max(...((_c = (_b = slide.srcSet) === null || _b === void 0 ? void 0 : _b.map((x) => x.height).filter((x) => Boolean(x))) !== null && _c !== void 0 ? _c : (slide.aspectRatio ? [width / slide.aspectRatio] : [])).concat(slide.height ? [slide.height] : []));
|
|
101
|
+
if (width > 0 && height > 0 && rect.width > 0 && rect.height > 0) {
|
|
102
|
+
maxSlideRect = cover
|
|
103
|
+
? {
|
|
104
|
+
width: Math.round(Math.min(width, (rect.width / rect.height) * height)),
|
|
105
|
+
height: Math.round(Math.min(height, (rect.height / rect.width) * width)),
|
|
106
|
+
}
|
|
107
|
+
: { width, height };
|
|
108
|
+
maxSlideRect = {
|
|
109
|
+
width: adjustDevicePixelRatio(maxSlideRect.width) * maxZoomPixelRatio,
|
|
110
|
+
height: adjustDevicePixelRatio(maxSlideRect.height) * maxZoomPixelRatio,
|
|
111
|
+
};
|
|
112
|
+
slideRect = cover
|
|
113
|
+
? {
|
|
114
|
+
width: Math.min(rect.width, maxSlideRect.width),
|
|
115
|
+
height: Math.min(rect.height, maxSlideRect.height),
|
|
116
|
+
}
|
|
117
|
+
: {
|
|
118
|
+
width: Math.round(Math.min(rect.width, (rect.height / height) * width)),
|
|
119
|
+
height: Math.round(Math.min(rect.height, (rect.width / width) * height)),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { slideRect, maxSlideRect };
|
|
124
|
+
};
|
|
125
|
+
const distance = (pointerA, pointerB) => ((pointerA.clientX - pointerB.clientX) ** 2 + (pointerA.clientY - pointerB.clientY) ** 2) ** 0.5;
|
|
126
|
+
const ZoomContainer = ({ slide, offset, rect, render, carousel, animation, zoom: originalZoomProps }) => {
|
|
127
|
+
var _a;
|
|
128
|
+
const zoomProps = { ...defaultZoomProps, ...originalZoomProps };
|
|
129
|
+
const { setMinZoom: currentSetMinZoom, setMaxZoom: currentSetMaxZoom } = useZoom();
|
|
130
|
+
const { setContainerRef, containerRef: currentContainerRef, containerRect: currentContainerRect, } = useContainerRect();
|
|
131
|
+
const { subscribeSensors, containerRef: currentControllerRef, containerRect: currentControllerRect, } = useController();
|
|
132
|
+
const { subscribe } = useEvents();
|
|
133
|
+
const currentReduceMotion = useMotionPreference();
|
|
134
|
+
const { slideRect: currentSlideRect, maxSlideRect: currentMaxSlideRect } = getSlideRects(slide, carousel.imageFit === "cover" || ("imageFit" in slide && slide.imageFit === "cover"), zoomProps.maxZoomPixelRatio, currentContainerRect);
|
|
135
|
+
const currentMaxZoom = currentSlideRect.width ? round(currentMaxSlideRect.width / currentSlideRect.width, 3) : 1;
|
|
136
|
+
const [state, setState] = React.useState({ zoom: 1, offsetX: 0, offsetY: 0 });
|
|
137
|
+
const refs = React.useRef({
|
|
138
|
+
state,
|
|
139
|
+
slideRect: currentSlideRect,
|
|
140
|
+
containerRef: currentContainerRef,
|
|
141
|
+
controllerRef: currentControllerRef,
|
|
142
|
+
containerRect: currentContainerRect,
|
|
143
|
+
controllerRect: currentControllerRect,
|
|
144
|
+
maxZoom: currentMaxZoom,
|
|
145
|
+
reduceMotion: currentReduceMotion,
|
|
146
|
+
setMinZoom: currentSetMinZoom,
|
|
147
|
+
setMaxZoom: currentSetMaxZoom,
|
|
148
|
+
activePointers: [],
|
|
149
|
+
lastPointerDown: 0,
|
|
150
|
+
zoomProps,
|
|
151
|
+
});
|
|
152
|
+
refs.current.state = state;
|
|
153
|
+
refs.current.slideRect = currentSlideRect;
|
|
154
|
+
refs.current.containerRef = currentContainerRef;
|
|
155
|
+
refs.current.controllerRef = currentControllerRef;
|
|
156
|
+
refs.current.containerRect = currentContainerRect;
|
|
157
|
+
refs.current.controllerRect = currentControllerRect;
|
|
158
|
+
refs.current.maxZoom = currentMaxZoom;
|
|
159
|
+
refs.current.reduceMotion = currentReduceMotion;
|
|
160
|
+
refs.current.setMinZoom = currentSetMinZoom;
|
|
161
|
+
refs.current.setMaxZoom = currentSetMaxZoom;
|
|
162
|
+
refs.current.zoomAnimationDuration = animation.zoom;
|
|
163
|
+
refs.current.zoomProps = zoomProps;
|
|
164
|
+
const changeOffsets = React.useCallback((dx, dy, newZoom) => {
|
|
165
|
+
const { state: { zoom, offsetX, offsetY }, containerRect, slideRect, } = refs.current;
|
|
166
|
+
const targetZoom = newZoom || zoom;
|
|
167
|
+
const newOffsetX = offsetX - (dx || 0);
|
|
168
|
+
const newOffsetY = offsetY - (dy || 0);
|
|
169
|
+
const maxOffsetX = containerRect ? (slideRect.width * targetZoom - containerRect.width) / 2 / targetZoom : 0;
|
|
170
|
+
const maxOffsetY = containerRect ? (slideRect.height * targetZoom - containerRect.height) / 2 / targetZoom : 0;
|
|
171
|
+
setState((prev) => ({
|
|
172
|
+
...prev,
|
|
173
|
+
offsetX: Math.min(Math.abs(newOffsetX), Math.max(maxOffsetX, 0)) * Math.sign(newOffsetX),
|
|
174
|
+
offsetY: Math.min(Math.abs(newOffsetY), Math.max(maxOffsetY, 0)) * Math.sign(newOffsetY),
|
|
175
|
+
}));
|
|
176
|
+
}, []);
|
|
177
|
+
useEnhancedEffect(() => {
|
|
178
|
+
if (refs.current.state.zoom > 1) {
|
|
179
|
+
changeOffsets();
|
|
180
|
+
}
|
|
181
|
+
}, [currentControllerRect.width, currentControllerRect.height, changeOffsets]);
|
|
182
|
+
useEnhancedEffect(() => {
|
|
183
|
+
var _a, _b;
|
|
184
|
+
const { current } = refs;
|
|
185
|
+
const { zoomAnimation, zoomAnimationStart, zoomAnimationDuration, reduceMotion, containerRef } = current;
|
|
186
|
+
zoomAnimation === null || zoomAnimation === void 0 ? void 0 : zoomAnimation.cancel();
|
|
187
|
+
if (zoomAnimationStart && containerRef.current) {
|
|
188
|
+
current.zoomAnimation = (_b = (_a = containerRef.current).animate) === null || _b === void 0 ? void 0 : _b.call(_a, [
|
|
189
|
+
{ transform: zoomAnimationStart },
|
|
190
|
+
{
|
|
191
|
+
transform: `scale(${state.zoom}) translate3d(${state.offsetX}px, ${state.offsetY}px, 0)`,
|
|
192
|
+
},
|
|
193
|
+
], {
|
|
194
|
+
duration: reduceMotion ? 0 : zoomAnimationDuration !== null && zoomAnimationDuration !== void 0 ? zoomAnimationDuration : 500,
|
|
195
|
+
easing: zoomAnimation ? "ease-out" : "ease-in-out",
|
|
196
|
+
});
|
|
197
|
+
current.zoomAnimationStart = undefined;
|
|
198
|
+
if (current.zoomAnimation) {
|
|
199
|
+
current.zoomAnimation.onfinish = () => {
|
|
200
|
+
current.zoomAnimation = undefined;
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}, [state.zoom, state.offsetX, state.offsetY]);
|
|
205
|
+
useEnhancedEffect(() => {
|
|
206
|
+
if (offset === 0) {
|
|
207
|
+
const { setMinZoom, setMaxZoom, maxZoom } = refs.current;
|
|
208
|
+
setMinZoom(state.zoom <= 1);
|
|
209
|
+
setMaxZoom(state.zoom >= maxZoom);
|
|
210
|
+
}
|
|
211
|
+
}, [offset, state.zoom]);
|
|
212
|
+
useEnhancedEffect(() => {
|
|
213
|
+
if (offset === 0) {
|
|
214
|
+
const { setMinZoom, setMaxZoom } = refs.current;
|
|
215
|
+
const resetZoom = () => {
|
|
216
|
+
setState({ zoom: 1, offsetX: 0, offsetY: 0 });
|
|
217
|
+
setMinZoom(true);
|
|
218
|
+
setMaxZoom(false);
|
|
219
|
+
};
|
|
220
|
+
resetZoom();
|
|
221
|
+
return () => {
|
|
222
|
+
resetZoom();
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return () => { };
|
|
226
|
+
}, [offset]);
|
|
227
|
+
const changeZoom = React.useCallback((value, rapid, dx, dy) => {
|
|
228
|
+
const { current } = refs;
|
|
229
|
+
const { state: { zoom }, containerRef, containerRect, maxZoom, } = current;
|
|
230
|
+
if (!containerRef.current || !containerRect)
|
|
231
|
+
return;
|
|
232
|
+
const newZoom = round(Math.min(Math.max(value, 1), maxZoom), 5);
|
|
233
|
+
if (newZoom === zoom)
|
|
234
|
+
return;
|
|
235
|
+
if (!rapid) {
|
|
236
|
+
current.zoomAnimationStart = window.getComputedStyle(containerRef.current).transform;
|
|
237
|
+
}
|
|
238
|
+
changeOffsets(dx ? dx * (1 / zoom - 1 / newZoom) : 0, dy ? dy * (1 / zoom - 1 / newZoom) : 0, newZoom);
|
|
239
|
+
setState((prev) => ({ ...prev, zoom: newZoom }));
|
|
240
|
+
}, [changeOffsets]);
|
|
241
|
+
const translateCoordinates = React.useCallback((event) => {
|
|
242
|
+
const { controllerRef } = refs.current;
|
|
243
|
+
if (controllerRef.current) {
|
|
244
|
+
const { pageX, pageY } = event;
|
|
245
|
+
const { scrollX, scrollY } = window;
|
|
246
|
+
const { left, top, width, height } = controllerRef.current.getBoundingClientRect();
|
|
247
|
+
return [pageX - left - scrollX - width / 2, pageY - top - scrollY - height / 2];
|
|
248
|
+
}
|
|
249
|
+
return [];
|
|
250
|
+
}, []);
|
|
251
|
+
const onKeyDown = React.useCallback((event) => {
|
|
252
|
+
const { state: { zoom }, zoomProps: { keyboardMoveDistance, zoomInMultiplier }, } = refs.current;
|
|
253
|
+
if (zoom > 1) {
|
|
254
|
+
const move = (deltaX, deltaY) => {
|
|
255
|
+
event.stopPropagation();
|
|
256
|
+
changeOffsets(deltaX, deltaY);
|
|
257
|
+
};
|
|
258
|
+
if (event.key === "ArrowDown") {
|
|
259
|
+
move(0, keyboardMoveDistance);
|
|
260
|
+
}
|
|
261
|
+
else if (event.key === "ArrowUp") {
|
|
262
|
+
move(0, -keyboardMoveDistance);
|
|
263
|
+
}
|
|
264
|
+
else if (event.key === "ArrowLeft") {
|
|
265
|
+
move(-keyboardMoveDistance, 0);
|
|
266
|
+
}
|
|
267
|
+
else if (event.key === "ArrowRight") {
|
|
268
|
+
move(keyboardMoveDistance, 0);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (event.key === "+" || (event.key === "=" && event.metaKey)) {
|
|
272
|
+
changeZoom(zoom * zoomInMultiplier);
|
|
273
|
+
}
|
|
274
|
+
else if (event.key === "-" || (event.key === "_" && event.metaKey)) {
|
|
275
|
+
changeZoom(zoom / zoomInMultiplier);
|
|
276
|
+
}
|
|
277
|
+
else if (event.key === "0" && event.metaKey) {
|
|
278
|
+
changeZoom(1);
|
|
279
|
+
}
|
|
280
|
+
}, [changeZoom, changeOffsets]);
|
|
281
|
+
const onWheel = React.useCallback((event) => {
|
|
282
|
+
const { state: { zoom }, zoomProps: { wheelZoomDistanceFactor }, } = refs.current;
|
|
283
|
+
if (event.ctrlKey) {
|
|
284
|
+
event.stopPropagation();
|
|
285
|
+
if (Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
|
|
286
|
+
changeZoom(zoom * (1 - event.deltaY / wheelZoomDistanceFactor), true, ...translateCoordinates(event));
|
|
287
|
+
}
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (zoom > 1) {
|
|
291
|
+
event.stopPropagation();
|
|
292
|
+
changeOffsets(event.deltaX, event.deltaY);
|
|
293
|
+
}
|
|
294
|
+
}, [changeZoom, changeOffsets, translateCoordinates]);
|
|
295
|
+
const clearPointer = React.useCallback((event) => {
|
|
296
|
+
const { activePointers } = refs.current;
|
|
297
|
+
activePointers.splice(0, activePointers.length, ...activePointers.filter((p) => p.pointerId !== event.pointerId));
|
|
298
|
+
}, []);
|
|
299
|
+
const replacePointer = React.useCallback((event) => {
|
|
300
|
+
clearPointer(event);
|
|
301
|
+
refs.current.activePointers.push(event);
|
|
302
|
+
}, [clearPointer]);
|
|
303
|
+
const onPointerDown = React.useCallback((event) => {
|
|
304
|
+
var _a;
|
|
305
|
+
const { current } = refs;
|
|
306
|
+
const { state: { zoom }, containerRef, activePointers, lastPointerDown, maxZoom, zoomProps: { doubleTapDelay, doubleClickDelay, zoomInMultiplier, doubleClickMaxStops }, } = current;
|
|
307
|
+
if (!((_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.contains(event.target))) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (zoom > 1) {
|
|
311
|
+
event.stopPropagation();
|
|
312
|
+
}
|
|
313
|
+
const { timeStamp } = event;
|
|
314
|
+
if (activePointers.length === 0 &&
|
|
315
|
+
timeStamp - lastPointerDown < (event.pointerType === "touch" ? doubleTapDelay : doubleClickDelay)) {
|
|
316
|
+
current.lastPointerDown = 0;
|
|
317
|
+
changeZoom(zoom !== maxZoom ? zoom * Math.max(maxZoom ** (1 / doubleClickMaxStops), zoomInMultiplier) : 1, false, ...translateCoordinates(event));
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
current.lastPointerDown = timeStamp;
|
|
321
|
+
}
|
|
322
|
+
replacePointer(event);
|
|
323
|
+
if (activePointers.length === 2) {
|
|
324
|
+
current.pinchZoomDistance = distance(activePointers[0], activePointers[1]);
|
|
325
|
+
}
|
|
326
|
+
}, [changeZoom, replacePointer, translateCoordinates]);
|
|
327
|
+
const onPointerMove = React.useCallback((event) => {
|
|
328
|
+
const { current } = refs;
|
|
329
|
+
const { state: { zoom }, activePointers, pinchZoomDistance, zoomProps: { pinchZoomDistanceFactor }, } = current;
|
|
330
|
+
const activePointer = activePointers.find((p) => p.pointerId === event.pointerId);
|
|
331
|
+
if (activePointers.length === 2 && pinchZoomDistance) {
|
|
332
|
+
event.stopPropagation();
|
|
333
|
+
replacePointer(event);
|
|
334
|
+
const currentDistance = distance(activePointers[0], activePointers[1]);
|
|
335
|
+
const delta = currentDistance - pinchZoomDistance;
|
|
336
|
+
if (Math.abs(delta) > 0) {
|
|
337
|
+
changeZoom(zoom * (1 + delta / pinchZoomDistanceFactor), true, ...activePointers
|
|
338
|
+
.map((x) => translateCoordinates(x))
|
|
339
|
+
.reduce((acc, coordinate) => coordinate.map((x, i) => acc[i] + x / 2)));
|
|
340
|
+
current.pinchZoomDistance = currentDistance;
|
|
341
|
+
}
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (zoom > 1) {
|
|
345
|
+
event.stopPropagation();
|
|
346
|
+
if (activePointer) {
|
|
347
|
+
if (activePointers.length === 1) {
|
|
348
|
+
changeOffsets((activePointer.clientX - event.clientX) / zoom, (activePointer.clientY - event.clientY) / zoom);
|
|
349
|
+
}
|
|
350
|
+
replacePointer(event);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}, [changeOffsets, replacePointer, changeZoom, translateCoordinates]);
|
|
354
|
+
const onPointerUp = React.useCallback((event) => {
|
|
355
|
+
const { current } = refs;
|
|
356
|
+
const { activePointers } = current;
|
|
357
|
+
if (activePointers.length === 2 && activePointers.find((p) => p.pointerId === event.pointerId)) {
|
|
358
|
+
current.pinchZoomDistance = undefined;
|
|
359
|
+
}
|
|
360
|
+
clearPointer(event);
|
|
361
|
+
}, [clearPointer]);
|
|
362
|
+
React.useEffect(() => offset === 0
|
|
363
|
+
? cleanup(subscribe("zoom-in", () => changeZoom(refs.current.state.zoom * refs.current.zoomProps.zoomInMultiplier)), subscribe("zoom-out", () => changeZoom(refs.current.state.zoom / refs.current.zoomProps.zoomInMultiplier)), subscribeSensors("onKeyDown", onKeyDown), subscribeSensors("onWheel", onWheel), subscribeSensors("onPointerDown", onPointerDown), subscribeSensors("onPointerMove", onPointerMove), subscribeSensors("onPointerUp", onPointerUp), subscribeSensors("onPointerLeave", onPointerUp), subscribeSensors("onPointerCancel", onPointerUp))
|
|
364
|
+
: () => { }, [offset, subscribe, subscribeSensors, onKeyDown, onPointerDown, onPointerMove, onPointerUp, onWheel, changeZoom]);
|
|
365
|
+
const { state: { zoom, offsetX, offsetY }, } = refs.current;
|
|
366
|
+
const scaledRect = offset === 0
|
|
367
|
+
? {
|
|
368
|
+
width: rect.width * zoom,
|
|
369
|
+
height: rect.height * zoom,
|
|
370
|
+
}
|
|
371
|
+
: rect;
|
|
372
|
+
let rendered = (_a = render.slide) === null || _a === void 0 ? void 0 : _a.call(render, slide, offset, scaledRect);
|
|
373
|
+
if (!rendered && !("type" in slide) && "src" in slide) {
|
|
374
|
+
rendered = (React.createElement(ImageSlide, { slide: slide, offset: offset, rect: scaledRect, render: render, imageFit: carousel.imageFit }));
|
|
375
|
+
}
|
|
376
|
+
return rendered ? (React.createElement("div", { ref: setContainerRef, className: clsx(cssClass("fullsize"), cssClass("flex_center")), ...(offset === 0
|
|
377
|
+
? { style: { transform: `scale(${zoom}) translate3d(${offsetX}px, ${offsetY}px, 0)` } }
|
|
378
|
+
: null) }, rendered)) : null;
|
|
379
|
+
};
|
|
380
|
+
const ZoomWrapper = ({ slide, offset, rect, render, carousel, animation }) => {
|
|
381
|
+
var _a;
|
|
382
|
+
const { setZoomSupported } = useZoom();
|
|
383
|
+
const imageSlide = !("type" in slide);
|
|
384
|
+
const zoomSupported = imageSlide && ("srcSet" in slide || ("width" in slide && "height" in slide));
|
|
385
|
+
React.useEffect(() => {
|
|
386
|
+
if (offset === 0) {
|
|
387
|
+
setZoomSupported(zoomSupported);
|
|
388
|
+
}
|
|
389
|
+
}, [offset, zoomSupported, setZoomSupported]);
|
|
390
|
+
if (zoomSupported) {
|
|
391
|
+
return (React.createElement(ZoomContainer, { slide: slide, offset: offset, rect: rect, render: render, carousel: carousel, animation: animation }));
|
|
392
|
+
}
|
|
393
|
+
const rendered = (_a = render.slide) === null || _a === void 0 ? void 0 : _a.call(render, slide, offset, rect);
|
|
394
|
+
if (rendered) {
|
|
395
|
+
return React.createElement(React.Fragment, null, rendered);
|
|
396
|
+
}
|
|
397
|
+
if (imageSlide) {
|
|
398
|
+
return React.createElement(ImageSlide, { slide: slide, offset: offset, rect: rect, render: render, imageFit: carousel.imageFit });
|
|
399
|
+
}
|
|
400
|
+
return null;
|
|
401
|
+
};
|
|
402
|
+
export const ZoomModule = createModule("zoom", ZoomContextProvider);
|
|
403
|
+
export const Zoom = ({ augment, append }) => {
|
|
404
|
+
augment(({ toolbar: { buttons, ...restToolbar }, render, carousel, animation, zoom, ...restProps }) => ({
|
|
405
|
+
toolbar: {
|
|
406
|
+
buttons: [React.createElement(ZoomButtonsGroup, { key: "zoom", labels: restProps.labels, render: render }), ...buttons],
|
|
407
|
+
...restToolbar,
|
|
408
|
+
},
|
|
409
|
+
render: {
|
|
410
|
+
...render,
|
|
411
|
+
slide: (slide, offset, rect) => (React.createElement(ZoomWrapper, { slide: slide, offset: offset, rect: rect, render: render, carousel: carousel, animation: animation })),
|
|
412
|
+
},
|
|
413
|
+
zoom: {
|
|
414
|
+
...defaultZoomProps,
|
|
415
|
+
...zoom,
|
|
416
|
+
},
|
|
417
|
+
carousel,
|
|
418
|
+
animation,
|
|
419
|
+
...restProps,
|
|
420
|
+
}));
|
|
421
|
+
append("controller", ZoomModule);
|
|
422
|
+
};
|
|
423
|
+
export default Zoom;
|
package/dist/plugins/index.d.ts
CHANGED
package/dist/plugins/index.js
CHANGED
package/dist/styles.css
CHANGED
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
.yarl__container {
|
|
17
17
|
-webkit-user-select: none;
|
|
18
18
|
-moz-user-select: none;
|
|
19
|
-
-ms-user-select: none;
|
|
20
19
|
user-select: none;
|
|
21
20
|
touch-action: var(--yarl__controller_touch_action, none);
|
|
22
21
|
}
|
|
@@ -61,7 +60,6 @@
|
|
|
61
60
|
-o-object-fit: contain;
|
|
62
61
|
object-fit: contain;
|
|
63
62
|
-moz-user-select: none;
|
|
64
|
-
-ms-user-select: none;
|
|
65
63
|
user-select: none;
|
|
66
64
|
-webkit-user-select: none;
|
|
67
65
|
-webkit-touch-callout: none;
|
|
@@ -144,7 +142,6 @@
|
|
|
144
142
|
display: flex;
|
|
145
143
|
justify-content: flex-end;
|
|
146
144
|
padding: 8px;
|
|
147
|
-
gap: 8px;
|
|
148
145
|
}
|
|
149
146
|
[dir=rtl] .yarl__toolbar {
|
|
150
147
|
inset: 0 auto auto 0;
|
|
@@ -215,6 +212,7 @@
|
|
|
215
212
|
.yarl__no_scroll {
|
|
216
213
|
height: 100%;
|
|
217
214
|
overflow: hidden;
|
|
215
|
+
overscroll-behavior: none;
|
|
218
216
|
}
|
|
219
217
|
.yarl__pad_scrollbar {
|
|
220
218
|
padding-right: var(--yarl__scrollbar_padding, 17px);
|
package/dist/types.d.ts
CHANGED
|
@@ -8,8 +8,12 @@ export interface SlideImage {
|
|
|
8
8
|
src?: string;
|
|
9
9
|
/** image 'alt' attribute */
|
|
10
10
|
alt?: string;
|
|
11
|
-
/**
|
|
11
|
+
/** @deprecated use `width` and `height` instead */
|
|
12
12
|
aspectRatio?: number;
|
|
13
|
+
/** image width in pixels */
|
|
14
|
+
width?: number;
|
|
15
|
+
/** image height in pixels */
|
|
16
|
+
height?: number;
|
|
13
17
|
/** `object-fit` setting */
|
|
14
18
|
imageFit?: ImageFit;
|
|
15
19
|
/** alternative images to be passed to the 'srcSet' */
|
|
@@ -18,6 +22,8 @@ export interface SlideImage {
|
|
|
18
22
|
src: string;
|
|
19
23
|
/** image width in pixels */
|
|
20
24
|
width: number;
|
|
25
|
+
/** image height in pixels */
|
|
26
|
+
height?: number;
|
|
21
27
|
}[];
|
|
22
28
|
}
|
|
23
29
|
/** Supported slide types */
|
|
@@ -198,5 +204,9 @@ export declare type Plugin = ({ addParent, addChild, addSibling, replace, remove
|
|
|
198
204
|
export declare type DeepPartial<T, K extends keyof T> = Omit<T, K> & {
|
|
199
205
|
[P in keyof Pick<T, K>]?: Partial<Pick<T, K>[P]>;
|
|
200
206
|
};
|
|
207
|
+
/** Deep non-nullable utility type */
|
|
208
|
+
export declare type DeepNonNullable<T> = NonNullable<{
|
|
209
|
+
[K in keyof T]-?: NonNullable<T[K]>;
|
|
210
|
+
}>;
|
|
201
211
|
/** Lightbox external props */
|
|
202
212
|
export declare type LightboxExternalProps = DeepPartial<Partial<LightboxProps>, "carousel" | "animation" | "render" | "toolbar" | "controller" | "on">;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yet-another-react-lightbox",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "Modern React lightbox component",
|
|
5
5
|
"author": "Igor Danchenko",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"./plugins/thumbnails": "./dist/plugins/Thumbnails.js",
|
|
19
19
|
"./plugins/thumbnails.css": "./dist/plugins/thumbnails.css",
|
|
20
20
|
"./plugins/video": "./dist/plugins/Video.js",
|
|
21
|
+
"./plugins/zoom": "./dist/plugins/Zoom.js",
|
|
21
22
|
"./styles.css": "./dist/styles.css"
|
|
22
23
|
},
|
|
23
24
|
"types": "dist/index.d.ts",
|
|
@@ -49,6 +50,9 @@
|
|
|
49
50
|
],
|
|
50
51
|
"plugins/video": [
|
|
51
52
|
"dist/plugins/Video.d.ts"
|
|
53
|
+
],
|
|
54
|
+
"plugins/zoom": [
|
|
55
|
+
"dist/plugins/Zoom.d.ts"
|
|
52
56
|
]
|
|
53
57
|
}
|
|
54
58
|
},
|
|
@@ -84,32 +88,32 @@
|
|
|
84
88
|
"react-dom": ">=16.8.0"
|
|
85
89
|
},
|
|
86
90
|
"devDependencies": {
|
|
87
|
-
"@commitlint/cli": "^17.0.
|
|
88
|
-
"@commitlint/config-conventional": "^17.0.
|
|
91
|
+
"@commitlint/cli": "^17.0.3",
|
|
92
|
+
"@commitlint/config-conventional": "^17.0.3",
|
|
89
93
|
"@semantic-release/changelog": "^6.0.1",
|
|
90
94
|
"@semantic-release/github": "^8.0.4",
|
|
91
95
|
"@testing-library/jest-dom": "^5.16.4",
|
|
92
96
|
"@testing-library/react": "^13.3.0",
|
|
93
97
|
"@testing-library/user-event": "^14.2.1",
|
|
94
|
-
"@types/jest": "^28.1.
|
|
98
|
+
"@types/jest": "^28.1.3",
|
|
95
99
|
"@types/react": "^18.0.14",
|
|
96
100
|
"@types/react-dom": "^18.0.5",
|
|
97
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
98
|
-
"@typescript-eslint/parser": "^5.
|
|
101
|
+
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
|
102
|
+
"@typescript-eslint/parser": "^5.30.0",
|
|
99
103
|
"autoprefixer": "^10.4.7",
|
|
100
104
|
"eslint": "^8.18.0",
|
|
101
105
|
"eslint-config-airbnb": "^19.0.4",
|
|
102
106
|
"eslint-config-airbnb-typescript": "^17.0.0",
|
|
103
107
|
"eslint-config-prettier": "^8.5.0",
|
|
104
108
|
"eslint-plugin-import": "^2.26.0",
|
|
105
|
-
"eslint-plugin-jsx-a11y": "^6.
|
|
106
|
-
"eslint-plugin-prettier": "^4.
|
|
107
|
-
"eslint-plugin-react": "^7.30.
|
|
109
|
+
"eslint-plugin-jsx-a11y": "^6.6.0",
|
|
110
|
+
"eslint-plugin-prettier": "^4.1.0",
|
|
111
|
+
"eslint-plugin-react": "^7.30.1",
|
|
108
112
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
109
113
|
"husky": "^8.0.1",
|
|
110
|
-
"jest": "^28.1.
|
|
111
|
-
"jest-environment-jsdom": "^28.1.
|
|
112
|
-
"lint-staged": "^13.0.
|
|
114
|
+
"jest": "^28.1.2",
|
|
115
|
+
"jest-environment-jsdom": "^28.1.2",
|
|
116
|
+
"lint-staged": "^13.0.3",
|
|
113
117
|
"npm-run-all": "^4.1.5",
|
|
114
118
|
"postcss": "^8.4.14",
|
|
115
119
|
"postcss-cli": "^9.1.0",
|
|
@@ -117,7 +121,7 @@
|
|
|
117
121
|
"react": "^18.2.0",
|
|
118
122
|
"react-dom": "^18.2.0",
|
|
119
123
|
"rimraf": "^3.0.2",
|
|
120
|
-
"sass": "^1.
|
|
124
|
+
"sass": "^1.53.0",
|
|
121
125
|
"ts-jest": "^28.0.5",
|
|
122
126
|
"typescript": "^4.7.4"
|
|
123
127
|
},
|