react-pic-gallery 2.0.1 → 2.0.3

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
@@ -3,9 +3,12 @@
3
3
  Small, accessible React image gallery and lightbox with a polished default UI and typed escape hatches for custom controls.
4
4
 
5
5
  [![NPM](https://img.shields.io/npm/v/react-pic-gallery.svg)](https://www.npmjs.com/package/react-pic-gallery)
6
+ [![Minified + gzip size](https://badgen.net/static/minified%20%2B%20gzip/8.5%20KB/blue)](https://bundlephobia.com/package/react-pic-gallery)
6
7
 
7
8
  [Live playground and docs](https://marcelrsoub.github.io/react-pic-gallery/)
8
9
 
10
+ ![react-pic-gallery displaying a three-column gallery of landscape photos](screenshot.png)
11
+
9
12
  ## Install
10
13
 
11
14
  ```bash
@@ -20,16 +23,17 @@ bun add react-pic-gallery
20
23
 
21
24
  React 18.3+ and React 19 are supported. The [Quick start docs](https://marcelrsoub.github.io/react-pic-gallery/docs/getting-started/) provide these commands in a package-manager switcher.
22
25
 
23
- The package is intentionally lightweight: it has no runtime dependencies, React is a peer dependency, and the built JavaScript and CSS together are about **5.9 KB gzipped** (4.0 KB JS + 1.9 KB CSS).
26
+ The package is intentionally lightweight: it has no runtime dependencies and React is a peer dependency. The built JavaScript and CSS together are about **8.5 KB gzipped** (6.13 KB JS + 2.34 KB CSS).
24
27
 
25
28
  ## Why react-pic-gallery
26
29
 
27
- - **Tiny by design** — no runtime dependencies, no context providers, no polyfills; ~5.9 KB gzipped total.
30
+ - **Tiny by design** — no runtime dependencies, no context providers, no polyfills; ~8.5 KB gzipped total.
28
31
  - **Simple by default** — one component, one stylesheet import, sensible accessible defaults.
29
32
  - **Yours when needed** — typed `renderActions`, `renderCaption`, and `renderControls` callbacks let you add custom UI without rebuilding the lightbox; extra fields on your image objects flow through fully typed.
30
33
  - **Accessible** — native modal `<dialog>`, focus containment and restoration, Escape to close, screen-reader announcements, `prefers-reduced-motion` support.
31
34
  - **Keyboard and touch friendly** — arrow-key navigation in the grid (roving tabindex) and in the lightbox, Enter to open, swipe navigation on touch devices.
32
35
  - **Themeable** — namespaced classes and CSS variables (`--gallery-accent`, `--gallery-overlay`, `--gallery-motion`, …).
36
+ - **Three layouts** — choose a uniform grid, proportional justified rows, or an editorial mosaic.
33
37
 
34
38
  ## Quick start
35
39
 
@@ -53,6 +57,21 @@ export function App() {
53
57
 
54
58
  `src` and `alt` are required. `id`, `thumbnailSrc`, `caption`, `width`, and `height` are optional. Image objects can include application-specific fields; those fields remain available in renderer callbacks when using TypeScript generics.
55
59
 
60
+ ## Gallery layouts
61
+
62
+ The default is the existing three-column grid. Choose another layout with `layout`:
63
+
64
+ ```tsx
65
+ <PicGallery images={images} layout='justified' />
66
+ <PicGallery images={images} layout='mosaic' />
67
+ ```
68
+
69
+ - **`grid`** — consistent tiles with a configurable fixed column count.
70
+ - **`justified`** — proportional photos arranged in aligned rows; the final row stays left-aligned.
71
+ - **`mosaic`** — alternating featured photos and smaller supporting tiles.
72
+
73
+ Image `width` and `height` are recommended, but optional. Justified rows use them when supplied and fall back to 3:2 when either dimension is missing or invalid. Accurate dimensions give the most faithful layout and help reserve space while images load. `rowHeight` sets the tile height for the grid, the base row height for the mosaic, and the target row height for justified galleries. `columns` applies to the grid only.
74
+
56
75
  ## Custom UI
57
76
 
58
77
  Add actions without rebuilding the lightbox:
@@ -116,7 +135,7 @@ The lightbox uses the native modal `<dialog>` element and includes:
116
135
  - Instagram-Stories-style edge taps on touch screens: tap the right edge of the image to go forward, the left edge to go back (navigation buttons are hidden on small screens where tap zones take over).
117
136
  - Reduced-motion support, safe-area padding, rounded image surfaces, and animated transitions.
118
137
 
119
- The thumbnail grid supports arrow-key navigation with a roving tabindex: Tab once to enter the grid, then move with Arrow keys (Left/Right step, Up/Down jump a row, Home/End jump to the ends), Enter to open, and focus is restored to the same tile when the lightbox closes.
138
+ Gallery tiles use roving focus. Left/Right follow image order; in the grid, Up/Down jump a row, and in justified and mosaic layouts they follow the nearest tile position. Home/End move to the first/last image. Enter opens the lightbox, and focus returns to the same tile when it closes.
120
139
 
121
140
  Native modal behavior targets modern browsers: Chrome 37+, Edge 79+, Firefox 98+, and Safari/iOS 15.4+. The package does not ship a dialog polyfill.
122
141
 
@@ -134,7 +153,7 @@ The stylesheet uses namespaced classes and CSS variables. Import it once, then o
134
153
  }
135
154
  ```
136
155
 
137
- The default gallery uses three columns. Pass `columns` for a different fixed count; `rowHeight` accepts CSS height values or numeric pixels.
156
+ The default grid uses three columns. Pass `columns` for a different fixed count. `rowHeight` accepts CSS height values or numeric pixels; its meaning depends on the selected layout as described above.
138
157
 
139
158
  ## API
140
159
 
@@ -143,15 +162,16 @@ The default gallery uses three columns. Pass `columns` for a different fixed cou
143
162
  | Prop | Type | Description |
144
163
  | --- | --- | --- |
145
164
  | `images` | `readonly GalleryImage[]` | Images to display. |
146
- | `columns` | `number` | Optional fixed column count. The default is three columns. |
147
- | `rowHeight` | `CSSProperties['height']` | Thumbnail height. |
165
+ | `layout` | `'grid' \| 'justified' \| 'mosaic'` | Defaults to `'grid'`. |
166
+ | `columns` | `number` | Optional fixed grid column count. Defaults to three; only applies to `grid`. |
167
+ | `rowHeight` | `CSSProperties['height']` | Grid tile height, mosaic base row height, or justified target row height. |
148
168
  | `renderActions` | `LightboxRenderer` | Adds controls to the default toolbar. |
149
169
  | `renderCaption` | `LightboxRenderer` | Replaces the current caption. |
150
170
  | `renderControls` | `LightboxRenderer` | Replaces the complete default control layer. |
151
171
  | `showCounter` | `boolean` | Shows the current image count. Defaults to `true`. |
152
172
  | `showNavigation` | `boolean` | Shows previous/next controls. Defaults to `true`. |
153
173
 
154
- `Gallery` accepts `images`, `onImageClick`, `columns`, `rowHeight`, `className`, and `style`.
174
+ `Gallery` accepts `images`, `onImageClick`, `layout`, `columns`, `rowHeight`, `className`, and `style`.
155
175
 
156
176
  `Lightbox` accepts `images`, controlled `index`, `onIndexChange`, the renderer props, `showCounter`, `showNavigation`, and `className`.
157
177
 
@@ -0,0 +1,18 @@
1
+ import { ReactNode } from 'react';
2
+ import { GalleryImage, LightboxRenderer } from './types';
3
+ import { UseCarouselNavigationResult } from './useCarouselNavigation';
4
+ export type CarouselProps<T extends GalleryImage = GalleryImage> = {
5
+ images: readonly T[];
6
+ index: number;
7
+ navigation: UseCarouselNavigationResult;
8
+ renderActions?: LightboxRenderer<T>;
9
+ renderCaption?: LightboxRenderer<T>;
10
+ renderControls?: LightboxRenderer<T>;
11
+ showCounter?: boolean;
12
+ showNavigation?: boolean;
13
+ close?: () => void;
14
+ onEmptyAreaClick?: () => void;
15
+ onImageClick?: () => void;
16
+ className?: string;
17
+ };
18
+ export declare function Carousel<T extends GalleryImage>(props: CarouselProps<T>): ReactNode;
package/dist/Gallery.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { GalleryImage, GalleryProps } from './types';
2
- declare function GalleryComponent<T extends GalleryImage>({ images, onImageClick, columns, rowHeight, className, style }: GalleryProps<T>): import('react').JSX.Element;
2
+ declare function GalleryComponent<T extends GalleryImage>({ images, onImageClick, layout, columns, rowHeight, className, style, renderCaption, showCounter, showNavigation }: GalleryProps<T>): import('react').JSX.Element;
3
3
  export declare const Gallery: typeof GalleryComponent;
4
4
  export {};
package/dist/Image.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AnimationEventHandler, CSSProperties } from 'react';
1
+ import { AnimationEventHandler, CSSProperties, MouseEventHandler } from 'react';
2
2
  type ImageProps = {
3
3
  src: string;
4
4
  alt: string;
@@ -7,10 +7,11 @@ type ImageProps = {
7
7
  style?: CSSProperties;
8
8
  imageStyle?: CSSProperties;
9
9
  onAnimationEnd?: AnimationEventHandler<HTMLSpanElement>;
10
+ onClick?: MouseEventHandler<HTMLSpanElement>;
10
11
  eager?: boolean;
11
12
  objectFit?: 'cover' | 'contain';
12
13
  width?: number;
13
14
  height?: number;
14
15
  };
15
- export declare function Image({ src, alt, className, imageClassName, style, imageStyle, onAnimationEnd, eager, objectFit, width, height }: ImageProps): import('react').JSX.Element;
16
+ export declare function Image({ src, alt, className, imageClassName, style, imageStyle, onAnimationEnd, onClick, eager, objectFit, width, height }: ImageProps): import('react').JSX.Element;
16
17
  export {};
@@ -1,2 +1,2 @@
1
1
  import { GalleryImage, PicGalleryProps } from './types';
2
- export declare function PicGallery<T extends GalleryImage>({ images, columns, rowHeight, className, galleryClassName, style, renderActions, renderCaption, renderControls, showCounter, showNavigation }: PicGalleryProps<T>): import('react').JSX.Element;
2
+ export declare function PicGallery<T extends GalleryImage>({ images, layout, columns, rowHeight, className, galleryClassName, style, renderActions, renderCaption, renderControls, showCounter, showNavigation }: PicGalleryProps<T>): import('react').JSX.Element;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Gallery } from './Gallery';
2
2
  export { Lightbox } from './Lightbox';
3
3
  export { PicGallery } from './PicGallery';
4
- export type { GalleryImage, GalleryProps, LightboxContext, LightboxProps, LightboxRenderer, PicGalleryProps } from './types';
4
+ export type { GalleryImage, GalleryLayout, GalleryProps, LightboxContext, LightboxProps, LightboxRenderer, PicGalleryProps } from './types';
5
5
  export { PicGallery as default } from './PicGallery';
@@ -0,0 +1,9 @@
1
+ import { GalleryImage } from './types';
2
+ export declare const FALLBACK_ASPECT_RATIO: number;
3
+ export type JustifiedRow = {
4
+ indices: number[];
5
+ widths: number[];
6
+ height: number;
7
+ };
8
+ export declare function getAspectRatio(image: Pick<GalleryImage, 'width' | 'height'>): number;
9
+ export declare function buildJustifiedRows(images: readonly Pick<GalleryImage, 'width' | 'height'>[], containerWidth: number, targetHeight: number, gap: number): JustifiedRow[];
@@ -1,41 +1,42 @@
1
- import { memo as e, useCallback as t, useEffect as n, useId as r, useRef as i, useState as a } from "react";
2
- import { jsx as o, jsxs as s } from "react/jsx-runtime";
3
- import { createPortal as c } from "react-dom";
1
+ import { memo as e, useCallback as t, useEffect as n, useId as r, useLayoutEffect as i, useRef as a, useState as o } from "react";
2
+ import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
3
+ import { createPortal as u } from "react-dom";
4
4
  //#region src/lib/Image.tsx
5
- function l({ src: e, alt: t, className: r = "", imageClassName: c = "", style: l, imageStyle: u, onAnimationEnd: d, eager: f = !1, objectFit: p = "cover", width: m, height: h }) {
6
- let g = i(null), [_, v] = a("loading");
5
+ function d({ src: e, alt: t, className: r = "", imageClassName: i = "", style: s, imageStyle: u, onAnimationEnd: d, onClick: f, eager: p = !1, objectFit: m = "cover", width: h, height: g }) {
6
+ let _ = a(null), [v, y] = o("loading");
7
7
  return n(() => {
8
- v("loading");
9
- let e = g.current;
10
- e?.complete && v(e.naturalWidth > 0 ? "loaded" : "error");
11
- }, [e]), /* @__PURE__ */ s("span", {
8
+ y("loading");
9
+ let e = _.current;
10
+ e?.complete && y(e.naturalWidth > 0 ? "loaded" : "error");
11
+ }, [e]), /* @__PURE__ */ l("span", {
12
12
  className: `react-pic-gallery__image ${r}`.trim(),
13
- style: l,
14
- "aria-busy": _ === "loading",
13
+ style: s,
14
+ "aria-busy": v === "loading",
15
15
  onAnimationEnd: d,
16
+ onClick: f,
16
17
  children: [
17
- /* @__PURE__ */ o("img", {
18
- ref: g,
18
+ /* @__PURE__ */ c("img", {
19
+ ref: _,
19
20
  src: e,
20
21
  alt: t,
21
- className: `react-pic-gallery__image-element ${c}`.trim(),
22
+ className: `react-pic-gallery__image-element ${i}`.trim(),
22
23
  style: {
23
- objectFit: p,
24
+ objectFit: m,
24
25
  ...u
25
26
  },
26
- loading: f ? "eager" : "lazy",
27
+ loading: p ? "eager" : "lazy",
27
28
  decoding: "async",
28
- width: m,
29
- height: h,
30
- onLoad: () => v("loaded"),
31
- onError: () => v("error")
29
+ width: h,
30
+ height: g,
31
+ onLoad: () => y("loaded"),
32
+ onError: () => y("error")
32
33
  }),
33
- _ === "loading" && /* @__PURE__ */ o("span", {
34
+ v === "loading" && /* @__PURE__ */ c("span", {
34
35
  className: "react-pic-gallery__image-loader",
35
36
  role: "progressbar",
36
37
  "aria-label": "Loading image"
37
38
  }),
38
- _ === "error" && /* @__PURE__ */ o("span", {
39
+ v === "error" && /* @__PURE__ */ c("span", {
39
40
  className: "react-pic-gallery__image-error",
40
41
  role: "status",
41
42
  children: "Unable to load image"
@@ -43,326 +44,526 @@ function l({ src: e, alt: t, className: r = "", imageClassName: c = "", style: l
43
44
  ]
44
45
  });
45
46
  }
47
+ function f(e) {
48
+ return e instanceof Element && e.closest("a[href], button, input, select, textarea, [role=\"combobox\"], [role=\"menu\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"tab\"]") !== null;
49
+ }
50
+ function p(e, t) {
51
+ if (!t || window.matchMedia?.("(max-width: 600px), (pointer: coarse)")?.matches !== !0) return null;
52
+ let n = t.getBoundingClientRect();
53
+ if (n.width <= 0) return null;
54
+ let r = (e - n.left) / n.width;
55
+ return r < .3 ? "previous" : r > .7 ? "next" : null;
56
+ }
57
+ function m() {
58
+ return /* @__PURE__ */ c("svg", {
59
+ viewBox: "0 0 24 24",
60
+ "aria-hidden": "true",
61
+ focusable: "false",
62
+ children: /* @__PURE__ */ c("path", { d: "M6 6l12 12M18 6L6 18" })
63
+ });
64
+ }
65
+ function h({ direction: e }) {
66
+ return /* @__PURE__ */ c("svg", {
67
+ viewBox: "0 0 24 24",
68
+ "aria-hidden": "true",
69
+ focusable: "false",
70
+ children: /* @__PURE__ */ c("path", { d: e === "left" ? "M14.5 5l-7 7 7 7" : "M9.5 5l7 7-7 7" })
71
+ });
72
+ }
73
+ function g(...e) {
74
+ return e.filter(Boolean).join(" ");
75
+ }
76
+ //#endregion
77
+ //#region src/lib/Carousel.tsx
78
+ function _(e) {
79
+ let { images: t, index: n, navigation: r, renderActions: i, renderCaption: a, renderControls: o, showCounter: u = !0, showNavigation: f = !0, close: _, onEmptyAreaClick: v, onImageClick: y, className: b } = e, x = t[n], S = {
80
+ image: x,
81
+ index: n,
82
+ count: t.length,
83
+ close: _ ?? (() => {}),
84
+ next: r.next,
85
+ previous: r.previous,
86
+ canGoNext: n < t.length - 1,
87
+ canGoPrevious: n > 0
88
+ }, C = f && t.length > 1, w = f && S.canGoPrevious, T = f && S.canGoNext;
89
+ return /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ l("p", {
90
+ className: "react-pic-gallery__sr-only",
91
+ "aria-live": "polite",
92
+ children: [
93
+ x.alt,
94
+ ", image ",
95
+ n + 1,
96
+ " of ",
97
+ t.length
98
+ ]
99
+ }), /* @__PURE__ */ l("div", {
100
+ className: g("react-pic-gallery__lightbox-content", b),
101
+ children: [
102
+ o ? /* @__PURE__ */ c("div", {
103
+ className: "react-pic-gallery__custom-controls",
104
+ children: o(S)
105
+ }) : /* @__PURE__ */ l("div", {
106
+ className: "react-pic-gallery__toolbar",
107
+ children: [/* @__PURE__ */ l("div", {
108
+ className: "react-pic-gallery__toolbar-actions",
109
+ children: [u && /* @__PURE__ */ l("span", {
110
+ "aria-live": "polite",
111
+ children: [
112
+ n + 1,
113
+ " / ",
114
+ t.length
115
+ ]
116
+ }), i?.(S)]
117
+ }), _ && /* @__PURE__ */ c("button", {
118
+ className: "react-pic-gallery__control",
119
+ type: "button",
120
+ onClick: _,
121
+ "aria-label": "Close image viewer",
122
+ children: /* @__PURE__ */ c(m, {})
123
+ })]
124
+ }),
125
+ /* @__PURE__ */ l("div", {
126
+ className: g("react-pic-gallery__viewport", C ? "react-pic-gallery__viewport--has-navigation" : void 0),
127
+ onMouseDown: (e) => {
128
+ e.target === e.currentTarget && v?.();
129
+ },
130
+ onClick: r.handleViewportClick,
131
+ onTouchCancel: r.handleTouchCancel,
132
+ onTouchEnd: r.handleTouchEnd,
133
+ onTouchStart: r.handleTouchStart,
134
+ children: [
135
+ w && /* @__PURE__ */ c("button", {
136
+ className: "react-pic-gallery__control react-pic-gallery__control--previous",
137
+ type: "button",
138
+ onClick: r.previous,
139
+ "aria-label": "Previous image",
140
+ children: /* @__PURE__ */ c(h, { direction: "left" })
141
+ }),
142
+ /* @__PURE__ */ c(d, {
143
+ src: x.src,
144
+ alt: x.alt,
145
+ eager: !0,
146
+ objectFit: "contain",
147
+ className: g("react-pic-gallery__lightbox-image", r.swipeDirection ? `react-pic-gallery__lightbox-image--swipe-${r.swipeDirection}` : void 0),
148
+ onAnimationEnd: r.handleImageAnimationEnd,
149
+ onClick: y ? (e) => {
150
+ f && t.length > 1 && p(e.clientX, e.currentTarget.parentElement) !== null || y();
151
+ } : void 0
152
+ }, x.src),
153
+ T && /* @__PURE__ */ c("button", {
154
+ className: "react-pic-gallery__control react-pic-gallery__control--next",
155
+ type: "button",
156
+ onClick: r.next,
157
+ "aria-label": "Next image",
158
+ children: /* @__PURE__ */ c(h, { direction: "right" })
159
+ })
160
+ ]
161
+ }),
162
+ (a || x.caption) && /* @__PURE__ */ c("div", {
163
+ className: "react-pic-gallery__caption",
164
+ children: a ? a(S) : x.caption
165
+ })
166
+ ]
167
+ })] });
168
+ }
169
+ //#endregion
170
+ //#region src/lib/layout.ts
171
+ var v = 3 / 2;
172
+ function y(e) {
173
+ return Number.isFinite(e.width) && Number.isFinite(e.height) && e.width !== void 0 && e.height !== void 0 && e.width > 0 && e.height > 0 ? e.width / e.height : v;
174
+ }
175
+ function b(e, t, n, r) {
176
+ if (e.length === 0) return [];
177
+ let i = Math.max(1, t), a = Math.max(1, n), o = Math.max(0, r), s = e.map(y), c = [], l = 0;
178
+ for (; l < e.length;) {
179
+ let t = l + 1, n = i / s[l], r = Math.abs(n - a), u = s[l];
180
+ for (let c = l + 1; c < e.length; c += 1) {
181
+ let e = u + s[c], d = (i - o * (c - l + 1 - 1)) / e, f = Math.abs(d - a);
182
+ if (f < r) t = c + 1, n = d, r = f;
183
+ else if (d < a) break;
184
+ u = e;
185
+ }
186
+ let d = t - l, f = s.slice(l, t).reduce((e, t) => e + t, 0), p = t === e.length ? Math.min(a, (i - o * (d - 1)) / f) : n;
187
+ c.push({
188
+ indices: Array.from({ length: d }, (e, t) => l + t),
189
+ widths: s.slice(l, t).map((e) => e * p),
190
+ height: p
191
+ }), l = t;
192
+ }
193
+ return c;
194
+ }
195
+ //#endregion
196
+ //#region src/lib/useCarouselNavigation.ts
197
+ function x(e) {
198
+ let { images: n, index: r, onIndexChange: i, disabled: s = !1, showNavigation: c = !0 } = e, l = a(n), u = a(r), d = a(i), m = a(s), h = a(c), g = a(null), [_, v] = o(null);
199
+ l.current = n, u.current = r, d.current = i, m.current = s, h.current = c;
200
+ let y = t(() => {
201
+ let e = u.current;
202
+ !m.current && e !== null && e > 0 && d.current(e - 1);
203
+ }, []), b = t(() => {
204
+ let e = u.current;
205
+ !m.current && e !== null && e < l.current.length - 1 && d.current(e + 1);
206
+ }, []), x = t(() => {
207
+ let e = u.current;
208
+ m.current || e === null || e === 0 || (v("previous"), d.current(e - 1));
209
+ }, []), S = t(() => {
210
+ let e = u.current;
211
+ m.current || e === null || e >= l.current.length - 1 || (v("next"), d.current(e + 1));
212
+ }, []);
213
+ return {
214
+ previous: y,
215
+ next: b,
216
+ swipeDirection: _,
217
+ handleKeyDown: t((e) => {
218
+ if (e.defaultPrevented) return;
219
+ let t = e.target;
220
+ t instanceof HTMLElement && (t.isContentEditable || t.closest("input, select, textarea, [role=\"combobox\"], [role=\"menu\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"tab\"]")) || (e.key === "ArrowLeft" ? (e.preventDefault(), y()) : e.key === "ArrowRight" && (e.preventDefault(), b()));
221
+ }, [b, y]),
222
+ handleTouchStart: t((e) => {
223
+ if (e.touches.length !== 1 || f(e.target)) {
224
+ g.current = null;
225
+ return;
226
+ }
227
+ let t = e.touches[0];
228
+ g.current = {
229
+ x: t.clientX,
230
+ y: t.clientY
231
+ };
232
+ }, []),
233
+ handleTouchEnd: t((e) => {
234
+ let t = g.current;
235
+ if (g.current = null, m.current || !t || e.changedTouches.length === 0) return;
236
+ let n = e.changedTouches[0], r = n.clientX - t.x, i = n.clientY - t.y, a = Math.abs(r);
237
+ a < 48 || a <= Math.abs(i) || (r < 0 ? S() : x());
238
+ }, [S, x]),
239
+ handleTouchCancel: t(() => {
240
+ g.current = null;
241
+ }, []),
242
+ handleImageAnimationEnd: t((e) => {
243
+ e.target === e.currentTarget && v(null);
244
+ }, []),
245
+ handleViewportClick: t((e) => {
246
+ if (!h.current || l.current.length <= 1 || m.current || !(e.target instanceof Element) || e.target.closest("button")) return;
247
+ let t = p(e.clientX, e.currentTarget);
248
+ t === "previous" ? y() : t === "next" && b();
249
+ }, [b, y])
250
+ };
251
+ }
46
252
  //#endregion
47
253
  //#region src/lib/Gallery.tsx
48
- var u = 3;
49
- function d({ images: e, onImageClick: n, columns: r, rowHeight: s, className: c = "", style: d }) {
50
- let f = i([]), [p, m] = a(0), h = r && r > 0 ? r : u, g = e.length - 1, _ = Math.min(p, Math.max(g, 0)), v = t((e) => {
51
- m(e), f.current[e]?.focus();
52
- }, []), y = t((e, t) => {
53
- let n = {
54
- ArrowRight: 1,
55
- ArrowLeft: -1,
56
- ArrowDown: h,
57
- ArrowUp: -h
58
- }[e.key], r = null;
59
- if (n !== void 0) {
60
- let e = t + n;
61
- e >= 0 && e <= g && (r = e);
62
- } else if (e.key === "Home") r = 0;
63
- else if (e.key === "End") r = g;
254
+ var S = 3, C = 192, w = 12, T = typeof window > "u" ? n : i;
255
+ function E({ images: e, onImageClick: t, renderCaption: n, showCounter: r, showNavigation: i }) {
256
+ let [a, s] = o(0), l = Math.max(0, Math.min(a, e.length - 1)), u = x({
257
+ images: e,
258
+ index: e.length > 0 ? l : null,
259
+ onIndexChange: (e) => {
260
+ e !== null && s(e);
261
+ },
262
+ showNavigation: i
263
+ });
264
+ return e.length === 0 ? null : /* @__PURE__ */ c("div", {
265
+ className: "react-pic-gallery__carousel",
266
+ role: "region",
267
+ "aria-roledescription": "carousel",
268
+ "aria-label": "Image carousel",
269
+ tabIndex: 0,
270
+ onKeyDown: u.handleKeyDown,
271
+ children: /* @__PURE__ */ c(_, {
272
+ images: e,
273
+ index: l,
274
+ navigation: u,
275
+ renderCaption: n,
276
+ showCounter: r,
277
+ showNavigation: i,
278
+ onImageClick: () => t?.(l)
279
+ })
280
+ });
281
+ }
282
+ function D({ images: e, onImageClick: n, layout: r = "grid", columns: i, rowHeight: u, className: f = "", style: p, renderCaption: m, showCounter: h, showNavigation: g }) {
283
+ let _ = a([]), v = a(null), y = a(null), [x, D] = o(0), [O, k] = o({
284
+ width: 1024,
285
+ rowHeight: C,
286
+ gap: w
287
+ }), A = i !== void 0 && Number.isFinite(i) && i >= 1 ? Math.floor(i) : S, j = e.length - 1, M = Math.min(x, Math.max(j, 0)), N = typeof u == "number" ? `${u}px` : u, P = {
288
+ ...p,
289
+ ...r === "grid" && i !== void 0 && Number.isFinite(i) && i >= 1 ? { gridTemplateColumns: `repeat(${A}, minmax(0, 1fr))` } : {},
290
+ ...N ? { "--gallery-row-height": N } : {}
291
+ };
292
+ T(() => {
293
+ if (r !== "justified") return;
294
+ let e = v.current, t = y.current;
295
+ if (!e || !t) return;
296
+ let n = () => {
297
+ let n = e.getBoundingClientRect().width || e.clientWidth || window.innerWidth || 1024, r = t.getBoundingClientRect(), i = Number.parseFloat(window.getComputedStyle(t).width), a = typeof u == "number" ? u : typeof u == "string" && /^\s*\d+(?:\.\d+)?px\s*$/.test(u) ? Number.parseFloat(u) : C;
298
+ k((e) => {
299
+ let t = {
300
+ width: n,
301
+ rowHeight: r.height || a,
302
+ gap: Number.isFinite(i) ? i : r.width || w
303
+ };
304
+ return e.width === t.width && e.rowHeight === t.rowHeight && e.gap === t.gap ? e : t;
305
+ });
306
+ };
307
+ n();
308
+ let i = typeof ResizeObserver > "u" ? null : new ResizeObserver(n);
309
+ return i?.observe(e), i?.observe(t), window.addEventListener("resize", n), () => {
310
+ i?.disconnect(), window.removeEventListener("resize", n);
311
+ };
312
+ }, [
313
+ r,
314
+ u,
315
+ N
316
+ ]);
317
+ let F = t((e) => {
318
+ D(e), _.current[e]?.focus();
319
+ }, []), I = t((t, n) => {
320
+ let i = _.current[t];
321
+ if (!i) return null;
322
+ let a = i.getBoundingClientRect();
323
+ if (r === "mosaic" && i.classList.contains("react-pic-gallery__tile--mosaic-featured")) {
324
+ let r = i.closest(".react-pic-gallery__mosaic-group"), o = a.left + a.width / 2, s = a.top + a.height / 2, c = null, l = Infinity;
325
+ if (_.current.slice(0, e.length).forEach((e, i) => {
326
+ if (!e || i === t || !r?.contains(e)) return;
327
+ let a = e.getBoundingClientRect(), u = a.left + a.width / 2, d = a.top + a.height / 2, f = n === "up" ? s - d : d - s;
328
+ if (f <= 0) return;
329
+ let p = f + Math.abs(u - o);
330
+ p < l && (l = p, c = i);
331
+ }), c !== null) return c;
332
+ }
333
+ let o = null, s = Infinity;
334
+ return _.current.slice(0, e.length).forEach((e, r) => {
335
+ if (!e || r === t) return;
336
+ let i = e.getBoundingClientRect(), c = n === "up" ? a.top - i.bottom : i.top - a.bottom;
337
+ if (c < 0) return;
338
+ let l = c + (i.right < a.left ? a.left - i.right : a.right < i.left ? i.left - a.right : 0);
339
+ l < s && (s = l, o = r);
340
+ }), o;
341
+ }, [e.length, r]), L = t((e, t) => {
342
+ let n = null;
343
+ if (e.key === "Home") n = 0;
344
+ else if (e.key === "End") n = j;
345
+ else if (r === "grid") {
346
+ let r = {
347
+ ArrowRight: 1,
348
+ ArrowLeft: -1,
349
+ ArrowDown: A,
350
+ ArrowUp: -A
351
+ }[e.key];
352
+ if (r !== void 0) {
353
+ let e = t + r;
354
+ e >= 0 && e <= j && (n = e);
355
+ }
356
+ } else if (e.key === "ArrowRight" && t < j) n = t + 1;
357
+ else if (e.key === "ArrowLeft" && t > 0) n = t - 1;
358
+ else if (e.key === "ArrowDown") n = I(t, "down");
359
+ else if (e.key === "ArrowUp") n = I(t, "up");
64
360
  else return;
65
- r !== null && r !== t && (e.preventDefault(), v(r));
361
+ n !== null && n !== t && (e.preventDefault(), F(n));
66
362
  }, [
67
- h,
68
- g,
69
- v
70
- ]), b = typeof s == "number" ? `${s}px` : s, x = {
71
- ...d,
72
- ...r ? { gridTemplateColumns: `repeat(${r}, minmax(0, 1fr))` } : {},
73
- ...b ? { "--gallery-row-height": b } : {}
74
- };
75
- return /* @__PURE__ */ o("div", {
76
- className: `react-pic-gallery__grid ${c}`.trim(),
77
- style: x,
78
- "data-testid": "react-pic-gallery-grid",
79
- children: e.map((e, t) => /* @__PURE__ */ o("button", {
80
- className: "react-pic-gallery__tile",
81
- type: "button",
82
- disabled: !n,
83
- tabIndex: t === _ ? 0 : -1,
84
- ref: (e) => {
85
- f.current[t] = e;
86
- },
87
- onKeyDown: (e) => y(e, t),
88
- onClick: () => {
89
- m(t), n?.(t);
90
- },
91
- "aria-label": `Open ${e.alt}`,
92
- children: /* @__PURE__ */ o(l, {
93
- src: e.thumbnailSrc ?? e.src,
94
- alt: e.alt,
95
- objectFit: "cover",
96
- width: e.width,
97
- height: e.height
363
+ A,
364
+ I,
365
+ j,
366
+ r,
367
+ F
368
+ ]), R = (e, t, r = {}) => /* @__PURE__ */ c("button", {
369
+ className: `react-pic-gallery__tile ${r.className ?? ""}`.trim(),
370
+ type: "button",
371
+ disabled: !n,
372
+ tabIndex: t === M ? 0 : -1,
373
+ ref: (e) => {
374
+ _.current[t] = e;
375
+ },
376
+ style: r.style,
377
+ onKeyDown: (e) => L(e, t),
378
+ onClick: () => {
379
+ D(t), n?.(t);
380
+ },
381
+ "aria-label": `Open ${e.alt}`,
382
+ children: /* @__PURE__ */ c(d, {
383
+ src: e.thumbnailSrc ?? e.src,
384
+ alt: e.alt,
385
+ objectFit: "cover",
386
+ width: e.width,
387
+ height: e.height
388
+ })
389
+ }, e.id ?? `${e.src}-${t}`), z = r === "justified" ? b(e, O.width, O.rowHeight, O.gap) : [], B = Array(e.length);
390
+ return z.forEach((e) => {
391
+ e.indices.forEach((t, n) => {
392
+ B[t] = {
393
+ width: `${e.widths[n]}px`,
394
+ height: `${e.height}px`
395
+ };
396
+ });
397
+ }), /* @__PURE__ */ l("div", {
398
+ ref: v,
399
+ className: `react-pic-gallery__gallery react-pic-gallery__gallery--${r} ${r === "grid" ? "react-pic-gallery__grid" : ""} ${f}`.trim(),
400
+ style: P,
401
+ "data-testid": r === "grid" ? "react-pic-gallery-grid" : void 0,
402
+ "data-layout": r,
403
+ children: [
404
+ r === "grid" && e.map((e, t) => R(e, t)),
405
+ r === "carousel" && /* @__PURE__ */ c(E, {
406
+ images: e,
407
+ onImageClick: n,
408
+ renderCaption: m,
409
+ showCounter: h,
410
+ showNavigation: g
411
+ }),
412
+ r === "justified" && /* @__PURE__ */ l(s, { children: [e.map((e, t) => R(e, t, {
413
+ className: "react-pic-gallery__tile--justified",
414
+ style: B[t]
415
+ })), /* @__PURE__ */ c("span", {
416
+ ref: y,
417
+ className: "react-pic-gallery__row-height-probe",
418
+ "aria-hidden": "true"
419
+ })] }),
420
+ r === "mosaic" && Array.from({ length: Math.ceil(e.length / 3) }, (t, n) => {
421
+ let r = n * 3, i = e.slice(r, r + 3), a = n % 2 == 0 ? "left" : "top";
422
+ return /* @__PURE__ */ c("div", {
423
+ className: `react-pic-gallery__mosaic-group react-pic-gallery__mosaic-group--${a} react-pic-gallery__mosaic-group--count-${i.length}`,
424
+ children: i.map((e, t) => {
425
+ let n = r + t, a = i.length === 3 ? t === 0 ? "featured" : "supporting" : "simple";
426
+ return R(e, n, { className: `react-pic-gallery__tile--mosaic react-pic-gallery__tile--mosaic-${a}` });
427
+ })
428
+ }, `mosaic-${r}`);
98
429
  })
99
- }, e.id ?? `${e.src}-${t}`))
430
+ ]
100
431
  });
101
432
  }
102
- var f = e(d), p = /* @__PURE__ */ new Set(), m = null, h = "a[href], button, input, select, textarea, [role=\"combobox\"], [role=\"menu\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"tab\"]", g = "input, select, textarea, [role=\"combobox\"], [role=\"menu\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"tab\"]", _ = "(max-width: 600px), (pointer: coarse)", v = .3;
103
- function y(e) {
104
- return e instanceof Element && e.closest(h) !== null;
105
- }
106
- function b(e) {
107
- if (!p.has(e)) {
108
- if (p.size === 0) {
433
+ var O = e(D), k = /* @__PURE__ */ new Set(), A = null;
434
+ function j(e) {
435
+ if (!k.has(e)) {
436
+ if (k.size === 0) {
109
437
  let e = document.body, t = window.innerWidth - document.documentElement.clientWidth, n = Number.parseFloat(window.getComputedStyle(e).paddingRight) || 0;
110
- m = {
438
+ A = {
111
439
  overflow: e.style.overflow,
112
440
  paddingRight: e.style.paddingRight
113
441
  }, e.style.overflow = "hidden", t > 0 && (e.style.paddingRight = `${n + t}px`);
114
442
  }
115
- p.add(e);
443
+ k.add(e);
116
444
  }
117
445
  }
118
- function x(e) {
119
- return p.delete(e), p.size > 0 || !m ? !1 : (document.body.style.overflow = m.overflow, document.body.style.paddingRight = m.paddingRight, m = null, !0);
446
+ function M(e) {
447
+ return k.delete(e), k.size > 0 || !A ? !1 : (document.body.style.overflow = A.overflow, document.body.style.paddingRight = A.paddingRight, A = null, !0);
120
448
  }
121
- function S(e) {
449
+ function N(e) {
122
450
  let t = e.trim();
123
451
  return t.endsWith("ms") ? Number.parseFloat(t) : t.endsWith("s") ? Number.parseFloat(t) * 1e3 : 0;
124
452
  }
125
- function ee(e) {
453
+ function P(e) {
126
454
  let t = window.getComputedStyle(e), n = t.animationDuration.split(","), r = t.animationDelay.split(",");
127
- return Math.max(...n.map((e, t) => S(e) + S(r[t % r.length] ?? "0s")), 0);
128
- }
129
- function C() {
130
- return /* @__PURE__ */ o("svg", {
131
- viewBox: "0 0 24 24",
132
- "aria-hidden": "true",
133
- focusable: "false",
134
- children: /* @__PURE__ */ o("path", { d: "M6 6l12 12M18 6L6 18" })
135
- });
455
+ return Math.max(...n.map((e, t) => N(e) + N(r[t % r.length] ?? "0s")), 0);
136
456
  }
137
- function w({ direction: e }) {
138
- return /* @__PURE__ */ o("svg", {
139
- viewBox: "0 0 24 24",
140
- "aria-hidden": "true",
141
- focusable: "false",
142
- children: /* @__PURE__ */ o("path", { d: e === "left" ? "M14.5 5l-7 7 7 7" : "M9.5 5l7 7-7 7" })
143
- });
144
- }
145
- function T(...e) {
146
- return e.filter(Boolean).join(" ");
147
- }
148
- function E({ images: e, index: u, onIndexChange: d, renderActions: f, renderCaption: p, renderControls: m, showCounter: h = !0, showNavigation: S = !0, className: E }) {
149
- let D = i(null), O = i(d), k = i(null), A = i(null), j = i(null), M = i(!1), N = i(!1), P = i({}), F = i(e), I = i(null), L = r(), [R, z] = a(!1), [B, V] = a(null), H = u !== null && u >= 0 && u < e.length ? u : null, U = H === null ? void 0 : e[H];
150
- O.current = d, j.current = H, M.current = R, F.current = e;
151
- let W = t(() => {
152
- k.current !== null && (window.clearTimeout(k.current), k.current = null), A.current = null;
153
- }, []), G = t(() => {
154
- let e = j.current;
155
- if (e !== null && A.current === null) {
457
+ function F({ images: e, index: i, onIndexChange: s, renderActions: d, renderCaption: f, renderControls: p, showCounter: m = !0, showNavigation: h = !0, className: v }) {
458
+ let y = a(null), b = a(s), S = a(null), C = a(null), w = a(null), T = a(!1), E = a({}), D = r(), [O, k] = o(!1), A = i !== null && i >= 0 && i < e.length ? i : null, N = A === null ? void 0 : e[A];
459
+ b.current = s, w.current = A;
460
+ let F = x({
461
+ images: e,
462
+ index: A,
463
+ onIndexChange: s,
464
+ disabled: O,
465
+ showNavigation: h && !p
466
+ }), I = t(() => {
467
+ S.current !== null && (window.clearTimeout(S.current), S.current = null), C.current = null;
468
+ }, []), L = t(() => {
469
+ let e = w.current;
470
+ if (e !== null && C.current === null) {
156
471
  if (typeof window < "u" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
157
- O.current(null);
472
+ b.current(null);
158
473
  return;
159
474
  }
160
- A.current = { index: e }, M.current = !0, z(!0);
475
+ C.current = { index: e }, k(!0);
161
476
  }
162
- }, []), K = t(() => {
163
- let e = j.current;
164
- !M.current && e !== null && e > 0 && O.current(e - 1);
165
- }, []), q = t(() => {
166
- let e = j.current;
167
- !M.current && e !== null && e < F.current.length - 1 && O.current(e + 1);
168
- }, []), J = t(() => {
169
- let e = j.current;
170
- M.current || e === null || e === 0 || (V("previous"), O.current(e - 1));
171
- }, []), Y = t(() => {
172
- let e = j.current;
173
- M.current || e === null || e >= F.current.length - 1 || (V("next"), O.current(e + 1));
174
- }, []), te = t((e) => {
175
- e.preventDefault(), G();
176
- }, [G]), X = t(() => {
177
- let e = A.current;
477
+ }, []), R = t((e) => {
478
+ e.preventDefault(), L();
479
+ }, [L]), z = t(() => {
480
+ let e = C.current;
178
481
  if (e) {
179
- if (j.current !== e.index) {
180
- W(), M.current = !1, z(!1);
482
+ if (w.current !== e.index) {
483
+ I(), k(!1);
181
484
  return;
182
485
  }
183
- W(), M.current = !1, z(!1), O.current(null);
184
- }
185
- }, [W]), ne = t((e) => {
186
- if (e.defaultPrevented) return;
187
- let t = e.target;
188
- t instanceof HTMLElement && (t.isContentEditable || t.closest(g)) || (e.key === "ArrowLeft" ? (e.preventDefault(), K()) : e.key === "ArrowRight" && (e.preventDefault(), q()));
189
- }, [q, K]), re = t((e) => {
190
- if (e.touches.length !== 1 || y(e.target)) {
191
- I.current = null;
192
- return;
486
+ I(), k(!1), b.current(null);
193
487
  }
194
- let t = e.touches[0];
195
- I.current = {
196
- x: t.clientX,
197
- y: t.clientY
198
- };
199
- }, []), ie = t((e) => {
200
- let t = I.current;
201
- if (I.current = null, !t || e.changedTouches.length === 0) return;
202
- let n = e.changedTouches[0], r = n.clientX - t.x, i = n.clientY - t.y, a = Math.abs(r);
203
- a < 48 || a <= Math.abs(i) || (r < 0 ? Y() : J());
204
- }, [Y, J]), ae = t(() => {
205
- I.current = null;
206
- }, []), oe = t((e) => {
207
- e.target === e.currentTarget && V(null);
208
- }, []), se = t((e) => {
209
- e.target === e.currentTarget && e.animationName === "react-pic-gallery-fade-out" && X();
210
- }, [X]), ce = t(() => {
211
- N.current || (W(), M.current = !1, z(!1), j.current !== null && O.current(null));
212
- }, [W]);
213
- if (n(() => {
214
- let e = A.current;
215
- e && H !== e.index && (W(), M.current = !1, z(!1));
216
- }, [H, W]), n(() => {
217
- if (!R || !A.current || !D.current) return;
218
- let e = ee(D.current);
219
- return k.current = window.setTimeout(X, e), () => {
220
- k.current !== null && (window.clearTimeout(k.current), k.current = null);
488
+ }, [I]), B = t((e) => {
489
+ e.target === e.currentTarget && e.animationName === "react-pic-gallery-fade-out" && z();
490
+ }, [z]), V = t(() => {
491
+ T.current || (I(), k(!1), w.current !== null && b.current(null));
492
+ }, [I]);
493
+ return n(() => {
494
+ let e = C.current;
495
+ e && A !== e.index && (I(), k(!1));
496
+ }, [A, I]), n(() => {
497
+ if (!O || !C.current || !y.current) return;
498
+ let e = P(y.current);
499
+ return S.current = window.setTimeout(z, e), () => {
500
+ S.current !== null && (window.clearTimeout(S.current), S.current = null);
221
501
  };
222
- }, [X, R]), n(() => {
223
- if (H === null) return;
224
- let e = D.current;
502
+ }, [z, O]), n(() => {
503
+ if (A === null) return;
504
+ let e = y.current;
225
505
  if (!e) return;
226
506
  let t = document.activeElement;
227
- return b(P.current), e.open || e.showModal(), () => {
228
- W(), N.current = !0, e.open && e.close(), N.current = !1, x(P.current) && t?.isConnected && t.focus();
507
+ return j(E.current), e.open || e.showModal(), () => {
508
+ I(), T.current = !0, e.open && e.close(), T.current = !1, M(E.current) && t?.isConnected && t.focus();
229
509
  };
230
- }, [H !== null, W]), H === null || !U || typeof document > "u") return null;
231
- let Z = {
232
- image: U,
233
- index: H,
234
- count: e.length,
235
- close: G,
236
- next: q,
237
- previous: K,
238
- canGoNext: H < e.length - 1,
239
- canGoPrevious: H > 0
240
- }, le = S && !m && Z.canGoPrevious, ue = S && !m && Z.canGoNext, Q = S && !m && e.length > 1, $ = (e) => {
241
- e.target === e.currentTarget && G();
242
- };
243
- return c(/* @__PURE__ */ s("dialog", {
244
- ref: D,
245
- className: T("react-pic-gallery__lightbox", R ? "react-pic-gallery__lightbox--closing" : void 0, E),
246
- "aria-labelledby": L,
247
- onAnimationEnd: se,
248
- onClose: ce,
249
- onMouseDown: $,
250
- onCancel: te,
251
- onKeyDown: ne,
252
- children: [
253
- /* @__PURE__ */ o("h2", {
254
- className: "react-pic-gallery__sr-only",
255
- id: L,
256
- children: "Image viewer"
257
- }),
258
- /* @__PURE__ */ s("p", {
259
- className: "react-pic-gallery__sr-only",
260
- "aria-live": "polite",
261
- children: [
262
- U.alt,
263
- ", image ",
264
- H + 1,
265
- " of ",
266
- e.length
267
- ]
268
- }),
269
- /* @__PURE__ */ s("div", {
270
- className: "react-pic-gallery__lightbox-content",
271
- children: [
272
- m ? /* @__PURE__ */ o("div", {
273
- className: "react-pic-gallery__custom-controls",
274
- children: m(Z)
275
- }) : /* @__PURE__ */ s("div", {
276
- className: "react-pic-gallery__toolbar",
277
- children: [/* @__PURE__ */ s("div", {
278
- className: "react-pic-gallery__toolbar-actions",
279
- children: [h && /* @__PURE__ */ s("span", {
280
- "aria-live": "polite",
281
- children: [
282
- H + 1,
283
- " / ",
284
- e.length
285
- ]
286
- }), f?.(Z)]
287
- }), /* @__PURE__ */ o("button", {
288
- className: "react-pic-gallery__control",
289
- type: "button",
290
- onClick: G,
291
- "aria-label": "Close image viewer",
292
- children: /* @__PURE__ */ o(C, {})
293
- })]
294
- }),
295
- /* @__PURE__ */ s("div", {
296
- className: T("react-pic-gallery__viewport", Q ? "react-pic-gallery__viewport--has-navigation" : void 0),
297
- onMouseDown: $,
298
- onClick: (e) => {
299
- if (!Q || M.current || !(e.target instanceof Element) || e.target.closest("button") || window.matchMedia?.(_)?.matches !== !0) return;
300
- let t = e.currentTarget.getBoundingClientRect();
301
- if (t.width <= 0) return;
302
- let n = (e.clientX - t.left) / t.width;
303
- n < v ? K() : n > .7 && q();
304
- },
305
- onTouchCancel: ae,
306
- onTouchEnd: ie,
307
- onTouchStart: re,
308
- children: [
309
- le && /* @__PURE__ */ o("button", {
310
- className: "react-pic-gallery__control react-pic-gallery__control--previous",
311
- type: "button",
312
- onClick: K,
313
- "aria-label": "Previous image",
314
- children: /* @__PURE__ */ o(w, { direction: "left" })
315
- }),
316
- /* @__PURE__ */ o(l, {
317
- src: U.src,
318
- alt: U.alt,
319
- eager: !0,
320
- objectFit: "contain",
321
- className: T("react-pic-gallery__lightbox-image", B ? `react-pic-gallery__lightbox-image--swipe-${B}` : void 0),
322
- onAnimationEnd: oe
323
- }, U.src),
324
- ue && /* @__PURE__ */ o("button", {
325
- className: "react-pic-gallery__control react-pic-gallery__control--next",
326
- type: "button",
327
- onClick: q,
328
- "aria-label": "Next image",
329
- children: /* @__PURE__ */ o(w, { direction: "right" })
330
- })
331
- ]
332
- }),
333
- (p || U.caption) && /* @__PURE__ */ o("div", {
334
- className: "react-pic-gallery__caption",
335
- children: p ? p(Z) : U.caption
336
- })
337
- ]
338
- })
339
- ]
510
+ }, [A !== null, I]), A === null || !N || typeof document > "u" ? null : u(/* @__PURE__ */ l("dialog", {
511
+ ref: y,
512
+ className: g("react-pic-gallery__lightbox", O ? "react-pic-gallery__lightbox--closing" : void 0, v),
513
+ "aria-labelledby": D,
514
+ onAnimationEnd: B,
515
+ onClose: V,
516
+ onMouseDown: (e) => {
517
+ e.target === e.currentTarget && L();
518
+ },
519
+ onCancel: R,
520
+ onKeyDown: F.handleKeyDown,
521
+ children: [/* @__PURE__ */ c("h2", {
522
+ className: "react-pic-gallery__sr-only",
523
+ id: D,
524
+ children: "Image viewer"
525
+ }), /* @__PURE__ */ c(_, {
526
+ images: e,
527
+ index: A,
528
+ navigation: F,
529
+ renderActions: d,
530
+ renderCaption: f,
531
+ renderControls: p,
532
+ showCounter: m,
533
+ showNavigation: h && !p,
534
+ close: L,
535
+ onEmptyAreaClick: L
536
+ })]
340
537
  }), document.body);
341
538
  }
342
539
  //#endregion
343
540
  //#region src/lib/PicGallery.tsx
344
- function D({ images: e, columns: t, rowHeight: n, className: r, galleryClassName: i, style: c, renderActions: l, renderCaption: u, renderControls: d, showCounter: p, showNavigation: m }) {
345
- let [h, g] = a(null);
346
- return /* @__PURE__ */ s("div", {
347
- className: `react-pic-gallery ${r ?? ""}`.trim(),
348
- style: c,
349
- children: [/* @__PURE__ */ o(f, {
541
+ function I({ images: e, layout: t, columns: n, rowHeight: r, className: i, galleryClassName: a, style: s, renderActions: u, renderCaption: d, renderControls: f, showCounter: p, showNavigation: m }) {
542
+ let [h, g] = o(null);
543
+ return /* @__PURE__ */ l("div", {
544
+ className: `react-pic-gallery ${i ?? ""}`.trim(),
545
+ style: s,
546
+ children: [/* @__PURE__ */ c(O, {
350
547
  images: e,
351
- columns: t,
352
- rowHeight: n,
353
- className: i,
354
- onImageClick: g
355
- }), /* @__PURE__ */ o(E, {
548
+ layout: t,
549
+ columns: n,
550
+ rowHeight: r,
551
+ className: a,
552
+ onImageClick: g,
553
+ renderCaption: d,
554
+ showCounter: p,
555
+ showNavigation: m
556
+ }), /* @__PURE__ */ c(F, {
356
557
  images: e,
357
558
  index: h,
358
559
  onIndexChange: g,
359
- renderActions: l,
360
- renderCaption: u,
361
- renderControls: d,
560
+ renderActions: u,
561
+ renderCaption: d,
562
+ renderControls: f,
362
563
  showCounter: p,
363
564
  showNavigation: m
364
565
  })]
365
566
  });
366
567
  }
367
568
  //#endregion
368
- export { f as Gallery, E as Lightbox, D as PicGallery, D as default };
569
+ export { O as Gallery, F as Lightbox, I as PicGallery, I as default };
package/dist/styles.css CHANGED
@@ -1,2 +1,2 @@
1
- :where(.react-pic-gallery,.react-pic-gallery__lightbox){--gallery-gap:.75rem;--gallery-radius:1rem;--gallery-row-height:12rem;--gallery-accent:#9ef01a;--gallery-overlay:#070b10f0;--gallery-control-size:2.75rem;--gallery-motion:.18s;color:#f8fafc}.react-pic-gallery{width:100%;min-width:0}.react-pic-gallery__grid{gap:var(--gallery-gap);grid-template-columns:repeat(3,minmax(0,1fr));width:100%;display:grid}.react-pic-gallery__tile{min-width:0;height:var(--gallery-row-height);cursor:pointer;border-radius:var(--gallery-radius);isolation:isolate;background:#e2e8f0;border:0;padding:0;position:relative;overflow:hidden}.react-pic-gallery__tile:after{content:"";pointer-events:none;border-radius:inherit;transition:border-color var(--gallery-motion) ease;border:2px solid #0000;position:absolute;inset:0}.react-pic-gallery__tile:hover .react-pic-gallery__image-element,.react-pic-gallery__tile:focus-visible .react-pic-gallery__image-element{transform:scale(1.04)}.react-pic-gallery__tile:focus-visible{outline:3px solid var(--gallery-accent);outline-offset:3px}.react-pic-gallery__tile:focus-visible:after{border-color:var(--gallery-accent)}.react-pic-gallery__tile:disabled{cursor:default}.react-pic-gallery__image{background:linear-gradient(135deg,#e2e8f0,#cbd5e1);width:100%;height:100%;display:block;position:relative;overflow:hidden}.react-pic-gallery__image-element{width:100%;height:100%;transition:transform var(--gallery-motion) ease, opacity var(--gallery-motion) ease;display:block}.react-pic-gallery__image-loader,.react-pic-gallery__image-error{place-items:center;display:grid;position:absolute;inset:0}.react-pic-gallery__image-loader:after{content:"";border:2px solid #0f172a29;border-top-color:var(--gallery-accent);border-radius:50%;width:1.5rem;height:1.5rem;animation:.7s linear infinite react-pic-gallery-spin}.react-pic-gallery__image-error{color:#475569;text-align:center;background:#e2e8f0e6;padding:1rem;font-size:.875rem}:where(dialog.react-pic-gallery__lightbox){z-index:1000;box-sizing:border-box;width:100%;max-width:none;height:100dvh;max-height:none;padding:max(1rem, env(safe-area-inset-top)) max(1rem, env(safe-area-inset-right)) max(1rem, env(safe-area-inset-bottom)) max(1rem, env(safe-area-inset-left));color:#f8fafc;background:var(--gallery-overlay);animation:react-pic-gallery-fade-in var(--gallery-motion) ease both;border:0;place-items:center;margin:0;display:grid;position:fixed;inset:0;overflow:hidden}dialog.react-pic-gallery__lightbox:not([open]){display:none}dialog.react-pic-gallery__lightbox::backdrop{background:0 0}.react-pic-gallery__lightbox--closing{animation:react-pic-gallery-fade-out var(--gallery-motion) ease both}.react-pic-gallery__lightbox-content{box-sizing:border-box;width:min(90rem,100vw - 2rem);min-width:0;max-width:100%;height:min(54rem,100dvh - 2rem);min-height:0;max-height:100%;animation:react-pic-gallery-rise-in var(--gallery-motion) ease both;flex-direction:column;display:flex}.react-pic-gallery__lightbox--closing .react-pic-gallery__lightbox-content{animation:react-pic-gallery-rise-out var(--gallery-motion) ease both}.react-pic-gallery__toolbar,.react-pic-gallery__custom-controls{flex-wrap:wrap;flex:none;justify-content:space-between;align-items:center;gap:1rem;min-height:3.5rem;display:flex}.react-pic-gallery__toolbar-actions{color:#cbd5e1;align-items:center;gap:.75rem;min-width:0;font-size:.875rem;display:flex}.react-pic-gallery__control{width:var(--gallery-control-size);height:var(--gallery-control-size);color:#f8fafc;cursor:pointer;transition:background var(--gallery-motion) ease, color var(--gallery-motion) ease, transform var(--gallery-motion) ease;background:0 0;border:0;border-radius:.7rem;flex:none;place-items:center;padding:0;font-size:1.4rem;line-height:1;display:inline-grid}.react-pic-gallery__control:hover{background:#f8fafc24}.react-pic-gallery__control:focus-visible{outline:3px solid var(--gallery-accent);outline-offset:3px}.react-pic-gallery__control svg{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.8px;width:1.25rem;height:1.25rem}.react-pic-gallery__viewport{touch-action:pan-y pinch-zoom;flex:1 1 0;place-items:center;min-width:0;min-height:0;padding:.75rem;display:grid;position:relative;overflow:hidden}.react-pic-gallery__viewport--has-navigation{padding-inline:3.5rem}.react-pic-gallery__viewport--has-navigation .react-pic-gallery__lightbox-image{inset-inline:3.5rem}.react-pic-gallery__lightbox-image{pointer-events:none;width:auto;min-width:0;max-width:82rem;height:auto;min-height:0;max-height:none;animation:react-pic-gallery-image-in var(--gallery-motion) ease both;background:0 0;border-radius:0;grid-template-rows:minmax(0,1fr);grid-template-columns:minmax(0,1fr);place-items:center;margin-inline:auto;display:grid;position:absolute;inset:.75rem;overflow:visible}.react-pic-gallery__lightbox-image .react-pic-gallery__image-element{pointer-events:auto;touch-action:inherit;border-radius:var(--gallery-radius);background:0 0;width:auto;max-width:100%;height:auto;max-height:100%;object-fit:contain!important}.react-pic-gallery__lightbox-image .react-pic-gallery__image-loader{pointer-events:none;background:0 0}.react-pic-gallery__lightbox-image .react-pic-gallery__image-error{pointer-events:none}.react-pic-gallery__lightbox-image--swipe-next{animation-name:react-pic-gallery-slide-in-from-right}.react-pic-gallery__lightbox-image--swipe-previous{animation-name:react-pic-gallery-slide-in-from-left}.react-pic-gallery__control--previous,.react-pic-gallery__control--next{z-index:1;position:absolute;top:50%;transform:translateY(-50%)}.react-pic-gallery__control--previous{left:0}.react-pic-gallery__control--next{right:0}.react-pic-gallery__caption{color:#cbd5e1;text-align:center;flex:none;max-height:8rem;padding:.75rem 0 0;font-size:.95rem;line-height:1.4;overflow:hidden}.react-pic-gallery__sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;padding:0;position:absolute;overflow:hidden}@keyframes react-pic-gallery-spin{to{transform:rotate(360deg)}}@keyframes react-pic-gallery-fade-in{0%{opacity:0}to{opacity:1}}@keyframes react-pic-gallery-fade-out{0%{opacity:1}to{opacity:0}}@keyframes react-pic-gallery-rise-in{0%{opacity:0;transform:translateY(.5rem)scale(.99)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes react-pic-gallery-rise-out{0%{opacity:1;transform:translateY(0)scale(1)}to{opacity:0;transform:translateY(.5rem)scale(.99)}}@keyframes react-pic-gallery-image-in{0%{opacity:0;transform:scale(.985)}to{opacity:1;transform:scale(1)}}@keyframes react-pic-gallery-slide-in-from-right{0%{opacity:0;transform:translate(18%)}to{opacity:1;transform:translate(0)}}@keyframes react-pic-gallery-slide-in-from-left{0%{opacity:0;transform:translate(-18%)}to{opacity:1;transform:translate(0)}}@media (width<=600px){:where(.react-pic-gallery,.react-pic-gallery__lightbox){--gallery-gap:.5rem;--gallery-radius:.75rem;--gallery-row-height:8rem}:where(dialog.react-pic-gallery__lightbox){padding:max(.375rem, env(safe-area-inset-top)) max(.375rem, env(safe-area-inset-right)) max(.375rem, env(safe-area-inset-bottom)) max(.375rem, env(safe-area-inset-left))}.react-pic-gallery__lightbox-content{width:min(90rem,100vw - .75rem);height:min(54rem,100dvh - .75rem)}.react-pic-gallery__toolbar,.react-pic-gallery__custom-controls{min-height:3rem}.react-pic-gallery__viewport{padding:.25rem}.react-pic-gallery__lightbox-image{inset:.25rem}.react-pic-gallery__viewport--has-navigation .react-pic-gallery__lightbox-image{inset-inline:.25rem}.react-pic-gallery__control--previous,.react-pic-gallery__control--next{display:none}}@media (height<=600px){:where(dialog.react-pic-gallery__lightbox){padding:max(.25rem, env(safe-area-inset-top)) max(1rem, env(safe-area-inset-right)) max(.25rem, env(safe-area-inset-bottom)) max(1rem, env(safe-area-inset-left))}.react-pic-gallery__lightbox-content{height:min(54rem,100dvh - .5rem)}.react-pic-gallery__toolbar,.react-pic-gallery__custom-controls{min-height:2.25rem}.react-pic-gallery__caption{max-height:4rem;padding:.35rem 0 0;font-size:.85rem}.react-pic-gallery__viewport{padding:.25rem}.react-pic-gallery__lightbox-image{inset:.25rem}}@media (prefers-reduced-motion:reduce){:where(.react-pic-gallery,.react-pic-gallery__lightbox){--gallery-motion:0s}.react-pic-gallery__image-loader:after{animation:none}}
1
+ :where(.react-pic-gallery,.react-pic-gallery__gallery,.react-pic-gallery__carousel,.react-pic-gallery__lightbox){--gallery-gap:.75rem;--gallery-radius:1rem;--gallery-row-height:12rem;--gallery-carousel-height:24rem;--gallery-accent:#9ef01a;--gallery-overlay:#070b10f0;--gallery-control-size:2.75rem;--gallery-motion:.18s;color:#f8fafc;-webkit-user-select:none;user-select:none}.react-pic-gallery{width:100%;min-width:0}.react-pic-gallery__carousel{width:100%;height:var(--gallery-carousel-height,24rem);flex-direction:column;min-width:0;display:flex;position:relative}.react-pic-gallery__carousel:focus-visible{outline:none}.react-pic-gallery__carousel .react-pic-gallery__lightbox-content{width:100%;max-width:none;height:100%;max-height:none}.react-pic-gallery__carousel .react-pic-gallery__toolbar{z-index:1;pointer-events:none;gap:.5rem;min-height:0;position:absolute;top:.75rem;left:.75rem}.react-pic-gallery__carousel .react-pic-gallery__toolbar-actions{background:#070b108c;border-radius:999px;padding:.2rem .55rem}.react-pic-gallery__gallery{width:100%;min-width:0;position:relative}.react-pic-gallery__grid{gap:var(--gallery-gap,.75rem);grid-template-columns:repeat(3,minmax(0,1fr));width:100%;display:grid}.react-pic-gallery__gallery--justified{align-content:flex-start;gap:var(--gallery-gap,.75rem);flex-wrap:wrap;display:flex}.react-pic-gallery__gallery--justified>.react-pic-gallery__tile--justified{flex:none;height:auto}.react-pic-gallery__row-height-probe{width:var(--gallery-gap,.75rem);height:var(--gallery-row-height,12rem);visibility:hidden;pointer-events:none;position:absolute;overflow:hidden}.react-pic-gallery__gallery--mosaic{gap:var(--gallery-gap,.75rem);display:grid}.react-pic-gallery__mosaic-group{grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);grid-template-rows:repeat(2, minmax(0, var(--gallery-row-height,12rem)));gap:var(--gallery-gap,.75rem);min-width:0;display:grid}.react-pic-gallery__mosaic-group--top{grid-template-columns:repeat(2,minmax(0,1fr))}.react-pic-gallery__mosaic-group .react-pic-gallery__tile--mosaic{min-width:0;height:auto}.react-pic-gallery__mosaic-group--left .react-pic-gallery__tile--mosaic-featured{grid-area:1/1/span 2}.react-pic-gallery__mosaic-group--top .react-pic-gallery__tile--mosaic-featured{grid-area:1/1/auto/-1}.react-pic-gallery__mosaic-group--count-2{grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:minmax(0, var(--gallery-row-height,12rem))}.react-pic-gallery__mosaic-group--count-1{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0, var(--gallery-row-height,12rem))}.react-pic-gallery__tile{min-width:0;height:var(--gallery-row-height,12rem);cursor:pointer;border-radius:var(--gallery-radius,1rem);isolation:isolate;background:0 0;border:0;padding:0;position:relative;overflow:hidden}.react-pic-gallery__tile:after{content:"";pointer-events:none;border-radius:inherit;transition:border-color var(--gallery-motion,.18s) ease;border:2px solid #0000;position:absolute;inset:0}.react-pic-gallery__tile:hover .react-pic-gallery__image-element,.react-pic-gallery__tile:focus-visible .react-pic-gallery__image-element{transform:scale(1.04)}.react-pic-gallery__tile:focus-visible{outline:3px solid var(--gallery-accent,#9ef01a);outline-offset:3px}.react-pic-gallery__tile:focus-visible:after{border-color:var(--gallery-accent,#9ef01a)}.react-pic-gallery__tile:disabled{cursor:default}.react-pic-gallery__image{border-radius:inherit;width:100%;height:100%;display:block;position:relative;overflow:hidden}.react-pic-gallery__image-element{width:100%;height:100%;transition:transform var(--gallery-motion,.18s) ease, opacity var(--gallery-motion,.18s) ease;display:block}.react-pic-gallery__image-loader,.react-pic-gallery__image-error{background:linear-gradient(135deg,#e2e8f0,#cbd5e1);place-items:center;display:grid;position:absolute;inset:0}.react-pic-gallery__image-loader:after{content:"";border:2px solid #0f172a29;border-top-color:var(--gallery-accent,#9ef01a);border-radius:50%;width:1.5rem;height:1.5rem;animation:.7s linear infinite react-pic-gallery-spin}.react-pic-gallery__image-error{color:#475569;text-align:center;background:#e2e8f0e6;padding:1rem;font-size:.875rem}:where(dialog.react-pic-gallery__lightbox){z-index:1000;box-sizing:border-box;width:100%;max-width:none;height:100dvh;max-height:none;padding:max(1rem, env(safe-area-inset-top)) max(1rem, env(safe-area-inset-right)) max(1rem, env(safe-area-inset-bottom)) max(1rem, env(safe-area-inset-left));color:#f8fafc;background:var(--gallery-overlay);animation:react-pic-gallery-fade-in var(--gallery-motion) ease both;border:0;place-items:center;margin:0;display:grid;position:fixed;inset:0;overflow:hidden}dialog.react-pic-gallery__lightbox:not([open]){display:none}dialog.react-pic-gallery__lightbox::backdrop{background:0 0}.react-pic-gallery__lightbox--closing{animation:react-pic-gallery-fade-out var(--gallery-motion) ease both}.react-pic-gallery__lightbox-content{box-sizing:border-box;width:min(90rem,100vw - 2rem);min-width:0;max-width:100%;height:min(54rem,100dvh - 2rem);min-height:0;max-height:100%;animation:react-pic-gallery-rise-in var(--gallery-motion) ease both;flex-direction:column;display:flex}.react-pic-gallery__lightbox--closing .react-pic-gallery__lightbox-content{animation:react-pic-gallery-rise-out var(--gallery-motion) ease both}.react-pic-gallery__toolbar,.react-pic-gallery__custom-controls{flex-wrap:wrap;flex:none;justify-content:space-between;align-items:center;gap:1rem;min-height:3.5rem;display:flex}.react-pic-gallery__toolbar-actions{color:#cbd5e1;align-items:center;gap:.75rem;min-width:0;font-size:.875rem;display:flex}.react-pic-gallery__control{width:var(--gallery-control-size);height:var(--gallery-control-size);color:#f8fafc;cursor:pointer;transition:background var(--gallery-motion) ease, color var(--gallery-motion) ease, transform var(--gallery-motion) ease;background:0 0;border:0;border-radius:.7rem;flex:none;place-items:center;padding:0;font-size:1.4rem;line-height:1;display:inline-grid}.react-pic-gallery__control:hover{background:#f8fafc24}.react-pic-gallery__control:focus-visible{outline:3px solid var(--gallery-accent);outline-offset:3px}.react-pic-gallery__control svg{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.8px;width:1.25rem;height:1.25rem}.react-pic-gallery__viewport{touch-action:pan-y pinch-zoom;flex:1 1 0;place-items:center;min-width:0;min-height:0;padding:.75rem;display:grid;position:relative;overflow:hidden}.react-pic-gallery__viewport--has-navigation{padding-inline:3.5rem}.react-pic-gallery__viewport--has-navigation .react-pic-gallery__lightbox-image{inset-inline:3.5rem}.react-pic-gallery__lightbox-image{pointer-events:none;width:auto;min-width:0;max-width:82rem;height:auto;min-height:0;max-height:none;animation:react-pic-gallery-image-in var(--gallery-motion) ease both;background:0 0;border-radius:0;grid-template-rows:minmax(0,1fr);grid-template-columns:minmax(0,1fr);place-items:center;margin-inline:auto;display:grid;position:absolute;inset:.75rem;overflow:visible}.react-pic-gallery__lightbox-image .react-pic-gallery__image-element{pointer-events:auto;touch-action:inherit;border-radius:var(--gallery-radius);background:0 0;width:auto;max-width:100%;height:auto;max-height:100%;object-fit:contain!important}.react-pic-gallery__lightbox-image .react-pic-gallery__image-loader{pointer-events:none;background:0 0}.react-pic-gallery__lightbox-image .react-pic-gallery__image-error{pointer-events:none}.react-pic-gallery__lightbox-image--swipe-next{animation-name:react-pic-gallery-slide-in-from-right}.react-pic-gallery__lightbox-image--swipe-previous{animation-name:react-pic-gallery-slide-in-from-left}.react-pic-gallery__control--previous,.react-pic-gallery__control--next{z-index:1;position:absolute;top:50%;transform:translateY(-50%)}.react-pic-gallery__control--previous{left:0}.react-pic-gallery__control--next{right:0}.react-pic-gallery__caption{color:#cbd5e1;text-align:center;flex:none;max-height:8rem;padding:.75rem 0 0;font-size:.95rem;line-height:1.4;overflow:hidden}.react-pic-gallery__sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;padding:0;position:absolute;overflow:hidden}@keyframes react-pic-gallery-spin{to{transform:rotate(360deg)}}@keyframes react-pic-gallery-fade-in{0%{opacity:0}to{opacity:1}}@keyframes react-pic-gallery-fade-out{0%{opacity:1}to{opacity:0}}@keyframes react-pic-gallery-rise-in{0%{opacity:0;transform:translateY(.5rem)scale(.99)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes react-pic-gallery-rise-out{0%{opacity:1;transform:translateY(0)scale(1)}to{opacity:0;transform:translateY(.5rem)scale(.99)}}@keyframes react-pic-gallery-image-in{0%{opacity:0;transform:scale(.985)}to{opacity:1;transform:scale(1)}}@keyframes react-pic-gallery-slide-in-from-right{0%{opacity:0;transform:translate(18%)}to{opacity:1;transform:translate(0)}}@keyframes react-pic-gallery-slide-in-from-left{0%{opacity:0;transform:translate(-18%)}to{opacity:1;transform:translate(0)}}@media (width<=600px){:where(.react-pic-gallery,.react-pic-gallery__lightbox){--gallery-gap:.5rem;--gallery-radius:.75rem;--gallery-row-height:8rem;--gallery-carousel-height:14rem}:where(dialog.react-pic-gallery__lightbox){padding:max(.375rem, env(safe-area-inset-top)) max(.375rem, env(safe-area-inset-right)) max(.375rem, env(safe-area-inset-bottom)) max(.375rem, env(safe-area-inset-left))}.react-pic-gallery__mosaic-group--count-3{grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:minmax(8rem, var(--gallery-row-height,12rem)) minmax(5rem, .65fr)}.react-pic-gallery__mosaic-group--count-3 .react-pic-gallery__tile--mosaic-featured{grid-area:1/1/auto/-1}.react-pic-gallery__lightbox-content{width:min(90rem,100vw - .75rem);height:min(54rem,100dvh - .75rem)}.react-pic-gallery__toolbar,.react-pic-gallery__custom-controls{min-height:3rem}.react-pic-gallery__viewport{padding:.25rem}.react-pic-gallery__lightbox-image{inset:.25rem}.react-pic-gallery__viewport--has-navigation .react-pic-gallery__lightbox-image{inset-inline:.25rem}.react-pic-gallery__control--previous,.react-pic-gallery__control--next{display:none}}@media (height<=600px){:where(dialog.react-pic-gallery__lightbox){padding:max(.25rem, env(safe-area-inset-top)) max(1rem, env(safe-area-inset-right)) max(.25rem, env(safe-area-inset-bottom)) max(1rem, env(safe-area-inset-left))}.react-pic-gallery__lightbox-content{height:min(54rem,100dvh - .5rem)}.react-pic-gallery__toolbar,.react-pic-gallery__custom-controls{min-height:2.25rem}.react-pic-gallery__caption{max-height:4rem;padding:.35rem 0 0;font-size:.85rem}.react-pic-gallery__viewport{padding:.25rem}.react-pic-gallery__lightbox-image{inset:.25rem}}@media (prefers-reduced-motion:reduce){:where(.react-pic-gallery,.react-pic-gallery__lightbox){--gallery-motion:0s}.react-pic-gallery__image-loader:after{animation:none}}
2
2
  /*$vite$:1*/
package/dist/types.d.ts CHANGED
@@ -8,6 +8,7 @@ export type GalleryImage = {
8
8
  width?: number;
9
9
  height?: number;
10
10
  };
11
+ export type GalleryLayout = 'grid' | 'justified' | 'mosaic' | 'carousel';
11
12
  export type LightboxContext<T extends GalleryImage = GalleryImage> = {
12
13
  image: T;
13
14
  index: number;
@@ -22,8 +23,12 @@ export type LightboxRenderer<T extends GalleryImage = GalleryImage> = (context:
22
23
  export type GalleryProps<T extends GalleryImage = GalleryImage> = {
23
24
  images: readonly T[];
24
25
  onImageClick?: (index: number) => void;
26
+ layout?: GalleryLayout;
25
27
  columns?: number;
26
28
  rowHeight?: CSSProperties['height'];
29
+ renderCaption?: LightboxRenderer<T>;
30
+ showCounter?: boolean;
31
+ showNavigation?: boolean;
27
32
  className?: string;
28
33
  style?: CSSProperties;
29
34
  };
@@ -40,6 +45,7 @@ export type LightboxProps<T extends GalleryImage = GalleryImage> = {
40
45
  };
41
46
  export type PicGalleryProps<T extends GalleryImage = GalleryImage> = {
42
47
  images: readonly T[];
48
+ layout?: GalleryLayout;
43
49
  columns?: number;
44
50
  rowHeight?: CSSProperties['height'];
45
51
  className?: string;
@@ -0,0 +1,21 @@
1
+ import { AnimationEvent as ReactAnimationEvent, KeyboardEvent as ReactKeyboardEvent, MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
2
+ import { GalleryImage } from './types';
3
+ export type UseCarouselNavigationOptions<T extends GalleryImage> = {
4
+ images: readonly T[];
5
+ index: number | null;
6
+ onIndexChange: (index: number | null) => void;
7
+ disabled?: boolean;
8
+ showNavigation?: boolean;
9
+ };
10
+ export type UseCarouselNavigationResult = {
11
+ previous: () => void;
12
+ next: () => void;
13
+ swipeDirection: 'next' | 'previous' | null;
14
+ handleKeyDown: (event: ReactKeyboardEvent<HTMLElement>) => void;
15
+ handleTouchStart: (event: ReactTouchEvent<HTMLElement>) => void;
16
+ handleTouchEnd: (event: ReactTouchEvent<HTMLElement>) => void;
17
+ handleTouchCancel: () => void;
18
+ handleImageAnimationEnd: (event: ReactAnimationEvent<HTMLElement>) => void;
19
+ handleViewportClick: (event: ReactMouseEvent<HTMLElement>) => void;
20
+ };
21
+ export declare function useCarouselNavigation<T extends GalleryImage>(options: UseCarouselNavigationOptions<T>): UseCarouselNavigationResult;
@@ -0,0 +1,11 @@
1
+ export declare const interactiveControlSelector = "a[href], button, input, select, textarea, [role=\"combobox\"], [role=\"menu\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"tab\"]";
2
+ export declare const keyboardInteractiveControlSelector = "input, select, textarea, [role=\"combobox\"], [role=\"menu\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"tab\"]";
3
+ export declare const edgeTapMediaQuery = "(max-width: 600px), (pointer: coarse)";
4
+ export declare const edgeTapZoneRatio = 0.3;
5
+ export declare function isInteractiveTarget(target: EventTarget | null): boolean;
6
+ export declare function getEdgeTapZone(clientX: number, viewport: HTMLElement | null): "next" | "previous" | null;
7
+ export declare function CloseIcon(): import('react').JSX.Element;
8
+ export declare function ChevronIcon({ direction }: {
9
+ direction: 'left' | 'right';
10
+ }): import('react').JSX.Element;
11
+ export declare function joinClassNames(...names: Array<string | undefined>): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-pic-gallery",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Small, accessible React image gallery and lightbox.",
5
5
  "author": "marcelrsoub",
6
6
  "license": "MIT",