uix-image-magnify 0.2.1 → 0.2.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 +518 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
# uix-image-magnify
|
|
2
|
+
|
|
3
|
+
**Fast, accessible, SSR-safe image magnifier and product gallery for React & Next.js.**
|
|
4
|
+
|
|
5
|
+
A lightweight, fully-typed React component set for zooming images with a lens, a side-by-side pane, inner pan-and-zoom, or a follow-style magnifier. Ships with a product-style gallery (thumbnails + arrows + counter), a headless slider hook, and SSR-safe variants that prevent hydration mismatches in Next.js App Router, Remix, etc.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🔍 **4 zoom modes** — `lens`, `side-by-side`, `inner`, `follow`
|
|
12
|
+
- 🖱️ **6 zoom triggers** — `hover`, `click`, `double-click`, `hold`, `wheel`, `none`
|
|
13
|
+
- 🖼️ **Product gallery** with thumbnails, arrows, counter, and loop
|
|
14
|
+
- ♿ **Accessible** — keyboard nav, ARIA roles, screen-reader live region
|
|
15
|
+
- 🧠 **SSR-safe** — dedicated `/ssr` entry point eliminates hydration mismatches
|
|
16
|
+
- 📱 **Responsive** — zoom pane automatically moves below the image on mobile
|
|
17
|
+
- 🎨 **Customizable** — every part is styleable; arrows accept custom React nodes
|
|
18
|
+
- 🪝 **Headless hooks** — `useImageMagnify`, `useImageSlider`
|
|
19
|
+
- 🧩 **TypeScript-first** with strict prop types
|
|
20
|
+
- 🌲 **Tree-shakeable** — `sideEffects: false`, ESM + CJS builds
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install uix-image-magnify
|
|
28
|
+
# or
|
|
29
|
+
yarn add uix-image-magnify
|
|
30
|
+
# or
|
|
31
|
+
pnpm add uix-image-magnify
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`react` and `react-dom` (`>=17`) are peer dependencies. `swiper` is installed automatically as a dependency for the gallery.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { ImageMagnify } from "uix-image-magnify";
|
|
42
|
+
|
|
43
|
+
export default function ProductImage() {
|
|
44
|
+
return (
|
|
45
|
+
<ImageMagnify
|
|
46
|
+
src="/images/shoe.jpg"
|
|
47
|
+
zoomSrc="/images/shoe-large.jpg"
|
|
48
|
+
alt="Red running shoe"
|
|
49
|
+
width={500}
|
|
50
|
+
zoomScale={2.5}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Single Image Magnifier
|
|
59
|
+
|
|
60
|
+
`ImageMagnify` renders an image and zooms it on interaction. The high-res `zoomSrc` is used for the zoomed view while the regular `src` is shown normally — perfect for product photos.
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { ImageMagnify } from "uix-image-magnify";
|
|
64
|
+
|
|
65
|
+
<ImageMagnify
|
|
66
|
+
src="/thumb.jpg"
|
|
67
|
+
zoomSrc="/large.jpg"
|
|
68
|
+
alt="Product"
|
|
69
|
+
width="100%"
|
|
70
|
+
mode="side-by-side"
|
|
71
|
+
zoomType="hover"
|
|
72
|
+
zoomScale={3}
|
|
73
|
+
panePosition="right"
|
|
74
|
+
paneWidth={420}
|
|
75
|
+
paneHeight={420}
|
|
76
|
+
lensShape="circle"
|
|
77
|
+
lensSize={160}
|
|
78
|
+
lazy
|
|
79
|
+
/>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Zoom modes
|
|
83
|
+
|
|
84
|
+
| Mode | Description |
|
|
85
|
+
| -------------- | ------------------------------------------------------------- |
|
|
86
|
+
| `lens` | A circular/square lens follows the cursor over the image. |
|
|
87
|
+
| `side-by-side` | A fixed pane appears beside the image (classic Amazon style). |
|
|
88
|
+
| `inner` | The image itself scales and pans inside its own container. |
|
|
89
|
+
| `follow` | A lens follows the cursor with an optional offset. |
|
|
90
|
+
|
|
91
|
+
> **Mobile:** on `side-by-side` + `panePosition="bottom"`, the pane flows below the image instead of being absolutely positioned — no overlap, no clipping.
|
|
92
|
+
|
|
93
|
+
### Zoom triggers
|
|
94
|
+
|
|
95
|
+
| Trigger | Behavior |
|
|
96
|
+
| -------------- | ------------------------------------------------------ |
|
|
97
|
+
| `hover` | Zooms while the pointer is over the image. |
|
|
98
|
+
| `click` | Toggles zoom on click. |
|
|
99
|
+
| `double-click` | Toggles zoom on double-click. |
|
|
100
|
+
| `hold` | Zooms only while the pointer is pressed down. |
|
|
101
|
+
| `wheel` | Scroll to zoom; supports `minZoomScale`/`maxZoomScale`.|
|
|
102
|
+
| `none` | Disables interaction (renders a plain image). |
|
|
103
|
+
|
|
104
|
+
> On touch devices, interaction behaves as tap-to-toggle regardless of `zoomType` (except `none`).
|
|
105
|
+
|
|
106
|
+
### `inner` mode panning
|
|
107
|
+
|
|
108
|
+
When `mode="inner"`, set `dragMode`:
|
|
109
|
+
|
|
110
|
+
- `dragMode="follow"` (default) — the pixel under the cursor stays under the cursor.
|
|
111
|
+
- `dragMode="pan"` — drag to pan around the zoomed image.
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
<ImageMagnify
|
|
115
|
+
src="/product.jpg"
|
|
116
|
+
alt="Product"
|
|
117
|
+
mode="inner"
|
|
118
|
+
dragMode="pan"
|
|
119
|
+
zoomType="click"
|
|
120
|
+
zoomScale={3}
|
|
121
|
+
/>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Wheel zoom
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
<ImageMagnify
|
|
128
|
+
src="/product.jpg"
|
|
129
|
+
alt="Product"
|
|
130
|
+
zoomType="wheel"
|
|
131
|
+
zoomScale={2}
|
|
132
|
+
minZoomScale={1.5}
|
|
133
|
+
maxZoomScale={6}
|
|
134
|
+
onZoomChange={(s) => console.log("scale:", s)}
|
|
135
|
+
/>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Product Gallery
|
|
141
|
+
|
|
142
|
+
`ImageMagnifyGallery` combines a Swiper-powered slider with the magnifier. It supports thumbnails, arrows, a counter, loop, keyboard navigation, and adjacent-image preloading.
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
import { ImageMagnifyGallery } from "uix-image-magnify";
|
|
146
|
+
|
|
147
|
+
const images = [
|
|
148
|
+
"/images/shoe-1.jpg",
|
|
149
|
+
"/images/shoe-2.jpg",
|
|
150
|
+
"/images/shoe-3.jpg",
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
<ImageMagnifyGallery
|
|
154
|
+
images={images}
|
|
155
|
+
alt="Running shoe"
|
|
156
|
+
mode="side-by-side"
|
|
157
|
+
zoomType="hover"
|
|
158
|
+
zoomScale={2.5}
|
|
159
|
+
showArrows
|
|
160
|
+
showThumbnails
|
|
161
|
+
showCounter
|
|
162
|
+
thumbnailSize={72}
|
|
163
|
+
thumbnailGap={10}
|
|
164
|
+
onIndexChange={(i) => console.log("active:", i)}
|
|
165
|
+
/>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Controlled gallery
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
const [active, setActive] = useState(0);
|
|
172
|
+
|
|
173
|
+
<ImageMagnifyGallery
|
|
174
|
+
images={images}
|
|
175
|
+
alt="Product"
|
|
176
|
+
activeIndex={active}
|
|
177
|
+
onIndexChange={setActive}
|
|
178
|
+
/>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Custom arrows
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
<ImageMagnifyGallery
|
|
185
|
+
images={images}
|
|
186
|
+
alt="Product"
|
|
187
|
+
prevArrow={<span>←</span>}
|
|
188
|
+
nextArrow={<span>→</span>}
|
|
189
|
+
arrowClassName="my-arrow"
|
|
190
|
+
arrowStyle={{ background: "#000", color: "#fff" }}
|
|
191
|
+
/>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Disable features
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
<ImageMagnifyGallery
|
|
198
|
+
images={images}
|
|
199
|
+
alt="Product"
|
|
200
|
+
showArrows={false}
|
|
201
|
+
showThumbnails={false}
|
|
202
|
+
showCounter={false}
|
|
203
|
+
enableKeyboard={false}
|
|
204
|
+
preloadAdjacent={false}
|
|
205
|
+
loop={false}
|
|
206
|
+
/>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## SSR & Next.js
|
|
212
|
+
|
|
213
|
+
The magnifier touches `window`, `matchMedia`, and pointer events. To avoid hydration mismatches, this package ships **SSR-safe wrappers** under the `uix-image-magnify/ssr` entry point:
|
|
214
|
+
|
|
215
|
+
- `ImageMagnifySSR` — plain `<img>` on server + first client render, swaps to `<ImageMagnify>` after hydration.
|
|
216
|
+
- `ImageMagnifyGallerySSR` — same approach for the gallery.
|
|
217
|
+
|
|
218
|
+
### Option 1 — Use the SSR wrappers (recommended)
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
// app/products/[id]/page.tsx (Server Component-safe import)
|
|
222
|
+
import { ImageMagnifySSR } from "uix-image-magnify/ssr";
|
|
223
|
+
|
|
224
|
+
export default function ProductImage({ src, alt }: { src: string; alt: string }) {
|
|
225
|
+
return (
|
|
226
|
+
<ImageMagnifySSR
|
|
227
|
+
src={src}
|
|
228
|
+
alt={alt}
|
|
229
|
+
width={500}
|
|
230
|
+
mode="side-by-side"
|
|
231
|
+
zoomType="hover"
|
|
232
|
+
/>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
import { ImageMagnifyGallerySSR } from "uix-image-magnify/ssr";
|
|
239
|
+
|
|
240
|
+
<ImageMagnifyGallerySSR images={images} alt="Product" />;
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Option 2 — `next/dynamic` with `ssr: false`
|
|
244
|
+
|
|
245
|
+
```tsx
|
|
246
|
+
import dynamic from "next/dynamic";
|
|
247
|
+
|
|
248
|
+
const ImageMagnify = dynamic(
|
|
249
|
+
() => import("uix-image-magnify").then((m) => m.ImageMagnify),
|
|
250
|
+
{ ssr: false }
|
|
251
|
+
);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Both approaches produce zero hydration warnings. Option 1 keeps the SSR-rendered `<img>` visible before hydration so users see the image immediately.
|
|
255
|
+
|
|
256
|
+
### Remix / React Router
|
|
257
|
+
|
|
258
|
+
`uix-image-magnify/ssr` works anywhere `useEffect` runs. No additional config required.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Hooks
|
|
263
|
+
|
|
264
|
+
### `useImageSlider`
|
|
265
|
+
|
|
266
|
+
A headless, SSR-safe slider state hook. No DOM, no browser globals.
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
import { useImageSlider } from "uix-image-magnify";
|
|
270
|
+
|
|
271
|
+
const { index, setIndex, prev, next, canPrev, canNext } = useImageSlider({
|
|
272
|
+
length: images.length,
|
|
273
|
+
defaultIndex: 0,
|
|
274
|
+
loop: true,
|
|
275
|
+
onIndexChange: (i) => console.log(i),
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Options**
|
|
280
|
+
|
|
281
|
+
| Option | Type | Default | Description |
|
|
282
|
+
| --------------- | ------------------------- | ------- | ------------------------------ |
|
|
283
|
+
| `length` | `number` | — | Total items. |
|
|
284
|
+
| `defaultIndex` | `number` | `0` | Initial index (uncontrolled). |
|
|
285
|
+
| `index` | `number` | — | Controlled index. |
|
|
286
|
+
| `onIndexChange` | `(index: number) => void` | — | Fired when the index changes. |
|
|
287
|
+
| `loop` | `boolean` | `true` | Wrap around at the ends. |
|
|
288
|
+
|
|
289
|
+
**Returns:** `{ index, setIndex, prev, next, canPrev, canNext }`.
|
|
290
|
+
|
|
291
|
+
### `useImageMagnify`
|
|
292
|
+
|
|
293
|
+
Use the same engine behind `<ImageMagnify>` directly if you need a custom renderer. Returns refs + event handlers.
|
|
294
|
+
|
|
295
|
+
```tsx
|
|
296
|
+
import { useImageMagnify } from "uix-image-magnify";
|
|
297
|
+
|
|
298
|
+
const {
|
|
299
|
+
isZoomed,
|
|
300
|
+
zoomScale,
|
|
301
|
+
bgImage,
|
|
302
|
+
containerRef,
|
|
303
|
+
imageRef,
|
|
304
|
+
lensRef,
|
|
305
|
+
paneRef,
|
|
306
|
+
cursorRef,
|
|
307
|
+
handlers,
|
|
308
|
+
} = useImageMagnify({
|
|
309
|
+
src: "/thumb.jpg",
|
|
310
|
+
zoomSrc: "/large.jpg",
|
|
311
|
+
mode: "side-by-side",
|
|
312
|
+
zoomType: "hover",
|
|
313
|
+
zoomScale: 2.5,
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Option highlights**
|
|
318
|
+
|
|
319
|
+
| Option | Type | Default |
|
|
320
|
+
| -------------- | --------------------------------------------------------------------- | ---------------- |
|
|
321
|
+
| `src` | `string` | — |
|
|
322
|
+
| `zoomSrc` | `string` | `src` |
|
|
323
|
+
| `zoomScale` | `number` | `2` |
|
|
324
|
+
| `minZoomScale` | `number` | `1.5` |
|
|
325
|
+
| `maxZoomScale` | `number` | `5` |
|
|
326
|
+
| `zoomType` | `"hover" \| "click" \| "double-click" \| "hold" \| "wheel" \| "none"` | `"hover"` |
|
|
327
|
+
| `mode` | `"lens" \| "side-by-side" \| "inner" \| "follow"` | `"side-by-side"` |
|
|
328
|
+
| `dragMode` | `"follow" \| "pan"` | `"follow"` |
|
|
329
|
+
| `panePosition` | `"left" \| "right" \| "top" \| "bottom"` | `"right"` |
|
|
330
|
+
| `lensSize` | `number` | `150` |
|
|
331
|
+
| `lensShape` | `"circle" \| "square"` | `"circle"` |
|
|
332
|
+
| `followOffset` | `number` | `0` |
|
|
333
|
+
| `zoomPreload` | `boolean` | `true` |
|
|
334
|
+
| `disabled` | `boolean` | `false` |
|
|
335
|
+
| `resetOnLeave` | `boolean` | `false` |
|
|
336
|
+
| `onZoomStart` | `() => void` | — |
|
|
337
|
+
| `onZoomEnd` | `() => void` | — |
|
|
338
|
+
| `onZoomChange` | `(scale: number) => void` | — |
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Props Reference
|
|
343
|
+
|
|
344
|
+
### `ImageMagnifyProps`
|
|
345
|
+
|
|
346
|
+
Extends `UseImageMagnifyOptions` **plus**:
|
|
347
|
+
|
|
348
|
+
| Prop | Type | Default | Description |
|
|
349
|
+
| ---------------- | ------------------------------ | ------- | ------------------------------------- |
|
|
350
|
+
| `src` | `string` | — | Regular image URL. |
|
|
351
|
+
| `alt` | `string` | — | Accessible label. |
|
|
352
|
+
| `width` | `number \| string` | — | Outer width. On mobile, forced to `100%`. |
|
|
353
|
+
| `className` | `string` | — | Outer wrapper class. |
|
|
354
|
+
| `style` | `CSSProperties` | — | Outer wrapper style. |
|
|
355
|
+
| `imageClassName` | `string` | — | `<img>` class. |
|
|
356
|
+
| `imageStyle` | `CSSProperties` | — | `<img>` style. |
|
|
357
|
+
| `lensClassName` | `string` | — | Lens element class. |
|
|
358
|
+
| `lensStyle` | `CSSProperties` | — | Lens element style. |
|
|
359
|
+
| `paneClassName` | `string` | — | Side-by-side pane class. |
|
|
360
|
+
| `paneStyle` | `CSSProperties` | — | Side-by-side pane style. |
|
|
361
|
+
| `paneWidth` | `number \| string` | `400` | Pane width. |
|
|
362
|
+
| `paneHeight` | `number \| string` | `400` | Pane height. |
|
|
363
|
+
| `lazy` | `boolean` | — | Use `loading="lazy"`. |
|
|
364
|
+
| `imageProps` | `ImgHTMLAttributes` | — | Extra props forwarded to the `<img>`. |
|
|
365
|
+
| `onLoad` | `ImgHTMLAttributes["onLoad"]` | — | Fired on image load. |
|
|
366
|
+
| `onError` | `ImgHTMLAttributes["onError"]` | — | Fired on image error. |
|
|
367
|
+
|
|
368
|
+
### `ImageMagnifyGalleryProps`
|
|
369
|
+
|
|
370
|
+
Extends `ImageMagnifyProps` **minus** `src` / `onLoad` / `onError`, plus:
|
|
371
|
+
|
|
372
|
+
| Prop | Type | Default | Description |
|
|
373
|
+
| ----------------------------- | ------------------------- | ------- | ------------------------------------ |
|
|
374
|
+
| `images` | `string[]` | — | Image URLs. |
|
|
375
|
+
| `alt` | `string` | — | Alt text (shared). |
|
|
376
|
+
| `activeIndex` | `number` | — | Controlled index. |
|
|
377
|
+
| `defaultIndex` | `number` | `0` | Initial index (uncontrolled). |
|
|
378
|
+
| `onIndexChange` | `(index: number) => void` | — | Fired when the active slide changes. |
|
|
379
|
+
| `showArrows` | `boolean` | `true` | Prev/next arrows. |
|
|
380
|
+
| `showThumbnails` | `boolean` | `true` | Thumbnail strip. |
|
|
381
|
+
| `showCounter` | `boolean` | `false` | "1 / N" counter overlay. |
|
|
382
|
+
| `loop` | `boolean` | `true` | Infinite loop. |
|
|
383
|
+
| `enableKeyboard` | `boolean` | `true` | Arrow-key navigation. |
|
|
384
|
+
| `preloadAdjacent` | `boolean` | `true` | Preload next/prev images. |
|
|
385
|
+
| `viewportClassName` | `string` | — | Main carousel viewport class. |
|
|
386
|
+
| `viewportStyle` | `CSSProperties` | — | Main carousel viewport style. |
|
|
387
|
+
| `thumbnailContainerClassName` | `string` | — | Thumbnail container class. |
|
|
388
|
+
| `thumbnailContainerStyle` | `CSSProperties` | — | Thumbnail container style. |
|
|
389
|
+
| `thumbnailClassName` | `string` | — | Thumbnail button class. |
|
|
390
|
+
| `thumbnailActiveClassName` | `string` | — | Active thumbnail class. |
|
|
391
|
+
| `thumbnailStyle` | `CSSProperties` | — | Thumbnail button style. |
|
|
392
|
+
| `thumbnailActiveStyle` | `CSSProperties` | — | Active thumbnail style. |
|
|
393
|
+
| `arrowClassName` | `string` | — | Arrow button class. |
|
|
394
|
+
| `arrowStyle` | `CSSProperties` | — | Arrow button style. |
|
|
395
|
+
| `thumbnailSize` | `number` | `80` | Thumbnail size (px). |
|
|
396
|
+
| `thumbnailGap` | `number` | `8` | Thumbnail gap (px). |
|
|
397
|
+
| `prevArrow` | `ReactNode` | — | Custom previous arrow. |
|
|
398
|
+
| `nextArrow` | `ReactNode` | — | Custom next arrow. |
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Styling
|
|
403
|
+
|
|
404
|
+
Every part of the component is styleable via `className` / `style` props. The magnifier uses minimal inline styles and does not require any CSS import.
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
<ImageMagnify
|
|
408
|
+
src="/a.jpg"
|
|
409
|
+
alt="Product"
|
|
410
|
+
width={480}
|
|
411
|
+
className="magnify-root"
|
|
412
|
+
imageClassName="magnify-image"
|
|
413
|
+
lensClassName="magnify-lens"
|
|
414
|
+
lensStyle={{ borderColor: "#111", boxShadow: "0 6px 20px rgba(0,0,0,.2)" }}
|
|
415
|
+
paneClassName="magnify-pane"
|
|
416
|
+
paneStyle={{ borderRadius: 12, border: "1px solid #eee" }}
|
|
417
|
+
/>
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
```css
|
|
421
|
+
.magnify-root {
|
|
422
|
+
border-radius: 12px;
|
|
423
|
+
overflow: hidden;
|
|
424
|
+
background: #f7f7f7;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.magnify-lens {
|
|
428
|
+
border-color: #111;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.magnify-pane {
|
|
432
|
+
background-color: #fafafa;
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
The gallery ships with inline defaults for arrows and thumbnails that you can override fully:
|
|
437
|
+
|
|
438
|
+
```tsx
|
|
439
|
+
<ImageMagnifyGallery
|
|
440
|
+
images={images}
|
|
441
|
+
alt="Product"
|
|
442
|
+
thumbnailSize={64}
|
|
443
|
+
thumbnailGap={12}
|
|
444
|
+
thumbnailStyle={{ borderRadius: 8 }}
|
|
445
|
+
thumbnailActiveStyle={{ borderColor: "#0af" }}
|
|
446
|
+
arrowStyle={{ background: "#111", color: "#fff" }}
|
|
447
|
+
/>
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## Accessibility
|
|
453
|
+
|
|
454
|
+
- Root gallery uses `role="region"`, `aria-roledescription="carousel"`, `aria-label`.
|
|
455
|
+
- Each image container is `role="img"`, `tabIndex={0}`, `aria-label={alt}`.
|
|
456
|
+
- Hidden `aria-live="polite"` region announces **"Image X of Y"** on slide change.
|
|
457
|
+
- Keyboard support on the container:
|
|
458
|
+
- **← / → / ↑ / ↓** — move the zoom pointer.
|
|
459
|
+
- **+ / = / -** — zoom in/out (when `zoomType="wheel"`).
|
|
460
|
+
- **Enter / Space** — toggle zoom.
|
|
461
|
+
- **Esc** — reset zoom.
|
|
462
|
+
- Disabled state exposes `aria-disabled`.
|
|
463
|
+
- Custom arrows accept any `ReactNode`; default icons are `<svg aria-hidden="true">` with `aria-label` on the buttons.
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## TypeScript
|
|
468
|
+
|
|
469
|
+
The package is written in strict TypeScript and exports all prop and option types:
|
|
470
|
+
|
|
471
|
+
```ts
|
|
472
|
+
import type {
|
|
473
|
+
ImageMagnifyProps,
|
|
474
|
+
ImageMagnifyGalleryProps,
|
|
475
|
+
UseImageMagnifyOptions,
|
|
476
|
+
UseImageSliderOptions,
|
|
477
|
+
UseImageSliderReturn,
|
|
478
|
+
ZoomType,
|
|
479
|
+
PanePosition,
|
|
480
|
+
LensShape,
|
|
481
|
+
MagnifyMode,
|
|
482
|
+
DragMode,
|
|
483
|
+
} from "uix-image-magnify";
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## Package Exports
|
|
489
|
+
|
|
490
|
+
| Import path | Contents |
|
|
491
|
+
| ----------------------- | ---------------------------------------------------------------------------------------- |
|
|
492
|
+
| `uix-image-magnify` | `ImageMagnify`, `ImageMagnifyGallery`, `useImageMagnify`, `useImageSlider` + all types |
|
|
493
|
+
| `uix-image-magnify/ssr` | `ImageMagnifySSR`, `ImageMagnifyGallerySSR` (and their non-SSR re-exports) |
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## Browser Support
|
|
498
|
+
|
|
499
|
+
Modern evergreen browsers (Chrome, Edge, Firefox, Safari). Requires:
|
|
500
|
+
|
|
501
|
+
- `ResizeObserver`
|
|
502
|
+
- `matchMedia`
|
|
503
|
+
- Pointer Events
|
|
504
|
+
- CSS `transform` / `background-position`
|
|
505
|
+
|
|
506
|
+
SSR wrappers render plain `<img>` on the server for maximum compatibility.
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
## License
|
|
511
|
+
|
|
512
|
+
MIT © uix-image-magnify contributors
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
### Contributing
|
|
517
|
+
|
|
518
|
+
Issues and PRs welcome. Please run `npm run typecheck` and `npm run build` before submitting.
|