yet-another-react-lightbox 1.8.1 → 1.9.2
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 +22 -26
- package/dist/core/contexts/Timeouts.js +2 -2
- package/dist/core/hooks/useSensors.js +5 -2
- package/dist/core/modules/Controller.d.ts +4 -1
- package/dist/core/modules/Controller.js +16 -5
- package/dist/core/modules/Navigation.js +3 -3
- 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 +2 -3
- package/dist/plugins/Thumbnails.d.ts +1 -4
- package/dist/plugins/Thumbnails.js +4 -4
- package/dist/plugins/Zoom.d.ts +51 -0
- package/dist/plugins/Zoom.js +425 -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 -2
- package/dist/types.d.ts +11 -1
- package/package.json +17 -13
package/README.md
CHANGED
|
@@ -14,10 +14,11 @@ Modern React lightbox component. Performant, easy to use, customizable and exten
|
|
|
14
14
|
- **Performance:** preloads limited number of images without compromising performance or UX
|
|
15
15
|
- **Responsive:** responsive images with automatic resolution switching are supported out of the box
|
|
16
16
|
- **Video:** video slides are supported via an optional plugin
|
|
17
|
+
- **Zoom:** image zoom is supported via an optional plugin
|
|
17
18
|
- **Customization:** customize any UI element or add your own custom slides
|
|
18
19
|
- **No bloat:** never bundle rarely used features; add optional features via plugins
|
|
19
|
-
- **RTL:** compatible with RTL layout
|
|
20
20
|
- **TypeScript:** type definitions come built-in in the package
|
|
21
|
+
- **RTL:** compatible with RTL layout
|
|
21
22
|
|
|
22
23
|
## Documentation
|
|
23
24
|
|
|
@@ -99,13 +100,14 @@ const App = () => {
|
|
|
99
100
|
{
|
|
100
101
|
src: "/image1x3840.jpg",
|
|
101
102
|
alt: "image 1",
|
|
102
|
-
|
|
103
|
+
width: 3840,
|
|
104
|
+
height: 2560,
|
|
103
105
|
srcSet: [
|
|
104
|
-
{ src: "/image1x320.jpg", width: 320 },
|
|
105
|
-
{ src: "/image1x640.jpg", width: 640 },
|
|
106
|
-
{ src: "/image1x1200.jpg", width: 1200 },
|
|
107
|
-
{ src: "/image1x2048.jpg", width: 2048 },
|
|
108
|
-
{ src: "/image1x3840.jpg", width: 3840 },
|
|
106
|
+
{ src: "/image1x320.jpg", width: 320, height: 213 },
|
|
107
|
+
{ src: "/image1x640.jpg", width: 640, height: 427 },
|
|
108
|
+
{ src: "/image1x1200.jpg", width: 1200, height: 800 },
|
|
109
|
+
{ src: "/image1x2048.jpg", width: 2048, height: 1365 },
|
|
110
|
+
{ src: "/image1x3840.jpg", width: 3840, height: 2560 },
|
|
109
111
|
]
|
|
110
112
|
},
|
|
111
113
|
// ...
|
|
@@ -134,6 +136,7 @@ The following plugins come bundled in the package:
|
|
|
134
136
|
- [Slideshow](https://yet-another-react-lightbox.vercel.app/plugins/slideshow) - adds slideshow autoplay feature
|
|
135
137
|
- [Thumbnails](https://yet-another-react-lightbox.vercel.app/plugins/thumbnails) - adds thumbnails track
|
|
136
138
|
- [Video](https://yet-another-react-lightbox.vercel.app/plugins/video) - adds support for video slides
|
|
139
|
+
- [Zoom](https://yet-another-react-lightbox.vercel.app/plugins/zoom) - adds image zoom feature
|
|
137
140
|
|
|
138
141
|
## License
|
|
139
142
|
|
|
@@ -4,4 +4,4 @@ export declare type IconButtonProps = Omit<React.DetailedHTMLProps<React.ButtonH
|
|
|
4
4
|
icon: React.ElementType;
|
|
5
5
|
renderIcon?: () => React.ReactNode;
|
|
6
6
|
};
|
|
7
|
-
export declare const IconButton: React.
|
|
7
|
+
export declare const IconButton: React.ForwardRefExoticComponent<Pick<IconButtonProps, "value" | "children" | "onPointerDown" | "onPointerMove" | "onPointerUp" | "onPointerLeave" | "onPointerCancel" | "onTouchStart" | "onTouchMove" | "onTouchEnd" | "onTouchCancel" | "onKeyDown" | "onKeyUp" | "onWheel" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "className" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "placeholder" | "slot" | "spellCheck" | "style" | "tabIndex" | "title" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancelCapture" | "onTouchEndCapture" | "onTouchMoveCapture" | "onTouchStartCapture" | "onPointerDownCapture" | "onPointerMoveCapture" | "onPointerUpCapture" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "form" | "label" | "autoFocus" | "disabled" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | "name" | "icon" | "renderIcon"> & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { clsx, cssClass } from "../utils.js";
|
|
3
|
-
export const IconButton = ({ label, className, icon: Icon, renderIcon, onClick, ...rest }) => (React.createElement("button", { type: "button", "aria-label": label, className: clsx(cssClass("button"), className), onClick: onClick, ...rest }, renderIcon ? renderIcon() : React.createElement(Icon, { className: cssClass("icon") })));
|
|
3
|
+
export const IconButton = React.forwardRef(({ label, className, icon: Icon, renderIcon, onClick, ...rest }, ref) => (React.createElement("button", { ref: ref, type: "button", "aria-label": label, className: clsx(cssClass("button"), className), onClick: onClick, ...rest }, renderIcon ? renderIcon() : React.createElement(Icon, { className: cssClass("icon") }))));
|
|
4
|
+
IconButton.displayName = "IconButton";
|
|
@@ -5,7 +5,7 @@ import { useEvents } from "../contexts/index.js";
|
|
|
5
5
|
import { ErrorIcon, LoadingIcon } from "./Icons.js";
|
|
6
6
|
import { activeSlideStatus, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, } from "../consts.js";
|
|
7
7
|
export const ImageSlide = ({ slide: image, offset, render, rect, imageFit }) => {
|
|
8
|
-
var _a;
|
|
8
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
9
9
|
const [status, setStatus] = React.useState(SLIDE_STATUS_LOADING);
|
|
10
10
|
const latestStatus = useLatest(status);
|
|
11
11
|
const { publish } = useEvents();
|
|
@@ -40,32 +40,28 @@ export const ImageSlide = ({ slide: image, offset, render, rect, imageFit }) =>
|
|
|
40
40
|
const onError = React.useCallback(() => {
|
|
41
41
|
setStatus(SLIDE_STATUS_ERROR);
|
|
42
42
|
}, []);
|
|
43
|
+
const cover = image.imageFit === "cover" || (image.imageFit !== "contain" && imageFit === "cover");
|
|
44
|
+
const nonInfinite = (value, fallback) => (Number.isFinite(value) ? value : fallback);
|
|
45
|
+
const maxWidth = adjustDevicePixelRatio(nonInfinite(Math.max(...((_b = (_a = image.srcSet) === null || _a === void 0 ? void 0 : _a.map((x) => x.width)) !== null && _b !== void 0 ? _b : []).concat(image.width ? [image.width] : [])), ((_c = imageRef.current) === null || _c === void 0 ? void 0 : _c.naturalWidth) || 0));
|
|
46
|
+
const maxHeight = adjustDevicePixelRatio(nonInfinite(Math.max(...((_e = (_d = image.srcSet) === null || _d === void 0 ? void 0 : _d.map((x) => x.height).filter((x) => Boolean(x))) !== null && _e !== void 0 ? _e : []).concat(image.height ? [image.height] : [])), (image.aspectRatio && maxWidth ? maxWidth / image.aspectRatio : (_f = imageRef.current) === null || _f === void 0 ? void 0 : _f.naturalHeight) || 0));
|
|
47
|
+
const style = maxWidth && maxHeight ? { maxWidth, maxHeight } : undefined;
|
|
48
|
+
const srcSet = (_g = image.srcSet) === null || _g === void 0 ? void 0 : _g.sort((a, b) => a.width - b.width).map((item) => `${item.src} ${item.width}w`).join(", ");
|
|
49
|
+
const estimateActualWidth = () => {
|
|
50
|
+
if (rect && !cover) {
|
|
51
|
+
if (image.width && image.height) {
|
|
52
|
+
return (rect.height / image.height) * image.width;
|
|
53
|
+
}
|
|
54
|
+
if (image.aspectRatio) {
|
|
55
|
+
return rect.height * image.aspectRatio;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return Number.MAX_VALUE;
|
|
59
|
+
};
|
|
60
|
+
const sizes = srcSet && rect && hasWindow()
|
|
61
|
+
? `${Math.ceil((Math.min(estimateActualWidth(), rect.width) / window.innerWidth) * 100)}vw`
|
|
62
|
+
: undefined;
|
|
43
63
|
return (React.createElement(React.Fragment, null,
|
|
44
|
-
React.createElement("img", { ref: setImageRef, onLoad: onLoad, onError: onError, className: clsx(cssClass("slide_image"), cssClass("fullsize"), (
|
|
45
|
-
cssClass("slide_image_cover"), status !== SLIDE_STATUS_COMPLETE && cssClass("slide_image_loading")), draggable: false, alt: image.alt, ...(image.srcSet
|
|
46
|
-
? {
|
|
47
|
-
...(rect && hasWindow()
|
|
48
|
-
? {
|
|
49
|
-
sizes: `${Math.ceil((Math.min(image.aspectRatio ? rect.height * image.aspectRatio : Number.MAX_VALUE, rect.width) /
|
|
50
|
-
window.innerWidth) *
|
|
51
|
-
100)}vw`,
|
|
52
|
-
}
|
|
53
|
-
: null),
|
|
54
|
-
srcSet: image.srcSet
|
|
55
|
-
.sort((a, b) => a.width - b.width)
|
|
56
|
-
.map((item) => `${item.src} ${item.width}w`)
|
|
57
|
-
.join(", "),
|
|
58
|
-
style: {
|
|
59
|
-
maxWidth: `${adjustDevicePixelRatio(Math.max(...image.srcSet.map((x) => x.width)))}px`,
|
|
60
|
-
},
|
|
61
|
-
}
|
|
62
|
-
: {
|
|
63
|
-
style: imageRef.current && ((_a = imageRef.current) === null || _a === void 0 ? void 0 : _a.naturalWidth) > 0
|
|
64
|
-
? {
|
|
65
|
-
maxWidth: `${adjustDevicePixelRatio(imageRef.current.naturalWidth)}px`,
|
|
66
|
-
}
|
|
67
|
-
: undefined,
|
|
68
|
-
}), src: image.src }),
|
|
64
|
+
React.createElement("img", { ref: setImageRef, onLoad: onLoad, onError: onError, className: clsx(cssClass("slide_image"), cssClass("fullsize"), cover && cssClass("slide_image_cover"), status !== SLIDE_STATUS_COMPLETE && cssClass("slide_image_loading")), draggable: false, alt: image.alt, style: style, sizes: sizes, srcSet: srcSet, src: image.src }),
|
|
69
65
|
status !== SLIDE_STATUS_COMPLETE && (React.createElement("div", { className: cssClass("slide_placeholder") },
|
|
70
66
|
status === SLIDE_STATUS_LOADING &&
|
|
71
67
|
((render === null || render === void 0 ? void 0 : render.iconLoading) ? (render.iconLoading()) : (React.createElement(LoadingIcon, { className: clsx(cssClass("icon"), cssClass("slide_loading")) }))),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { makeUseContext } from "../utils.js";
|
|
2
|
+
import { isDefined, makeUseContext } from "../utils.js";
|
|
3
3
|
const TimeoutsContext = React.createContext(null);
|
|
4
4
|
export const useTimeouts = makeUseContext("useTimeouts", "TimeoutsContext", TimeoutsContext);
|
|
5
5
|
export const TimeoutsProvider = ({ children }) => {
|
|
@@ -16,7 +16,7 @@ export const TimeoutsProvider = ({ children }) => {
|
|
|
16
16
|
return id;
|
|
17
17
|
};
|
|
18
18
|
const clearTimeout = (id) => {
|
|
19
|
-
if (
|
|
19
|
+
if (isDefined(id)) {
|
|
20
20
|
removeTimeout(id);
|
|
21
21
|
window.clearTimeout(id);
|
|
22
22
|
}
|
|
@@ -4,7 +4,10 @@ export const useSensors = () => {
|
|
|
4
4
|
return React.useMemo(() => {
|
|
5
5
|
const notifySubscribers = (type, event) => {
|
|
6
6
|
var _a;
|
|
7
|
-
(_a = subscribers[type]) === null || _a === void 0 ? void 0 : _a.forEach((listener) =>
|
|
7
|
+
(_a = subscribers[type]) === null || _a === void 0 ? void 0 : _a.forEach((listener) => {
|
|
8
|
+
if (!event.isPropagationStopped())
|
|
9
|
+
listener(event);
|
|
10
|
+
});
|
|
8
11
|
};
|
|
9
12
|
return {
|
|
10
13
|
registerSensors: {
|
|
@@ -25,7 +28,7 @@ export const useSensors = () => {
|
|
|
25
28
|
if (!subscribers[type]) {
|
|
26
29
|
subscribers[type] = [];
|
|
27
30
|
}
|
|
28
|
-
subscribers[type].
|
|
31
|
+
subscribers[type].unshift(callback);
|
|
29
32
|
return () => {
|
|
30
33
|
const listeners = subscribers[type];
|
|
31
34
|
if (listeners) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { Component, ComponentProps } from "../../types.js";
|
|
3
|
-
import { SubscribeSensors } from "../hooks/index.js";
|
|
3
|
+
import { ContainerRect, SubscribeSensors } from "../hooks/index.js";
|
|
4
4
|
declare type ControllerState = {
|
|
5
5
|
currentIndex: number;
|
|
6
6
|
globalIndex: number;
|
|
@@ -8,6 +8,9 @@ declare type ControllerState = {
|
|
|
8
8
|
export declare type ControllerContextType = ControllerState & {
|
|
9
9
|
latestProps: React.MutableRefObject<ComponentProps>;
|
|
10
10
|
subscribeSensors: SubscribeSensors<HTMLDivElement>;
|
|
11
|
+
transferFocus: () => void;
|
|
12
|
+
containerRect: ContainerRect;
|
|
13
|
+
containerRef: React.RefObject<HTMLDivElement>;
|
|
11
14
|
};
|
|
12
15
|
export declare const useController: () => ControllerContextType;
|
|
13
16
|
export declare const Controller: Component;
|
|
@@ -8,7 +8,7 @@ const SWIPE_OFFSET_THRESHOLD = 30;
|
|
|
8
8
|
const ControllerContext = React.createContext(null);
|
|
9
9
|
export const useController = makeUseContext("useController", "ControllerContext", ControllerContext);
|
|
10
10
|
export const Controller = ({ children, ...props }) => {
|
|
11
|
-
const { containerRef, setContainerRef } = useContainerRect();
|
|
11
|
+
const { containerRef, setContainerRef, containerRect } = useContainerRect();
|
|
12
12
|
const { registerSensors, subscribeSensors } = useSensors();
|
|
13
13
|
const { subscribe, publish } = useEvents();
|
|
14
14
|
const { setTimeout, clearTimeout } = useTimeouts();
|
|
@@ -31,7 +31,7 @@ export const Controller = ({ children, ...props }) => {
|
|
|
31
31
|
refs.current.props = props;
|
|
32
32
|
useEnhancedEffect(() => {
|
|
33
33
|
const preventDefault = (event) => {
|
|
34
|
-
if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) {
|
|
34
|
+
if (Math.abs(event.deltaX) > Math.abs(event.deltaY) || event.ctrlKey) {
|
|
35
35
|
event.preventDefault();
|
|
36
36
|
}
|
|
37
37
|
};
|
|
@@ -269,12 +269,24 @@ export const Controller = ({ children, ...props }) => {
|
|
|
269
269
|
}
|
|
270
270
|
}, [updateSwipeOffset, setTimeout, clearTimeout, swipe, resetSwipe, rerender, isSwipeValid, containerRef]);
|
|
271
271
|
React.useEffect(() => subscribeSensors("onWheel", onWheel), [subscribeSensors, onWheel]);
|
|
272
|
+
const transferFocus = React.useCallback(() => { var _a; return (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, [containerRef]);
|
|
272
273
|
const context = React.useMemo(() => ({
|
|
273
274
|
latestProps,
|
|
274
275
|
currentIndex: state.currentIndex,
|
|
275
276
|
globalIndex: state.globalIndex,
|
|
276
277
|
subscribeSensors,
|
|
277
|
-
|
|
278
|
+
transferFocus,
|
|
279
|
+
containerRect,
|
|
280
|
+
containerRef,
|
|
281
|
+
}), [
|
|
282
|
+
latestProps,
|
|
283
|
+
state.currentIndex,
|
|
284
|
+
state.globalIndex,
|
|
285
|
+
subscribeSensors,
|
|
286
|
+
transferFocus,
|
|
287
|
+
containerRect,
|
|
288
|
+
containerRef,
|
|
289
|
+
]);
|
|
278
290
|
return (React.createElement("div", { ref: setContainerRef, className: clsx(cssClass("container"), cssClass("fullsize"), refs.current.swipeState === "swipe" && cssClass("container_swipe")), style: {
|
|
279
291
|
...(refs.current.swipeAnimationDuration !== LightboxDefaultProps.animation.swipe
|
|
280
292
|
? {
|
|
@@ -286,7 +298,6 @@ export const Controller = ({ children, ...props }) => {
|
|
|
286
298
|
[cssVar("controller_touch_action")]: props.controller.touchAction,
|
|
287
299
|
}
|
|
288
300
|
: null),
|
|
289
|
-
}, role: "presentation", "aria-live": "polite", tabIndex: -1, ...registerSensors },
|
|
290
|
-
React.createElement(ControllerContext.Provider, { value: context }, children)));
|
|
301
|
+
}, role: "presentation", "aria-live": "polite", tabIndex: -1, ...registerSensors }, containerRect && (React.createElement(ControllerContext.Provider, { value: context }, children))));
|
|
291
302
|
};
|
|
292
303
|
export const ControllerModule = createModule("controller", Controller);
|
|
@@ -12,11 +12,11 @@ export const Navigation = ({ slides, carousel: { finite }, labels, render: { but
|
|
|
12
12
|
const { currentIndex, subscribeSensors } = useController();
|
|
13
13
|
const { publish } = useEvents();
|
|
14
14
|
const isRTL = useLatest(useRTL());
|
|
15
|
-
React.useEffect(() => subscribeSensors("
|
|
16
|
-
if (event.
|
|
15
|
+
React.useEffect(() => subscribeSensors("onKeyDown", (event) => {
|
|
16
|
+
if (event.key === "ArrowLeft") {
|
|
17
17
|
publish(isRTL.current ? "next" : "prev");
|
|
18
18
|
}
|
|
19
|
-
else if (event.
|
|
19
|
+
else if (event.key === "ArrowRight") {
|
|
20
20
|
publish(isRTL.current ? "prev" : "next");
|
|
21
21
|
}
|
|
22
22
|
}), [subscribeSensors, publish, isRTL]);
|
package/dist/core/utils.d.ts
CHANGED
|
@@ -7,4 +7,6 @@ export declare const label: (labels: Labels | undefined, lbl: string) => string;
|
|
|
7
7
|
export declare const cleanup: (...cleaners: (() => void)[]) => () => void;
|
|
8
8
|
export declare const makeUseContext: <T>(name: string, contextName: string, context: React.Context<T | null>) => () => T;
|
|
9
9
|
export declare const hasWindow: () => boolean;
|
|
10
|
+
export declare const isDefined: <T = any>(x: T | undefined) => x is T;
|
|
10
11
|
export declare const adjustDevicePixelRatio: (value: number) => number;
|
|
12
|
+
export declare const round: (value: number, decimals?: number) => number;
|
package/dist/core/utils.js
CHANGED
|
@@ -17,4 +17,9 @@ export const makeUseContext = (name, contextName, context) => () => {
|
|
|
17
17
|
return ctx;
|
|
18
18
|
};
|
|
19
19
|
export const hasWindow = () => typeof window !== "undefined";
|
|
20
|
+
export const isDefined = (x) => typeof x !== "undefined";
|
|
20
21
|
export const adjustDevicePixelRatio = (value) => hasWindow() ? Math.round(value / (window.devicePixelRatio || 1)) : value;
|
|
22
|
+
export const round = (value, decimals = 0) => {
|
|
23
|
+
const factor = 10 ** decimals;
|
|
24
|
+
return Math.round((value + Number.EPSILON) * factor) / factor;
|
|
25
|
+
};
|
package/dist/plugins/Captions.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { cssClass, cssVar, makeUseContext } from "../core/utils.js";
|
|
2
|
+
import { cssClass, cssVar, isDefined, makeUseContext } from "../core/utils.js";
|
|
3
3
|
import { useEvents } from "../core/contexts/Events.js";
|
|
4
4
|
import { createModule } from "../core/index.js";
|
|
5
5
|
const defaultTextAlign = "start";
|
|
@@ -27,7 +27,7 @@ export const CaptionsComponent = ({ children }) => {
|
|
|
27
27
|
const { subscribe } = useEvents();
|
|
28
28
|
const [toolbarWidth, setToolbarWidth] = React.useState();
|
|
29
29
|
React.useEffect(() => subscribe("toolbar-width", (topic, event) => {
|
|
30
|
-
if (
|
|
30
|
+
if (!isDefined(event) || typeof event === "number") {
|
|
31
31
|
setToolbarWidth(event);
|
|
32
32
|
}
|
|
33
33
|
}), [subscribe]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { Component, LightboxProps, Plugin
|
|
2
|
+
import { Component, LightboxProps, Plugin } from "../types.js";
|
|
3
3
|
declare module "../types.js" {
|
|
4
4
|
interface LightboxProps {
|
|
5
5
|
/** if `true`, enter fullscreen mode automatically when the lightbox opens */
|
|
@@ -37,9 +37,8 @@ declare global {
|
|
|
37
37
|
}
|
|
38
38
|
export declare const FullscreenContainer: Component;
|
|
39
39
|
/** Fullscreen button props */
|
|
40
|
-
export declare type FullscreenButtonProps = Pick<LightboxProps, "labels"> & {
|
|
40
|
+
export declare type FullscreenButtonProps = Pick<LightboxProps, "labels" | "render"> & {
|
|
41
41
|
auto: boolean;
|
|
42
|
-
render: Render;
|
|
43
42
|
};
|
|
44
43
|
/** Fullscreen button */
|
|
45
44
|
export declare const FullscreenButton: React.FC<FullscreenButtonProps>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { Component, LightboxProps, Plugin } from "../types.js";
|
|
2
|
+
import { Component, DeepNonNullable, LightboxProps, Plugin } from "../types.js";
|
|
3
3
|
import { ContainerRect } from "../core/index.js";
|
|
4
4
|
export declare type Position = "top" | "bottom" | "start" | "end";
|
|
5
5
|
declare module "../types.js" {
|
|
@@ -33,9 +33,6 @@ declare module "../types.js" {
|
|
|
33
33
|
}) => React.ReactNode;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
declare type DeepNonNullable<T> = NonNullable<{
|
|
37
|
-
[K in keyof T]-?: NonNullable<T[K]>;
|
|
38
|
-
}>;
|
|
39
36
|
declare type ThumbnailsInternal = DeepNonNullable<LightboxProps["thumbnails"]>;
|
|
40
37
|
declare type ThumbnailsTrackProps = Pick<LightboxProps, "slides" | "carousel" | "animation" | "render"> & {
|
|
41
38
|
container: React.RefObject<HTMLDivElement>;
|
|
@@ -213,8 +213,8 @@ export const ThumbnailsTrack = ({ container, startingIndex, slides, carousel, an
|
|
|
213
213
|
return (React.createElement(Thumbnail, { key: slideIndex, rect: thumbnailRect, slide: slide, imageFit: imageFit, render: render, active: slideIndex === index, fadeIn: fadeIn, fadeOut: fadeOut, placeholder: Boolean(placeholder), onClick: handleClick(slideIndex) }));
|
|
214
214
|
}))));
|
|
215
215
|
};
|
|
216
|
-
export const ThumbnailsComponent = ({ thumbnails:
|
|
217
|
-
const thumbnails = { ...defaultThumbnailsProps, ...
|
|
216
|
+
export const ThumbnailsComponent = ({ thumbnails: thumbnailsProps, slides, index, carousel, animation, render, children, }) => {
|
|
217
|
+
const thumbnails = { ...defaultThumbnailsProps, ...thumbnailsProps };
|
|
218
218
|
const ref = React.useRef(null);
|
|
219
219
|
const track = (React.createElement(ThumbnailsTrack, { container: ref, slides: slides, thumbnails: thumbnails, carousel: carousel, animation: animation, render: render, startingIndex: index, thumbnailRect: { width: thumbnails.width, height: thumbnails.height } }));
|
|
220
220
|
return (React.createElement("div", { ref: ref, className: clsx(cssClass(cssPrefix()), cssClass(cssPrefix(`${thumbnails.position}`)), cssClass("fullsize")) },
|
|
@@ -223,10 +223,10 @@ export const ThumbnailsComponent = ({ thumbnails: originalThumbnails, slides, in
|
|
|
223
223
|
(thumbnails.position === "end" || thumbnails.position === "bottom") && track));
|
|
224
224
|
};
|
|
225
225
|
export const Thumbnails = ({ augment, contains, append, addParent }) => {
|
|
226
|
-
augment(({ thumbnails
|
|
226
|
+
augment(({ thumbnails, ...restProps }) => ({
|
|
227
227
|
thumbnails: {
|
|
228
228
|
...defaultThumbnailsProps,
|
|
229
|
-
...
|
|
229
|
+
...thumbnails,
|
|
230
230
|
},
|
|
231
231
|
...restProps,
|
|
232
232
|
}));
|
|
@@ -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,425 @@
|
|
|
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
|
|
136
|
+
? Math.max(round(currentMaxSlideRect.width / currentSlideRect.width, 5), 1)
|
|
137
|
+
: 1;
|
|
138
|
+
const [state, setState] = React.useState({ zoom: 1, offsetX: 0, offsetY: 0 });
|
|
139
|
+
const refs = React.useRef({
|
|
140
|
+
state,
|
|
141
|
+
slideRect: currentSlideRect,
|
|
142
|
+
containerRef: currentContainerRef,
|
|
143
|
+
controllerRef: currentControllerRef,
|
|
144
|
+
containerRect: currentContainerRect,
|
|
145
|
+
controllerRect: currentControllerRect,
|
|
146
|
+
maxZoom: currentMaxZoom,
|
|
147
|
+
reduceMotion: currentReduceMotion,
|
|
148
|
+
setMinZoom: currentSetMinZoom,
|
|
149
|
+
setMaxZoom: currentSetMaxZoom,
|
|
150
|
+
activePointers: [],
|
|
151
|
+
lastPointerDown: 0,
|
|
152
|
+
zoomProps,
|
|
153
|
+
});
|
|
154
|
+
refs.current.state = state;
|
|
155
|
+
refs.current.slideRect = currentSlideRect;
|
|
156
|
+
refs.current.containerRef = currentContainerRef;
|
|
157
|
+
refs.current.controllerRef = currentControllerRef;
|
|
158
|
+
refs.current.containerRect = currentContainerRect;
|
|
159
|
+
refs.current.controllerRect = currentControllerRect;
|
|
160
|
+
refs.current.maxZoom = currentMaxZoom;
|
|
161
|
+
refs.current.reduceMotion = currentReduceMotion;
|
|
162
|
+
refs.current.setMinZoom = currentSetMinZoom;
|
|
163
|
+
refs.current.setMaxZoom = currentSetMaxZoom;
|
|
164
|
+
refs.current.zoomAnimationDuration = animation.zoom;
|
|
165
|
+
refs.current.zoomProps = zoomProps;
|
|
166
|
+
const changeOffsets = React.useCallback((dx, dy, newZoom) => {
|
|
167
|
+
const { state: { zoom, offsetX, offsetY }, containerRect, slideRect, } = refs.current;
|
|
168
|
+
const targetZoom = newZoom || zoom;
|
|
169
|
+
const newOffsetX = offsetX - (dx || 0);
|
|
170
|
+
const newOffsetY = offsetY - (dy || 0);
|
|
171
|
+
const maxOffsetX = containerRect ? (slideRect.width * targetZoom - containerRect.width) / 2 / targetZoom : 0;
|
|
172
|
+
const maxOffsetY = containerRect ? (slideRect.height * targetZoom - containerRect.height) / 2 / targetZoom : 0;
|
|
173
|
+
setState((prev) => ({
|
|
174
|
+
...prev,
|
|
175
|
+
offsetX: Math.min(Math.abs(newOffsetX), Math.max(maxOffsetX, 0)) * Math.sign(newOffsetX),
|
|
176
|
+
offsetY: Math.min(Math.abs(newOffsetY), Math.max(maxOffsetY, 0)) * Math.sign(newOffsetY),
|
|
177
|
+
}));
|
|
178
|
+
}, []);
|
|
179
|
+
useEnhancedEffect(() => {
|
|
180
|
+
if (refs.current.state.zoom > 1) {
|
|
181
|
+
changeOffsets();
|
|
182
|
+
}
|
|
183
|
+
}, [currentControllerRect.width, currentControllerRect.height, changeOffsets]);
|
|
184
|
+
useEnhancedEffect(() => {
|
|
185
|
+
var _a, _b;
|
|
186
|
+
const { current } = refs;
|
|
187
|
+
const { zoomAnimation, zoomAnimationStart, zoomAnimationDuration, reduceMotion, containerRef } = current;
|
|
188
|
+
zoomAnimation === null || zoomAnimation === void 0 ? void 0 : zoomAnimation.cancel();
|
|
189
|
+
if (zoomAnimationStart && containerRef.current) {
|
|
190
|
+
current.zoomAnimation = (_b = (_a = containerRef.current).animate) === null || _b === void 0 ? void 0 : _b.call(_a, [
|
|
191
|
+
{ transform: zoomAnimationStart },
|
|
192
|
+
{
|
|
193
|
+
transform: `scale(${state.zoom}) translate3d(${state.offsetX}px, ${state.offsetY}px, 0)`,
|
|
194
|
+
},
|
|
195
|
+
], {
|
|
196
|
+
duration: reduceMotion ? 0 : zoomAnimationDuration !== null && zoomAnimationDuration !== void 0 ? zoomAnimationDuration : 500,
|
|
197
|
+
easing: zoomAnimation ? "ease-out" : "ease-in-out",
|
|
198
|
+
});
|
|
199
|
+
current.zoomAnimationStart = undefined;
|
|
200
|
+
if (current.zoomAnimation) {
|
|
201
|
+
current.zoomAnimation.onfinish = () => {
|
|
202
|
+
current.zoomAnimation = undefined;
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}, [state.zoom, state.offsetX, state.offsetY]);
|
|
207
|
+
useEnhancedEffect(() => {
|
|
208
|
+
if (offset === 0) {
|
|
209
|
+
const { setMinZoom, setMaxZoom } = refs.current;
|
|
210
|
+
const resetZoom = () => {
|
|
211
|
+
setState({ zoom: 1, offsetX: 0, offsetY: 0 });
|
|
212
|
+
setMinZoom(true);
|
|
213
|
+
setMaxZoom(false);
|
|
214
|
+
};
|
|
215
|
+
resetZoom();
|
|
216
|
+
return () => {
|
|
217
|
+
resetZoom();
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return () => { };
|
|
221
|
+
}, [offset]);
|
|
222
|
+
useEnhancedEffect(() => {
|
|
223
|
+
if (offset === 0) {
|
|
224
|
+
const { setMinZoom, setMaxZoom, maxZoom } = refs.current;
|
|
225
|
+
setMinZoom(state.zoom <= 1);
|
|
226
|
+
setMaxZoom(state.zoom >= maxZoom);
|
|
227
|
+
}
|
|
228
|
+
}, [offset, state.zoom]);
|
|
229
|
+
const changeZoom = React.useCallback((value, rapid, dx, dy) => {
|
|
230
|
+
const { current } = refs;
|
|
231
|
+
const { state: { zoom }, containerRef, containerRect, maxZoom, } = current;
|
|
232
|
+
if (!containerRef.current || !containerRect)
|
|
233
|
+
return;
|
|
234
|
+
const newZoom = round(Math.min(Math.max(value, 1), maxZoom), 5);
|
|
235
|
+
if (newZoom === zoom)
|
|
236
|
+
return;
|
|
237
|
+
if (!rapid) {
|
|
238
|
+
current.zoomAnimationStart = window.getComputedStyle(containerRef.current).transform;
|
|
239
|
+
}
|
|
240
|
+
changeOffsets(dx ? dx * (1 / zoom - 1 / newZoom) : 0, dy ? dy * (1 / zoom - 1 / newZoom) : 0, newZoom);
|
|
241
|
+
setState((prev) => ({ ...prev, zoom: newZoom }));
|
|
242
|
+
}, [changeOffsets]);
|
|
243
|
+
const translateCoordinates = React.useCallback((event) => {
|
|
244
|
+
const { controllerRef } = refs.current;
|
|
245
|
+
if (controllerRef.current) {
|
|
246
|
+
const { pageX, pageY } = event;
|
|
247
|
+
const { scrollX, scrollY } = window;
|
|
248
|
+
const { left, top, width, height } = controllerRef.current.getBoundingClientRect();
|
|
249
|
+
return [pageX - left - scrollX - width / 2, pageY - top - scrollY - height / 2];
|
|
250
|
+
}
|
|
251
|
+
return [];
|
|
252
|
+
}, []);
|
|
253
|
+
const onKeyDown = React.useCallback((event) => {
|
|
254
|
+
const { state: { zoom }, zoomProps: { keyboardMoveDistance, zoomInMultiplier }, } = refs.current;
|
|
255
|
+
if (zoom > 1) {
|
|
256
|
+
const move = (deltaX, deltaY) => {
|
|
257
|
+
event.stopPropagation();
|
|
258
|
+
changeOffsets(deltaX, deltaY);
|
|
259
|
+
};
|
|
260
|
+
if (event.key === "ArrowDown") {
|
|
261
|
+
move(0, keyboardMoveDistance);
|
|
262
|
+
}
|
|
263
|
+
else if (event.key === "ArrowUp") {
|
|
264
|
+
move(0, -keyboardMoveDistance);
|
|
265
|
+
}
|
|
266
|
+
else if (event.key === "ArrowLeft") {
|
|
267
|
+
move(-keyboardMoveDistance, 0);
|
|
268
|
+
}
|
|
269
|
+
else if (event.key === "ArrowRight") {
|
|
270
|
+
move(keyboardMoveDistance, 0);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (event.key === "+" || (event.key === "=" && event.metaKey)) {
|
|
274
|
+
changeZoom(zoom * zoomInMultiplier);
|
|
275
|
+
}
|
|
276
|
+
else if (event.key === "-" || (event.key === "_" && event.metaKey)) {
|
|
277
|
+
changeZoom(zoom / zoomInMultiplier);
|
|
278
|
+
}
|
|
279
|
+
else if (event.key === "0" && event.metaKey) {
|
|
280
|
+
changeZoom(1);
|
|
281
|
+
}
|
|
282
|
+
}, [changeZoom, changeOffsets]);
|
|
283
|
+
const onWheel = React.useCallback((event) => {
|
|
284
|
+
const { state: { zoom }, zoomProps: { wheelZoomDistanceFactor }, } = refs.current;
|
|
285
|
+
if (event.ctrlKey) {
|
|
286
|
+
event.stopPropagation();
|
|
287
|
+
if (Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
|
|
288
|
+
changeZoom(zoom * (1 - event.deltaY / wheelZoomDistanceFactor), true, ...translateCoordinates(event));
|
|
289
|
+
}
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (zoom > 1) {
|
|
293
|
+
event.stopPropagation();
|
|
294
|
+
changeOffsets(event.deltaX, event.deltaY);
|
|
295
|
+
}
|
|
296
|
+
}, [changeZoom, changeOffsets, translateCoordinates]);
|
|
297
|
+
const clearPointer = React.useCallback((event) => {
|
|
298
|
+
const { activePointers } = refs.current;
|
|
299
|
+
activePointers.splice(0, activePointers.length, ...activePointers.filter((p) => p.pointerId !== event.pointerId));
|
|
300
|
+
}, []);
|
|
301
|
+
const replacePointer = React.useCallback((event) => {
|
|
302
|
+
clearPointer(event);
|
|
303
|
+
refs.current.activePointers.push(event);
|
|
304
|
+
}, [clearPointer]);
|
|
305
|
+
const onPointerDown = React.useCallback((event) => {
|
|
306
|
+
var _a;
|
|
307
|
+
const { current } = refs;
|
|
308
|
+
const { state: { zoom }, containerRef, activePointers, lastPointerDown, maxZoom, zoomProps: { doubleTapDelay, doubleClickDelay, zoomInMultiplier, doubleClickMaxStops }, } = current;
|
|
309
|
+
if (!((_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.contains(event.target))) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (zoom > 1) {
|
|
313
|
+
event.stopPropagation();
|
|
314
|
+
}
|
|
315
|
+
const { timeStamp } = event;
|
|
316
|
+
if (activePointers.length === 0 &&
|
|
317
|
+
timeStamp - lastPointerDown < (event.pointerType === "touch" ? doubleTapDelay : doubleClickDelay)) {
|
|
318
|
+
current.lastPointerDown = 0;
|
|
319
|
+
changeZoom(zoom !== maxZoom ? zoom * Math.max(maxZoom ** (1 / doubleClickMaxStops), zoomInMultiplier) : 1, false, ...translateCoordinates(event));
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
current.lastPointerDown = timeStamp;
|
|
323
|
+
}
|
|
324
|
+
replacePointer(event);
|
|
325
|
+
if (activePointers.length === 2) {
|
|
326
|
+
current.pinchZoomDistance = distance(activePointers[0], activePointers[1]);
|
|
327
|
+
}
|
|
328
|
+
}, [changeZoom, replacePointer, translateCoordinates]);
|
|
329
|
+
const onPointerMove = React.useCallback((event) => {
|
|
330
|
+
const { current } = refs;
|
|
331
|
+
const { state: { zoom }, activePointers, pinchZoomDistance, zoomProps: { pinchZoomDistanceFactor }, } = current;
|
|
332
|
+
const activePointer = activePointers.find((p) => p.pointerId === event.pointerId);
|
|
333
|
+
if (activePointers.length === 2 && pinchZoomDistance) {
|
|
334
|
+
event.stopPropagation();
|
|
335
|
+
replacePointer(event);
|
|
336
|
+
const currentDistance = distance(activePointers[0], activePointers[1]);
|
|
337
|
+
const delta = currentDistance - pinchZoomDistance;
|
|
338
|
+
if (Math.abs(delta) > 0) {
|
|
339
|
+
changeZoom(zoom * (1 + delta / pinchZoomDistanceFactor), true, ...activePointers
|
|
340
|
+
.map((x) => translateCoordinates(x))
|
|
341
|
+
.reduce((acc, coordinate) => coordinate.map((x, i) => acc[i] + x / 2)));
|
|
342
|
+
current.pinchZoomDistance = currentDistance;
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (zoom > 1) {
|
|
347
|
+
event.stopPropagation();
|
|
348
|
+
if (activePointer) {
|
|
349
|
+
if (activePointers.length === 1) {
|
|
350
|
+
changeOffsets((activePointer.clientX - event.clientX) / zoom, (activePointer.clientY - event.clientY) / zoom);
|
|
351
|
+
}
|
|
352
|
+
replacePointer(event);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}, [changeOffsets, replacePointer, changeZoom, translateCoordinates]);
|
|
356
|
+
const onPointerUp = React.useCallback((event) => {
|
|
357
|
+
const { current } = refs;
|
|
358
|
+
const { activePointers } = current;
|
|
359
|
+
if (activePointers.length === 2 && activePointers.find((p) => p.pointerId === event.pointerId)) {
|
|
360
|
+
current.pinchZoomDistance = undefined;
|
|
361
|
+
}
|
|
362
|
+
clearPointer(event);
|
|
363
|
+
}, [clearPointer]);
|
|
364
|
+
React.useEffect(() => offset === 0
|
|
365
|
+
? 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))
|
|
366
|
+
: () => { }, [offset, subscribe, subscribeSensors, onKeyDown, onPointerDown, onPointerMove, onPointerUp, onWheel, changeZoom]);
|
|
367
|
+
const { state: { zoom, offsetX, offsetY }, } = refs.current;
|
|
368
|
+
const scaledRect = offset === 0
|
|
369
|
+
? {
|
|
370
|
+
width: rect.width * zoom,
|
|
371
|
+
height: rect.height * zoom,
|
|
372
|
+
}
|
|
373
|
+
: rect;
|
|
374
|
+
let rendered = (_a = render.slide) === null || _a === void 0 ? void 0 : _a.call(render, slide, offset, scaledRect);
|
|
375
|
+
if (!rendered && !("type" in slide) && "src" in slide) {
|
|
376
|
+
rendered = (React.createElement(ImageSlide, { slide: slide, offset: offset, rect: scaledRect, render: render, imageFit: carousel.imageFit }));
|
|
377
|
+
}
|
|
378
|
+
return rendered ? (React.createElement("div", { ref: setContainerRef, className: clsx(cssClass("fullsize"), cssClass("flex_center")), ...(offset === 0
|
|
379
|
+
? { style: { transform: `scale(${zoom}) translate3d(${offsetX}px, ${offsetY}px, 0)` } }
|
|
380
|
+
: null) }, rendered)) : null;
|
|
381
|
+
};
|
|
382
|
+
const ZoomWrapper = ({ slide, offset, rect, render, carousel, animation }) => {
|
|
383
|
+
var _a;
|
|
384
|
+
const { setZoomSupported } = useZoom();
|
|
385
|
+
const imageSlide = !("type" in slide);
|
|
386
|
+
const zoomSupported = imageSlide && ("srcSet" in slide || ("width" in slide && "height" in slide));
|
|
387
|
+
React.useEffect(() => {
|
|
388
|
+
if (offset === 0) {
|
|
389
|
+
setZoomSupported(zoomSupported);
|
|
390
|
+
}
|
|
391
|
+
}, [offset, zoomSupported, setZoomSupported]);
|
|
392
|
+
if (zoomSupported) {
|
|
393
|
+
return (React.createElement(ZoomContainer, { slide: slide, offset: offset, rect: rect, render: render, carousel: carousel, animation: animation }));
|
|
394
|
+
}
|
|
395
|
+
const rendered = (_a = render.slide) === null || _a === void 0 ? void 0 : _a.call(render, slide, offset, rect);
|
|
396
|
+
if (rendered) {
|
|
397
|
+
return React.createElement(React.Fragment, null, rendered);
|
|
398
|
+
}
|
|
399
|
+
if (imageSlide) {
|
|
400
|
+
return React.createElement(ImageSlide, { slide: slide, offset: offset, rect: rect, render: render, imageFit: carousel.imageFit });
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
};
|
|
404
|
+
export const ZoomModule = createModule("zoom", ZoomContextProvider);
|
|
405
|
+
export const Zoom = ({ augment, append }) => {
|
|
406
|
+
augment(({ toolbar: { buttons, ...restToolbar }, render, carousel, animation, zoom, ...restProps }) => ({
|
|
407
|
+
toolbar: {
|
|
408
|
+
buttons: [React.createElement(ZoomButtonsGroup, { key: "zoom", labels: restProps.labels, render: render }), ...buttons],
|
|
409
|
+
...restToolbar,
|
|
410
|
+
},
|
|
411
|
+
render: {
|
|
412
|
+
...render,
|
|
413
|
+
slide: (slide, offset, rect) => (React.createElement(ZoomWrapper, { slide: slide, offset: offset, rect: rect, render: render, carousel: carousel, animation: animation })),
|
|
414
|
+
},
|
|
415
|
+
zoom: {
|
|
416
|
+
...defaultZoomProps,
|
|
417
|
+
...zoom,
|
|
418
|
+
},
|
|
419
|
+
carousel,
|
|
420
|
+
animation,
|
|
421
|
+
...restProps,
|
|
422
|
+
}));
|
|
423
|
+
append("controller", ZoomModule);
|
|
424
|
+
};
|
|
425
|
+
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;
|
|
@@ -214,6 +212,7 @@
|
|
|
214
212
|
.yarl__no_scroll {
|
|
215
213
|
height: 100%;
|
|
216
214
|
overflow: hidden;
|
|
215
|
+
overscroll-behavior: none;
|
|
217
216
|
}
|
|
218
217
|
.yarl__pad_scrollbar {
|
|
219
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.2",
|
|
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
|
},
|