@jekrch/react-viewport-lightbox 0.1.0 → 0.3.0
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 +64 -21
- package/dist/index.cjs +240 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -6
- package/dist/index.d.ts +107 -6
- package/dist/index.js +240 -43
- package/dist/index.js.map +1 -1
- package/dist/styles.css +93 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @jekrch/react-viewport-lightbox
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@jekrch/react-viewport-lightbox)
|
|
3
|
+
[](https://www.npmjs.com/package/@jekrch/react-viewport-lightbox)
|
|
4
4
|
[](https://bundlephobia.com/package/@jekrch/react-viewport-lightbox)
|
|
5
5
|
[](https://codecov.io/gh/jekrch/react-viewport-lightbox)
|
|
6
6
|
[](https://www.npmjs.com/package/@jekrch/react-viewport-lightbox)
|
|
7
|
-
[](./LICENSE)
|
|
8
8
|
|
|
9
9
|
A touch-friendly React image viewer and lightbox with zoom, pan, pinch, and swipe.
|
|
10
10
|
It ships an `<ImageViewer>` shell with render slots for headers, footers, and
|
|
@@ -75,25 +75,35 @@ animation and calls `onClose` after the exit completes.
|
|
|
75
75
|
|
|
76
76
|
## Props
|
|
77
77
|
|
|
78
|
-
| Prop
|
|
79
|
-
|
|
|
80
|
-
| `items`
|
|
81
|
-
| `index`
|
|
82
|
-
| `onIndexChange`
|
|
83
|
-
| `onNavigate`
|
|
84
|
-
| `onClose`
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
78
|
+
| Prop | Type | Default | Description |
|
|
79
|
+
| ---------------------- | --------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
80
|
+
| `items` | `ViewerItem[]` | required | Images to display. |
|
|
81
|
+
| `index` | `number` | required | Controlled index of the active item. |
|
|
82
|
+
| `onIndexChange` | `(index: number) => void` | required | Called when navigation changes the index. |
|
|
83
|
+
| `onNavigate` | `(direction: "prev" \| "next") => void` | optional | Fired when a slide starts (before `onIndexChange`), so overlays can animate out in sync with the image. |
|
|
84
|
+
| `onClose` | `() => void` | required | Called after the exit animation completes. |
|
|
85
|
+
| `onEscape` | `() => boolean` | optional | Called on Escape before the viewer closes. Return `true` to mark the key handled and veto the default close (e.g. dismiss your own overlay first); `false`/`undefined` falls through to closing. |
|
|
86
|
+
| `getOriginRect` | `(index: number) => ViewerRect \| null` | optional | Enables a shared-element "zoom from thumbnail" open/close transition. Return the source element's rect (e.g. `el.getBoundingClientRect()`) for the given index, or `null` to fall back to the fade. Honors reduced-motion. |
|
|
87
|
+
| `zoom` | `boolean` | `true` | Enable wheel/pinch/double-tap zoom + pan. |
|
|
88
|
+
| `zoomToCursor` | `boolean` | `true` | Anchor wheel/pinch zoom on the pointer: scrolling zooms toward the cursor and a pinch zooms toward the gesture midpoint. Set `false` to zoom about the viewport center. |
|
|
89
|
+
| `showCounter` | `boolean` | `true` | Show the `index / total` counter. |
|
|
90
|
+
| `showZoomControls` | `boolean` | `true` | Show the built-in zoom in/out/reset buttons. Independent of `zoom` (the gestures): set `false` to keep zoom/pan while a consumer overlay owns the chrome. Auto-hidden on touch-primary devices and while content is shifted. |
|
|
91
|
+
| `disableNavigation` | `boolean` | `false` | Suppress built-in arrow-key navigation and swipe commit without tearing the viewer down (e.g. while an overlay handles left/right itself). Does not hide the on-screen nav buttons. |
|
|
92
|
+
| `loop` | `boolean` | `false` | Wrap around at the ends (buttons + arrow keys). |
|
|
93
|
+
| `closeOnBackdropClick` | `boolean` | `false` | Close the viewer when the empty area around the image is clicked. Image, bars, and controls are unaffected. |
|
|
94
|
+
| `renderHeader` | `(ctx) => ReactNode` | optional | Top-left title area. |
|
|
95
|
+
| `renderHeaderActions` | `(ctx) => ReactNode` | optional | Extra top-right buttons (before Close). |
|
|
96
|
+
| `renderNavStart` | `(ctx) => ReactNode` | optional | Pinned to the left edge of the nav row (e.g. a details toggle); costs no extra height, nav group stays centered. |
|
|
97
|
+
| `renderNavEnd` | `(ctx) => ReactNode` | optional | Pinned to the right edge of the nav row. |
|
|
98
|
+
| `navSlotPlacement` | `"edge" \| "inline"` | `"edge"` | Where `renderNavStart`/`renderNavEnd` sit: `"edge"` pins them to the row edges (nav group stays optically centered); `"inline"` places them directly flanking the arrows as one centered cluster. |
|
|
99
|
+
| `navHeight` | `number \| string` | `2.375rem` | Size of the prev/next nav arrows. A number is pixels; a string is used verbatim. Sets `--rvl-nav-height`, so it can also be themed in CSS. |
|
|
100
|
+
| `navInset` | `number \| string` | `1.3rem` | Gap between the bottom nav controls and the viewport's bottom edge (floored by the safe-area inset). Number is pixels; string verbatim. Sets `--rvl-nav-inset`. |
|
|
101
|
+
| `counterFontSize` | `number \| string` | `0.29×` | Overrides the counter font size (which otherwise scales with `navHeight`). Number is pixels; string verbatim. Sets `--rvl-counter-font-size`. |
|
|
102
|
+
| `renderFooter` | `(ctx) => ReactNode` | optional | Content below the nav row. |
|
|
103
|
+
| `renderOverlay` | `(ctx) => ReactNode` | optional | Drawers and graphs layered over the image. |
|
|
104
|
+
| `classNames` | `Partial<Record<ViewerSlot, string>>` | optional | Per-slot `className` overrides. |
|
|
105
|
+
| `icons` | `Partial<ViewerIcons>` | optional | Override `close`, `zoomIn`, `zoomOut`, `prev`, and `next`. |
|
|
106
|
+
| `ariaLabel` | `string` | item `alt` | Dialog label. |
|
|
97
107
|
|
|
98
108
|
### `ViewerItem`
|
|
99
109
|
|
|
@@ -149,6 +159,38 @@ const items: ViewerItem<Detail>[] = [
|
|
|
149
159
|
`ViewerItem`, `ViewerContext`, and `ImageViewerProps` are all generic over `TData`
|
|
150
160
|
(defaulting to `unknown`), so this is fully type-safe and entirely opt-in.
|
|
151
161
|
|
|
162
|
+
### Zoom from thumbnail
|
|
163
|
+
|
|
164
|
+
Pass `getOriginRect` to make the viewer expand out of the clicked thumbnail on open
|
|
165
|
+
and collapse back into it on close (a shared-element transition), instead of the
|
|
166
|
+
default fade. Keep a ref to each thumbnail and return its current on-screen rect for
|
|
167
|
+
the given index; return `null` (or omit the prop) to fall back to the fade. It's
|
|
168
|
+
called again with the active index on close, so navigating then closing collapses
|
|
169
|
+
into the right thumbnail, and it honors `prefers-reduced-motion`.
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
const thumbs = useRef<(HTMLElement | null)[]>([]);
|
|
173
|
+
|
|
174
|
+
{
|
|
175
|
+
items.map((it, i) => (
|
|
176
|
+
<button key={it.id} ref={(el) => (thumbs.current[i] = el)} onClick={() => open(i)}>
|
|
177
|
+
<img src={it.thumbnail ?? it.src} alt={it.alt} />
|
|
178
|
+
</button>
|
|
179
|
+
));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
<ImageViewer
|
|
183
|
+
items={items}
|
|
184
|
+
index={index}
|
|
185
|
+
onIndexChange={setIndex}
|
|
186
|
+
onClose={() => setOpen(false)}
|
|
187
|
+
getOriginRect={(i) => thumbs.current[i]?.getBoundingClientRect() ?? null}
|
|
188
|
+
/>;
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The transition reads most seamlessly when the thumbnail and full image share an
|
|
192
|
+
aspect ratio (the thumbnail is, after all, the same picture).
|
|
193
|
+
|
|
152
194
|
## Slots & `ViewerContext`
|
|
153
195
|
|
|
154
196
|
Every `render*` slot receives a `ViewerContext` with navigation, zoom, and layout
|
|
@@ -160,6 +202,7 @@ interface ViewerContext<TData = unknown> {
|
|
|
160
202
|
index: number;
|
|
161
203
|
item: ViewerItem<TData>; // item.data holds your per-slide payload
|
|
162
204
|
total: number;
|
|
205
|
+
closing: boolean; // true once the exit animation starts, so overlays can fade out in step
|
|
163
206
|
|
|
164
207
|
hasPrev: boolean;
|
|
165
208
|
hasNext: boolean;
|