yet-another-react-lightbox 1.7.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -7
- package/dist/core/components/IconButton.d.ts +1 -1
- package/dist/core/components/IconButton.js +2 -1
- package/dist/core/components/ImageSlide.d.ts +4 -3
- package/dist/core/components/ImageSlide.js +24 -31
- package/dist/core/config.js +16 -0
- 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/Carousel.js +1 -1
- package/dist/core/modules/Controller.d.ts +4 -3
- package/dist/core/modules/Controller.js +30 -25
- package/dist/core/modules/Navigation.js +8 -6
- package/dist/core/utils.d.ts +2 -0
- package/dist/core/utils.js +5 -0
- package/dist/plugins/Captions.js +2 -2
- package/dist/plugins/Fullscreen.d.ts +4 -4
- package/dist/plugins/Fullscreen.js +12 -3
- package/dist/plugins/Thumbnails.d.ts +48 -0
- package/dist/plugins/Thumbnails.js +241 -0
- 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 +2 -0
- package/dist/plugins/index.js +2 -0
- package/dist/plugins/thumbnails.css +150 -0
- package/dist/styles.css +5 -7
- package/dist/types.d.ts +19 -5
- package/package.json +26 -17
|
@@ -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;
|
|
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)) || []), ...[...(slide.width ? [slide.width] : [])]);
|
|
100
|
+
const height = Math.max(...(((_b = slide.srcSet) === null || _b === void 0 ? void 0 : _b.map((x) => x.width)) || (slide.aspectRatio ? [width / slide.aspectRatio] : [])), ...[...(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
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
.yarl__thumbnails {
|
|
2
|
+
display: flex;
|
|
3
|
+
}
|
|
4
|
+
.yarl__thumbnails_top, .yarl__thumbnails_bottom {
|
|
5
|
+
flex-flow: column nowrap;
|
|
6
|
+
}
|
|
7
|
+
.yarl__thumbnails_top .yarl__thumbnails_track, .yarl__thumbnails_bottom .yarl__thumbnails_track {
|
|
8
|
+
flex-flow: row nowrap;
|
|
9
|
+
}
|
|
10
|
+
.yarl__thumbnails_start, .yarl__thumbnails_end {
|
|
11
|
+
flex-flow: row nowrap;
|
|
12
|
+
}
|
|
13
|
+
.yarl__thumbnails_start .yarl__thumbnails_track, .yarl__thumbnails_end .yarl__thumbnails_track {
|
|
14
|
+
flex-flow: column nowrap;
|
|
15
|
+
}
|
|
16
|
+
.yarl__thumbnails_wrapper {
|
|
17
|
+
flex-grow: 1;
|
|
18
|
+
position: relative;
|
|
19
|
+
}
|
|
20
|
+
.yarl__thumbnails_container {
|
|
21
|
+
color: #fff;
|
|
22
|
+
background-color: #000;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
padding: 16px;
|
|
25
|
+
position: relative;
|
|
26
|
+
z-index: 0;
|
|
27
|
+
-webkit-user-select: none;
|
|
28
|
+
-moz-user-select: none;
|
|
29
|
+
user-select: none;
|
|
30
|
+
-webkit-touch-callout: none;
|
|
31
|
+
}
|
|
32
|
+
.yarl__thumbnails_container::before, .yarl__thumbnails_container::after {
|
|
33
|
+
content: "";
|
|
34
|
+
position: absolute;
|
|
35
|
+
z-index: 1;
|
|
36
|
+
}
|
|
37
|
+
.yarl__thumbnails_top .yarl__thumbnails_container::before, .yarl__thumbnails_bottom .yarl__thumbnails_container::before {
|
|
38
|
+
left: 0;
|
|
39
|
+
background: linear-gradient(to left, transparent, #000);
|
|
40
|
+
}
|
|
41
|
+
.yarl__thumbnails_top .yarl__thumbnails_container::after, .yarl__thumbnails_bottom .yarl__thumbnails_container::after {
|
|
42
|
+
right: 0;
|
|
43
|
+
background: linear-gradient(to right, transparent, #000);
|
|
44
|
+
}
|
|
45
|
+
.yarl__thumbnails_top .yarl__thumbnails_container::before, .yarl__thumbnails_top .yarl__thumbnails_container::after, .yarl__thumbnails_bottom .yarl__thumbnails_container::before, .yarl__thumbnails_bottom .yarl__thumbnails_container::after {
|
|
46
|
+
top: 0;
|
|
47
|
+
bottom: 0;
|
|
48
|
+
width: 60px;
|
|
49
|
+
}
|
|
50
|
+
.yarl__thumbnails_start .yarl__thumbnails_container::before, .yarl__thumbnails_end .yarl__thumbnails_container::before {
|
|
51
|
+
top: 0;
|
|
52
|
+
background: linear-gradient(to top, transparent, #000);
|
|
53
|
+
}
|
|
54
|
+
.yarl__thumbnails_start .yarl__thumbnails_container::after, .yarl__thumbnails_end .yarl__thumbnails_container::after {
|
|
55
|
+
bottom: 0;
|
|
56
|
+
background: linear-gradient(to bottom, transparent, #000);
|
|
57
|
+
}
|
|
58
|
+
.yarl__thumbnails_start .yarl__thumbnails_container::before, .yarl__thumbnails_start .yarl__thumbnails_container::after, .yarl__thumbnails_end .yarl__thumbnails_container::before, .yarl__thumbnails_end .yarl__thumbnails_container::after {
|
|
59
|
+
left: 0;
|
|
60
|
+
right: 0;
|
|
61
|
+
height: 60px;
|
|
62
|
+
}
|
|
63
|
+
.yarl__thumbnails_track {
|
|
64
|
+
display: flex;
|
|
65
|
+
gap: var(--yarl__thumbnails_thumbnail_gap, 16px);
|
|
66
|
+
}
|
|
67
|
+
.yarl__thumbnails_thumbnail {
|
|
68
|
+
cursor: pointer;
|
|
69
|
+
-webkit-appearance: none;
|
|
70
|
+
-moz-appearance: none;
|
|
71
|
+
appearance: none;
|
|
72
|
+
background: transparent;
|
|
73
|
+
border: var(--yarl__thumbnails_thumbnail_border, 1px) solid rgba(255, 255, 255, 0.8);
|
|
74
|
+
border-radius: var(--yarl__thumbnails_thumbnail_border_radius, 4px);
|
|
75
|
+
-webkit-tap-highlight-color: transparent;
|
|
76
|
+
flex-shrink: 0;
|
|
77
|
+
position: relative;
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
padding: var(--yarl__thumbnails_thumbnail_padding, 4px);
|
|
80
|
+
width: var(--yarl__thumbnails_thumbnail_width, 120px);
|
|
81
|
+
height: var(--yarl__thumbnails_thumbnail_height, 80px);
|
|
82
|
+
box-sizing: content-box;
|
|
83
|
+
}
|
|
84
|
+
.yarl__thumbnails_thumbnail_active {
|
|
85
|
+
border-color: #fff;
|
|
86
|
+
}
|
|
87
|
+
.yarl__thumbnails_thumbnail_fadein {
|
|
88
|
+
opacity: 0;
|
|
89
|
+
-webkit-animation: yarl__thumbnails_thumbnail_fadein var(--yarl__thumbnails_thumbnail_fadein_duration, 0.5s) ease-in-out var(--yarl__thumbnails_thumbnail_fadein_delay, 0s) forwards;
|
|
90
|
+
animation: yarl__thumbnails_thumbnail_fadein var(--yarl__thumbnails_thumbnail_fadein_duration, 0.5s) ease-in-out var(--yarl__thumbnails_thumbnail_fadein_delay, 0s) forwards;
|
|
91
|
+
}
|
|
92
|
+
.yarl__thumbnails_thumbnail_fadeout {
|
|
93
|
+
-webkit-animation: yarl__thumbnails_thumbnail_fadeout var(--yarl__thumbnails_thumbnail_fadeout_duration, 0.5s) ease-in-out var(--yarl__thumbnails_thumbnail_fadeout_delay, 0s) forwards;
|
|
94
|
+
animation: yarl__thumbnails_thumbnail_fadeout var(--yarl__thumbnails_thumbnail_fadeout_duration, 0.5s) ease-in-out var(--yarl__thumbnails_thumbnail_fadeout_delay, 0s) forwards;
|
|
95
|
+
cursor: unset;
|
|
96
|
+
}
|
|
97
|
+
.yarl__thumbnails_thumbnail_placeholder {
|
|
98
|
+
visibility: hidden;
|
|
99
|
+
cursor: unset;
|
|
100
|
+
}
|
|
101
|
+
.yarl__thumbnails_thumbnail_icon {
|
|
102
|
+
color: rgba(255, 255, 255, 0.8);
|
|
103
|
+
-webkit-filter: drop-shadow(2px 2px 2px rgba(0, 0, 0, 0.8));
|
|
104
|
+
filter: drop-shadow(2px 2px 2px rgba(0, 0, 0, 0.8));
|
|
105
|
+
position: absolute;
|
|
106
|
+
top: 50%;
|
|
107
|
+
left: 50%;
|
|
108
|
+
-webkit-transform: translate3d(-50%, -50%, 0);
|
|
109
|
+
transform: translate3d(-50%, -50%, 0);
|
|
110
|
+
width: 32px;
|
|
111
|
+
height: 32px;
|
|
112
|
+
}
|
|
113
|
+
.yarl__thumbnails_contain_image {
|
|
114
|
+
-o-object-fit: contain;
|
|
115
|
+
object-fit: contain;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@-webkit-keyframes yarl__thumbnails_thumbnail_fadein {
|
|
119
|
+
from {
|
|
120
|
+
opacity: 0;
|
|
121
|
+
}
|
|
122
|
+
to {
|
|
123
|
+
opacity: 1;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@keyframes yarl__thumbnails_thumbnail_fadein {
|
|
128
|
+
from {
|
|
129
|
+
opacity: 0;
|
|
130
|
+
}
|
|
131
|
+
to {
|
|
132
|
+
opacity: 1;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
@-webkit-keyframes yarl__thumbnails_thumbnail_fadeout {
|
|
136
|
+
from {
|
|
137
|
+
opacity: 1;
|
|
138
|
+
}
|
|
139
|
+
to {
|
|
140
|
+
opacity: 0;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
@keyframes yarl__thumbnails_thumbnail_fadeout {
|
|
144
|
+
from {
|
|
145
|
+
opacity: 1;
|
|
146
|
+
}
|
|
147
|
+
to {
|
|
148
|
+
opacity: 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
package/dist/styles.css
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
.yarl__fullsize {
|
|
2
|
+
width: 100%;
|
|
3
|
+
height: 100%;
|
|
4
|
+
}
|
|
1
5
|
.yarl__portal {
|
|
2
6
|
position: fixed;
|
|
3
7
|
inset: 0;
|
|
@@ -12,13 +16,10 @@
|
|
|
12
16
|
.yarl__container {
|
|
13
17
|
-webkit-user-select: none;
|
|
14
18
|
-moz-user-select: none;
|
|
15
|
-
-ms-user-select: none;
|
|
16
19
|
user-select: none;
|
|
17
20
|
touch-action: var(--yarl__controller_touch_action, none);
|
|
18
21
|
}
|
|
19
22
|
.yarl__container {
|
|
20
|
-
width: 100%;
|
|
21
|
-
height: 100%;
|
|
22
23
|
position: relative;
|
|
23
24
|
overflow: hidden;
|
|
24
25
|
color: #fff;
|
|
@@ -56,12 +57,9 @@
|
|
|
56
57
|
transition: unset;
|
|
57
58
|
}
|
|
58
59
|
.yarl__slide_image {
|
|
59
|
-
width: 100%;
|
|
60
|
-
height: 100%;
|
|
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);
|