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/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useContainerWidth = exports.computeLayoutHeight = exports.getRemoteUri = exports.toViewerImages = exports.resolveImagesWithAspectRatios = exports.measureImageAspectRatio = exports.normalizeImages = exports.normalizeImageInput = exports.ANDROID_RIPPLE = exports.DEFAULT_MAX_VISIBLE_IMAGES = exports.DEFAULT_LAYOUT_MAX_HEIGHT = exports.DEFAULT_LAYOUT_MIN_HEIGHT = exports.DEFAULT_BORDER_RADIUS = exports.DEFAULT_SPACING = exports.DEFAULT_PLACEHOLDER_BG = exports.DEFAULT_PLACEHOLDER_COLOR = exports.ImageCollague = exports.CollageTile = exports.renderCollageImage = exports.CollageImage = exports.CollageWithViewer = exports.ImageCollage = void 0;
4
+ var ImageCollage_1 = require("./ImageCollage");
5
+ Object.defineProperty(exports, "ImageCollage", { enumerable: true, get: function () { return ImageCollage_1.ImageCollage; } });
6
+ var CollageWithViewer_1 = require("./CollageWithViewer");
7
+ Object.defineProperty(exports, "CollageWithViewer", { enumerable: true, get: function () { return CollageWithViewer_1.CollageWithViewer; } });
8
+ var CollageImage_1 = require("./CollageImage");
9
+ Object.defineProperty(exports, "CollageImage", { enumerable: true, get: function () { return CollageImage_1.CollageImage; } });
10
+ Object.defineProperty(exports, "renderCollageImage", { enumerable: true, get: function () { return CollageImage_1.renderCollageImage; } });
11
+ var CollageTile_1 = require("./CollageTile");
12
+ Object.defineProperty(exports, "CollageTile", { enumerable: true, get: function () { return CollageTile_1.CollageTile; } });
13
+ /** @deprecated Use `ImageCollage` instead. Kept for backwards compatibility. */
14
+ var ImageCollage_2 = require("./ImageCollage");
15
+ Object.defineProperty(exports, "ImageCollague", { enumerable: true, get: function () { return ImageCollage_2.ImageCollage; } });
16
+ var constants_1 = require("./constants");
17
+ Object.defineProperty(exports, "DEFAULT_PLACEHOLDER_COLOR", { enumerable: true, get: function () { return constants_1.DEFAULT_PLACEHOLDER_COLOR; } });
18
+ Object.defineProperty(exports, "DEFAULT_PLACEHOLDER_BG", { enumerable: true, get: function () { return constants_1.DEFAULT_PLACEHOLDER_BG; } });
19
+ Object.defineProperty(exports, "DEFAULT_SPACING", { enumerable: true, get: function () { return constants_1.DEFAULT_SPACING; } });
20
+ Object.defineProperty(exports, "DEFAULT_BORDER_RADIUS", { enumerable: true, get: function () { return constants_1.DEFAULT_BORDER_RADIUS; } });
21
+ Object.defineProperty(exports, "DEFAULT_LAYOUT_MIN_HEIGHT", { enumerable: true, get: function () { return constants_1.DEFAULT_LAYOUT_MIN_HEIGHT; } });
22
+ Object.defineProperty(exports, "DEFAULT_LAYOUT_MAX_HEIGHT", { enumerable: true, get: function () { return constants_1.DEFAULT_LAYOUT_MAX_HEIGHT; } });
23
+ Object.defineProperty(exports, "DEFAULT_MAX_VISIBLE_IMAGES", { enumerable: true, get: function () { return constants_1.DEFAULT_MAX_VISIBLE_IMAGES; } });
24
+ Object.defineProperty(exports, "ANDROID_RIPPLE", { enumerable: true, get: function () { return constants_1.ANDROID_RIPPLE; } });
25
+ var imageSources_1 = require("./utils/imageSources");
26
+ Object.defineProperty(exports, "normalizeImageInput", { enumerable: true, get: function () { return imageSources_1.normalizeImageInput; } });
27
+ Object.defineProperty(exports, "normalizeImages", { enumerable: true, get: function () { return imageSources_1.normalizeImages; } });
28
+ Object.defineProperty(exports, "measureImageAspectRatio", { enumerable: true, get: function () { return imageSources_1.measureImageAspectRatio; } });
29
+ Object.defineProperty(exports, "resolveImagesWithAspectRatios", { enumerable: true, get: function () { return imageSources_1.resolveImagesWithAspectRatios; } });
30
+ Object.defineProperty(exports, "toViewerImages", { enumerable: true, get: function () { return imageSources_1.toViewerImages; } });
31
+ Object.defineProperty(exports, "getRemoteUri", { enumerable: true, get: function () { return imageSources_1.getRemoteUri; } });
32
+ var layoutHeight_1 = require("./utils/layoutHeight");
33
+ Object.defineProperty(exports, "computeLayoutHeight", { enumerable: true, get: function () { return layoutHeight_1.computeLayoutHeight; } });
34
+ var useContainerWidth_1 = require("./hooks/useContainerWidth");
35
+ Object.defineProperty(exports, "useContainerWidth", { enumerable: true, get: function () { return useContainerWidth_1.useContainerWidth; } });
@@ -0,0 +1,93 @@
1
+ import type { ReactElement } from "react";
2
+ import type { ImageSourcePropType, ImageStyle, StyleProp, ViewStyle } from "react-native";
3
+ export type ImagePriority = "low" | "normal" | "high";
4
+ /** Remote URL, RN image source, or object with optional aspect ratio. */
5
+ export type CollageImageInput = string | ImageSourcePropType | {
6
+ uri: string;
7
+ aspectRatio?: number;
8
+ };
9
+ export type NormalizedCollageImage = {
10
+ source: ImageSourcePropType;
11
+ aspectRatio?: number;
12
+ cacheKey: string;
13
+ remoteUri?: string;
14
+ };
15
+ export type CollageImageRenderProps = {
16
+ source: ImageSourcePropType;
17
+ remoteUri?: string;
18
+ style?: StyleProp<ImageStyle>;
19
+ priority?: ImagePriority;
20
+ transition: number;
21
+ };
22
+ export type CollageImageRenderer = (props: CollageImageRenderProps) => ReactElement;
23
+ export type ImageViewerImage = {
24
+ uri: string;
25
+ };
26
+ export type CollageViewerRenderProps = {
27
+ images: ImageViewerImage[];
28
+ visible: boolean;
29
+ imageIndex: number;
30
+ onRequestClose: () => void;
31
+ };
32
+ export type CollageViewerRenderer = (props: CollageViewerRenderProps) => ReactElement | null;
33
+ export type ImageCollageProps = {
34
+ images: CollageImageInput[] | null | undefined;
35
+ /** Fixed layout height; when omitted, height is derived from container width. */
36
+ height?: number;
37
+ /** Explicit container width; when omitted, width is measured via `onLayout`. */
38
+ width?: number;
39
+ /**
40
+ * @deprecated Prefer automatic `onLayout` sizing. Subtracted from screen width
41
+ * only before the container has been measured.
42
+ */
43
+ horizontalInset?: number;
44
+ borderRadius?: number;
45
+ spacing?: number;
46
+ /**
47
+ * Maximum tiles to show. When there are more images, the last visible tile
48
+ * displays a `+N` overlay for the remaining count.
49
+ *
50
+ * @example `maxVisibleImages={3}` with 4 images → 3-tile layout, third tile shows `+1`
51
+ * @example `maxVisibleImages={4}` with 5 images → 2×2 grid, fourth tile shows `+1`
52
+ */
53
+ maxVisibleImages?: number;
54
+ onImagePress?: (index: number) => void;
55
+ layoutMinHeight?: number;
56
+ layoutMaxHeight?: number;
57
+ placeholderColor?: string;
58
+ /** Measure missing aspect ratios before rendering. */
59
+ measureAspectRatios?: boolean;
60
+ /** Optional per-tile loading priority (used by custom renderers such as expo-image). */
61
+ getImagePriority?: (index: number) => ImagePriority;
62
+ /** Custom image renderer. Defaults to React Native `Image`. */
63
+ renderImage?: CollageImageRenderer;
64
+ style?: StyleProp<ViewStyle>;
65
+ };
66
+ export type CollageWithViewerProps = ImageCollageProps & {
67
+ renderViewer: CollageViewerRenderer;
68
+ onImagePress?: (index: number) => void;
69
+ };
70
+ export type ImageViewerProps = {
71
+ images: ImageViewerImage[];
72
+ visible: boolean;
73
+ imageIndex?: number;
74
+ onRequestClose: () => void;
75
+ swipeToCloseEnabled?: boolean;
76
+ doubleTapToZoomEnabled?: boolean;
77
+ presentationStyle?: "fullScreen" | "pageSheet" | "formSheet" | "overFullScreen";
78
+ showCloseButton?: boolean;
79
+ showIndexFooter?: boolean;
80
+ closeButtonLabel?: string;
81
+ };
82
+ export type ImageCollageWithViewerProps = Omit<ImageCollageProps, "onImagePress"> & {
83
+ viewerProps?: Omit<ImageViewerProps, "images" | "visible" | "imageIndex" | "onRequestClose">;
84
+ /** Override the built-in `react-native-image-viewing` viewer. */
85
+ renderViewer?: CollageViewerRenderer;
86
+ onImagePress?: (index: number) => void;
87
+ };
88
+ /** Expo-only collage options (`react-native-image-collage/expo`). */
89
+ export type ExpoImageCollageOptions = {
90
+ blurhash?: string;
91
+ prioritizeFirstImage?: boolean;
92
+ };
93
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,11 @@
1
+ import { type ImageSourcePropType } from "react-native";
2
+ import type { CollageImageInput, NormalizedCollageImage } from "../types";
3
+ export declare function getRemoteUri(source: ImageSourcePropType): string | undefined;
4
+ export declare function normalizeImageInput(input: CollageImageInput): NormalizedCollageImage | null;
5
+ export declare function normalizeImages(images: CollageImageInput[] | null | undefined): NormalizedCollageImage[];
6
+ export declare function measureImageAspectRatio(image: NormalizedCollageImage): Promise<number>;
7
+ export declare function resolveImagesWithAspectRatios(images: NormalizedCollageImage[]): Promise<NormalizedCollageImage[]>;
8
+ export declare function toViewerImages(images: NormalizedCollageImage[]): {
9
+ uri: string;
10
+ }[];
11
+ //# sourceMappingURL=imageSources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageSources.d.ts","sourceRoot":"","sources":["../../src/utils/imageSources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAoB1E,wBAAgB,YAAY,CAC1B,MAAM,EAAE,mBAAmB,GAC1B,MAAM,GAAG,SAAS,CAUpB;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,iBAAiB,GACvB,sBAAsB,GAAG,IAAI,CAgE/B;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,iBAAiB,EAAE,GAAG,IAAI,GAAG,SAAS,GAC7C,sBAAsB,EAAE,CAI1B;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,MAAM,CAAC,CA0BjB;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,sBAAsB,EAAE,GAC/B,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAenC;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,sBAAsB,EAAE,GAC/B;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,CAOnB"}
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRemoteUri = getRemoteUri;
4
+ exports.normalizeImageInput = normalizeImageInput;
5
+ exports.normalizeImages = normalizeImages;
6
+ exports.measureImageAspectRatio = measureImageAspectRatio;
7
+ exports.resolveImagesWithAspectRatios = resolveImagesWithAspectRatios;
8
+ exports.toViewerImages = toViewerImages;
9
+ const react_native_1 = require("react-native");
10
+ function cacheKeyForSource(source) {
11
+ if (typeof source === "number") {
12
+ return `asset-${source}`;
13
+ }
14
+ if (typeof source === "object" && source) {
15
+ if ("uri" in source && source.uri) {
16
+ return source.uri;
17
+ }
18
+ if ("testUri" in source && typeof source.testUri === "string") {
19
+ return source.testUri;
20
+ }
21
+ }
22
+ return JSON.stringify(source);
23
+ }
24
+ function getRemoteUri(source) {
25
+ if (typeof source === "number") {
26
+ return react_native_1.Image.resolveAssetSource(source)?.uri;
27
+ }
28
+ if (typeof source === "object" && source && "uri" in source && source.uri) {
29
+ return source.uri;
30
+ }
31
+ return undefined;
32
+ }
33
+ function normalizeImageInput(input) {
34
+ if (input == null || input === "") {
35
+ return null;
36
+ }
37
+ if (typeof input === "string") {
38
+ const source = { uri: input };
39
+ return {
40
+ source,
41
+ aspectRatio: undefined,
42
+ cacheKey: input,
43
+ remoteUri: input,
44
+ };
45
+ }
46
+ if (typeof input === "number") {
47
+ const source = input;
48
+ const resolved = react_native_1.Image.resolveAssetSource(source);
49
+ const aspectRatio = resolved?.width && resolved?.height
50
+ ? resolved.width / resolved.height
51
+ : undefined;
52
+ return {
53
+ source,
54
+ aspectRatio,
55
+ cacheKey: cacheKeyForSource(source),
56
+ remoteUri: resolved?.uri,
57
+ };
58
+ }
59
+ if (typeof input === "object" && input != null && "uri" in input) {
60
+ const uri = input.uri;
61
+ if (typeof uri === "string") {
62
+ const keys = Object.keys(input);
63
+ const isUriDescriptor = keys.every((key) => key === "uri" || key === "aspectRatio");
64
+ if (isUriDescriptor) {
65
+ const aspectRatio = "aspectRatio" in input
66
+ ? input.aspectRatio
67
+ : undefined;
68
+ return {
69
+ source: { uri },
70
+ aspectRatio,
71
+ cacheKey: uri,
72
+ remoteUri: uri,
73
+ };
74
+ }
75
+ }
76
+ }
77
+ const source = input;
78
+ const remoteUri = getRemoteUri(source);
79
+ return {
80
+ source,
81
+ aspectRatio: undefined,
82
+ cacheKey: cacheKeyForSource(source),
83
+ remoteUri,
84
+ };
85
+ }
86
+ function normalizeImages(images) {
87
+ return (images ?? [])
88
+ .map(normalizeImageInput)
89
+ .filter((image) => image != null);
90
+ }
91
+ function measureImageAspectRatio(image) {
92
+ if (image.aspectRatio != null) {
93
+ return Promise.resolve(image.aspectRatio);
94
+ }
95
+ if (typeof image.source === "number") {
96
+ const resolved = react_native_1.Image.resolveAssetSource(image.source);
97
+ if (resolved?.width && resolved?.height) {
98
+ return Promise.resolve(resolved.width / resolved.height);
99
+ }
100
+ return Promise.reject(new Error("Unable to resolve local image dimensions"));
101
+ }
102
+ const uri = getRemoteUri(image.source);
103
+ if (!uri) {
104
+ return Promise.reject(new Error("Unable to measure image without a URI"));
105
+ }
106
+ return new Promise((resolve, reject) => {
107
+ react_native_1.Image.getSize(uri, (width, height) => resolve(width / height), reject);
108
+ });
109
+ }
110
+ async function resolveImagesWithAspectRatios(images) {
111
+ return Promise.all(images.map(async (image) => {
112
+ if (image.aspectRatio != null) {
113
+ return image;
114
+ }
115
+ try {
116
+ const aspectRatio = await measureImageAspectRatio(image);
117
+ return { ...image, aspectRatio };
118
+ }
119
+ catch {
120
+ return image;
121
+ }
122
+ }));
123
+ }
124
+ function toViewerImages(images) {
125
+ return images
126
+ .map((image) => {
127
+ const uri = image.remoteUri ?? getRemoteUri(image.source);
128
+ return uri ? { uri } : null;
129
+ })
130
+ .filter((image) => image != null);
131
+ }
@@ -0,0 +1,9 @@
1
+ import type { NormalizedCollageImage } from "../types";
2
+ export declare function computeLayoutHeight({ contentWidth, images, height, layoutMinHeight, layoutMaxHeight, }: {
3
+ contentWidth: number;
4
+ images: NormalizedCollageImage[];
5
+ height?: number;
6
+ layoutMinHeight: number;
7
+ layoutMaxHeight: number;
8
+ }): number;
9
+ //# sourceMappingURL=layoutHeight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layoutHeight.d.ts","sourceRoot":"","sources":["../../src/utils/layoutHeight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAsBvD,wBAAgB,mBAAmB,CAAC,EAClC,YAAY,EACZ,MAAM,EACN,MAAM,EACN,eAAe,EACf,eAAe,GAChB,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,sBAAsB,EAAE,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG,MAAM,CAmBT"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeLayoutHeight = computeLayoutHeight;
4
+ function heightRatioForImageCount(count) {
5
+ if (count <= 1)
6
+ return 0.9;
7
+ if (count === 2)
8
+ return 0.75;
9
+ if (count === 3)
10
+ return 0.9;
11
+ if (count === 4)
12
+ return 1.0;
13
+ return 1.05;
14
+ }
15
+ function averageAspectRatio(images) {
16
+ const ratios = images
17
+ .map((image) => image.aspectRatio)
18
+ .filter((ratio) => ratio != null && ratio > 0);
19
+ if (!ratios.length) {
20
+ return undefined;
21
+ }
22
+ return ratios.reduce((sum, ratio) => sum + ratio, 0) / ratios.length;
23
+ }
24
+ function computeLayoutHeight({ contentWidth, images, height, layoutMinHeight, layoutMaxHeight, }) {
25
+ if (height != null) {
26
+ return Math.max(layoutMinHeight, Math.min(height, layoutMaxHeight));
27
+ }
28
+ const count = images.length;
29
+ let ratio = heightRatioForImageCount(count);
30
+ if (count === 1 && images[0]?.aspectRatio) {
31
+ ratio = 1 / images[0].aspectRatio;
32
+ }
33
+ else if (count === 2) {
34
+ const averageRatio = averageAspectRatio(images);
35
+ if (averageRatio) {
36
+ ratio = 1 / averageRatio;
37
+ }
38
+ }
39
+ const computedHeight = Math.round(contentWidth * ratio);
40
+ return Math.max(layoutMinHeight, Math.min(computedHeight, layoutMaxHeight));
41
+ }
@@ -0,0 +1,97 @@
1
+ import React from "react";
2
+ import type { CollageImageRenderer, ImagePriority, NormalizedCollageImage } from "../types";
3
+ type SharedTileConfig = {
4
+ onPress?: (index: number) => void;
5
+ borderRadius: number;
6
+ placeholderColor: string;
7
+ getImagePriority?: (index: number) => ImagePriority;
8
+ renderImage?: CollageImageRenderer;
9
+ };
10
+ type RenderCollageContentOptions = {
11
+ images: NormalizedCollageImage[];
12
+ layoutHeight: number;
13
+ spacing: number;
14
+ borderRadius: number;
15
+ placeholderColor: string;
16
+ maxVisibleImages: number;
17
+ onImagePress?: (index: number) => void;
18
+ renderImage?: CollageImageRenderer;
19
+ sharedTileConfig: SharedTileConfig;
20
+ };
21
+ export declare function renderCollageContent(options: RenderCollageContentOptions): React.JSX.Element;
22
+ export declare function getCollageLayoutStyle({ count, maxVisibleImages, layoutHeight, spacing, borderRadius, }: {
23
+ count: number;
24
+ maxVisibleImages: number;
25
+ layoutHeight: number;
26
+ spacing: number;
27
+ borderRadius: number;
28
+ }): {
29
+ containerStyle: {
30
+ height: number;
31
+ borderRadius: number;
32
+ overflow: "hidden";
33
+ gap?: undefined;
34
+ minHeight?: undefined;
35
+ };
36
+ row: boolean;
37
+ } | {
38
+ containerStyle: {
39
+ height: number;
40
+ gap: number;
41
+ borderRadius?: undefined;
42
+ overflow?: undefined;
43
+ minHeight?: undefined;
44
+ };
45
+ row: boolean;
46
+ } | {
47
+ containerStyle: {
48
+ height: number;
49
+ gap: number;
50
+ overflow: "hidden";
51
+ minHeight: number;
52
+ borderRadius?: undefined;
53
+ };
54
+ row: boolean;
55
+ };
56
+ declare const styles: {
57
+ row: {
58
+ flexDirection: "row";
59
+ overflow: "hidden";
60
+ minHeight: number;
61
+ width: "100%";
62
+ };
63
+ flexTile: {
64
+ flex: number;
65
+ flexBasis: number;
66
+ minWidth: number;
67
+ minHeight: number;
68
+ };
69
+ flexColumn: {
70
+ flex: number;
71
+ flexBasis: number;
72
+ minWidth: number;
73
+ minHeight: number;
74
+ };
75
+ overflowTile: {
76
+ overflow: "hidden";
77
+ minHeight: number;
78
+ minWidth: number;
79
+ };
80
+ overflowOverlay: {
81
+ backgroundColor: string;
82
+ alignItems: "center";
83
+ justifyContent: "center";
84
+ position: "absolute";
85
+ left: 0;
86
+ right: 0;
87
+ top: 0;
88
+ bottom: 0;
89
+ };
90
+ overflowText: {
91
+ color: string;
92
+ fontSize: number;
93
+ fontWeight: "800";
94
+ };
95
+ };
96
+ export { styles as collageLayoutStyles };
97
+ //# sourceMappingURL=renderCollageLayouts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderCollageLayouts.d.ts","sourceRoot":"","sources":["../../src/utils/renderCollageLayouts.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAY1B,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EACb,sBAAsB,EACvB,MAAM,UAAU,CAAC;AAElB,KAAK,gBAAgB,GAAG;IACtB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,aAAa,CAAC;IACpD,WAAW,CAAC,EAAE,oBAAoB,CAAC;CACpC,CAAC;AAEF,KAAK,2BAA2B,GAAG;IACjC,MAAM,EAAE,sBAAsB,EAAE,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,oBAAoB,CAAC;IACnC,gBAAgB,EAAE,gBAAgB,CAAC;CACpC,CAAC;AAkNF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,qBAmBxE;AAED,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,YAAY,GACb,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCA;AAED,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAYV,CAAC;AAEH,OAAO,EAAE,MAAM,IAAI,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.collageLayoutStyles = void 0;
7
+ exports.renderCollageContent = renderCollageContent;
8
+ exports.getCollageLayoutStyle = getCollageLayoutStyle;
9
+ const react_1 = __importDefault(require("react"));
10
+ const react_native_1 = require("react-native");
11
+ const CollageImage_1 = require("../CollageImage");
12
+ const CollageTile_1 = require("../CollageTile");
13
+ const constants_1 = require("../constants");
14
+ function renderTile(image, index, shared, style, priority, transition) {
15
+ return (<CollageTile_1.CollageTile key={`${image.cacheKey}-${index}`} source={image.source} remoteUri={image.remoteUri} index={index} onPress={shared.onPress} borderRadius={shared.borderRadius} placeholderColor={shared.placeholderColor} renderImage={shared.renderImage} priority={priority ?? shared.getImagePriority?.(index) ?? "normal"} transition={transition} style={style}/>);
16
+ }
17
+ function renderOverflowTile({ image, tileIndex, overflowCount, borderRadius, placeholderColor, onImagePress, renderImage, }) {
18
+ return (<react_native_1.Pressable onPress={() => onImagePress?.(tileIndex)} android_ripple={constants_1.ANDROID_RIPPLE} style={[
19
+ styles.overflowTile,
20
+ styles.flexTile,
21
+ {
22
+ borderRadius,
23
+ backgroundColor: placeholderColor,
24
+ },
25
+ ]}>
26
+ {(0, CollageImage_1.renderCollageImage)({
27
+ source: image.source,
28
+ remoteUri: image.remoteUri,
29
+ priority: "normal",
30
+ transition: 0,
31
+ }, renderImage)}
32
+ {overflowCount > 0 && (<react_native_1.View pointerEvents="none" style={styles.overflowOverlay}>
33
+ <react_native_1.Text style={styles.overflowText}>{`+${overflowCount}`}</react_native_1.Text>
34
+ </react_native_1.View>)}
35
+ </react_native_1.Pressable>);
36
+ }
37
+ function resolveVisibleLayout(count, maxVisibleImages) {
38
+ const hasOverflow = count > maxVisibleImages;
39
+ const visibleCount = hasOverflow ? maxVisibleImages : count;
40
+ const overflowCount = hasOverflow ? count - maxVisibleImages : 0;
41
+ const overflowTileIndex = visibleCount - 1;
42
+ return {
43
+ hasOverflow,
44
+ visibleCount,
45
+ overflowCount,
46
+ overflowTileIndex,
47
+ };
48
+ }
49
+ function renderSingleLayout(images, options, layout) {
50
+ const { layoutHeight, sharedTileConfig } = options;
51
+ const image = images[0];
52
+ if (layout.hasOverflow) {
53
+ return renderOverflowTile({
54
+ image,
55
+ tileIndex: 0,
56
+ overflowCount: layout.overflowCount,
57
+ borderRadius: options.borderRadius,
58
+ placeholderColor: options.placeholderColor,
59
+ onImagePress: options.onImagePress,
60
+ renderImage: options.renderImage,
61
+ });
62
+ }
63
+ return renderTile(image, 0, sharedTileConfig, { height: layoutHeight });
64
+ }
65
+ function renderTwoLayout(images, options, layout) {
66
+ const { sharedTileConfig } = options;
67
+ return (<>
68
+ {renderTile(images[0], 0, sharedTileConfig, styles.flexTile)}
69
+ {layout.hasOverflow ? (renderOverflowTile({
70
+ image: images[1],
71
+ tileIndex: 1,
72
+ overflowCount: layout.overflowCount,
73
+ borderRadius: options.borderRadius,
74
+ placeholderColor: options.placeholderColor,
75
+ onImagePress: options.onImagePress,
76
+ renderImage: options.renderImage,
77
+ })) : (renderTile(images[1], 1, sharedTileConfig, styles.flexTile))}
78
+ </>);
79
+ }
80
+ function renderThreeLayout(images, options, layout) {
81
+ const { spacing, sharedTileConfig } = options;
82
+ return (<>
83
+ {renderTile(images[0], 0, sharedTileConfig, styles.flexTile)}
84
+ <react_native_1.View style={[styles.flexColumn, { gap: spacing }]}>
85
+ {renderTile(images[1], 1, sharedTileConfig, styles.flexTile)}
86
+ {layout.hasOverflow ? (renderOverflowTile({
87
+ image: images[2],
88
+ tileIndex: 2,
89
+ overflowCount: layout.overflowCount,
90
+ borderRadius: options.borderRadius,
91
+ placeholderColor: options.placeholderColor,
92
+ onImagePress: options.onImagePress,
93
+ renderImage: options.renderImage,
94
+ })) : (renderTile(images[2], 2, sharedTileConfig, styles.flexTile))}
95
+ </react_native_1.View>
96
+ </>);
97
+ }
98
+ function renderGridLayout(images, options, layout) {
99
+ const { spacing, sharedTileConfig } = options;
100
+ const topRow = images.slice(0, 2);
101
+ const bottomLeft = images[2];
102
+ const bottomRight = images[3];
103
+ return (<>
104
+ <react_native_1.View style={[styles.row, { flex: 1, gap: spacing }]}>
105
+ {topRow.map((image, index) => renderTile(image, index, sharedTileConfig, styles.flexTile))}
106
+ </react_native_1.View>
107
+ <react_native_1.View style={[styles.row, { flex: 1, gap: spacing }]}>
108
+ {bottomLeft
109
+ ? renderTile(bottomLeft, 2, sharedTileConfig, styles.flexTile)
110
+ : null}
111
+ {bottomRight ? (layout.hasOverflow ? (renderOverflowTile({
112
+ image: bottomRight,
113
+ tileIndex: 3,
114
+ overflowCount: layout.overflowCount,
115
+ borderRadius: options.borderRadius,
116
+ placeholderColor: options.placeholderColor,
117
+ onImagePress: options.onImagePress,
118
+ renderImage: options.renderImage,
119
+ })) : (renderTile(bottomRight, 3, sharedTileConfig, styles.flexTile))) : null}
120
+ </react_native_1.View>
121
+ </>);
122
+ }
123
+ function renderCollageContent(options) {
124
+ const { images, maxVisibleImages } = options;
125
+ const count = images.length;
126
+ const layout = resolveVisibleLayout(count, maxVisibleImages);
127
+ const visibleImages = images.slice(0, layout.visibleCount);
128
+ if (layout.visibleCount === 1) {
129
+ return renderSingleLayout(visibleImages, options, layout);
130
+ }
131
+ if (layout.visibleCount === 2) {
132
+ return renderTwoLayout(visibleImages, options, layout);
133
+ }
134
+ if (layout.visibleCount === 3) {
135
+ return renderThreeLayout(visibleImages, options, layout);
136
+ }
137
+ return renderGridLayout(visibleImages, options, layout);
138
+ }
139
+ function getCollageLayoutStyle({ count, maxVisibleImages, layoutHeight, spacing, borderRadius, }) {
140
+ const { visibleCount } = resolveVisibleLayout(count, maxVisibleImages);
141
+ if (visibleCount === 1) {
142
+ return {
143
+ containerStyle: {
144
+ height: layoutHeight,
145
+ borderRadius,
146
+ overflow: "hidden",
147
+ },
148
+ row: false,
149
+ };
150
+ }
151
+ if (visibleCount === 2 || visibleCount === 3) {
152
+ return {
153
+ containerStyle: {
154
+ height: layoutHeight,
155
+ gap: spacing,
156
+ },
157
+ row: true,
158
+ };
159
+ }
160
+ return {
161
+ containerStyle: {
162
+ height: layoutHeight,
163
+ gap: spacing,
164
+ overflow: "hidden",
165
+ minHeight: 0,
166
+ },
167
+ row: false,
168
+ };
169
+ }
170
+ const styles = react_native_1.StyleSheet.create({
171
+ row: { flexDirection: "row", overflow: "hidden", minHeight: 0, width: "100%" },
172
+ flexTile: { flex: 1, flexBasis: 0, minWidth: 0, minHeight: 0 },
173
+ flexColumn: { flex: 1, flexBasis: 0, minWidth: 0, minHeight: 0 },
174
+ overflowTile: { overflow: "hidden", minHeight: 0, minWidth: 0 },
175
+ overflowOverlay: {
176
+ ...react_native_1.StyleSheet.absoluteFillObject,
177
+ backgroundColor: "rgba(0,0,0,0.35)",
178
+ alignItems: "center",
179
+ justifyContent: "center",
180
+ },
181
+ overflowText: { color: "#fff", fontSize: 24, fontWeight: "800" },
182
+ });
183
+ exports.collageLayoutStyles = styles;
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ import type { ImageCollageWithViewerProps } from "../types";
3
+ export declare const ImageCollageWithViewer: React.NamedExoticComponent<ImageCollageWithViewerProps>;
4
+ //# sourceMappingURL=ImageCollageWithViewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageCollageWithViewer.d.ts","sourceRoot":"","sources":["../../src/viewer/ImageCollageWithViewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAC;AAE7C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAG5D,eAAO,MAAM,sBAAsB,yDAkBjC,CAAC"}