yet-another-react-lightbox 1.3.6 → 1.4.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 +1 -1
- package/dist/Lightbox.js +0 -1
- package/dist/core/components/Icons.js +1 -3
- package/dist/core/components/ImageSlide.d.ts +4 -2
- package/dist/core/components/ImageSlide.js +10 -10
- package/dist/core/modules/Carousel.js +19 -14
- package/dist/core/modules/Controller.d.ts +3 -3
- package/dist/core/modules/Controller.js +16 -17
- package/dist/core/modules/NoScroll.js +0 -1
- package/dist/plugins/Captions.d.ts +22 -0
- package/dist/plugins/Captions.js +34 -0
- package/dist/plugins/Fullscreen.js +0 -4
- package/dist/plugins/Inline.js +0 -3
- package/dist/plugins/Video.js +3 -9
- package/dist/plugins/captions.css +39 -0
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/types.d.ts +17 -1
- package/dist/types.js +3 -0
- package/package.json +13 -8
package/README.md
CHANGED
package/dist/Lightbox.js
CHANGED
|
@@ -24,7 +24,6 @@ const LightboxComponent = (props) => {
|
|
|
24
24
|
return React.createElement(React.Fragment, null, renderNode(createNode(CoreModule, config), augmentedProps));
|
|
25
25
|
};
|
|
26
26
|
LightboxComponent.propTypes = LightboxPropTypes;
|
|
27
|
-
/** Modern React lightbox component */
|
|
28
27
|
export const Lightbox = (props) => {
|
|
29
28
|
const { carousel, animation, render, toolbar, controller, on, ...restProps } = props;
|
|
30
29
|
const { carousel: defaultCarousel, animation: defaultAnimation, render: defaultRender, toolbar: defaultToolbar, controller: defaultController, on: defaultOn, ...restDefaultProps } = LightboxDefaultProps;
|
|
@@ -10,7 +10,5 @@ export const createIcon = (name, glyph) => {
|
|
|
10
10
|
export const CloseIcon = createIcon("Close", React.createElement("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }));
|
|
11
11
|
export const PreviousIcon = createIcon("Previous", React.createElement("path", { d: "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" }));
|
|
12
12
|
export const NextIcon = createIcon("Next", React.createElement("path", { d: "M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" }));
|
|
13
|
-
export const LoadingIcon = createIcon("Loading", React.createElement(React.Fragment, null, Array.from({ length: 8 }).map((_, index, array) => (React.createElement("line", {
|
|
14
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
15
|
-
key: index, x1: "12", y1: "6.5", x2: "12", y2: "1.8", strokeLinecap: "round", strokeWidth: "2.6", stroke: "currentColor", strokeOpacity: (1 / array.length) * (index + 1), transform: `rotate(${(360 / array.length) * index}, 12, 12)` })))));
|
|
13
|
+
export const LoadingIcon = createIcon("Loading", React.createElement(React.Fragment, null, Array.from({ length: 8 }).map((_, index, array) => (React.createElement("line", { key: index, x1: "12", y1: "6.5", x2: "12", y2: "1.8", strokeLinecap: "round", strokeWidth: "2.6", stroke: "currentColor", strokeOpacity: (1 / array.length) * (index + 1), transform: `rotate(${(360 / array.length) * index}, 12, 12)` })))));
|
|
16
14
|
export const ErrorIcon = createIcon("Error", React.createElement("path", { d: "M21.9,21.9l-8.49-8.49l0,0L3.59,3.59l0,0L2.1,2.1L0.69,3.51L3,5.83V19c0,1.1,0.9,2,2,2h13.17l2.31,2.31L21.9,21.9z M5,18 l3.5-4.5l2.5,3.01L12.17,15l3,3H5z M21,18.17L5.83,3H19c1.1,0,2,0.9,2,2V18.17z" }));
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { Render, SlideImage } from "../../types.js";
|
|
3
|
+
import { ContainerRect } from "../hooks/index.js";
|
|
3
4
|
export declare type ImageSlideProps = {
|
|
4
5
|
slide: SlideImage;
|
|
5
|
-
render
|
|
6
|
+
render?: Render;
|
|
7
|
+
rect?: ContainerRect;
|
|
6
8
|
};
|
|
7
|
-
export declare const ImageSlide: ({ slide: image, render }: ImageSlideProps) => JSX.Element;
|
|
9
|
+
export declare const ImageSlide: ({ slide: image, render, rect }: ImageSlideProps) => JSX.Element;
|
|
@@ -2,13 +2,11 @@ import * as React from "react";
|
|
|
2
2
|
import { clsx, cssClass } from "../utils.js";
|
|
3
3
|
import { useLatest } from "../hooks/index.js";
|
|
4
4
|
import { ErrorIcon, LoadingIcon } from "./Icons.js";
|
|
5
|
-
|
|
6
|
-
export const ImageSlide = ({ slide: image, render }) => {
|
|
5
|
+
export const ImageSlide = ({ slide: image, render, rect }) => {
|
|
7
6
|
var _a, _b, _c;
|
|
8
7
|
const [state, setState] = React.useState("loading");
|
|
9
8
|
const latestState = useLatest(state);
|
|
10
9
|
const imageRef = React.useRef(null);
|
|
11
|
-
const { containerRect } = useController();
|
|
12
10
|
const handleLoading = React.useCallback((img) => {
|
|
13
11
|
if (latestState.current === "complete") {
|
|
14
12
|
return;
|
|
@@ -37,11 +35,13 @@ export const ImageSlide = ({ slide: image, render }) => {
|
|
|
37
35
|
return (React.createElement(React.Fragment, null,
|
|
38
36
|
React.createElement("img", { ref: setImageRef, onLoad: onLoad, onError: onError, className: clsx(cssClass("slide_image"), state !== "complete" && cssClass("slide_image_loading")), draggable: false, alt: image.alt, ...(image.srcSet
|
|
39
37
|
? {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
...(rect && typeof window !== "undefined"
|
|
39
|
+
? {
|
|
40
|
+
sizes: `${Math.ceil((Math.min(image.aspectRatio ? rect.height * image.aspectRatio : Number.MAX_VALUE, rect.width) /
|
|
41
|
+
window.innerWidth) *
|
|
42
|
+
100)}vw`,
|
|
43
|
+
}
|
|
44
|
+
: null),
|
|
45
45
|
srcSet: image.srcSet
|
|
46
46
|
.sort((a, b) => a.width - b.width)
|
|
47
47
|
.map((item) => `${item.src} ${item.width}w`)
|
|
@@ -59,7 +59,7 @@ export const ImageSlide = ({ slide: image, render }) => {
|
|
|
59
59
|
}), src: image.src }),
|
|
60
60
|
state !== "complete" && (React.createElement("div", { className: cssClass("slide_placeholder") },
|
|
61
61
|
state === "loading" &&
|
|
62
|
-
(render.iconLoading ? (render.iconLoading()) : (React.createElement(LoadingIcon, { className: clsx(cssClass("icon"), cssClass("slide_loading")) }))),
|
|
62
|
+
((render === null || render === void 0 ? void 0 : render.iconLoading) ? (render.iconLoading()) : (React.createElement(LoadingIcon, { className: clsx(cssClass("icon"), cssClass("slide_loading")) }))),
|
|
63
63
|
state === "error" &&
|
|
64
|
-
(render.iconError ? (render.iconError()) : (React.createElement(ErrorIcon, { className: clsx(cssClass("icon"), cssClass("slide_error")) })))))));
|
|
64
|
+
((render === null || render === void 0 ? void 0 : render.iconError) ? (render.iconError()) : (React.createElement(ErrorIcon, { className: clsx(cssClass("icon"), cssClass("slide_error")) })))))));
|
|
65
65
|
};
|
|
@@ -1,35 +1,40 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { LightboxDefaultProps } from "../../types.js";
|
|
3
|
+
import { useContainerRect } from "../hooks/index.js";
|
|
3
4
|
import { createModule } from "../config.js";
|
|
4
5
|
import { clsx, cssClass, cssVar } from "../utils.js";
|
|
5
6
|
import { ImageSlide } from "../components/index.js";
|
|
6
7
|
import { useController } from "./Controller.js";
|
|
7
|
-
const CarouselSlide = ({ slide, offset
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
const CarouselSlide = ({ slide, offset }) => {
|
|
9
|
+
const { setContainerRef, containerRect } = useContainerRect();
|
|
10
|
+
const { latestProps } = useController();
|
|
11
|
+
const { render } = latestProps.current;
|
|
12
|
+
const renderSlide = (rect) => {
|
|
13
|
+
var _a, _b, _c, _d;
|
|
14
|
+
let rendered = (_a = render.slide) === null || _a === void 0 ? void 0 : _a.call(render, slide, offset, rect);
|
|
15
|
+
if (!rendered && "src" in slide) {
|
|
16
|
+
rendered = React.createElement(ImageSlide, { slide: slide, render: render, rect: rect });
|
|
14
17
|
}
|
|
15
|
-
return
|
|
18
|
+
return rendered ? (React.createElement(React.Fragment, null, (_b = render.slideHeader) === null || _b === void 0 ? void 0 :
|
|
19
|
+
_b.call(render, slide),
|
|
20
|
+
((_c = render.slideContainer) !== null && _c !== void 0 ? _c : ((_, x) => x))(slide, rendered), (_d = render.slideFooter) === null || _d === void 0 ? void 0 :
|
|
21
|
+
_d.call(render, slide))) : null;
|
|
16
22
|
};
|
|
17
|
-
return (React.createElement("div", { className: clsx(cssClass("slide"), cssClass("flex_center")), style: { [cssVar("slide_offset")]: offset } }, renderSlide()));
|
|
23
|
+
return (React.createElement("div", { ref: setContainerRef, className: clsx(cssClass("slide"), cssClass("flex_center")), style: { [cssVar("slide_offset")]: offset } }, containerRect && renderSlide(containerRect)));
|
|
18
24
|
};
|
|
19
|
-
export const Carousel = (
|
|
20
|
-
const { slides, carousel: { finite, preload, padding, spacing }, render, } = props;
|
|
25
|
+
export const Carousel = ({ slides, carousel: { finite, preload, padding, spacing } }) => {
|
|
21
26
|
const { currentIndex, globalIndex } = useController();
|
|
22
27
|
const items = [];
|
|
23
28
|
if ((slides === null || slides === void 0 ? void 0 : slides.length) > 0) {
|
|
24
29
|
for (let i = currentIndex - preload; i < currentIndex; i += 1) {
|
|
25
30
|
if (!finite || i >= 0) {
|
|
26
|
-
items.push(React.createElement(CarouselSlide, { key: globalIndex + i - currentIndex, slide: slides[(i + preload * slides.length) % slides.length], offset: i - currentIndex
|
|
31
|
+
items.push(React.createElement(CarouselSlide, { key: globalIndex + i - currentIndex, slide: slides[(i + preload * slides.length) % slides.length], offset: i - currentIndex }));
|
|
27
32
|
}
|
|
28
33
|
}
|
|
29
|
-
items.push(React.createElement(CarouselSlide, { key: globalIndex, slide: slides[currentIndex], offset: 0
|
|
34
|
+
items.push(React.createElement(CarouselSlide, { key: globalIndex, slide: slides[currentIndex], offset: 0 }));
|
|
30
35
|
for (let i = currentIndex + 1; i <= currentIndex + preload; i += 1) {
|
|
31
36
|
if (!finite || i <= slides.length - 1) {
|
|
32
|
-
items.push(React.createElement(CarouselSlide, { key: globalIndex + i - currentIndex, slide: slides[i % slides.length], offset: i - currentIndex
|
|
37
|
+
items.push(React.createElement(CarouselSlide, { key: globalIndex + i - currentIndex, slide: slides[i % slides.length], offset: i - currentIndex }));
|
|
33
38
|
}
|
|
34
39
|
}
|
|
35
40
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { Component } from "../../types.js";
|
|
3
|
-
import {
|
|
2
|
+
import { Component, ComponentProps } from "../../types.js";
|
|
3
|
+
import { SubscribeSensors } from "../hooks/index.js";
|
|
4
4
|
export declare type ControllerContextType = {
|
|
5
|
+
latestProps: React.MutableRefObject<ComponentProps>;
|
|
5
6
|
containerRef: React.RefObject<HTMLDivElement>;
|
|
6
|
-
containerRect: ContainerRect;
|
|
7
7
|
currentIndex: number;
|
|
8
8
|
globalIndex: number;
|
|
9
9
|
subscribeSensors: SubscribeSensors<HTMLDivElement>;
|
|
@@ -2,13 +2,13 @@ import * as React from "react";
|
|
|
2
2
|
import { LightboxDefaultProps } from "../../types.js";
|
|
3
3
|
import { cleanup, clsx, cssClass, cssVar, makeUseContext } from "../utils.js";
|
|
4
4
|
import { createModule } from "../config.js";
|
|
5
|
-
import { useContainerRect, useEnhancedEffect, useSensors } from "../hooks/index.js";
|
|
5
|
+
import { useContainerRect, useEnhancedEffect, useLatest, useSensors } from "../hooks/index.js";
|
|
6
6
|
import { useEvents, useTimeouts } from "../contexts/index.js";
|
|
7
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
|
|
11
|
+
const { containerRef, setContainerRef } = useContainerRect();
|
|
12
12
|
const { registerSensors, subscribeSensors } = useSensors();
|
|
13
13
|
const { subscribe, publish } = useEvents();
|
|
14
14
|
const { setTimeout, clearTimeout } = useTimeouts();
|
|
@@ -16,6 +16,7 @@ export const Controller = ({ children, ...props }) => {
|
|
|
16
16
|
currentIndex: props.index,
|
|
17
17
|
globalIndex: props.index,
|
|
18
18
|
});
|
|
19
|
+
const latestProps = useLatest(props);
|
|
19
20
|
const refs = React.useRef({
|
|
20
21
|
state,
|
|
21
22
|
props,
|
|
@@ -27,9 +28,6 @@ export const Controller = ({ children, ...props }) => {
|
|
|
27
28
|
});
|
|
28
29
|
refs.current.state = state;
|
|
29
30
|
refs.current.props = props;
|
|
30
|
-
refs.current.containerRect = containerRect;
|
|
31
|
-
// prevent browser back/forward navigation on touchpad left/right swipe
|
|
32
|
-
// this has to be done via non-passive native event handler
|
|
33
31
|
useEnhancedEffect(() => {
|
|
34
32
|
const preventDefault = (event) => event.preventDefault();
|
|
35
33
|
const node = containerRef.current;
|
|
@@ -93,7 +91,7 @@ export const Controller = ({ children, ...props }) => {
|
|
|
93
91
|
let newSwipeState = "swipe-animation";
|
|
94
92
|
let newSwipeAnimationDuration = swipeAnimationDuration;
|
|
95
93
|
if (!direction) {
|
|
96
|
-
const containerWidth = (_a = current
|
|
94
|
+
const containerWidth = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth;
|
|
97
95
|
const elapsedTime = current.swipeStartTime ? Date.now() - current.swipeStartTime : 0;
|
|
98
96
|
const expectedTime = containerWidth
|
|
99
97
|
? (swipeAnimationDuration / containerWidth) * Math.abs(swipeOffset)
|
|
@@ -107,7 +105,6 @@ export const Controller = ({ children, ...props }) => {
|
|
|
107
105
|
newSwipeAnimationDuration =
|
|
108
106
|
(newSwipeAnimationDuration / expectedTime) * Math.max(elapsedTime, expectedTime / 5);
|
|
109
107
|
}
|
|
110
|
-
// eslint-disable-next-line no-param-reassign
|
|
111
108
|
direction = swipeOffset > 0 ? "prev" : "next";
|
|
112
109
|
}
|
|
113
110
|
else {
|
|
@@ -116,7 +113,7 @@ export const Controller = ({ children, ...props }) => {
|
|
|
116
113
|
}
|
|
117
114
|
const newState = {};
|
|
118
115
|
if (direction === "prev") {
|
|
119
|
-
if (isSwipeValid(
|
|
116
|
+
if (isSwipeValid(1)) {
|
|
120
117
|
newState.currentIndex = (currentIndex - 1 + slidesCount) % slidesCount;
|
|
121
118
|
newState.globalIndex = globalIndex - 1;
|
|
122
119
|
}
|
|
@@ -126,7 +123,7 @@ export const Controller = ({ children, ...props }) => {
|
|
|
126
123
|
}
|
|
127
124
|
}
|
|
128
125
|
else if (direction === "next") {
|
|
129
|
-
if (isSwipeValid(
|
|
126
|
+
if (isSwipeValid(-1)) {
|
|
130
127
|
newState.currentIndex = (currentIndex + 1) % slidesCount;
|
|
131
128
|
newState.globalIndex = globalIndex + 1;
|
|
132
129
|
}
|
|
@@ -146,7 +143,7 @@ export const Controller = ({ children, ...props }) => {
|
|
|
146
143
|
}, newSwipeAnimationDuration);
|
|
147
144
|
}
|
|
148
145
|
setState((prev) => ({ ...prev, ...newState }));
|
|
149
|
-
}, [setTimeout, resetSwipe, isSwipeValid, rerender]);
|
|
146
|
+
}, [setTimeout, resetSwipe, isSwipeValid, rerender, containerRef]);
|
|
150
147
|
React.useEffect(() => cleanup(subscribe("prev", () => swipe("prev")), subscribe("next", () => swipe("next"))), [subscribe, swipe]);
|
|
151
148
|
React.useEffect(() => subscribeSensors("onKeyUp", (event) => {
|
|
152
149
|
if (event.code === "Escape") {
|
|
@@ -205,11 +202,9 @@ export const Controller = ({ children, ...props }) => {
|
|
|
205
202
|
const onWheel = React.useCallback((event) => {
|
|
206
203
|
var _a;
|
|
207
204
|
if (event.ctrlKey) {
|
|
208
|
-
// zoom
|
|
209
205
|
return;
|
|
210
206
|
}
|
|
211
207
|
if (Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
|
|
212
|
-
// pan-y
|
|
213
208
|
return;
|
|
214
209
|
}
|
|
215
210
|
const { current } = refs;
|
|
@@ -218,6 +213,9 @@ export const Controller = ({ children, ...props }) => {
|
|
|
218
213
|
current.wheelResidualMomentum = event.deltaX;
|
|
219
214
|
return;
|
|
220
215
|
}
|
|
216
|
+
if (!isSwipeValid(-event.deltaX)) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
221
219
|
current.swipeIntent += event.deltaX;
|
|
222
220
|
clearTimeout(current.swipeIntentCleanup);
|
|
223
221
|
if (Math.abs(current.swipeIntent) > SWIPE_OFFSET_THRESHOLD) {
|
|
@@ -235,7 +233,7 @@ export const Controller = ({ children, ...props }) => {
|
|
|
235
233
|
}
|
|
236
234
|
}
|
|
237
235
|
else if (current.swipeState === "swipe") {
|
|
238
|
-
const containerWidth = (_a = current
|
|
236
|
+
const containerWidth = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth;
|
|
239
237
|
if (containerWidth) {
|
|
240
238
|
current.swipeOffset -= event.deltaX;
|
|
241
239
|
current.swipeOffset =
|
|
@@ -261,19 +259,20 @@ export const Controller = ({ children, ...props }) => {
|
|
|
261
259
|
else {
|
|
262
260
|
current.wheelResidualMomentum = event.deltaX;
|
|
263
261
|
}
|
|
264
|
-
}, [updateSwipeOffset, setTimeout, clearTimeout, swipe, resetSwipe, rerender]);
|
|
262
|
+
}, [updateSwipeOffset, setTimeout, clearTimeout, swipe, resetSwipe, rerender, isSwipeValid, containerRef]);
|
|
265
263
|
React.useEffect(() => subscribeSensors("onWheel", onWheel), [subscribeSensors, onWheel]);
|
|
266
264
|
const context = React.useMemo(() => ({
|
|
265
|
+
latestProps,
|
|
267
266
|
containerRef,
|
|
268
|
-
containerRect,
|
|
269
267
|
currentIndex: state.currentIndex,
|
|
270
268
|
globalIndex: state.globalIndex,
|
|
271
269
|
subscribeSensors,
|
|
272
|
-
}), [
|
|
270
|
+
}), [latestProps, containerRef, state.currentIndex, state.globalIndex, subscribeSensors]);
|
|
273
271
|
return (React.createElement("div", { ref: setContainerRef, className: clsx(cssClass("container"), refs.current.swipeState === "swipe" && cssClass("container_swipe")), style: refs.current.swipeAnimationDuration !== LightboxDefaultProps.animation.swipe
|
|
274
272
|
? {
|
|
275
273
|
[cssVar("swipe_animation_duration")]: `${Math.round(refs.current.swipeAnimationDuration)}ms`,
|
|
276
274
|
}
|
|
277
|
-
: undefined, role: "presentation", "aria-live": "polite", tabIndex: -1, ...registerSensors },
|
|
275
|
+
: undefined, role: "presentation", "aria-live": "polite", tabIndex: -1, ...registerSensors },
|
|
276
|
+
React.createElement(ControllerContext.Provider, { value: context }, children)));
|
|
278
277
|
};
|
|
279
278
|
export const ControllerModule = createModule("controller", Controller);
|
|
@@ -7,7 +7,6 @@ const scrollbarPadding = cssVar("scrollbar_padding");
|
|
|
7
7
|
export const NoScroll = ({ children }) => {
|
|
8
8
|
React.useEffect(() => {
|
|
9
9
|
const scrollbarWidth = Math.round(window.innerWidth - document.documentElement.clientWidth);
|
|
10
|
-
// using an arbitrary threshold to counter the 1px difference in some browsers
|
|
11
10
|
if (scrollbarWidth > 1) {
|
|
12
11
|
document.body.style.setProperty(scrollbarPadding, `${scrollbarWidth}px`);
|
|
13
12
|
document.body.classList.add(padScrollbar);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Plugin } from "../types.js";
|
|
2
|
+
declare type TextAlignment = "start" | "end" | "center";
|
|
3
|
+
declare module "../types.js" {
|
|
4
|
+
interface SlideImage {
|
|
5
|
+
/** slide title */
|
|
6
|
+
title?: string;
|
|
7
|
+
/** slide description */
|
|
8
|
+
description?: string;
|
|
9
|
+
}
|
|
10
|
+
interface LightboxProps {
|
|
11
|
+
/** Captions plugin settings */
|
|
12
|
+
captions?: {
|
|
13
|
+
/** description text alignment */
|
|
14
|
+
descriptionTextAlign?: TextAlignment;
|
|
15
|
+
/** maximum number of lines to display in the description section */
|
|
16
|
+
descriptionMaxLines?: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Captions plugin */
|
|
21
|
+
export declare const Captions: Plugin;
|
|
22
|
+
export default Captions;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cssClass, cssVar } from "../core/utils.js";
|
|
3
|
+
const defaultTextAlign = "start";
|
|
4
|
+
const defaultMaxLines = 3;
|
|
5
|
+
const cls = (className) => cssClass(`slide_${className}`);
|
|
6
|
+
const hasTitle = (slide) => "title" in slide ? typeof slide.title === "string" : false;
|
|
7
|
+
const hasDescription = (slide) => "description" in slide ? typeof slide.description === "string" : false;
|
|
8
|
+
const Title = ({ title }) => (React.createElement("div", { className: cls(`title_container`) },
|
|
9
|
+
React.createElement("div", { className: cls("title") }, title)));
|
|
10
|
+
const Description = ({ description, align, maxLines }) => (React.createElement("div", { className: cls("description_container") },
|
|
11
|
+
React.createElement("div", { className: cls("description"), ...(align !== defaultTextAlign || maxLines !== defaultMaxLines
|
|
12
|
+
? {
|
|
13
|
+
style: {
|
|
14
|
+
[cssVar("slide_description_text_align")]: align,
|
|
15
|
+
[cssVar("slide_description_max_lines")]: maxLines,
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
: null) }, description.split("\n").flatMap((line, index) => [...(index > 0 ? [React.createElement("br", { key: index })] : []), line]))));
|
|
19
|
+
export const Captions = ({ augment }) => {
|
|
20
|
+
augment(({ render: { slideFooter: renderFooter, ...restRender }, captions, ...restProps }) => ({
|
|
21
|
+
render: {
|
|
22
|
+
slideFooter: (slide) => {
|
|
23
|
+
var _a, _b;
|
|
24
|
+
return (React.createElement(React.Fragment, null, renderFooter === null || renderFooter === void 0 ? void 0 :
|
|
25
|
+
renderFooter(slide),
|
|
26
|
+
hasTitle(slide) && React.createElement(Title, { title: slide.title }),
|
|
27
|
+
hasDescription(slide) && (React.createElement(Description, { description: slide.description, align: (_a = captions === null || captions === void 0 ? void 0 : captions.descriptionTextAlign) !== null && _a !== void 0 ? _a : defaultTextAlign, maxLines: (_b = captions === null || captions === void 0 ? void 0 : captions.descriptionMaxLines) !== null && _b !== void 0 ? _b : defaultMaxLines }))));
|
|
28
|
+
},
|
|
29
|
+
...restRender,
|
|
30
|
+
},
|
|
31
|
+
...restProps,
|
|
32
|
+
}));
|
|
33
|
+
};
|
|
34
|
+
export default Captions;
|
|
@@ -2,7 +2,6 @@ import * as React from "react";
|
|
|
2
2
|
import { createIcon, IconButton, label, useController, useLatest } from "../core/index.js";
|
|
3
3
|
const EnterFullscreenIcon = createIcon("EnterFullscreen", React.createElement("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }));
|
|
4
4
|
const ExitFullscreenIcon = createIcon("ExitFullscreen", React.createElement("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }));
|
|
5
|
-
/** Fullscreen button */
|
|
6
5
|
export const FullscreenButton = ({ auto, labels, render }) => {
|
|
7
6
|
const [fullscreen, setFullscreen] = React.useState(false);
|
|
8
7
|
const latestAuto = useLatest(auto);
|
|
@@ -33,7 +32,6 @@ export const FullscreenButton = ({ auto, labels, render }) => {
|
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
34
|
catch (err) {
|
|
36
|
-
//
|
|
37
35
|
}
|
|
38
36
|
}
|
|
39
37
|
}, [containerRef]);
|
|
@@ -54,7 +52,6 @@ export const FullscreenButton = ({ auto, labels, render }) => {
|
|
|
54
52
|
}
|
|
55
53
|
}
|
|
56
54
|
catch (err) {
|
|
57
|
-
//
|
|
58
55
|
}
|
|
59
56
|
}
|
|
60
57
|
}, [getFullscreenElement]);
|
|
@@ -95,7 +92,6 @@ export const FullscreenButton = ({ auto, labels, render }) => {
|
|
|
95
92
|
return null;
|
|
96
93
|
return render.buttonFullscreen ? (React.createElement(React.Fragment, null, render.buttonFullscreen({ fullscreen, toggleFullscreen }))) : (React.createElement(IconButton, { label: fullscreen ? label(labels, "Exit Fullscreen") : label(labels, "Enter Fullscreen"), icon: fullscreen ? ExitFullscreenIcon : EnterFullscreenIcon, renderIcon: fullscreen ? render.iconExitFullscreen : render.iconEnterFullscreen, onClick: toggleFullscreen }));
|
|
97
94
|
};
|
|
98
|
-
/** Fullscreen plugin */
|
|
99
95
|
export const Fullscreen = ({ augment }) => {
|
|
100
96
|
augment(({ toolbar: { buttons, ...restToolbar }, ...restProps }) => ({
|
|
101
97
|
toolbar: {
|
package/dist/plugins/Inline.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { createModule } from "../core/index.js";
|
|
3
|
-
/** Inline plugin container */
|
|
4
3
|
export const InlineContainer = ({ inline, children }) => React.createElement("div", { ...inline }, children);
|
|
5
|
-
/** Inline plugin module */
|
|
6
4
|
export const InlineModule = createModule("inline", InlineContainer);
|
|
7
|
-
/** Inline plugin */
|
|
8
5
|
export const Inline = ({ augment, replace, remove }) => {
|
|
9
6
|
augment(({ toolbar: { buttons, ...restToolbar }, open, close, controller: { focus, ...restController }, ...restProps }) => ({
|
|
10
7
|
open: true,
|
package/dist/plugins/Video.js
CHANGED
|
@@ -12,7 +12,6 @@ SlideTypesPropTypes.push(PropTypes.shape({
|
|
|
12
12
|
type: PropTypes.string.isRequired,
|
|
13
13
|
}).isRequired),
|
|
14
14
|
}));
|
|
15
|
-
/** Video slide */
|
|
16
15
|
export const VideoSlide = ({ slide: { sources, poster, width, height } }) => {
|
|
17
16
|
const { setContainerRef, containerRect } = useContainerRect();
|
|
18
17
|
const scaleWidthAndHeight = () => {
|
|
@@ -27,21 +26,16 @@ export const VideoSlide = ({ slide: { sources, poster, width, height } }) => {
|
|
|
27
26
|
return (React.createElement(React.Fragment, null, sources && (React.createElement("div", { ref: setContainerRef, style: {
|
|
28
27
|
width: "100%",
|
|
29
28
|
height: "100%",
|
|
30
|
-
}, className: clsx(cssClass("video_container"), cssClass("flex_center")) }, containerRect && (
|
|
31
|
-
// eslint-disable-next-line jsx-a11y/media-has-caption
|
|
32
|
-
React.createElement("video", { controls: true, playsInline: true, poster: poster, ...scaleWidthAndHeight() }, sources.map(({ src, type }, index) => (
|
|
33
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
34
|
-
React.createElement("source", { key: index, src: src, type: type })))))))));
|
|
29
|
+
}, className: clsx(cssClass("video_container"), cssClass("flex_center")) }, containerRect && (React.createElement("video", { controls: true, playsInline: true, poster: poster, ...scaleWidthAndHeight() }, sources.map(({ src, type }, index) => (React.createElement("source", { key: index, src: src, type: type })))))))));
|
|
35
30
|
};
|
|
36
|
-
/** Video plugin */
|
|
37
31
|
export const Video = ({ augment }) => {
|
|
38
32
|
augment(({ render: { slide: renderSlide, ...restRender }, ...restProps }) => ({
|
|
39
33
|
render: {
|
|
40
|
-
slide: (slide) => {
|
|
34
|
+
slide: (slide, offset, rect) => {
|
|
41
35
|
if ("type" in slide && slide.type === "video") {
|
|
42
36
|
return React.createElement(VideoSlide, { slide: slide });
|
|
43
37
|
}
|
|
44
|
-
return renderSlide === null || renderSlide === void 0 ? void 0 : renderSlide(slide);
|
|
38
|
+
return renderSlide === null || renderSlide === void 0 ? void 0 : renderSlide(slide, offset, rect);
|
|
45
39
|
},
|
|
46
40
|
...restRender,
|
|
47
41
|
},
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
.yarl__slide_title {
|
|
2
|
+
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
|
|
3
|
+
font-size: 1.25rem;
|
|
4
|
+
font-weight: 700;
|
|
5
|
+
max-width: calc(100% - 96px - 8px);
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
text-overflow: ellipsis;
|
|
8
|
+
white-space: nowrap;
|
|
9
|
+
}
|
|
10
|
+
.yarl__slide_title_container {
|
|
11
|
+
position: absolute;
|
|
12
|
+
left: 0;
|
|
13
|
+
right: 0;
|
|
14
|
+
top: 0;
|
|
15
|
+
padding: 16px;
|
|
16
|
+
background: rgba(0, 0, 0, 0.5);
|
|
17
|
+
}
|
|
18
|
+
.yarl__slide_description {
|
|
19
|
+
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
|
|
20
|
+
font-size: 1rem;
|
|
21
|
+
line-height: 1.2;
|
|
22
|
+
font-weight: 500;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
-webkit-hyphens: auto;
|
|
25
|
+
-ms-hyphens: auto;
|
|
26
|
+
hyphens: auto;
|
|
27
|
+
display: -webkit-box;
|
|
28
|
+
-webkit-box-orient: vertical;
|
|
29
|
+
-webkit-line-clamp: var(--yarl__slide_description_max_lines, 3);
|
|
30
|
+
text-align: var(--yarl__slide_description_text_align, start);
|
|
31
|
+
}
|
|
32
|
+
.yarl__slide_description_container {
|
|
33
|
+
position: absolute;
|
|
34
|
+
left: 0;
|
|
35
|
+
right: 0;
|
|
36
|
+
bottom: 0;
|
|
37
|
+
padding: 16px;
|
|
38
|
+
background: rgba(0, 0, 0, 0.5);
|
|
39
|
+
}
|
package/dist/plugins/index.d.ts
CHANGED
package/dist/plugins/index.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
|
+
import { ContainerRect } from "./core/hooks/useContainerRect.js";
|
|
3
4
|
/** Image slide properties */
|
|
4
5
|
export interface SlideImage {
|
|
5
6
|
/** image URL */
|
|
@@ -49,7 +50,19 @@ export interface ControllerSettings {
|
|
|
49
50
|
/** Custom render functions. */
|
|
50
51
|
export interface Render {
|
|
51
52
|
/** render custom slide type, or override the default image slide */
|
|
52
|
-
slide?: (
|
|
53
|
+
slide?: (
|
|
54
|
+
/** slide */
|
|
55
|
+
slide: Slide,
|
|
56
|
+
/** slide offset (`0` - current slide, `1` - next slide, `-1` - previous slide, etc.) */
|
|
57
|
+
offset: number,
|
|
58
|
+
/** container rect */
|
|
59
|
+
rect: ContainerRect) => React.ReactNode;
|
|
60
|
+
/** render custom slide header */
|
|
61
|
+
slideHeader?: (slide: Slide) => React.ReactNode;
|
|
62
|
+
/** render custom slide footer */
|
|
63
|
+
slideFooter?: (slide: Slide) => React.ReactNode;
|
|
64
|
+
/** render custom slide container */
|
|
65
|
+
slideContainer?: (slide: Slide, children: React.ReactNode) => React.ReactNode;
|
|
53
66
|
/** render custom Prev icon */
|
|
54
67
|
iconPrev?: () => React.ReactNode;
|
|
55
68
|
/** render custom Next icon */
|
|
@@ -127,6 +140,9 @@ export declare const LightboxPropTypes: {
|
|
|
127
140
|
slides: PropTypes.Validator<any[]>;
|
|
128
141
|
render: PropTypes.Validator<PropTypes.InferProps<{
|
|
129
142
|
slide: PropTypes.Requireable<(...args: any[]) => any>;
|
|
143
|
+
slideHeader: PropTypes.Requireable<(...args: any[]) => any>;
|
|
144
|
+
slideFooter: PropTypes.Requireable<(...args: any[]) => any>;
|
|
145
|
+
slideContainer: PropTypes.Requireable<(...args: any[]) => any>;
|
|
130
146
|
iconPrev: PropTypes.Requireable<(...args: any[]) => any>;
|
|
131
147
|
iconNext: PropTypes.Requireable<(...args: any[]) => any>;
|
|
132
148
|
iconClose: PropTypes.Requireable<(...args: any[]) => any>;
|
package/dist/types.js
CHANGED
|
@@ -16,6 +16,9 @@ export const LightboxPropTypes = {
|
|
|
16
16
|
slides: PropTypes.arrayOf(PropTypes.oneOfType(SlideTypesPropTypes).isRequired).isRequired,
|
|
17
17
|
render: PropTypes.shape({
|
|
18
18
|
slide: PropTypes.func,
|
|
19
|
+
slideHeader: PropTypes.func,
|
|
20
|
+
slideFooter: PropTypes.func,
|
|
21
|
+
slideContainer: PropTypes.func,
|
|
19
22
|
iconPrev: PropTypes.func,
|
|
20
23
|
iconNext: PropTypes.func,
|
|
21
24
|
iconClose: PropTypes.func,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yet-another-react-lightbox",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Modern React lightbox component",
|
|
5
5
|
"author": "Igor Danchenko",
|
|
6
6
|
"license": "MIT",
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
".": "./dist/index.js",
|
|
11
11
|
"./core": "./dist/core/index.js",
|
|
12
12
|
"./plugins": "./dist/plugins/index.js",
|
|
13
|
+
"./plugins/captions": "./dist/plugins/Captions.js",
|
|
14
|
+
"./plugins/captions.css": "./dist/plugins/captions.css",
|
|
13
15
|
"./plugins/fullscreen": "./dist/plugins/Fullscreen.js",
|
|
14
16
|
"./plugins/inline": "./dist/plugins/Inline.js",
|
|
15
17
|
"./plugins/video": "./dist/plugins/Video.js",
|
|
@@ -27,6 +29,9 @@
|
|
|
27
29
|
"plugins": [
|
|
28
30
|
"dist/plugins/index.d.ts"
|
|
29
31
|
],
|
|
32
|
+
"plugins/captions": [
|
|
33
|
+
"dist/plugins/Captions.d.ts"
|
|
34
|
+
],
|
|
30
35
|
"plugins/fullscreen": [
|
|
31
36
|
"dist/plugins/Fullscreen.d.ts"
|
|
32
37
|
],
|
|
@@ -59,8 +64,8 @@
|
|
|
59
64
|
"build": "npm-run-all clean build:scss build:css build:js build:dts",
|
|
60
65
|
"build:js": "tsc -p tsconfig.build.js.json",
|
|
61
66
|
"build:dts": "tsc -p tsconfig.build.dts.json",
|
|
62
|
-
"build:css": "postcss src
|
|
63
|
-
"build:scss": "sass src
|
|
67
|
+
"build:css": "postcss src/*.css src/**/*.css --base src -d dist -u autoprefixer --no-map",
|
|
68
|
+
"build:scss": "sass src --no-source-map",
|
|
64
69
|
"start": "npm-run-all clean --parallel \"build:* -- -w\"",
|
|
65
70
|
"lint": "eslint .",
|
|
66
71
|
"test": "jest"
|
|
@@ -80,13 +85,13 @@
|
|
|
80
85
|
"@testing-library/jest-dom": "^5.16.4",
|
|
81
86
|
"@testing-library/react": "^13.3.0",
|
|
82
87
|
"@testing-library/user-event": "^14.2.0",
|
|
83
|
-
"@types/jest": "^
|
|
88
|
+
"@types/jest": "^28.1.0",
|
|
84
89
|
"@types/react": "^18.0.10",
|
|
85
90
|
"@types/react-dom": "^18.0.5",
|
|
86
91
|
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
|
87
92
|
"@typescript-eslint/parser": "^5.27.0",
|
|
88
93
|
"autoprefixer": "^10.4.7",
|
|
89
|
-
"eslint": "^8.
|
|
94
|
+
"eslint": "^8.17.0",
|
|
90
95
|
"eslint-config-airbnb": "^19.0.4",
|
|
91
96
|
"eslint-config-airbnb-typescript": "^17.0.0",
|
|
92
97
|
"eslint-config-prettier": "^8.5.0",
|
|
@@ -106,9 +111,9 @@
|
|
|
106
111
|
"react": "^18.1.0",
|
|
107
112
|
"react-dom": "^18.1.0",
|
|
108
113
|
"rimraf": "^3.0.2",
|
|
109
|
-
"sass": "^1.52.
|
|
110
|
-
"ts-jest": "^28.0.
|
|
111
|
-
"typescript": "^4.7.
|
|
114
|
+
"sass": "^1.52.2",
|
|
115
|
+
"ts-jest": "^28.0.4",
|
|
116
|
+
"typescript": "^4.7.3"
|
|
112
117
|
},
|
|
113
118
|
"keywords": [
|
|
114
119
|
"react",
|