yet-another-react-lightbox 1.3.1 → 1.3.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Yet Another React Lightbox
2
2
 
3
- Modern React lightbox component.
3
+ Modern React lightbox component. Performant, easy to use, customizable and extendable.
4
4
 
5
5
  ## Overview
6
6
 
@@ -8,9 +8,23 @@ Modern React lightbox component.
8
8
  [![Bundle Size](https://img.shields.io/bundlephobia/minzip/yet-another-react-lightbox?color=blue)](https://bundlephobia.com/package/yet-another-react-lightbox)
9
9
  [![License MIT](https://img.shields.io/npm/l/yet-another-react-lightbox?color=blue)](LICENSE)
10
10
 
11
+ - **Built for React:** works with React 18, 17 and 16.8.0+
12
+ - **UX:** supports keyboard, mouse, touchpad and touchscreen navigation
13
+ - **Preloading:** never displays partially downloaded images
14
+ - **Performance:** preloads limited number of images without compromising performance or UX
15
+ - **Responsive:** responsive images with automatic resolution switching are supported out of the box
16
+ - **Video:** video slides are supported via an optional plugin
17
+ - **Customization:** customize any UI element or add your own custom slides
18
+ - **No bloat:** never bundle rarely used features; add optional features via plugins
19
+ - **TypeScript:** type definitions come built-in in the package
20
+
11
21
  ## Documentation
12
22
 
13
- [https://yet-another-react-lightbox.vercel.app/](https://yet-another-react-lightbox.vercel.app/)
23
+ [https://yet-another-react-lightbox.vercel.app/documentation](https://yet-another-react-lightbox.vercel.app/documentation)
24
+
25
+ ## Examples
26
+
27
+ [https://yet-another-react-lightbox.vercel.app/examples](https://yet-another-react-lightbox.vercel.app/examples)
14
28
 
15
29
  ## Installation
16
30
 
@@ -3,5 +3,6 @@ import { LightboxProps } from "./types.js";
3
3
  declare type DeepPartial<T, K extends keyof T> = Omit<T, K> & {
4
4
  [P in keyof Pick<T, K>]?: Partial<Pick<T, K>[P]>;
5
5
  };
6
- export declare const Lightbox: (props: DeepPartial<Partial<LightboxProps>, "carousel" | "animation" | "render" | "toolbar" | "on">) => JSX.Element;
6
+ /** Modern React lightbox component */
7
+ export declare const Lightbox: (props: DeepPartial<Partial<LightboxProps>, "carousel" | "animation" | "render" | "toolbar" | "controller" | "on">) => JSX.Element;
7
8
  export {};
package/dist/Lightbox.js CHANGED
@@ -24,8 +24,9 @@ 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 */
27
28
  export const Lightbox = (props) => {
28
- const { carousel, animation, render, toolbar, on, ...restProps } = props;
29
- const { carousel: defaultCarousel, animation: defaultAnimation, render: defaultRender, toolbar: defaultToolbar, on: defaultOn, ...restDefaultProps } = LightboxDefaultProps;
30
- return (React.createElement(LightboxComponent, { carousel: { ...defaultCarousel, ...carousel }, animation: { ...defaultAnimation, ...animation }, render: { ...defaultRender, ...render }, toolbar: { ...defaultToolbar, ...toolbar }, on: { ...defaultOn, ...on }, ...restDefaultProps, ...restProps }));
29
+ const { carousel, animation, render, toolbar, controller, on, ...restProps } = props;
30
+ const { carousel: defaultCarousel, animation: defaultAnimation, render: defaultRender, toolbar: defaultToolbar, controller: defaultController, on: defaultOn, ...restDefaultProps } = LightboxDefaultProps;
31
+ return (React.createElement(LightboxComponent, { carousel: { ...defaultCarousel, ...carousel }, animation: { ...defaultAnimation, ...animation }, render: { ...defaultRender, ...render }, toolbar: { ...defaultToolbar, ...toolbar }, controller: { ...defaultController, ...controller }, on: { ...defaultOn, ...on }, ...restDefaultProps, ...restProps }));
31
32
  };
@@ -10,5 +10,7 @@ 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", { 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", {
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)` })))));
14
16
  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" }));
@@ -35,8 +35,10 @@ export const ImageSlide = ({ slide: image, render }) => {
35
35
  setState("error");
36
36
  }, []);
37
37
  return (React.createElement(React.Fragment, null,
38
- React.createElement("img", { ref: setImageRef, onLoad: onLoad, onError: onError, className: clsx(cssClass("slide_image"), state !== "complete" && cssClass("slide_image_loading")), draggable: false, alt: image.title, ...(image.srcSet
38
+ 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
39
  ? {
40
+ // this approach does not account for carousel padding,
41
+ // but the margin of error should be negligible in most cases
40
42
  sizes: `${Math.ceil((Math.min(image.aspectRatio ? containerRect.height * image.aspectRatio : Number.MAX_VALUE, containerRect.width) /
41
43
  window.innerWidth) *
42
44
  100)}vw`,
@@ -5,8 +5,16 @@ import { clsx, cssClass, cssVar } from "../utils.js";
5
5
  import { ImageSlide } from "../components/index.js";
6
6
  import { useController } from "./Controller.js";
7
7
  const CarouselSlide = ({ slide, offset, render }) => {
8
- var _a;
9
- return (React.createElement("div", { className: clsx(cssClass("slide"), cssClass("flex_center")), style: { [cssVar("slide_offset")]: offset } }, ((_a = render.slide) === null || _a === void 0 ? void 0 : _a.call(render, slide)) || ("src" in slide && React.createElement(ImageSlide, { slide: slide, render: render }))));
8
+ const renderSlide = () => {
9
+ if (render.slide) {
10
+ const rendered = render.slide(slide);
11
+ if (rendered) {
12
+ return rendered;
13
+ }
14
+ }
15
+ return "src" in slide ? React.createElement(ImageSlide, { slide: slide, render: render }) : null;
16
+ };
17
+ return (React.createElement("div", { className: clsx(cssClass("slide"), cssClass("flex_center")), style: { [cssVar("slide_offset")]: offset } }, renderSlide()));
10
18
  };
11
19
  export const Carousel = (props) => {
12
20
  const { slides, carousel: { finite, preload, padding, spacing }, render, } = props;
@@ -28,6 +28,8 @@ export const Controller = ({ children, ...props }) => {
28
28
  refs.current.state = state;
29
29
  refs.current.props = props;
30
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
31
33
  useEnhancedEffect(() => {
32
34
  const preventDefault = (event) => event.preventDefault();
33
35
  const node = containerRef.current;
@@ -42,8 +44,10 @@ export const Controller = ({ children, ...props }) => {
42
44
  }, [containerRef]);
43
45
  React.useEffect(() => {
44
46
  var _a;
45
- (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
46
- }, [containerRef]);
47
+ if (refs.current.props.controller.focus) {
48
+ (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
49
+ }
50
+ }, [containerRef, refs]);
47
51
  React.useEffect(() => {
48
52
  var _a, _b;
49
53
  (_b = (_a = refs.current.props.on).view) === null || _b === void 0 ? void 0 : _b.call(_a, state.currentIndex);
@@ -103,6 +107,7 @@ export const Controller = ({ children, ...props }) => {
103
107
  newSwipeAnimationDuration =
104
108
  (newSwipeAnimationDuration / expectedTime) * Math.max(elapsedTime, expectedTime / 5);
105
109
  }
110
+ // eslint-disable-next-line no-param-reassign
106
111
  direction = swipeOffset > 0 ? "prev" : "next";
107
112
  }
108
113
  else {
@@ -200,9 +205,11 @@ export const Controller = ({ children, ...props }) => {
200
205
  const onWheel = React.useCallback((event) => {
201
206
  var _a;
202
207
  if (event.ctrlKey) {
208
+ // zoom
203
209
  return;
204
210
  }
205
211
  if (Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
212
+ // pan-y
206
213
  return;
207
214
  }
208
215
  const { current } = refs;
@@ -19,7 +19,7 @@ export const Navigation = ({ slides, carousel: { finite }, labels, render: { but
19
19
  }
20
20
  }), [subscribeSensors, publish]);
21
21
  return (React.createElement(React.Fragment, null,
22
- buttonPrev ? (buttonPrev()) : (React.createElement(NavigationButton, { label: "Previous Image", action: "prev", icon: PreviousIcon, renderIcon: iconPrev, disabled: finite && currentIndex === 0, labels: labels, publish: publish })),
23
- buttonNext ? (buttonNext()) : (React.createElement(NavigationButton, { label: "Next Image", action: "next", icon: NextIcon, renderIcon: iconNext, disabled: finite && currentIndex === slides.length - 1, labels: labels, publish: publish }))));
22
+ buttonPrev ? (buttonPrev()) : (React.createElement(NavigationButton, { label: "Previous", action: "prev", icon: PreviousIcon, renderIcon: iconPrev, disabled: finite && currentIndex === 0, labels: labels, publish: publish })),
23
+ buttonNext ? (buttonNext()) : (React.createElement(NavigationButton, { label: "Next", action: "next", icon: NextIcon, renderIcon: iconNext, disabled: finite && currentIndex === slides.length - 1, labels: labels, publish: publish }))));
24
24
  };
25
25
  export const NavigationModule = createModule("navigation", Navigation);
@@ -7,6 +7,7 @@ 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
10
11
  if (scrollbarWidth > 1) {
11
12
  document.body.style.setProperty(scrollbarPadding, `${scrollbarWidth}px`);
12
13
  document.body.classList.add(padScrollbar);
@@ -2,14 +2,18 @@ import * as React from "react";
2
2
  import { LightboxProps, Plugin, Render } from "../types.js";
3
3
  declare module "../types.js" {
4
4
  interface LightboxProps {
5
+ /** enter fullscreen mode automatically when the lightbox opens */
5
6
  fullscreen?: boolean;
6
7
  }
7
8
  interface Render {
9
+ /** render custom Enter/Exit Fullscreen button */
8
10
  buttonFullscreen?: ({ fullscreen, toggleFullscreen, }: {
9
11
  fullscreen: boolean;
10
12
  toggleFullscreen: () => void;
11
13
  }) => React.ReactNode;
14
+ /** render custom Enter Fullscreen icon */
12
15
  iconEnterFullscreen?: () => React.ReactNode;
16
+ /** render custom Exit Fullscreen icon */
13
17
  iconExitFullscreen?: () => React.ReactNode;
14
18
  }
15
19
  }
@@ -31,12 +35,13 @@ declare global {
31
35
  msRequestFullscreen?: () => void;
32
36
  }
33
37
  }
34
- declare type FullscreenButtonProps = Pick<LightboxProps, "labels"> & {
38
+ /** Fullscreen button props */
39
+ export declare type FullscreenButtonProps = Pick<LightboxProps, "labels"> & {
35
40
  auto: boolean;
36
41
  render: Render;
37
42
  };
38
- declare const FullscreenButton: ({ auto, labels, render }: FullscreenButtonProps) => JSX.Element | null;
39
- declare const Fullscreen: Plugin;
40
- export type { FullscreenButtonProps };
41
- export { Fullscreen, FullscreenButton };
43
+ /** Fullscreen button */
44
+ export declare const FullscreenButton: ({ auto, labels, render }: FullscreenButtonProps) => JSX.Element | null;
45
+ /** Fullscreen plugin */
46
+ export declare const Fullscreen: Plugin;
42
47
  export default Fullscreen;
@@ -2,7 +2,8 @@ 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
- const FullscreenButton = ({ auto, labels, render }) => {
5
+ /** Fullscreen button */
6
+ export const FullscreenButton = ({ auto, labels, render }) => {
6
7
  const [fullscreen, setFullscreen] = React.useState(false);
7
8
  const latestAuto = useLatest(auto);
8
9
  const { containerRef } = useController();
@@ -32,6 +33,7 @@ const FullscreenButton = ({ auto, labels, render }) => {
32
33
  }
33
34
  }
34
35
  catch (err) {
36
+ //
35
37
  }
36
38
  }
37
39
  }, [containerRef]);
@@ -52,6 +54,7 @@ const FullscreenButton = ({ auto, labels, render }) => {
52
54
  }
53
55
  }
54
56
  catch (err) {
57
+ //
55
58
  }
56
59
  }
57
60
  }, [getFullscreenElement]);
@@ -92,7 +95,8 @@ const FullscreenButton = ({ auto, labels, render }) => {
92
95
  return null;
93
96
  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 }));
94
97
  };
95
- const Fullscreen = ({ augment }) => {
98
+ /** Fullscreen plugin */
99
+ export const Fullscreen = ({ augment }) => {
96
100
  augment(({ toolbar: { buttons, ...restToolbar }, ...restProps }) => ({
97
101
  toolbar: {
98
102
  buttons: [
@@ -104,5 +108,4 @@ const Fullscreen = ({ augment }) => {
104
108
  ...restProps,
105
109
  }));
106
110
  };
107
- export { Fullscreen, FullscreenButton };
108
111
  export default Fullscreen;
@@ -2,11 +2,14 @@ import * as React from "react";
2
2
  import { Component, Plugin } from "../types.js";
3
3
  declare module "../types.js" {
4
4
  interface LightboxProps {
5
+ /** HTML div element attributes to be passed to inline plugin container */
5
6
  inline?: React.HTMLAttributes<HTMLDivElement>;
6
7
  }
7
8
  }
8
- declare const InlineContainer: Component;
9
- declare const InlineModule: import("../types.js").Module;
10
- declare const Inline: Plugin;
11
- export { Inline, InlineModule, InlineContainer };
9
+ /** Inline plugin container */
10
+ export declare const InlineContainer: Component;
11
+ /** Inline plugin module */
12
+ export declare const InlineModule: import("../types.js").Module;
13
+ /** Inline plugin */
14
+ export declare const Inline: Plugin;
12
15
  export default Inline;
@@ -1,9 +1,12 @@
1
1
  import * as React from "react";
2
2
  import { createModule } from "../core/index.js";
3
- const InlineContainer = ({ inline, children }) => React.createElement("div", { ...inline }, children);
4
- const InlineModule = createModule("inline", InlineContainer);
5
- const Inline = ({ augment, replace, remove }) => {
6
- augment(({ toolbar: { buttons, ...restToolbar }, open, close, ...restProps }) => ({
3
+ /** Inline plugin container */
4
+ export const InlineContainer = ({ inline, children }) => React.createElement("div", { ...inline }, children);
5
+ /** Inline plugin module */
6
+ export const InlineModule = createModule("inline", InlineContainer);
7
+ /** Inline plugin */
8
+ export const Inline = ({ augment, replace, remove }) => {
9
+ augment(({ toolbar: { buttons, ...restToolbar }, open, close, controller: { focus, ...restController }, ...restProps }) => ({
7
10
  open: true,
8
11
  close: () => { },
9
12
  toolbar: {
@@ -11,10 +14,10 @@ const Inline = ({ augment, replace, remove }) => {
11
14
  ...restToolbar,
12
15
  },
13
16
  inline: { style: { width: "100%", height: "100%" } },
17
+ controller: { focus: false, ...restController },
14
18
  ...restProps,
15
19
  }));
16
20
  remove("no-scroll");
17
21
  replace("portal", InlineModule);
18
22
  };
19
- export { Inline, InlineModule, InlineContainer };
20
23
  export default Inline;
@@ -1,23 +1,33 @@
1
1
  /// <reference types="react" />
2
2
  import { Plugin } from "../types.js";
3
+ /** Video slide attributes */
3
4
  export interface SlideVideo {
5
+ /** video slide type marker */
4
6
  type: "video";
7
+ /** video placeholder image */
5
8
  poster?: string;
9
+ /** video width */
6
10
  width?: number;
11
+ /** video height */
7
12
  height?: number;
13
+ /** vide source files */
8
14
  sources?: {
15
+ /** video source URL */
9
16
  src: string;
17
+ /** video source type (e.g., `video/mp4`) */
10
18
  type: string;
11
19
  }[];
12
20
  }
13
21
  declare module "../types.js" {
14
22
  interface SlideTypes {
23
+ /** video slide type */
15
24
  SlideVideo: SlideVideo;
16
25
  }
17
26
  }
18
- declare const VideoSlide: ({ slide: { sources, poster, width, height } }: {
27
+ /** Video slide */
28
+ export declare const VideoSlide: ({ slide: { sources, poster, width, height } }: {
19
29
  slide: SlideVideo;
20
30
  }) => JSX.Element;
21
- declare const Video: Plugin;
22
- export { Video, VideoSlide };
31
+ /** Video plugin */
32
+ export declare const Video: Plugin;
23
33
  export default Video;
@@ -12,7 +12,8 @@ SlideTypesPropTypes.push(PropTypes.shape({
12
12
  type: PropTypes.string.isRequired,
13
13
  }).isRequired),
14
14
  }));
15
- const VideoSlide = ({ slide: { sources, poster, width, height } }) => {
15
+ /** Video slide */
16
+ export const VideoSlide = ({ slide: { sources, poster, width, height } }) => {
16
17
  const { setContainerRef, containerRect } = useContainerRect();
17
18
  const scaleWidthAndHeight = () => {
18
19
  if (!width || !height || !containerRect)
@@ -26,9 +27,14 @@ const VideoSlide = ({ slide: { sources, poster, width, height } }) => {
26
27
  return (React.createElement(React.Fragment, null, sources && (React.createElement("div", { ref: setContainerRef, style: {
27
28
  width: "100%",
28
29
  height: "100%",
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 })))))))));
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 })))))))));
30
35
  };
31
- const Video = ({ augment }) => {
36
+ /** Video plugin */
37
+ export const Video = ({ augment }) => {
32
38
  augment(({ render: { slide: renderSlide, ...restRender }, ...restProps }) => ({
33
39
  render: {
34
40
  slide: (slide) => {
@@ -42,5 +48,4 @@ const Video = ({ augment }) => {
42
48
  ...restProps,
43
49
  }));
44
50
  };
45
- export { Video, VideoSlide };
46
51
  export default Video;
package/dist/types.d.ts CHANGED
@@ -1,57 +1,113 @@
1
1
  import * as React from "react";
2
2
  import PropTypes from "prop-types";
3
+ /** Image slide properties */
3
4
  export interface SlideImage {
5
+ /** image URL */
4
6
  src?: string;
5
- title?: string;
7
+ /** image 'alt' attribute */
8
+ alt?: string;
9
+ /** image aspect ratio */
6
10
  aspectRatio?: number;
11
+ /** alternative images to be passed to the 'srcSet' */
7
12
  srcSet?: {
13
+ /** image URL */
8
14
  src: string;
15
+ /** image width in pixels */
9
16
  width: number;
10
17
  }[];
11
18
  }
19
+ /** Supported slide types */
12
20
  export interface SlideTypes {
21
+ /** image slide type */
13
22
  SlideImage: SlideImage;
14
23
  }
24
+ /** Slide */
15
25
  export declare type Slide = SlideTypes[keyof SlideTypes];
16
- export interface Carousel {
26
+ /** Carousel settings */
27
+ export interface CarouselSettings {
28
+ /** if `true`, the lightbox carousel doesn't wrap around */
17
29
  finite: boolean;
30
+ /** the lightbox preloads (2 * preload + 1) slides */
18
31
  preload: number;
32
+ /** padding around each slide */
19
33
  padding: string | number;
34
+ /** spacing between slides (e.g., "100px", "50%" or 0) */
20
35
  spacing: string | number;
21
36
  }
22
- export interface Animation {
37
+ /** Animation settings */
38
+ export interface AnimationSettings {
39
+ /** fade-in / fade-out animation duration */
23
40
  fade: number;
41
+ /** swipe animation duration */
24
42
  swipe: number;
25
43
  }
44
+ /** Controller settings */
45
+ export interface ControllerSettings {
46
+ /** if true, the lightbox captures focus when it opens v */
47
+ focus: boolean;
48
+ }
49
+ /** Custom render functions. */
26
50
  export interface Render {
51
+ /** render custom slide type, or override the default image slide */
27
52
  slide?: (slide: Slide) => React.ReactNode;
53
+ /** render custom Prev icon */
28
54
  iconPrev?: () => React.ReactNode;
55
+ /** render custom Next icon */
29
56
  iconNext?: () => React.ReactNode;
57
+ /** render custom Close icon */
30
58
  iconClose?: () => React.ReactNode;
59
+ /** render custom Loading icon */
31
60
  iconLoading?: () => React.ReactNode;
61
+ /** render custom Error icon */
32
62
  iconError?: () => React.ReactNode;
63
+ /** render custom Prev button */
33
64
  buttonPrev?: () => React.ReactNode;
65
+ /** render custom Next button */
34
66
  buttonNext?: () => React.ReactNode;
67
+ /** render custom Close button */
35
68
  buttonClose?: () => React.ReactNode;
36
69
  }
70
+ /** Lifecycle callbacks */
37
71
  export interface Callbacks {
72
+ /** a callback called when a slide becomes active */
38
73
  view?: (index: number) => void;
74
+ /** a callback called when the portal starts opening */
39
75
  entering?: () => void;
76
+ /** a callback called when the portal opens */
40
77
  entered?: () => void;
78
+ /** a callback called when the portal starts closing */
41
79
  exiting?: () => void;
80
+ /** a callback called when the portal closes */
42
81
  exited?: () => void;
43
82
  }
83
+ /** Lightbox properties */
44
84
  export interface LightboxProps {
85
+ /** if `true`, the lightbox is open */
45
86
  open: boolean;
87
+ /** a callback to close the lightbox */
46
88
  close: () => void;
89
+ /**
90
+ * Starting slide index. The lightbox reads this property only when it opens.
91
+ * Changing this property while the lightbox is already open has no effect.
92
+ */
47
93
  index: number;
94
+ /** slides to display in the lightbox */
48
95
  slides: Slide[];
96
+ /** custom render functions */
49
97
  render: Render;
98
+ /** custom UI labels */
50
99
  labels: Labels;
100
+ /** enabled plugins */
51
101
  plugins: Plugin[];
52
- toolbar: Toolbar;
53
- carousel: Carousel;
54
- animation: Animation;
102
+ /** toolbar settings */
103
+ toolbar: ToolbarSettings;
104
+ /** carousel settings */
105
+ carousel: CarouselSettings;
106
+ /** animation settings */
107
+ animation: AnimationSettings;
108
+ /** controller settings */
109
+ controller: ControllerSettings;
110
+ /** lifecycle callbacks */
55
111
  on: Callbacks;
56
112
  }
57
113
  export declare const SlideTypesPropTypes: PropTypes.Validator<any>[];
@@ -86,6 +142,9 @@ export declare const LightboxPropTypes: {
86
142
  fade: PropTypes.Validator<number>;
87
143
  swipe: PropTypes.Validator<number>;
88
144
  }>>;
145
+ controller: PropTypes.Validator<PropTypes.InferProps<{
146
+ focus: PropTypes.Validator<boolean>;
147
+ }>>;
89
148
  on: PropTypes.Validator<PropTypes.InferProps<{
90
149
  view: PropTypes.Requireable<(...args: any[]) => any>;
91
150
  entering: PropTypes.Requireable<(...args: any[]) => any>;
@@ -101,34 +160,56 @@ export declare const LightboxDefaultProps: {
101
160
  slides: Slide[];
102
161
  render: Render;
103
162
  plugins: Plugin[];
104
- toolbar: Toolbar;
163
+ toolbar: ToolbarSettings;
105
164
  labels: Labels;
106
- animation: Animation;
107
- carousel: Carousel;
165
+ animation: AnimationSettings;
166
+ carousel: CarouselSettings;
167
+ controller: ControllerSettings;
108
168
  on: Callbacks;
109
169
  };
170
+ /** Custom UI labels */
110
171
  export declare type Labels = {
111
172
  [key: string]: string;
112
173
  };
113
- export interface Toolbar {
174
+ /** Toolbar settings */
175
+ export interface ToolbarSettings {
176
+ /** buttons to render in the toolbar */
114
177
  buttons: ("close" | React.ReactNode)[];
115
178
  }
179
+ /** Lightbox component properties */
116
180
  export declare type ComponentProps = Omit<LightboxProps, "plugins">;
181
+ /** Lightbox component */
117
182
  export declare type Component = React.ComponentType<React.PropsWithChildren<ComponentProps>>;
183
+ /** Lightbox module */
118
184
  export declare type Module = {
185
+ /** module name */
119
186
  name: string;
187
+ /** module component */
120
188
  component: Component;
121
189
  };
190
+ /** Lightbox component tree node */
122
191
  export declare type Node = {
192
+ /** module */
123
193
  module: Module;
194
+ /** module child nodes */
124
195
  children?: Node[];
125
196
  };
197
+ /** Lightbox props augmentation */
126
198
  export declare type Augmentation = (props: LightboxProps) => LightboxProps;
127
- export declare type Plugin = ({ addParent, addChild, addSibling, replace, remove, }: {
199
+ /** Plugin methods */
200
+ export declare type PluginMethods = {
201
+ /** add module as parent */
128
202
  addParent: (target: string, module: Module) => void;
203
+ /** add module as child */
129
204
  addChild: (target: string, module: Module, precede?: boolean) => void;
205
+ /** add module as sibling */
130
206
  addSibling: (target: string, module: Module, precede?: boolean) => void;
207
+ /** replace module */
131
208
  replace: (target: string, module: Module) => void;
209
+ /** remove module */
132
210
  remove: (target: string) => void;
211
+ /** augment lightbox props */
133
212
  augment: (augmentation: Augmentation) => void;
134
- }) => void;
213
+ };
214
+ /** Lightbox plugin */
215
+ export declare type Plugin = ({ addParent, addChild, addSibling, replace, remove, augment }: PluginMethods) => void;
package/dist/types.js CHANGED
@@ -44,6 +44,9 @@ export const LightboxPropTypes = {
44
44
  fade: PropTypes.number.isRequired,
45
45
  swipe: PropTypes.number.isRequired,
46
46
  }).isRequired,
47
+ controller: PropTypes.shape({
48
+ focus: PropTypes.bool.isRequired,
49
+ }).isRequired,
47
50
  on: PropTypes.shape({
48
51
  view: PropTypes.func,
49
52
  entering: PropTypes.func,
@@ -71,5 +74,8 @@ export const LightboxDefaultProps = {
71
74
  padding: "16px",
72
75
  spacing: "30%",
73
76
  },
77
+ controller: {
78
+ focus: true,
79
+ },
74
80
  on: {},
75
81
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yet-another-react-lightbox",
3
- "version": "1.3.1",
3
+ "version": "1.3.4",
4
4
  "description": "Modern React lightbox component",
5
5
  "author": "Igor Danchenko",
6
6
  "license": "MIT",
@@ -42,7 +42,7 @@
42
42
  "dist"
43
43
  ],
44
44
  "sideEffects": false,
45
- "homepage": "https://github.com/igordanchenko/yet-another-react-lightbox#readme",
45
+ "homepage": "https://yet-another-react-lightbox.vercel.app/",
46
46
  "repository": {
47
47
  "type": "git",
48
48
  "url": "https://github.com/igordanchenko/yet-another-react-lightbox.git"
@@ -56,8 +56,9 @@
56
56
  "scripts": {
57
57
  "prepare": "husky install",
58
58
  "clean": "rimraf dist",
59
- "build": "npm-run-all clean build:scss build:css build:js",
60
- "build:js": "tsc -p tsconfig.build.json",
59
+ "build": "npm-run-all clean build:scss build:css build:js build:dts",
60
+ "build:js": "tsc -p tsconfig.build.js.json",
61
+ "build:dts": "tsc -p tsconfig.build.dts.json",
61
62
  "build:css": "postcss src/styles.css -u autoprefixer --no-map -d dist",
62
63
  "build:scss": "sass src/styles.scss src/styles.css --no-source-map ",
63
64
  "start": "npm-run-all clean --parallel \"build:* -- -w\"",
@@ -72,15 +73,15 @@
72
73
  "react-dom": ">=16.8.0"
73
74
  },
74
75
  "devDependencies": {
75
- "@commitlint/cli": "^17.0.1",
76
- "@commitlint/config-conventional": "^17.0.0",
76
+ "@commitlint/cli": "^17.0.2",
77
+ "@commitlint/config-conventional": "^17.0.2",
77
78
  "@semantic-release/changelog": "^6.0.1",
78
79
  "@semantic-release/github": "^8.0.4",
79
80
  "@testing-library/jest-dom": "^5.16.4",
80
81
  "@testing-library/react": "^13.3.0",
81
82
  "@testing-library/user-event": "^14.2.0",
82
83
  "@types/jest": "^27.5.1",
83
- "@types/react": "^18.0.9",
84
+ "@types/react": "^18.0.10",
84
85
  "@types/react-dom": "^18.0.5",
85
86
  "@typescript-eslint/eslint-plugin": "^5.27.0",
86
87
  "@typescript-eslint/parser": "^5.27.0",
@@ -97,7 +98,7 @@
97
98
  "husky": "^8.0.1",
98
99
  "jest": "^28.1.0",
99
100
  "jest-environment-jsdom": "^28.1.0",
100
- "lint-staged": "^12.4.3",
101
+ "lint-staged": "^13.0.0",
101
102
  "npm-run-all": "^4.1.5",
102
103
  "postcss": "^8.4.14",
103
104
  "postcss-cli": "^9.1.0",