react-pic-gallery 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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/7.7%20KB/blue)](https://bundlephobia.com/package/react-pic-gallery@2.0.2)
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 **7.7 KB gzipped** (5.55 KB JS + 2.16 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; ~7.7 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
 
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 }: GalleryProps<T>): import('react').JSX.Element;
3
3
  export declare const Gallery: typeof GalleryComponent;
4
4
  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,24 +1,24 @@
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, eager: f = !1, objectFit: p = "cover", width: m, height: h }) {
6
+ let g = a(null), [_, v] = o("loading");
7
7
  return n(() => {
8
8
  v("loading");
9
9
  let e = g.current;
10
10
  e?.complete && v(e.naturalWidth > 0 ? "loaded" : "error");
11
- }, [e]), /* @__PURE__ */ s("span", {
11
+ }, [e]), /* @__PURE__ */ l("span", {
12
12
  className: `react-pic-gallery__image ${r}`.trim(),
13
- style: l,
13
+ style: s,
14
14
  "aria-busy": _ === "loading",
15
15
  onAnimationEnd: d,
16
16
  children: [
17
- /* @__PURE__ */ o("img", {
17
+ /* @__PURE__ */ c("img", {
18
18
  ref: g,
19
19
  src: e,
20
20
  alt: t,
21
- className: `react-pic-gallery__image-element ${c}`.trim(),
21
+ className: `react-pic-gallery__image-element ${i}`.trim(),
22
22
  style: {
23
23
  objectFit: p,
24
24
  ...u
@@ -30,12 +30,12 @@ function l({ src: e, alt: t, className: r = "", imageClassName: c = "", style: l
30
30
  onLoad: () => v("loaded"),
31
31
  onError: () => v("error")
32
32
  }),
33
- _ === "loading" && /* @__PURE__ */ o("span", {
33
+ _ === "loading" && /* @__PURE__ */ c("span", {
34
34
  className: "react-pic-gallery__image-loader",
35
35
  role: "progressbar",
36
36
  "aria-label": "Loading image"
37
37
  }),
38
- _ === "error" && /* @__PURE__ */ o("span", {
38
+ _ === "error" && /* @__PURE__ */ c("span", {
39
39
  className: "react-pic-gallery__image-error",
40
40
  role: "status",
41
41
  children: "Unable to load image"
@@ -44,295 +44,412 @@ function l({ src: e, alt: t, className: r = "", imageClassName: c = "", style: l
44
44
  });
45
45
  }
46
46
  //#endregion
47
+ //#region src/lib/layout.ts
48
+ var f = 3 / 2;
49
+ function p(e) {
50
+ 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 : f;
51
+ }
52
+ function m(e, t, n, r) {
53
+ if (e.length === 0) return [];
54
+ let i = Math.max(1, t), a = Math.max(1, n), o = Math.max(0, r), s = e.map(p), c = [], l = 0;
55
+ for (; l < e.length;) {
56
+ let t = l + 1, n = i / s[l], r = Math.abs(n - a), u = s[l];
57
+ for (let c = l + 1; c < e.length; c += 1) {
58
+ let e = u + s[c], d = (i - o * (c - l + 1 - 1)) / e, f = Math.abs(d - a);
59
+ if (f < r) t = c + 1, n = d, r = f;
60
+ else if (d < a) break;
61
+ u = e;
62
+ }
63
+ 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;
64
+ c.push({
65
+ indices: Array.from({ length: d }, (e, t) => l + t),
66
+ widths: s.slice(l, t).map((e) => e * p),
67
+ height: p
68
+ }), l = t;
69
+ }
70
+ return c;
71
+ }
72
+ //#endregion
47
73
  //#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;
74
+ var h = 3, g = 192, _ = 12, v = typeof window > "u" ? n : i;
75
+ function y({ images: e, onImageClick: n, layout: r = "grid", columns: i, rowHeight: u, className: f = "", style: p }) {
76
+ let y = a([]), b = a(null), x = a(null), [S, C] = o(0), [w, T] = o({
77
+ width: 1024,
78
+ rowHeight: g,
79
+ gap: _
80
+ }), E = i !== void 0 && Number.isFinite(i) && i >= 1 ? Math.floor(i) : h, D = e.length - 1, O = Math.min(S, Math.max(D, 0)), k = typeof u == "number" ? `${u}px` : u, A = {
81
+ ...p,
82
+ ...r === "grid" && i !== void 0 && Number.isFinite(i) && i >= 1 ? { gridTemplateColumns: `repeat(${E}, minmax(0, 1fr))` } : {},
83
+ ...k ? { "--gallery-row-height": k } : {}
84
+ };
85
+ v(() => {
86
+ if (r !== "justified") return;
87
+ let e = b.current, t = x.current;
88
+ if (!e || !t) return;
89
+ let n = () => {
90
+ 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) : g;
91
+ T((e) => {
92
+ let t = {
93
+ width: n,
94
+ rowHeight: r.height || a,
95
+ gap: Number.isFinite(i) ? i : r.width || _
96
+ };
97
+ return e.width === t.width && e.rowHeight === t.rowHeight && e.gap === t.gap ? e : t;
98
+ });
99
+ };
100
+ n();
101
+ let i = typeof ResizeObserver > "u" ? null : new ResizeObserver(n);
102
+ return i?.observe(e), i?.observe(t), window.addEventListener("resize", n), () => {
103
+ i?.disconnect(), window.removeEventListener("resize", n);
104
+ };
105
+ }, [
106
+ r,
107
+ u,
108
+ k
109
+ ]);
110
+ let j = t((e) => {
111
+ C(e), y.current[e]?.focus();
112
+ }, []), M = t((t, n) => {
113
+ let i = y.current[t];
114
+ if (!i) return null;
115
+ let a = i.getBoundingClientRect();
116
+ if (r === "mosaic" && i.classList.contains("react-pic-gallery__tile--mosaic-featured")) {
117
+ 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;
118
+ if (y.current.slice(0, e.length).forEach((e, i) => {
119
+ if (!e || i === t || !r?.contains(e)) return;
120
+ let a = e.getBoundingClientRect(), u = a.left + a.width / 2, d = a.top + a.height / 2, f = n === "up" ? s - d : d - s;
121
+ if (f <= 0) return;
122
+ let p = f + Math.abs(u - o);
123
+ p < l && (l = p, c = i);
124
+ }), c !== null) return c;
125
+ }
126
+ let o = null, s = Infinity;
127
+ return y.current.slice(0, e.length).forEach((e, r) => {
128
+ if (!e || r === t) return;
129
+ let i = e.getBoundingClientRect(), c = n === "up" ? a.top - i.bottom : i.top - a.bottom;
130
+ if (c < 0) return;
131
+ let l = c + (i.right < a.left ? a.left - i.right : a.right < i.left ? i.left - a.right : 0);
132
+ l < s && (s = l, o = r);
133
+ }), o;
134
+ }, [e.length, r]), N = t((e, t) => {
135
+ let n = null;
136
+ if (e.key === "Home") n = 0;
137
+ else if (e.key === "End") n = D;
138
+ else if (r === "grid") {
139
+ let r = {
140
+ ArrowRight: 1,
141
+ ArrowLeft: -1,
142
+ ArrowDown: E,
143
+ ArrowUp: -E
144
+ }[e.key];
145
+ if (r !== void 0) {
146
+ let e = t + r;
147
+ e >= 0 && e <= D && (n = e);
148
+ }
149
+ } else if (e.key === "ArrowRight" && t < D) n = t + 1;
150
+ else if (e.key === "ArrowLeft" && t > 0) n = t - 1;
151
+ else if (e.key === "ArrowDown") n = M(t, "down");
152
+ else if (e.key === "ArrowUp") n = M(t, "up");
64
153
  else return;
65
- r !== null && r !== t && (e.preventDefault(), v(r));
154
+ n !== null && n !== t && (e.preventDefault(), j(n));
66
155
  }, [
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
156
+ E,
157
+ M,
158
+ D,
159
+ r,
160
+ j
161
+ ]), P = (e, t, r = {}) => /* @__PURE__ */ c("button", {
162
+ className: `react-pic-gallery__tile ${r.className ?? ""}`.trim(),
163
+ type: "button",
164
+ disabled: !n,
165
+ tabIndex: t === O ? 0 : -1,
166
+ ref: (e) => {
167
+ y.current[t] = e;
168
+ },
169
+ style: r.style,
170
+ onKeyDown: (e) => N(e, t),
171
+ onClick: () => {
172
+ C(t), n?.(t);
173
+ },
174
+ "aria-label": `Open ${e.alt}`,
175
+ children: /* @__PURE__ */ c(d, {
176
+ src: e.thumbnailSrc ?? e.src,
177
+ alt: e.alt,
178
+ objectFit: "cover",
179
+ width: e.width,
180
+ height: e.height
181
+ })
182
+ }, e.id ?? `${e.src}-${t}`), F = r === "justified" ? m(e, w.width, w.rowHeight, w.gap) : [], I = Array(e.length);
183
+ return F.forEach((e) => {
184
+ e.indices.forEach((t, n) => {
185
+ I[t] = {
186
+ width: `${e.widths[n]}px`,
187
+ height: `${e.height}px`
188
+ };
189
+ });
190
+ }), /* @__PURE__ */ l("div", {
191
+ ref: b,
192
+ className: `react-pic-gallery__gallery react-pic-gallery__gallery--${r} ${r === "grid" ? "react-pic-gallery__grid" : ""} ${f}`.trim(),
193
+ style: A,
194
+ "data-testid": r === "grid" ? "react-pic-gallery-grid" : void 0,
195
+ "data-layout": r,
196
+ children: [
197
+ r === "grid" && e.map((e, t) => P(e, t)),
198
+ r === "justified" && /* @__PURE__ */ l(s, { children: [e.map((e, t) => P(e, t, {
199
+ className: "react-pic-gallery__tile--justified",
200
+ style: I[t]
201
+ })), /* @__PURE__ */ c("span", {
202
+ ref: x,
203
+ className: "react-pic-gallery__row-height-probe",
204
+ "aria-hidden": "true"
205
+ })] }),
206
+ r === "mosaic" && Array.from({ length: Math.ceil(e.length / 3) }, (t, n) => {
207
+ let r = n * 3, i = e.slice(r, r + 3), a = n % 2 == 0 ? "left" : "top";
208
+ return /* @__PURE__ */ c("div", {
209
+ className: `react-pic-gallery__mosaic-group react-pic-gallery__mosaic-group--${a} react-pic-gallery__mosaic-group--count-${i.length}`,
210
+ children: i.map((e, t) => {
211
+ let n = r + t, a = i.length === 3 ? t === 0 ? "featured" : "supporting" : "simple";
212
+ return P(e, n, { className: `react-pic-gallery__tile--mosaic react-pic-gallery__tile--mosaic-${a}` });
213
+ })
214
+ }, `mosaic-${r}`);
98
215
  })
99
- }, e.id ?? `${e.src}-${t}`))
216
+ ]
100
217
  });
101
218
  }
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;
219
+ var b = e(y), x = /* @__PURE__ */ new Set(), S = null, C = "a[href], button, input, select, textarea, [role=\"combobox\"], [role=\"menu\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"tab\"]", w = "input, select, textarea, [role=\"combobox\"], [role=\"menu\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"tab\"]", T = "(max-width: 600px), (pointer: coarse)", E = .3;
220
+ function D(e) {
221
+ return e instanceof Element && e.closest(C) !== null;
105
222
  }
106
- function b(e) {
107
- if (!p.has(e)) {
108
- if (p.size === 0) {
223
+ function O(e) {
224
+ if (!x.has(e)) {
225
+ if (x.size === 0) {
109
226
  let e = document.body, t = window.innerWidth - document.documentElement.clientWidth, n = Number.parseFloat(window.getComputedStyle(e).paddingRight) || 0;
110
- m = {
227
+ S = {
111
228
  overflow: e.style.overflow,
112
229
  paddingRight: e.style.paddingRight
113
230
  }, e.style.overflow = "hidden", t > 0 && (e.style.paddingRight = `${n + t}px`);
114
231
  }
115
- p.add(e);
232
+ x.add(e);
116
233
  }
117
234
  }
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);
235
+ function k(e) {
236
+ return x.delete(e), x.size > 0 || !S ? !1 : (document.body.style.overflow = S.overflow, document.body.style.paddingRight = S.paddingRight, S = null, !0);
120
237
  }
121
- function S(e) {
238
+ function A(e) {
122
239
  let t = e.trim();
123
240
  return t.endsWith("ms") ? Number.parseFloat(t) : t.endsWith("s") ? Number.parseFloat(t) * 1e3 : 0;
124
241
  }
125
- function ee(e) {
242
+ function j(e) {
126
243
  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);
244
+ return Math.max(...n.map((e, t) => A(e) + A(r[t % r.length] ?? "0s")), 0);
128
245
  }
129
- function C() {
130
- return /* @__PURE__ */ o("svg", {
246
+ function M() {
247
+ return /* @__PURE__ */ c("svg", {
131
248
  viewBox: "0 0 24 24",
132
249
  "aria-hidden": "true",
133
250
  focusable: "false",
134
- children: /* @__PURE__ */ o("path", { d: "M6 6l12 12M18 6L6 18" })
251
+ children: /* @__PURE__ */ c("path", { d: "M6 6l12 12M18 6L6 18" })
135
252
  });
136
253
  }
137
- function w({ direction: e }) {
138
- return /* @__PURE__ */ o("svg", {
254
+ function N({ direction: e }) {
255
+ return /* @__PURE__ */ c("svg", {
139
256
  viewBox: "0 0 24 24",
140
257
  "aria-hidden": "true",
141
258
  focusable: "false",
142
- children: /* @__PURE__ */ o("path", { d: e === "left" ? "M14.5 5l-7 7 7 7" : "M9.5 5l7 7-7 7" })
259
+ children: /* @__PURE__ */ c("path", { d: e === "left" ? "M14.5 5l-7 7 7 7" : "M9.5 5l7 7-7 7" })
143
260
  });
144
261
  }
145
- function T(...e) {
262
+ function P(...e) {
146
263
  return e.filter(Boolean).join(" ");
147
264
  }
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) {
265
+ function F({ images: e, index: i, onIndexChange: s, renderActions: f, renderCaption: p, renderControls: m, showCounter: h = !0, showNavigation: g = !0, className: _ }) {
266
+ let v = a(null), y = a(s), b = a(null), x = a(null), S = a(null), C = a(!1), A = a(!1), F = a({}), I = a(e), L = a(null), R = r(), [z, B] = o(!1), [V, H] = o(null), U = i !== null && i >= 0 && i < e.length ? i : null, W = U === null ? void 0 : e[U];
267
+ y.current = s, S.current = U, C.current = z, I.current = e;
268
+ let G = t(() => {
269
+ b.current !== null && (window.clearTimeout(b.current), b.current = null), x.current = null;
270
+ }, []), K = t(() => {
271
+ let e = S.current;
272
+ if (e !== null && x.current === null) {
156
273
  if (typeof window < "u" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
157
- O.current(null);
274
+ y.current(null);
158
275
  return;
159
276
  }
160
- A.current = { index: e }, M.current = !0, z(!0);
277
+ x.current = { index: e }, C.current = !0, B(!0);
161
278
  }
162
- }, []), K = t(() => {
163
- let e = j.current;
164
- !M.current && e !== null && e > 0 && O.current(e - 1);
165
279
  }, []), q = t(() => {
166
- let e = j.current;
167
- !M.current && e !== null && e < F.current.length - 1 && O.current(e + 1);
280
+ let e = S.current;
281
+ !C.current && e !== null && e > 0 && y.current(e - 1);
168
282
  }, []), J = t(() => {
169
- let e = j.current;
170
- M.current || e === null || e === 0 || (V("previous"), O.current(e - 1));
283
+ let e = S.current;
284
+ !C.current && e !== null && e < I.current.length - 1 && y.current(e + 1);
171
285
  }, []), 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;
286
+ let e = S.current;
287
+ C.current || e === null || e === 0 || (H("previous"), y.current(e - 1));
288
+ }, []), X = t(() => {
289
+ let e = S.current;
290
+ C.current || e === null || e >= I.current.length - 1 || (H("next"), y.current(e + 1));
291
+ }, []), ee = t((e) => {
292
+ e.preventDefault(), K();
293
+ }, [K]), Z = t(() => {
294
+ let e = x.current;
178
295
  if (e) {
179
- if (j.current !== e.index) {
180
- W(), M.current = !1, z(!1);
296
+ if (S.current !== e.index) {
297
+ G(), C.current = !1, B(!1);
181
298
  return;
182
299
  }
183
- W(), M.current = !1, z(!1), O.current(null);
300
+ G(), C.current = !1, B(!1), y.current(null);
184
301
  }
185
- }, [W]), ne = t((e) => {
302
+ }, [G]), te = t((e) => {
186
303
  if (e.defaultPrevented) return;
187
304
  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;
305
+ t instanceof HTMLElement && (t.isContentEditable || t.closest(w)) || (e.key === "ArrowLeft" ? (e.preventDefault(), q()) : e.key === "ArrowRight" && (e.preventDefault(), J()));
306
+ }, [J, q]), ne = t((e) => {
307
+ if (e.touches.length !== 1 || D(e.target)) {
308
+ L.current = null;
192
309
  return;
193
310
  }
194
311
  let t = e.touches[0];
195
- I.current = {
312
+ L.current = {
196
313
  x: t.clientX,
197
314
  y: t.clientY
198
315
  };
199
- }, []), ie = t((e) => {
200
- let t = I.current;
201
- if (I.current = null, !t || e.changedTouches.length === 0) return;
316
+ }, []), re = t((e) => {
317
+ let t = L.current;
318
+ if (L.current = null, !t || e.changedTouches.length === 0) return;
202
319
  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;
320
+ a < 48 || a <= Math.abs(i) || (r < 0 ? X() : Y());
321
+ }, [X, Y]), ie = t(() => {
322
+ L.current = null;
323
+ }, []), ae = t((e) => {
324
+ e.target === e.currentTarget && H(null);
206
325
  }, []), 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]);
326
+ e.target === e.currentTarget && e.animationName === "react-pic-gallery-fade-out" && Z();
327
+ }, [Z]), se = t(() => {
328
+ A.current || (G(), C.current = !1, B(!1), S.current !== null && y.current(null));
329
+ }, [G]);
213
330
  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);
331
+ let e = x.current;
332
+ e && U !== e.index && (G(), C.current = !1, B(!1));
333
+ }, [U, G]), n(() => {
334
+ if (!z || !x.current || !v.current) return;
335
+ let e = j(v.current);
336
+ return b.current = window.setTimeout(Z, e), () => {
337
+ b.current !== null && (window.clearTimeout(b.current), b.current = null);
221
338
  };
222
- }, [X, R]), n(() => {
223
- if (H === null) return;
224
- let e = D.current;
339
+ }, [Z, z]), n(() => {
340
+ if (U === null) return;
341
+ let e = v.current;
225
342
  if (!e) return;
226
343
  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();
344
+ return O(F.current), e.open || e.showModal(), () => {
345
+ G(), A.current = !0, e.open && e.close(), A.current = !1, k(F.current) && t?.isConnected && t.focus();
229
346
  };
230
- }, [H !== null, W]), H === null || !U || typeof document > "u") return null;
231
- let Z = {
232
- image: U,
233
- index: H,
347
+ }, [U !== null, G]), U === null || !W || typeof document > "u") return null;
348
+ let Q = {
349
+ image: W,
350
+ index: U,
234
351
  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();
352
+ close: K,
353
+ next: J,
354
+ previous: q,
355
+ canGoNext: U < e.length - 1,
356
+ canGoPrevious: U > 0
357
+ }, ce = g && !m && Q.canGoPrevious, le = g && !m && Q.canGoNext, $ = g && !m && e.length > 1, ue = (e) => {
358
+ e.target === e.currentTarget && K();
242
359
  };
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,
360
+ return u(/* @__PURE__ */ l("dialog", {
361
+ ref: v,
362
+ className: P("react-pic-gallery__lightbox", z ? "react-pic-gallery__lightbox--closing" : void 0, _),
363
+ "aria-labelledby": R,
364
+ onAnimationEnd: oe,
365
+ onClose: se,
366
+ onMouseDown: ue,
367
+ onCancel: ee,
368
+ onKeyDown: te,
252
369
  children: [
253
- /* @__PURE__ */ o("h2", {
370
+ /* @__PURE__ */ c("h2", {
254
371
  className: "react-pic-gallery__sr-only",
255
- id: L,
372
+ id: R,
256
373
  children: "Image viewer"
257
374
  }),
258
- /* @__PURE__ */ s("p", {
375
+ /* @__PURE__ */ l("p", {
259
376
  className: "react-pic-gallery__sr-only",
260
377
  "aria-live": "polite",
261
378
  children: [
262
- U.alt,
379
+ W.alt,
263
380
  ", image ",
264
- H + 1,
381
+ U + 1,
265
382
  " of ",
266
383
  e.length
267
384
  ]
268
385
  }),
269
- /* @__PURE__ */ s("div", {
386
+ /* @__PURE__ */ l("div", {
270
387
  className: "react-pic-gallery__lightbox-content",
271
388
  children: [
272
- m ? /* @__PURE__ */ o("div", {
389
+ m ? /* @__PURE__ */ c("div", {
273
390
  className: "react-pic-gallery__custom-controls",
274
- children: m(Z)
275
- }) : /* @__PURE__ */ s("div", {
391
+ children: m(Q)
392
+ }) : /* @__PURE__ */ l("div", {
276
393
  className: "react-pic-gallery__toolbar",
277
- children: [/* @__PURE__ */ s("div", {
394
+ children: [/* @__PURE__ */ l("div", {
278
395
  className: "react-pic-gallery__toolbar-actions",
279
- children: [h && /* @__PURE__ */ s("span", {
396
+ children: [h && /* @__PURE__ */ l("span", {
280
397
  "aria-live": "polite",
281
398
  children: [
282
- H + 1,
399
+ U + 1,
283
400
  " / ",
284
401
  e.length
285
402
  ]
286
- }), f?.(Z)]
287
- }), /* @__PURE__ */ o("button", {
403
+ }), f?.(Q)]
404
+ }), /* @__PURE__ */ c("button", {
288
405
  className: "react-pic-gallery__control",
289
406
  type: "button",
290
- onClick: G,
407
+ onClick: K,
291
408
  "aria-label": "Close image viewer",
292
- children: /* @__PURE__ */ o(C, {})
409
+ children: /* @__PURE__ */ c(M, {})
293
410
  })]
294
411
  }),
295
- /* @__PURE__ */ s("div", {
296
- className: T("react-pic-gallery__viewport", Q ? "react-pic-gallery__viewport--has-navigation" : void 0),
297
- onMouseDown: $,
412
+ /* @__PURE__ */ l("div", {
413
+ className: P("react-pic-gallery__viewport", $ ? "react-pic-gallery__viewport--has-navigation" : void 0),
414
+ onMouseDown: ue,
298
415
  onClick: (e) => {
299
- if (!Q || M.current || !(e.target instanceof Element) || e.target.closest("button") || window.matchMedia?.(_)?.matches !== !0) return;
416
+ if (!$ || C.current || !(e.target instanceof Element) || e.target.closest("button") || window.matchMedia?.(T)?.matches !== !0) return;
300
417
  let t = e.currentTarget.getBoundingClientRect();
301
418
  if (t.width <= 0) return;
302
419
  let n = (e.clientX - t.left) / t.width;
303
- n < v ? K() : n > .7 && q();
420
+ n < E ? q() : n > .7 && J();
304
421
  },
305
- onTouchCancel: ae,
306
- onTouchEnd: ie,
307
- onTouchStart: re,
422
+ onTouchCancel: ie,
423
+ onTouchEnd: re,
424
+ onTouchStart: ne,
308
425
  children: [
309
- le && /* @__PURE__ */ o("button", {
426
+ ce && /* @__PURE__ */ c("button", {
310
427
  className: "react-pic-gallery__control react-pic-gallery__control--previous",
311
428
  type: "button",
312
- onClick: K,
429
+ onClick: q,
313
430
  "aria-label": "Previous image",
314
- children: /* @__PURE__ */ o(w, { direction: "left" })
431
+ children: /* @__PURE__ */ c(N, { direction: "left" })
315
432
  }),
316
- /* @__PURE__ */ o(l, {
317
- src: U.src,
318
- alt: U.alt,
433
+ /* @__PURE__ */ c(d, {
434
+ src: W.src,
435
+ alt: W.alt,
319
436
  eager: !0,
320
437
  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", {
438
+ className: P("react-pic-gallery__lightbox-image", V ? `react-pic-gallery__lightbox-image--swipe-${V}` : void 0),
439
+ onAnimationEnd: ae
440
+ }, W.src),
441
+ le && /* @__PURE__ */ c("button", {
325
442
  className: "react-pic-gallery__control react-pic-gallery__control--next",
326
443
  type: "button",
327
- onClick: q,
444
+ onClick: J,
328
445
  "aria-label": "Next image",
329
- children: /* @__PURE__ */ o(w, { direction: "right" })
446
+ children: /* @__PURE__ */ c(N, { direction: "right" })
330
447
  })
331
448
  ]
332
449
  }),
333
- (p || U.caption) && /* @__PURE__ */ o("div", {
450
+ (p || W.caption) && /* @__PURE__ */ c("div", {
334
451
  className: "react-pic-gallery__caption",
335
- children: p ? p(Z) : U.caption
452
+ children: p ? p(Q) : W.caption
336
453
  })
337
454
  ]
338
455
  })
@@ -341,28 +458,29 @@ function E({ images: e, index: u, onIndexChange: d, renderActions: f, renderCapt
341
458
  }
342
459
  //#endregion
343
460
  //#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, {
461
+ 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 }) {
462
+ let [h, g] = o(null);
463
+ return /* @__PURE__ */ l("div", {
464
+ className: `react-pic-gallery ${i ?? ""}`.trim(),
465
+ style: s,
466
+ children: [/* @__PURE__ */ c(b, {
350
467
  images: e,
351
- columns: t,
352
- rowHeight: n,
353
- className: i,
468
+ layout: t,
469
+ columns: n,
470
+ rowHeight: r,
471
+ className: a,
354
472
  onImageClick: g
355
- }), /* @__PURE__ */ o(E, {
473
+ }), /* @__PURE__ */ c(F, {
356
474
  images: e,
357
475
  index: h,
358
476
  onIndexChange: g,
359
- renderActions: l,
360
- renderCaption: u,
361
- renderControls: d,
477
+ renderActions: u,
478
+ renderCaption: d,
479
+ renderControls: f,
362
480
  showCounter: p,
363
481
  showNavigation: m
364
482
  })]
365
483
  });
366
484
  }
367
485
  //#endregion
368
- export { f as Gallery, E as Lightbox, D as PicGallery, D as default };
486
+ export { b 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__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__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:#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,.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{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,.18s) ease, opacity var(--gallery-motion,.18s) 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,#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}: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';
11
12
  export type LightboxContext<T extends GalleryImage = GalleryImage> = {
12
13
  image: T;
13
14
  index: number;
@@ -22,6 +23,7 @@ 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'];
27
29
  className?: string;
@@ -40,6 +42,7 @@ export type LightboxProps<T extends GalleryImage = GalleryImage> = {
40
42
  };
41
43
  export type PicGalleryProps<T extends GalleryImage = GalleryImage> = {
42
44
  images: readonly T[];
45
+ layout?: GalleryLayout;
43
46
  columns?: number;
44
47
  rowHeight?: CSSProperties['height'];
45
48
  className?: 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.2",
4
4
  "description": "Small, accessible React image gallery and lightbox.",
5
5
  "author": "marcelrsoub",
6
6
  "license": "MIT",