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.
@@ -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;
@@ -22,7 +22,6 @@
22
22
  font-weight: 500;
23
23
  overflow: hidden;
24
24
  -webkit-hyphens: auto;
25
- -ms-hyphens: auto;
26
25
  hyphens: auto;
27
26
  display: -webkit-box;
28
27
  -webkit-box-orient: vertical;
@@ -4,3 +4,4 @@ export * from "./Inline.js";
4
4
  export * from "./Slideshow.js";
5
5
  export * from "./Thumbnails.js";
6
6
  export * from "./Video.js";
7
+ export * from "./Zoom.js";
@@ -4,3 +4,4 @@ export * from "./Inline.js";
4
4
  export * from "./Slideshow.js";
5
5
  export * from "./Thumbnails.js";
6
6
  export * from "./Video.js";
7
+ export * from "./Zoom.js";
@@ -26,7 +26,6 @@
26
26
  z-index: 0;
27
27
  -webkit-user-select: none;
28
28
  -moz-user-select: none;
29
- -ms-user-select: none;
30
29
  user-select: none;
31
30
  -webkit-touch-callout: none;
32
31
  }
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
- /** image aspect ratio */
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.8.0",
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.2",
88
- "@commitlint/config-conventional": "^17.0.2",
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.2",
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.28.0",
98
- "@typescript-eslint/parser": "^5.28.0",
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.5.1",
106
- "eslint-plugin-prettier": "^4.0.0",
107
- "eslint-plugin-react": "^7.30.0",
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.1",
111
- "jest-environment-jsdom": "^28.1.1",
112
- "lint-staged": "^13.0.2",
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.52.3",
124
+ "sass": "^1.53.0",
121
125
  "ts-jest": "^28.0.5",
122
126
  "typescript": "^4.7.4"
123
127
  },