react-native-image-collage 0.2.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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +564 -0
  3. package/dist/CollageImage.d.ts +7 -0
  4. package/dist/CollageImage.d.ts.map +1 -0
  5. package/dist/CollageImage.js +49 -0
  6. package/dist/CollageTile.d.ts +18 -0
  7. package/dist/CollageTile.d.ts.map +1 -0
  8. package/dist/CollageTile.js +58 -0
  9. package/dist/CollageWithViewer.d.ts +4 -0
  10. package/dist/CollageWithViewer.d.ts.map +1 -0
  11. package/dist/CollageWithViewer.js +65 -0
  12. package/dist/ImageCollage.d.ts +4 -0
  13. package/dist/ImageCollage.d.ts.map +1 -0
  14. package/dist/ImageCollage.js +135 -0
  15. package/dist/ImageCollageWithViewer.d.ts +4 -0
  16. package/dist/ImageCollageWithViewer.d.ts.map +1 -0
  17. package/dist/ImageCollageWithViewer.js +59 -0
  18. package/dist/ImageViewer.d.ts +4 -0
  19. package/dist/ImageViewer.d.ts.map +1 -0
  20. package/dist/ImageViewer.js +76 -0
  21. package/dist/constants.d.ts +13 -0
  22. package/dist/constants.d.ts.map +1 -0
  23. package/dist/constants.js +13 -0
  24. package/dist/expo/createExpoImageRenderer.d.ts +9 -0
  25. package/dist/expo/createExpoImageRenderer.d.ts.map +1 -0
  26. package/dist/expo/createExpoImageRenderer.js +20 -0
  27. package/dist/expo/index.d.ts +15 -0
  28. package/dist/expo/index.d.ts.map +1 -0
  29. package/dist/expo/index.js +59 -0
  30. package/dist/hooks/useContainerWidth.d.ts +10 -0
  31. package/dist/hooks/useContainerWidth.d.ts.map +1 -0
  32. package/dist/hooks/useContainerWidth.js +22 -0
  33. package/dist/index.d.ts +12 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +35 -0
  36. package/dist/types.d.ts +93 -0
  37. package/dist/types.d.ts.map +1 -0
  38. package/dist/types.js +2 -0
  39. package/dist/utils/imageSources.d.ts +11 -0
  40. package/dist/utils/imageSources.d.ts.map +1 -0
  41. package/dist/utils/imageSources.js +131 -0
  42. package/dist/utils/layoutHeight.d.ts +9 -0
  43. package/dist/utils/layoutHeight.d.ts.map +1 -0
  44. package/dist/utils/layoutHeight.js +41 -0
  45. package/dist/utils/renderCollageLayouts.d.ts +97 -0
  46. package/dist/utils/renderCollageLayouts.d.ts.map +1 -0
  47. package/dist/utils/renderCollageLayouts.js +183 -0
  48. package/dist/viewer/ImageCollageWithViewer.d.ts +4 -0
  49. package/dist/viewer/ImageCollageWithViewer.d.ts.map +1 -0
  50. package/dist/viewer/ImageCollageWithViewer.js +43 -0
  51. package/dist/viewer/ImageViewer.d.ts +5 -0
  52. package/dist/viewer/ImageViewer.d.ts.map +1 -0
  53. package/dist/viewer/ImageViewer.js +85 -0
  54. package/dist/viewer/index.d.ts +4 -0
  55. package/dist/viewer/index.d.ts.map +1 -0
  56. package/dist/viewer/index.js +8 -0
  57. package/package.json +68 -0
  58. package/src/CollageImage.tsx +41 -0
  59. package/src/CollageTile.tsx +69 -0
  60. package/src/CollageWithViewer.tsx +53 -0
  61. package/src/ImageCollage.tsx +168 -0
  62. package/src/constants.ts +11 -0
  63. package/src/expo/createExpoImageRenderer.tsx +43 -0
  64. package/src/expo/index.tsx +99 -0
  65. package/src/hooks/useContainerWidth.ts +29 -0
  66. package/src/index.ts +42 -0
  67. package/src/types.ts +120 -0
  68. package/src/utils/imageSources.ts +170 -0
  69. package/src/utils/layoutHeight.ts +54 -0
  70. package/src/utils/renderCollageLayouts.tsx +329 -0
  71. package/src/viewer/ImageCollageWithViewer.tsx +24 -0
  72. package/src/viewer/ImageViewer.tsx +93 -0
  73. package/src/viewer/index.ts +10 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Faisal Khawaj
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,564 @@
1
+ # react-native-image-collage
2
+
3
+ An easy-to-use collage layout component for React Native — similar to how **Facebook / Instagram show images in a post**.
4
+
5
+ Supports **1 to N images**, automatic layouts, `+N` overflow badges, optional full-screen viewer, and works with **React Native CLI** and **Expo**.
6
+
7
+ ```bash
8
+ npm install react-native-image-collage
9
+ # or
10
+ yarn add react-native-image-collage
11
+ ```
12
+
13
+ ---
14
+
15
+ ## Table of contents
16
+
17
+ - [Quick start](#quick-start)
18
+ - [Layouts](#layouts)
19
+ - [Use cases](#use-cases)
20
+ - [Single image](#1-single-image)
21
+ - [Two images](#2-two-images-side-by-side)
22
+ - [Three images](#3-three-images-facebook-style)
23
+ - [Four images](#4-four-images-2×2-grid)
24
+ - [Five or more images (+N overflow)](#5-five-or-more-images-n-overflow)
25
+ - [Custom overflow count](#6-custom-overflow-count)
26
+ - [Tap to open handler](#7-tap-to-open-handler)
27
+ - [Collage + built-in full-screen viewer](#8-collage--built-in-full-screen-viewer)
28
+ - [Collage + your own gallery](#9-collage--your-own-gallery)
29
+ - [Expo with blurhash & caching](#10-expo-with-blurhash--caching)
30
+ - [Custom image component (FastImage, etc.)](#11-custom-image-component-fastimage-etc)
31
+ - [Local images (require)](#12-local-images-require)
32
+ - [Images with aspect ratio](#13-images-with-aspect-ratio)
33
+ - [Inside a card or padded container](#14-inside-a-card-or-padded-container)
34
+ - [Entry points](#entry-points)
35
+ - [Props](#props)
36
+ - [Image input formats](#image-input-formats)
37
+ - [Exports](#exports)
38
+ - [Local development](#local-development)
39
+ - [License](#license)
40
+
41
+ ---
42
+
43
+ ## Quick start
44
+
45
+ ```tsx
46
+ import React from "react";
47
+ import { View, StyleSheet } from "react-native";
48
+ import { ImageCollage } from "react-native-image-collage";
49
+
50
+ const images = [
51
+ {
52
+ uri: "https://picsum.photos/206",
53
+ aspectRatio: 1.5,
54
+ },
55
+ "https://picsum.photos/207",
56
+ "https://picsum.photos/208",
57
+ ];
58
+
59
+ export default function PostImages() {
60
+ return (
61
+ <View style={styles.container}>
62
+ <ImageCollage
63
+ images={images}
64
+ spacing={2}
65
+ borderRadius={8}
66
+ layoutMinHeight={180}
67
+ layoutMaxHeight={400}
68
+ onImagePress={(index) => console.log("Tapped image", index)}
69
+ />
70
+ </View>
71
+ );
72
+ }
73
+
74
+ const styles = StyleSheet.create({
75
+ container: { paddingHorizontal: 16 },
76
+ });
77
+ ```
78
+
79
+ > Width is measured from the parent container automatically. Height is computed from width and image count (or aspect ratio when available).
80
+
81
+ ---
82
+
83
+ ## Layouts
84
+
85
+ | Images | Layout |
86
+ |--------|--------|
87
+ | **1** | Full-width single image |
88
+ | **2** | Side by side |
89
+ | **3** | One large left, two stacked right (Facebook style) |
90
+ | **4** | 2×2 grid |
91
+ | **5+** | 2×2 grid with `+N` on the last visible tile |
92
+
93
+ ```
94
+ 1 image 2 images 3 images 4 images
95
+ ┌──────────┐ ┌────┬────┐ ┌────┬────┐ ┌────┬────┐
96
+ │ │ │ │ │ │ │ 2 │ │ 1 │ 2 │
97
+ │ 1 │ │ 1 │ 2 │ │ 1 ├────┤ ├────┼────┤
98
+ │ │ │ │ │ │ │ 3 │ │ 3 │ 4 │
99
+ └──────────┘ └────┴────┘ └────┴────┘ └────┴────┘
100
+
101
+ 5+ images (default maxVisibleImages=4)
102
+ ┌────┬────┐
103
+ │ 1 │ 2 │
104
+ ├────┼────┤
105
+ │ 3 │+2 │ ← 4th tile shows last image with +2 overlay (6 total)
106
+ └────┴────┘
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Use cases
112
+
113
+ ### 1. Single image
114
+
115
+ ```tsx
116
+ <ImageCollage
117
+ images={[
118
+ {
119
+ uri: "https://picsum.photos/205",
120
+ aspectRatio: 1.5,
121
+ },
122
+ ]}
123
+ spacing={2}
124
+ />
125
+ ```
126
+
127
+ When `aspectRatio` is provided (or measured), height adapts to the image proportions.
128
+
129
+ ---
130
+
131
+ ### 2. Two images (side by side)
132
+
133
+ ```tsx
134
+ <ImageCollage
135
+ images={[
136
+ "https://picsum.photos/200",
137
+ "https://picsum.photos/201",
138
+ ]}
139
+ spacing={2}
140
+ />
141
+ ```
142
+
143
+ ---
144
+
145
+ ### 3. Three images (Facebook style)
146
+
147
+ ```tsx
148
+ <ImageCollage images={photoUrls} spacing={2} borderRadius={8} />
149
+ ```
150
+
151
+ One image on the left, two stacked on the right — the classic social feed layout.
152
+
153
+ ---
154
+
155
+ ### 4. Four images (2×2 grid)
156
+
157
+ ```tsx
158
+ <ImageCollage images={photoUrls} spacing={2} />
159
+ ```
160
+
161
+ ---
162
+
163
+ ### 5. Five or more images (+N overflow)
164
+
165
+ By default, up to **4 tiles** are shown. Extra images are indicated with a `+N` badge on the last tile.
166
+
167
+ ```tsx
168
+ // 6 images → shows 4 tiles, last tile displays "+2"
169
+ <ImageCollage images={sixPhotoUrls} />
170
+ ```
171
+
172
+ | Total images | What you see |
173
+ |--------------|--------------|
174
+ | 5 | 2×2 grid, `+1` on 4th tile |
175
+ | 6 | 2×2 grid, `+2` on 4th tile |
176
+ | 10 | 2×2 grid, `+6` on 4th tile |
177
+
178
+ ---
179
+
180
+ ### 6. Custom overflow count
181
+
182
+ Use `maxVisibleImages` to control how many tiles appear before the `+N` badge.
183
+
184
+ ```tsx
185
+ // 4 images → 3-tile layout, "+1" on the 3rd tile
186
+ <ImageCollage images={photoUrls} maxVisibleImages={3} />
187
+
188
+ // 3 images → 2-tile row, "+1" on the 2nd tile
189
+ <ImageCollage images={photoUrls} maxVisibleImages={2} />
190
+ ```
191
+
192
+ | Total images | `maxVisibleImages` | Result |
193
+ |--------------|-------------------|--------|
194
+ | 4 | `3` | 3-tile layout, `+1` on 3rd tile |
195
+ | 5 | `4` | 2×2 grid, `+1` on 4th tile (default) |
196
+ | 6 | `4` | 2×2 grid, `+2` on 4th tile |
197
+ | 3 | `2` | 2-tile row, `+1` on 2nd tile |
198
+
199
+ Formula: **`+N = totalImages - maxVisibleImages`**
200
+
201
+ ---
202
+
203
+ ### 7. Tap to open handler
204
+
205
+ ```tsx
206
+ <ImageCollage
207
+ images={photoUrls}
208
+ onImagePress={(index) => {
209
+ // index = 0-based position of the tapped tile
210
+ navigation.navigate("PhotoDetail", { index });
211
+ }}
212
+ />
213
+ ```
214
+
215
+ ---
216
+
217
+ ### 8. Collage + built-in full-screen viewer
218
+
219
+ Opens a zoomable full-screen gallery when a tile is tapped.
220
+
221
+ ```bash
222
+ npm install react-native-image-viewing
223
+ ```
224
+
225
+ ```tsx
226
+ import { ImageCollageWithViewer } from "react-native-image-collage/viewer";
227
+
228
+ <ImageCollageWithViewer
229
+ images={photoUrls}
230
+ spacing={2}
231
+ borderRadius={8}
232
+ viewerProps={{
233
+ swipeToCloseEnabled: true,
234
+ doubleTapToZoomEnabled: true,
235
+ showCloseButton: true,
236
+ showIndexFooter: true,
237
+ closeButtonLabel: "Close",
238
+ }}
239
+ />
240
+ ```
241
+
242
+ ---
243
+
244
+ ### 9. Collage + your own gallery
245
+
246
+ Bring your own lightbox / gallery component.
247
+
248
+ ```tsx
249
+ import { CollageWithViewer } from "react-native-image-collage";
250
+
251
+ <CollageWithViewer
252
+ images={photoUrls}
253
+ spacing={2}
254
+ renderViewer={({ images, visible, imageIndex, onRequestClose }) => (
255
+ <MyGallery
256
+ uris={images.map((img) => img.uri)}
257
+ visible={visible}
258
+ initialIndex={imageIndex}
259
+ onClose={onRequestClose}
260
+ />
261
+ )}
262
+ />
263
+ ```
264
+
265
+ ---
266
+
267
+ ### 10. Expo with blurhash & caching
268
+
269
+ Uses `expo-image` for better performance, disk caching, and blurhash placeholders.
270
+
271
+ ```bash
272
+ npx expo install react-native-image-collage expo-image
273
+ ```
274
+
275
+ ```tsx
276
+ import { ImageCollageWithViewer } from "react-native-image-collage/expo";
277
+
278
+ <ImageCollageWithViewer
279
+ images={photoUrls}
280
+ spacing={2}
281
+ blurhash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
282
+ prioritizeFirstImage
283
+ />
284
+ ```
285
+
286
+ > `blurhash` and `prioritizeFirstImage` are only available on the `/expo` entry.
287
+
288
+ ---
289
+
290
+ ### 11. Custom image component (FastImage, etc.)
291
+
292
+ ```tsx
293
+ import { ImageCollage } from "react-native-image-collage";
294
+ import FastImage from "react-native-fast-image";
295
+
296
+ <ImageCollage
297
+ images={photoUrls}
298
+ renderImage={({ source, style }) => (
299
+ <FastImage
300
+ source={source}
301
+ style={style}
302
+ resizeMode={FastImage.resizeMode.cover}
303
+ />
304
+ )}
305
+ />
306
+ ```
307
+
308
+ ---
309
+
310
+ ### 12. Local images (require)
311
+
312
+ ```tsx
313
+ <ImageCollage
314
+ images={[
315
+ require("./assets/photo1.png"),
316
+ require("./assets/photo2.png"),
317
+ ]}
318
+ />
319
+ ```
320
+
321
+ ---
322
+
323
+ ### 13. Images with aspect ratio
324
+
325
+ Pass `aspectRatio` to skip network measurement and get accurate height faster.
326
+
327
+ ```tsx
328
+ const images = [
329
+ { uri: "https://picsum.photos/208", aspectRatio: 1.91 },
330
+ { uri: "https://picsum.photos/206", aspectRatio: 1 },
331
+ { uri: "https://picsum.photos/204", aspectRatio: 0.75 },
332
+ ];
333
+
334
+ <ImageCollage images={images} />
335
+ ```
336
+
337
+ If `aspectRatio` is omitted, remote and local images are measured automatically (`measureAspectRatios` defaults to `true`).
338
+
339
+ ---
340
+
341
+ ### 14. Inside a card or padded container
342
+
343
+ The collage measures its **parent width** via `onLayout` — no need to pass screen width manually.
344
+
345
+ ```tsx
346
+ <View style={{ paddingHorizontal: 16 }}>
347
+ <ImageCollage images={photoUrls} spacing={2} />
348
+ </View>
349
+ ```
350
+
351
+ Optional overrides:
352
+
353
+ ```tsx
354
+ // Fixed width
355
+ <ImageCollage images={photoUrls} width={320} />
356
+
357
+ // Fixed height
358
+ <ImageCollage images={photoUrls} height={280} />
359
+ ```
360
+
361
+ ---
362
+
363
+ ## Entry points
364
+
365
+ | Import | Requires | Best for |
366
+ |--------|----------|----------|
367
+ | `react-native-image-collage` | Nothing extra | RN CLI, Expo, custom setups |
368
+ | `react-native-image-collage/viewer` | `react-native-image-viewing` | Built-in full-screen viewer |
369
+ | `react-native-image-collage/expo` | `expo-image` | Blurhash, caching, priority loading |
370
+
371
+ ---
372
+
373
+ ## Props
374
+
375
+ ### `ImageCollage`
376
+
377
+ | Prop | Type | Default | Description |
378
+ |------|------|---------|-------------|
379
+ | `images` | `CollageImageInput[]` | **required** | Array of image URLs, sources, or `{ uri, aspectRatio }` objects |
380
+ | `onImagePress` | `(index: number) => void` | — | Called when a tile is tapped. Receives the 0-based image index |
381
+ | `spacing` | `number` | `6` | Gap between tiles (px) |
382
+ | `borderRadius` | `number` | `12` | Corner radius of each tile |
383
+ | `layoutMinHeight` | `number` | `200` | Minimum collage height |
384
+ | `layoutMaxHeight` | `number` | `520` | Maximum collage height |
385
+ | `height` | `number` | auto | Fixed height. When omitted, height is computed from container width |
386
+ | `width` | `number` | measured | Explicit container width. When omitted, measured from parent via `onLayout` |
387
+ | `maxVisibleImages` | `number` | `4` | Max tiles before `+N` overflow badge on the last tile |
388
+ | `placeholderColor` | `string` | `#E8E8E8` | Background color behind tiles while images load |
389
+ | `measureAspectRatios` | `boolean` | `true` | Automatically measure images when `aspectRatio` is not provided |
390
+ | `renderImage` | `CollageImageRenderer` | RN `Image` | Custom image component (FastImage, expo-image, etc.) |
391
+ | `getImagePriority` | `(index) => 'low' \| 'normal' \| 'high'` | — | Per-tile load priority hint for custom renderers |
392
+ | `style` | `ViewStyle` | — | Style applied to the collage container |
393
+ | `horizontalInset` | `number` | `0` | **Deprecated.** Fallback screen inset before first layout measure |
394
+
395
+ ---
396
+
397
+ ### `ImageCollageWithViewer`
398
+ Import from `react-native-image-collage/viewer`
399
+
400
+ Accepts **all `ImageCollage` props**, plus:
401
+
402
+ | Prop | Type | Default | Description |
403
+ |------|------|---------|-------------|
404
+ | `onImagePress` | `(index: number) => void` | — | Called when a tile is tapped, before the viewer opens |
405
+ | `viewerProps` | `object` | — | Props passed to the built-in viewer (see below) |
406
+ | `renderViewer` | `CollageViewerRenderer` | built-in | Replace the default full-screen viewer |
407
+
408
+ #### `viewerProps`
409
+
410
+ | Prop | Type | Default | Description |
411
+ |------|------|---------|-------------|
412
+ | `swipeToCloseEnabled` | `boolean` | `true` | Swipe down to close |
413
+ | `doubleTapToZoomEnabled` | `boolean` | `true` | Double-tap to zoom |
414
+ | `presentationStyle` | `string` | `'fullScreen'` | iOS modal style |
415
+ | `showCloseButton` | `boolean` | `true` | Show close button header |
416
+ | `showIndexFooter` | `boolean` | `true` | Show `1 / N` index footer |
417
+ | `closeButtonLabel` | `string` | `'Close'` | Close button text |
418
+
419
+ ---
420
+
421
+ ### `CollageWithViewer`
422
+ Import from `react-native-image-collage`
423
+
424
+ | Prop | Type | Default | Description |
425
+ |------|------|---------|-------------|
426
+ | `renderViewer` | `CollageViewerRenderer` | **required** | Your gallery / lightbox component |
427
+ | `onImagePress` | `(index: number) => void` | — | Called when a tile is tapped, before viewer opens |
428
+
429
+ Plus all `ImageCollage` props.
430
+
431
+ ---
432
+
433
+ ### `ImageViewer` (standalone)
434
+ Import from `react-native-image-collage/viewer`
435
+
436
+ | Prop | Type | Default | Description |
437
+ |------|------|---------|-------------|
438
+ | `images` | `{ uri: string }[]` | **required** | Images for the viewer |
439
+ | `visible` | `boolean` | **required** | Whether the viewer is open |
440
+ | `onRequestClose` | `() => void` | **required** | Called when viewer should close |
441
+ | `imageIndex` | `number` | `0` | Initially displayed image |
442
+ | `swipeToCloseEnabled` | `boolean` | `true` | Swipe down to close |
443
+ | `doubleTapToZoomEnabled` | `boolean` | `true` | Double-tap to zoom |
444
+ | `presentationStyle` | `string` | `'fullScreen'` | iOS modal style |
445
+ | `showCloseButton` | `boolean` | `true` | Show close button |
446
+ | `showIndexFooter` | `boolean` | `true` | Show index footer |
447
+ | `closeButtonLabel` | `string` | `'Close'` | Close button label |
448
+
449
+ ---
450
+
451
+ ### Expo-only props
452
+ Import from `react-native-image-collage/expo`
453
+
454
+ Available on `ImageCollage` and `ImageCollageWithViewer`:
455
+
456
+ | Prop | Type | Default | Description |
457
+ |------|------|---------|-------------|
458
+ | `blurhash` | `string` | built-in default | Blurhash placeholder while images load |
459
+ | `prioritizeFirstImage` | `boolean` | `true` | Load the first image with high priority |
460
+
461
+ ---
462
+
463
+ ## Image input formats
464
+
465
+ `images` accepts any of the following per item:
466
+
467
+ ```tsx
468
+ // 1. URL string
469
+ "https://picsum.photos/200"
470
+
471
+ // 2. Object with optional aspect ratio (recommended for network images)
472
+ { uri: "https://picsum.photos/201", aspectRatio: 1.5 }
473
+
474
+ // 3. React Native image source (local require, headers, etc.)
475
+ require("./photo.png")
476
+ { uri: "https://example.com/photo.jpg", headers: { Authorization: "..." } }
477
+ ```
478
+
479
+ ---
480
+
481
+ ## Exports
482
+
483
+ ### Components
484
+
485
+ | Component | Entry |
486
+ |-----------|-------|
487
+ | `ImageCollage` | `.` / `/expo` |
488
+ | `CollageTile` | `.` |
489
+ | `CollageImage` | `.` |
490
+ | `CollageWithViewer` | `.` |
491
+ | `ImageCollageWithViewer` | `/viewer` / `/expo` |
492
+ | `ImageViewer` | `/viewer` |
493
+
494
+ ### Utilities
495
+
496
+ ```tsx
497
+ import {
498
+ normalizeImages,
499
+ resolveImagesWithAspectRatios,
500
+ toViewerImages,
501
+ getRemoteUri,
502
+ computeLayoutHeight,
503
+ useContainerWidth,
504
+ createExpoImageRenderer, // /expo only
505
+ createDefaultViewerRenderer, // /viewer only
506
+ } from "react-native-image-collage";
507
+ ```
508
+
509
+ ### Constants
510
+
511
+ ```tsx
512
+ import {
513
+ DEFAULT_SPACING, // 6
514
+ DEFAULT_BORDER_RADIUS, // 12
515
+ DEFAULT_LAYOUT_MIN_HEIGHT, // 200
516
+ DEFAULT_LAYOUT_MAX_HEIGHT, // 520
517
+ DEFAULT_MAX_VISIBLE_IMAGES,// 4
518
+ DEFAULT_PLACEHOLDER_COLOR, // #E8E8E8
519
+ } from "react-native-image-collage";
520
+ ```
521
+
522
+ ---
523
+
524
+ ## Local development
525
+
526
+ ```bash
527
+ git clone <repo>
528
+ cd react-native-image-collage
529
+ npm install
530
+ npm run build
531
+ npm run typecheck
532
+ ```
533
+
534
+ ### Example app (manual testing)
535
+
536
+ An Expo playground lives in `example/`. It is **not** published to npm.
537
+
538
+ ```bash
539
+ npm run example
540
+ # or
541
+ cd example && npm install && npx expo start
542
+ ```
543
+
544
+ Use the on-screen controls to change image count, `maxVisibleImages`, and entry point (`/viewer`, `/expo`).
545
+
546
+ If you see `private properties are not supported`, stop Metro and run:
547
+
548
+ ```bash
549
+ cd example && npx expo start --clear
550
+ ```
551
+
552
+ Ensure **Expo Go matches SDK 54** on your physical device.
553
+
554
+ ### Link into another app
555
+
556
+ ```bash
557
+ npm install /path/to/react-native-image-collage
558
+ ```
559
+
560
+ ---
561
+
562
+ ## License
563
+
564
+ MIT
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { StyleProp, ImageStyle, type ImageSourcePropType } from "react-native";
3
+ import type { CollageImageRenderProps, CollageImageRenderer } from "./types";
4
+ export declare const CollageImage: React.NamedExoticComponent<CollageImageRenderProps>;
5
+ export declare function renderCollageImage(props: CollageImageRenderProps, renderImage?: CollageImageRenderer, style?: StyleProp<ImageStyle>): React.JSX.Element;
6
+ export type { ImageSourcePropType };
7
+ //# sourceMappingURL=CollageImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CollageImage.d.ts","sourceRoot":"","sources":["../src/CollageImage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAGL,SAAS,EAET,UAAU,EACV,KAAK,mBAAmB,EACzB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE7E,eAAO,MAAM,YAAY,qDAavB,CAAC;AAEH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,EAC9B,WAAW,CAAC,EAAE,oBAAoB,EAClC,KAAK,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,qBAS9B;AAED,YAAY,EAAE,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CollageImage = void 0;
37
+ exports.renderCollageImage = renderCollageImage;
38
+ const react_1 = __importStar(require("react"));
39
+ const react_native_1 = require("react-native");
40
+ exports.CollageImage = (0, react_1.memo)(function CollageImage({ source, style, transition = react_native_1.Platform.OS === "android" ? 80 : 150, }) {
41
+ return (<react_native_1.Image source={source} resizeMode="cover" fadeDuration={transition} style={[react_native_1.StyleSheet.absoluteFill, style]}/>);
42
+ });
43
+ function renderCollageImage(props, renderImage, style) {
44
+ const imageProps = style ? { ...props, style } : props;
45
+ if (renderImage) {
46
+ return renderImage(imageProps);
47
+ }
48
+ return <exports.CollageImage {...imageProps}/>;
49
+ }
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import { StyleProp, ViewStyle, type ImageSourcePropType } from "react-native";
3
+ import type { CollageImageRenderer, ImagePriority } from "./types";
4
+ type CollageTileProps = {
5
+ source: ImageSourcePropType;
6
+ remoteUri?: string;
7
+ index: number;
8
+ onPress?: (index: number) => void;
9
+ borderRadius: number;
10
+ style?: StyleProp<ViewStyle>;
11
+ priority?: ImagePriority;
12
+ placeholderColor: string;
13
+ renderImage?: CollageImageRenderer;
14
+ transition?: number;
15
+ };
16
+ export declare const CollageTile: React.NamedExoticComponent<CollageTileProps>;
17
+ export {};
18
+ //# sourceMappingURL=CollageTile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CollageTile.d.ts","sourceRoot":"","sources":["../src/CollageTile.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AACjD,OAAO,EAGL,SAAS,EAET,SAAS,EACT,KAAK,mBAAmB,EACzB,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EACd,MAAM,SAAS,CAAC;AAEjB,KAAK,gBAAgB,GAAG;IACtB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,oBAAoB,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,WAAW,8CAmCtB,CAAC"}