react-pic-gallery 2.0.0 → 2.0.1

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
@@ -20,7 +20,16 @@ bun add react-pic-gallery
20
20
 
21
21
  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
22
 
23
- The package is intentionally lightweight: it has no runtime dependencies, React is a peer dependency, and the built JavaScript and CSS together are roughly 6 KB gzipped.
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).
24
+
25
+ ## Why react-pic-gallery
26
+
27
+ - **Tiny by design** — no runtime dependencies, no context providers, no polyfills; ~5.9 KB gzipped total.
28
+ - **Simple by default** — one component, one stylesheet import, sensible accessible defaults.
29
+ - **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
+ - **Accessible** — native modal `<dialog>`, focus containment and restoration, Escape to close, screen-reader announcements, `prefers-reduced-motion` support.
31
+ - **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
+ - **Themeable** — namespaced classes and CSS variables (`--gallery-accent`, `--gallery-overlay`, `--gallery-motion`, …).
24
33
 
25
34
  ## Quick start
26
35
 
@@ -104,9 +113,11 @@ The lightbox uses the native modal `<dialog>` element and includes:
104
113
  - Backdrop closing with stable page width while scrolling is locked.
105
114
  - Native lazy loading for thumbnails and explicit image loading/error states.
106
115
  - Horizontal swipe navigation on touch devices.
107
- - Pinch zoom up to 3x with one-finger panning while zoomed.
116
+ - 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).
108
117
  - Reduced-motion support, safe-area padding, rounded image surfaces, and animated transitions.
109
118
 
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.
120
+
110
121
  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.
111
122
 
112
123
  ## Styling
@@ -45,21 +45,49 @@ function l({ src: e, alt: t, className: r = "", imageClassName: c = "", style: l
45
45
  }
46
46
  //#endregion
47
47
  //#region src/lib/Gallery.tsx
48
- function u({ images: e, onImageClick: t, columns: n, rowHeight: r, className: i = "", style: a }) {
49
- let s = typeof r == "number" ? `${r}px` : r, c = {
50
- ...a,
51
- ...n ? { gridTemplateColumns: `repeat(${n}, minmax(0, 1fr))` } : {},
52
- ...s ? { "--gallery-row-height": s } : {}
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;
64
+ else return;
65
+ r !== null && r !== t && (e.preventDefault(), v(r));
66
+ }, [
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 } : {}
53
74
  };
54
75
  return /* @__PURE__ */ o("div", {
55
- className: `react-pic-gallery__grid ${i}`.trim(),
56
- style: c,
76
+ className: `react-pic-gallery__grid ${c}`.trim(),
77
+ style: x,
57
78
  "data-testid": "react-pic-gallery-grid",
58
- children: e.map((e, n) => /* @__PURE__ */ o("button", {
79
+ children: e.map((e, t) => /* @__PURE__ */ o("button", {
59
80
  className: "react-pic-gallery__tile",
60
81
  type: "button",
61
- disabled: !t,
62
- onClick: () => t?.(n),
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
+ },
63
91
  "aria-label": `Open ${e.alt}`,
64
92
  children: /* @__PURE__ */ o(l, {
65
93
  src: e.thumbnailSrc ?? e.src,
@@ -68,40 +96,37 @@ function u({ images: e, onImageClick: t, columns: n, rowHeight: r, className: i
68
96
  width: e.width,
69
97
  height: e.height
70
98
  })
71
- }, e.id ?? `${e.src}-${n}`))
99
+ }, e.id ?? `${e.src}-${t}`))
72
100
  });
73
101
  }
74
- var d = e(u), f = /* @__PURE__ */ new Set(), p = null, m = "a[href], button, input, select, textarea, [role=\"combobox\"], [role=\"menu\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"tab\"]", h = ".react-pic-gallery__image-element", g = 3;
75
- function _(e) {
76
- return e instanceof Element && e.closest(m) !== null;
77
- }
78
- function v(e, t, n) {
79
- return Math.min(Math.max(e, t), n);
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;
80
105
  }
81
- function ee(e) {
82
- if (!f.has(e)) {
83
- if (f.size === 0) {
106
+ function b(e) {
107
+ if (!p.has(e)) {
108
+ if (p.size === 0) {
84
109
  let e = document.body, t = window.innerWidth - document.documentElement.clientWidth, n = Number.parseFloat(window.getComputedStyle(e).paddingRight) || 0;
85
- p = {
110
+ m = {
86
111
  overflow: e.style.overflow,
87
112
  paddingRight: e.style.paddingRight
88
113
  }, e.style.overflow = "hidden", t > 0 && (e.style.paddingRight = `${n + t}px`);
89
114
  }
90
- f.add(e);
115
+ p.add(e);
91
116
  }
92
117
  }
93
- function te(e) {
94
- return f.delete(e), f.size > 0 || !p ? !1 : (document.body.style.overflow = p.overflow, document.body.style.paddingRight = p.paddingRight, p = null, !0);
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);
95
120
  }
96
- function y(e) {
121
+ function S(e) {
97
122
  let t = e.trim();
98
123
  return t.endsWith("ms") ? Number.parseFloat(t) : t.endsWith("s") ? Number.parseFloat(t) * 1e3 : 0;
99
124
  }
100
- function ne(e) {
125
+ function ee(e) {
101
126
  let t = window.getComputedStyle(e), n = t.animationDuration.split(","), r = t.animationDelay.split(",");
102
- return Math.max(...n.map((e, t) => y(e) + y(r[t % r.length] ?? "0s")), 0);
127
+ return Math.max(...n.map((e, t) => S(e) + S(r[t % r.length] ?? "0s")), 0);
103
128
  }
104
- function re() {
129
+ function C() {
105
130
  return /* @__PURE__ */ o("svg", {
106
131
  viewBox: "0 0 24 24",
107
132
  "aria-hidden": "true",
@@ -109,7 +134,7 @@ function re() {
109
134
  children: /* @__PURE__ */ o("path", { d: "M6 6l12 12M18 6L6 18" })
110
135
  });
111
136
  }
112
- function b({ direction: e }) {
137
+ function w({ direction: e }) {
113
138
  return /* @__PURE__ */ o("svg", {
114
139
  viewBox: "0 0 24 24",
115
140
  "aria-hidden": "true",
@@ -117,171 +142,113 @@ function b({ direction: e }) {
117
142
  children: /* @__PURE__ */ o("path", { d: e === "left" ? "M14.5 5l-7 7 7 7" : "M9.5 5l7 7-7 7" })
118
143
  });
119
144
  }
120
- function x(...e) {
145
+ function T(...e) {
121
146
  return e.filter(Boolean).join(" ");
122
147
  }
123
- function S({ images: e, index: u, onIndexChange: d, renderActions: f, renderCaption: p, renderControls: y, showCounter: S = !0, showNavigation: C = !0, className: ie }) {
124
- let w = i(null), T = i(d), E = i(null), D = i(null), O = i(null), k = i(!1), A = i(!1), j = i({}), M = i(e), N = i(null), P = i(/* @__PURE__ */ new Map()), F = i(null), I = i(null), L = r(), [R, z] = a(!1), [B, V] = a(null), [H, U] = a(1), [W, G] = a({
125
- x: 0,
126
- y: 0
127
- }), [ae, oe] = a("50% 50%"), K = u !== null && u >= 0 && u < e.length ? u : null, q = K === null ? void 0 : e[K];
128
- T.current = d, O.current = K, k.current = R, M.current = e;
129
- let J = t(() => {
130
- E.current !== null && (window.clearTimeout(E.current), E.current = null), D.current = null;
131
- }, []), Y = t(() => {
132
- let e = O.current;
133
- if (e !== null && D.current === null) {
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) {
134
156
  if (typeof window < "u" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
135
- T.current(null);
157
+ O.current(null);
136
158
  return;
137
159
  }
138
- D.current = { index: e }, k.current = !0, z(!0);
160
+ A.current = { index: e }, M.current = !0, z(!0);
139
161
  }
140
- }, []), X = t(() => {
141
- let e = O.current;
142
- !k.current && e !== null && e > 0 && T.current(e - 1);
143
- }, []), Z = t(() => {
144
- let e = O.current;
145
- !k.current && e !== null && e < M.current.length - 1 && T.current(e + 1);
146
- }, []), se = t(() => {
147
- let e = O.current;
148
- k.current || e === null || e === 0 || (V("previous"), T.current(e - 1));
149
- }, []), ce = t(() => {
150
- let e = O.current;
151
- k.current || e === null || e >= M.current.length - 1 || (V("next"), T.current(e + 1));
152
- }, []), le = t((e) => {
153
- e.preventDefault(), Y();
154
- }, [Y]), Q = t(() => {
155
- let e = D.current;
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;
156
178
  if (e) {
157
- if (O.current !== e.index) {
158
- J(), k.current = !1, z(!1);
179
+ if (j.current !== e.index) {
180
+ W(), M.current = !1, z(!1);
159
181
  return;
160
182
  }
161
- J(), k.current = !1, z(!1), T.current(null);
183
+ W(), M.current = !1, z(!1), O.current(null);
162
184
  }
163
- }, [J]), ue = t((e) => {
185
+ }, [W]), ne = t((e) => {
164
186
  if (e.defaultPrevented) return;
165
187
  let t = e.target;
166
- t instanceof HTMLElement && (t.isContentEditable || t.closest(m)) || (e.key === "ArrowLeft" ? (e.preventDefault(), X()) : e.key === "ArrowRight" && (e.preventDefault(), Z()));
167
- }, [Z, X]), de = t((e) => {
168
- if (H > 1 || e.touches.length !== 1 || _(e.target)) {
169
- N.current = null;
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;
170
192
  return;
171
193
  }
172
194
  let t = e.touches[0];
173
- N.current = {
195
+ I.current = {
174
196
  x: t.clientX,
175
197
  y: t.clientY
176
198
  };
177
- }, [H]), fe = t((e) => {
178
- let t = N.current;
179
- if (N.current = null, !t || e.changedTouches.length === 0) return;
199
+ }, []), ie = t((e) => {
200
+ let t = I.current;
201
+ if (I.current = null, !t || e.changedTouches.length === 0) return;
180
202
  let n = e.changedTouches[0], r = n.clientX - t.x, i = n.clientY - t.y, a = Math.abs(r);
181
- a < 48 || a <= Math.abs(i) || (r < 0 ? ce() : se());
182
- }, [
183
- ce,
184
- se,
185
- H
186
- ]), pe = t(() => {
187
- N.current = null;
188
- }, []), me = t((e) => {
189
- if (!(e.target instanceof Element) || e.target.closest(h) === null || e.pointerType === "mouse" && e.button !== 0) return;
190
- let t = P.current;
191
- if (t.set(e.pointerId, {
192
- x: e.clientX,
193
- y: e.clientY
194
- }), e.currentTarget.setPointerCapture?.(e.pointerId), t.size === 2) {
195
- let [n, r] = [...t.values()], i = Math.hypot(r.x - n.x, r.y - n.y), a = e.currentTarget.querySelector(h)?.getBoundingClientRect(), o = (n.x + r.x) / 2, s = (n.y + r.y) / 2;
196
- a && a.width > 0 && a.height > 0 && oe(`${v((o - a.left) / a.width * 100, 0, 100)}% ${v((s - a.top) / a.height * 100, 0, 100)}%`), F.current = {
197
- distance: Math.max(i, 1),
198
- scale: H,
199
- offset: W
200
- }, I.current = null;
201
- } else H > 1 && (I.current = {
202
- x: e.clientX,
203
- y: e.clientY,
204
- offset: W
205
- });
206
- }, [W, H]), he = t((e) => {
207
- let t = P.current;
208
- if (t.has(e.pointerId)) {
209
- if (t.set(e.pointerId, {
210
- x: e.clientX,
211
- y: e.clientY
212
- }), t.size >= 2 && F.current) {
213
- let [n, r] = [...t.values()], i = Math.max(Math.hypot(r.x - n.x, r.y - n.y), 1), a = v(F.current.scale * (i / F.current.distance), 1, g);
214
- U(a), G(F.current.offset), e.preventDefault();
215
- return;
216
- }
217
- if (t.size === 1 && I.current && H > 1) {
218
- let t = e.currentTarget.querySelector(h), n = t?.clientWidth ?? 0, r = t?.clientHeight ?? 0, i = Math.max(0, n * (H - 1) / 2), a = Math.max(0, r * (H - 1) / 2), o = I.current.offset.x + e.clientX - I.current.x, s = I.current.offset.y + e.clientY - I.current.y;
219
- G({
220
- x: v(o, -i, i),
221
- y: v(s, -a, a)
222
- }), e.preventDefault();
223
- }
224
- }
225
- }, [H]), ge = t((e) => {
226
- if (P.current.delete(e.pointerId), e.currentTarget.hasPointerCapture?.(e.pointerId) && e.currentTarget.releasePointerCapture(e.pointerId), P.current.size < 2 && (F.current = null), P.current.size === 1 && H > 1) {
227
- let [e] = [...P.current.values()];
228
- I.current = {
229
- x: e.x,
230
- y: e.y,
231
- offset: W
232
- };
233
- } else I.current = null;
234
- }, [W, H]), _e = t((e) => {
203
+ a < 48 || a <= Math.abs(i) || (r < 0 ? Y() : J());
204
+ }, [Y, J]), ae = t(() => {
205
+ I.current = null;
206
+ }, []), oe = t((e) => {
235
207
  e.target === e.currentTarget && V(null);
236
- }, []), ve = t((e) => {
237
- e.target === e.currentTarget && e.animationName === "react-pic-gallery-fade-out" && Q();
238
- }, [Q]), ye = t(() => {
239
- A.current || (J(), k.current = !1, z(!1), O.current !== null && T.current(null));
240
- }, [J]);
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]);
241
213
  if (n(() => {
242
- U(1), G({
243
- x: 0,
244
- y: 0
245
- }), oe("50% 50%"), P.current.clear(), F.current = null, I.current = null;
246
- }, [K]), n(() => {
247
- let e = D.current;
248
- e && K !== e.index && (J(), k.current = !1, z(!1));
249
- }, [K, J]), n(() => {
250
- if (!R || !D.current || !w.current) return;
251
- let e = ne(w.current);
252
- return E.current = window.setTimeout(Q, e), () => {
253
- E.current !== null && (window.clearTimeout(E.current), E.current = null);
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);
254
221
  };
255
- }, [Q, R]), n(() => {
256
- if (K === null) return;
257
- let e = w.current;
222
+ }, [X, R]), n(() => {
223
+ if (H === null) return;
224
+ let e = D.current;
258
225
  if (!e) return;
259
226
  let t = document.activeElement;
260
- return ee(j.current), e.open || e.showModal(), () => {
261
- J(), A.current = !0, e.open && e.close(), A.current = !1, te(j.current) && t?.isConnected && t.focus();
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();
262
229
  };
263
- }, [K !== null, J]), K === null || !q || typeof document > "u") return null;
264
- let $ = {
265
- image: q,
266
- index: K,
230
+ }, [H !== null, W]), H === null || !U || typeof document > "u") return null;
231
+ let Z = {
232
+ image: U,
233
+ index: H,
267
234
  count: e.length,
268
- close: Y,
269
- next: Z,
270
- previous: X,
271
- canGoNext: K < e.length - 1,
272
- canGoPrevious: K > 0
273
- }, be = C && !y && $.canGoPrevious, xe = C && !y && $.canGoNext, Se = C && !y && e.length > 1, Ce = (e) => {
274
- e.target === e.currentTarget && Y();
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();
275
242
  };
276
243
  return c(/* @__PURE__ */ s("dialog", {
277
- ref: w,
278
- className: x("react-pic-gallery__lightbox", R ? "react-pic-gallery__lightbox--closing" : void 0, ie),
244
+ ref: D,
245
+ className: T("react-pic-gallery__lightbox", R ? "react-pic-gallery__lightbox--closing" : void 0, E),
279
246
  "aria-labelledby": L,
280
- onAnimationEnd: ve,
281
- onClose: ye,
282
- onMouseDown: Ce,
283
- onCancel: le,
284
- onKeyDown: ue,
247
+ onAnimationEnd: se,
248
+ onClose: ce,
249
+ onMouseDown: $,
250
+ onCancel: te,
251
+ onKeyDown: ne,
285
252
  children: [
286
253
  /* @__PURE__ */ o("h2", {
287
254
  className: "react-pic-gallery__sr-only",
@@ -292,9 +259,9 @@ function S({ images: e, index: u, onIndexChange: d, renderActions: f, renderCapt
292
259
  className: "react-pic-gallery__sr-only",
293
260
  "aria-live": "polite",
294
261
  children: [
295
- q.alt,
262
+ U.alt,
296
263
  ", image ",
297
- K + 1,
264
+ H + 1,
298
265
  " of ",
299
266
  e.length
300
267
  ]
@@ -302,74 +269,70 @@ function S({ images: e, index: u, onIndexChange: d, renderActions: f, renderCapt
302
269
  /* @__PURE__ */ s("div", {
303
270
  className: "react-pic-gallery__lightbox-content",
304
271
  children: [
305
- y ? /* @__PURE__ */ o("div", {
272
+ m ? /* @__PURE__ */ o("div", {
306
273
  className: "react-pic-gallery__custom-controls",
307
- children: y($)
274
+ children: m(Z)
308
275
  }) : /* @__PURE__ */ s("div", {
309
276
  className: "react-pic-gallery__toolbar",
310
277
  children: [/* @__PURE__ */ s("div", {
311
278
  className: "react-pic-gallery__toolbar-actions",
312
- children: [S && /* @__PURE__ */ s("span", {
279
+ children: [h && /* @__PURE__ */ s("span", {
313
280
  "aria-live": "polite",
314
281
  children: [
315
- K + 1,
282
+ H + 1,
316
283
  " / ",
317
284
  e.length
318
285
  ]
319
- }), f?.($)]
286
+ }), f?.(Z)]
320
287
  }), /* @__PURE__ */ o("button", {
321
288
  className: "react-pic-gallery__control",
322
289
  type: "button",
323
- onClick: Y,
290
+ onClick: G,
324
291
  "aria-label": "Close image viewer",
325
- children: /* @__PURE__ */ o(re, {})
292
+ children: /* @__PURE__ */ o(C, {})
326
293
  })]
327
294
  }),
328
295
  /* @__PURE__ */ s("div", {
329
- className: x("react-pic-gallery__viewport", Se ? "react-pic-gallery__viewport--has-navigation" : void 0),
330
- onMouseDown: Ce,
331
- onPointerCancel: ge,
332
- onPointerDown: me,
333
- onPointerMove: he,
334
- onPointerUp: ge,
335
- onTouchCancel: pe,
336
- onTouchEnd: fe,
337
- onTouchStart: de,
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,
338
308
  children: [
339
- be && /* @__PURE__ */ o("button", {
309
+ le && /* @__PURE__ */ o("button", {
340
310
  className: "react-pic-gallery__control react-pic-gallery__control--previous",
341
311
  type: "button",
342
- onClick: X,
312
+ onClick: K,
343
313
  "aria-label": "Previous image",
344
- children: /* @__PURE__ */ o(b, { direction: "left" })
314
+ children: /* @__PURE__ */ o(w, { direction: "left" })
345
315
  }),
346
316
  /* @__PURE__ */ o(l, {
347
- src: q.src,
348
- alt: q.alt,
317
+ src: U.src,
318
+ alt: U.alt,
349
319
  eager: !0,
350
320
  objectFit: "contain",
351
- width: q.width,
352
- height: q.height,
353
- className: x("react-pic-gallery__lightbox-image", B ? `react-pic-gallery__lightbox-image--swipe-${B}` : void 0),
354
- imageStyle: {
355
- transform: `translate3d(${W.x}px, ${W.y}px, 0) scale(${H})`,
356
- transformOrigin: ae,
357
- transition: P.current.size > 0 ? "none" : void 0
358
- },
359
- onAnimationEnd: _e
360
- }, q.src),
361
- xe && /* @__PURE__ */ o("button", {
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", {
362
325
  className: "react-pic-gallery__control react-pic-gallery__control--next",
363
326
  type: "button",
364
- onClick: Z,
327
+ onClick: q,
365
328
  "aria-label": "Next image",
366
- children: /* @__PURE__ */ o(b, { direction: "right" })
329
+ children: /* @__PURE__ */ o(w, { direction: "right" })
367
330
  })
368
331
  ]
369
332
  }),
370
- (p || q.caption) && /* @__PURE__ */ o("div", {
333
+ (p || U.caption) && /* @__PURE__ */ o("div", {
371
334
  className: "react-pic-gallery__caption",
372
- children: p ? p($) : q.caption
335
+ children: p ? p(Z) : U.caption
373
336
  })
374
337
  ]
375
338
  })
@@ -378,28 +341,28 @@ function S({ images: e, index: u, onIndexChange: d, renderActions: f, renderCapt
378
341
  }
379
342
  //#endregion
380
343
  //#region src/lib/PicGallery.tsx
381
- function C({ images: e, columns: t, rowHeight: n, className: r, galleryClassName: i, style: c, renderActions: l, renderCaption: u, renderControls: f, showCounter: p, showNavigation: m }) {
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 }) {
382
345
  let [h, g] = a(null);
383
346
  return /* @__PURE__ */ s("div", {
384
347
  className: `react-pic-gallery ${r ?? ""}`.trim(),
385
348
  style: c,
386
- children: [/* @__PURE__ */ o(d, {
349
+ children: [/* @__PURE__ */ o(f, {
387
350
  images: e,
388
351
  columns: t,
389
352
  rowHeight: n,
390
353
  className: i,
391
354
  onImageClick: g
392
- }), /* @__PURE__ */ o(S, {
355
+ }), /* @__PURE__ */ o(E, {
393
356
  images: e,
394
357
  index: h,
395
358
  onIndexChange: g,
396
359
  renderActions: l,
397
360
  renderCaption: u,
398
- renderControls: f,
361
+ renderControls: d,
399
362
  showCounter: p,
400
363
  showNavigation: m
401
364
  })]
402
365
  });
403
366
  }
404
367
  //#endregion
405
- export { d as Gallery, S as Lightbox, C as PicGallery, C as default };
368
+ export { f as Gallery, E as Lightbox, D as PicGallery, D 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:100%;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{width:min(100%,90rem);height:min(100%,54rem);min-height:0;animation:react-pic-gallery-rise-in var(--gallery-motion) ease both;grid-template-rows:auto minmax(0,1fr) auto;display:grid}.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;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;transform:translateY(-1px)}.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;place-items:center;min-height:0;padding:.75rem;display:grid;position:relative}.react-pic-gallery__viewport--has-navigation{padding-inline:3.5rem}.react-pic-gallery__lightbox-image{pointer-events:none;border-radius:var(--gallery-radius);width:min(100%,82rem);min-width:0;height:100%;min-height:0;animation:react-pic-gallery-image-in var(--gallery-motion) ease both;background:0 0;place-items:center;display:grid;overflow:visible}.react-pic-gallery__lightbox-image .react-pic-gallery__image-element{pointer-events:auto;touch-action:none;border-radius:inherit;object-fit:contain;background:0 0;width:auto;max-width:100%;height:auto;max-height:100%}.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:hover,.react-pic-gallery__control--next:hover{transform:translateY(calc(-50% - 1px))}.react-pic-gallery__control--previous{left:0}.react-pic-gallery__control--next{right:0}.react-pic-gallery__caption{color:#cbd5e1;text-align:center;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}.react-pic-gallery__lightbox-content{height:100%}.react-pic-gallery__viewport{padding-inline:.25rem}.react-pic-gallery__viewport--has-navigation{padding-inline:2.75rem}.react-pic-gallery__control--previous{left:-.25rem}.react-pic-gallery__control--next{right:-.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__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}}
2
2
  /*$vite$:1*/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-pic-gallery",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Small, accessible React image gallery and lightbox.",
5
5
  "author": "marcelrsoub",
6
6
  "license": "MIT",
@@ -36,7 +36,7 @@
36
36
  "dev:docs": "astro dev --config docs-site/astro.config.mjs",
37
37
  "build:docs": "astro build --config docs-site/astro.config.mjs",
38
38
  "preview:docs": "astro preview --config docs-site/astro.config.mjs",
39
- "github-pages": "npm run build:docs && gh-pages -d build",
39
+ "github-pages": "npm run build:docs && gh-pages -d build --dotfiles",
40
40
  "prepack": "npm run build"
41
41
  },
42
42
  "devDependencies": {