@oxyhq/bloom 0.32.1 → 0.33.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/lib/commonjs/hooks/index.js +13 -0
- package/lib/commonjs/hooks/index.js.map +1 -1
- package/lib/commonjs/hooks/useImagePreload.js +52 -0
- package/lib/commonjs/hooks/useImagePreload.js.map +1 -0
- package/lib/commonjs/image-aspect-ratio-cache/imageAspectRatioCache.js +65 -0
- package/lib/commonjs/image-aspect-ratio-cache/imageAspectRatioCache.js.map +1 -0
- package/lib/commonjs/image-aspect-ratio-cache/index.js +37 -0
- package/lib/commonjs/image-aspect-ratio-cache/index.js.map +1 -0
- package/lib/commonjs/index.js +15 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +15 -0
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js +664 -0
- package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js.map +1 -0
- package/lib/commonjs/zoomable-image-gallery/constants.js +41 -0
- package/lib/commonjs/zoomable-image-gallery/constants.js.map +1 -0
- package/lib/commonjs/zoomable-image-gallery/index.js +19 -0
- package/lib/commonjs/zoomable-image-gallery/index.js.map +1 -0
- package/lib/commonjs/zoomable-image-gallery/types.js +2 -0
- package/lib/commonjs/zoomable-image-gallery/types.js.map +1 -0
- package/lib/module/hooks/index.js +1 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/useImagePreload.js +48 -0
- package/lib/module/hooks/useImagePreload.js.map +1 -0
- package/lib/module/image-aspect-ratio-cache/imageAspectRatioCache.js +57 -0
- package/lib/module/image-aspect-ratio-cache/imageAspectRatioCache.js.map +1 -0
- package/lib/module/image-aspect-ratio-cache/index.js +4 -0
- package/lib/module/image-aspect-ratio-cache/index.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +2 -0
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js +659 -0
- package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js.map +1 -0
- package/lib/module/zoomable-image-gallery/constants.js +38 -0
- package/lib/module/zoomable-image-gallery/constants.js.map +1 -0
- package/lib/module/zoomable-image-gallery/index.js +4 -0
- package/lib/module/zoomable-image-gallery/index.js.map +1 -0
- package/lib/module/zoomable-image-gallery/types.js +2 -0
- package/lib/module/zoomable-image-gallery/types.js.map +1 -0
- package/lib/typescript/commonjs/hooks/index.d.ts +1 -0
- package/lib/typescript/commonjs/hooks/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/useImagePreload.d.ts +10 -0
- package/lib/typescript/commonjs/hooks/useImagePreload.d.ts.map +1 -0
- package/lib/typescript/commonjs/image-aspect-ratio-cache/imageAspectRatioCache.d.ts +14 -0
- package/lib/typescript/commonjs/image-aspect-ratio-cache/imageAspectRatioCache.d.ts.map +1 -0
- package/lib/typescript/commonjs/image-aspect-ratio-cache/index.d.ts +2 -0
- package/lib/typescript/commonjs/image-aspect-ratio-cache/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +1 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +1 -0
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/zoomable-image-gallery/ZoomableImageGallery.d.ts +5 -0
- package/lib/typescript/commonjs/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +1 -0
- package/lib/typescript/commonjs/zoomable-image-gallery/constants.d.ts +33 -0
- package/lib/typescript/commonjs/zoomable-image-gallery/constants.d.ts.map +1 -0
- package/lib/typescript/commonjs/zoomable-image-gallery/index.d.ts +3 -0
- package/lib/typescript/commonjs/zoomable-image-gallery/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/zoomable-image-gallery/types.d.ts +46 -0
- package/lib/typescript/commonjs/zoomable-image-gallery/types.d.ts.map +1 -0
- package/lib/typescript/module/hooks/index.d.ts +1 -0
- package/lib/typescript/module/hooks/index.d.ts.map +1 -1
- package/lib/typescript/module/hooks/useImagePreload.d.ts +10 -0
- package/lib/typescript/module/hooks/useImagePreload.d.ts.map +1 -0
- package/lib/typescript/module/image-aspect-ratio-cache/imageAspectRatioCache.d.ts +14 -0
- package/lib/typescript/module/image-aspect-ratio-cache/imageAspectRatioCache.d.ts.map +1 -0
- package/lib/typescript/module/image-aspect-ratio-cache/index.d.ts +2 -0
- package/lib/typescript/module/image-aspect-ratio-cache/index.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +1 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +1 -0
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/zoomable-image-gallery/ZoomableImageGallery.d.ts +5 -0
- package/lib/typescript/module/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +1 -0
- package/lib/typescript/module/zoomable-image-gallery/constants.d.ts +33 -0
- package/lib/typescript/module/zoomable-image-gallery/constants.d.ts.map +1 -0
- package/lib/typescript/module/zoomable-image-gallery/index.d.ts +3 -0
- package/lib/typescript/module/zoomable-image-gallery/index.d.ts.map +1 -0
- package/lib/typescript/module/zoomable-image-gallery/types.d.ts +46 -0
- package/lib/typescript/module/zoomable-image-gallery/types.d.ts.map +1 -0
- package/package.json +27 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useImagePreload.ts +51 -0
- package/src/image-aspect-ratio-cache/imageAspectRatioCache.ts +62 -0
- package/src/image-aspect-ratio-cache/index.ts +7 -0
- package/src/index.ts +1 -0
- package/src/index.web.ts +1 -0
- package/src/zoomable-image-gallery/ZoomableImageGallery.tsx +695 -0
- package/src/zoomable-image-gallery/constants.ts +25 -0
- package/src/zoomable-image-gallery/index.ts +8 -0
- package/src/zoomable-image-gallery/types.ts +49 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/zoomable-image-gallery/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;AAE3E,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,IAAI,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,IAAI,CAAC;CAC5E;AAED,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/bloom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -39,6 +39,17 @@
|
|
|
39
39
|
"default": "./lib/commonjs/image-resolver/index.js"
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
|
+
"./image-aspect-ratio-cache": {
|
|
43
|
+
"react-native": "./src/image-aspect-ratio-cache/index.ts",
|
|
44
|
+
"import": {
|
|
45
|
+
"types": "./lib/typescript/module/image-aspect-ratio-cache/index.d.ts",
|
|
46
|
+
"default": "./lib/module/image-aspect-ratio-cache/index.js"
|
|
47
|
+
},
|
|
48
|
+
"require": {
|
|
49
|
+
"types": "./lib/typescript/commonjs/image-aspect-ratio-cache/index.d.ts",
|
|
50
|
+
"default": "./lib/commonjs/image-aspect-ratio-cache/index.js"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
42
53
|
"./theme": {
|
|
43
54
|
"react-native": "./src/theme/index.ts",
|
|
44
55
|
"import": {
|
|
@@ -382,6 +393,17 @@
|
|
|
382
393
|
"default": "./lib/commonjs/media-inset-border/index.js"
|
|
383
394
|
}
|
|
384
395
|
},
|
|
396
|
+
"./zoomable-image-gallery": {
|
|
397
|
+
"react-native": "./src/zoomable-image-gallery/index.ts",
|
|
398
|
+
"import": {
|
|
399
|
+
"types": "./lib/typescript/module/zoomable-image-gallery/index.d.ts",
|
|
400
|
+
"default": "./lib/module/zoomable-image-gallery/index.js"
|
|
401
|
+
},
|
|
402
|
+
"require": {
|
|
403
|
+
"types": "./lib/typescript/commonjs/zoomable-image-gallery/index.d.ts",
|
|
404
|
+
"default": "./lib/commonjs/zoomable-image-gallery/index.js"
|
|
405
|
+
}
|
|
406
|
+
},
|
|
385
407
|
"./pressable-scale": {
|
|
386
408
|
"react-native": "./src/pressable-scale/index.ts",
|
|
387
409
|
"import": {
|
|
@@ -1054,8 +1076,10 @@
|
|
|
1054
1076
|
"@types/react-dom": "^19.2.3",
|
|
1055
1077
|
"@types/react-native": "*",
|
|
1056
1078
|
"@vitejs/plugin-react": "^6.0.2",
|
|
1079
|
+
"expo-blur": "~56.0.3",
|
|
1057
1080
|
"expo-font": "^56.0.5",
|
|
1058
1081
|
"expo-haptics": "~56.0.3",
|
|
1082
|
+
"expo-image": "~56.0.11",
|
|
1059
1083
|
"expo-router": "^56.0.0",
|
|
1060
1084
|
"jest": "^30.3.0",
|
|
1061
1085
|
"jest-environment-jsdom": "^30.4.1",
|
|
@@ -1079,8 +1103,10 @@
|
|
|
1079
1103
|
},
|
|
1080
1104
|
"peerDependencies": {
|
|
1081
1105
|
"expo": "*",
|
|
1106
|
+
"expo-blur": "*",
|
|
1082
1107
|
"expo-font": "*",
|
|
1083
1108
|
"expo-haptics": "*",
|
|
1109
|
+
"expo-image": "*",
|
|
1084
1110
|
"expo-router": ">=3.0.0",
|
|
1085
1111
|
"react": ">=18.0.0",
|
|
1086
1112
|
"react-dom": ">=18.0.0",
|
package/src/hooks/index.ts
CHANGED
|
@@ -7,3 +7,4 @@ export { useHaptics, BloomHapticsProvider } from './useHaptics';
|
|
|
7
7
|
export type { HapticStrength, BloomHapticsProviderProps } from './useHaptics';
|
|
8
8
|
export { useGutters } from './useGutters';
|
|
9
9
|
export type { Gutter, Gutters } from './useGutters';
|
|
10
|
+
export { useImagePreload, preloadImage } from './useImagePreload';
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useEffect, useRef, useMemo } from 'react';
|
|
2
|
+
import { Image } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook to preload images for better perceived performance
|
|
6
|
+
* Preloads images when they're likely to be viewed soon
|
|
7
|
+
*/
|
|
8
|
+
export function useImagePreload(urls: (string | undefined)[], enabled: boolean = true) {
|
|
9
|
+
const preloadedRef = useRef<Set<string>>(new Set());
|
|
10
|
+
|
|
11
|
+
// Memoize valid URLs to avoid unnecessary re-runs
|
|
12
|
+
const validUrls = useMemo(() => {
|
|
13
|
+
return urls.filter((url): url is string =>
|
|
14
|
+
Boolean(url && (url.startsWith('http://') || url.startsWith('https://')))
|
|
15
|
+
);
|
|
16
|
+
}, [urls]);
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!enabled || !validUrls.length) return;
|
|
20
|
+
|
|
21
|
+
validUrls.forEach((url) => {
|
|
22
|
+
if (preloadedRef.current.has(url)) return;
|
|
23
|
+
|
|
24
|
+
preloadedRef.current.add(url);
|
|
25
|
+
Image.prefetch(url).catch(() => {
|
|
26
|
+
// Silently handle prefetch errors - remove from set on error to allow retry
|
|
27
|
+
preloadedRef.current.delete(url);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}, [validUrls, enabled]);
|
|
31
|
+
|
|
32
|
+
// Limit cache size to prevent memory issues (keep last 1000)
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (preloadedRef.current.size > 1000) {
|
|
35
|
+
const entries = Array.from(preloadedRef.current);
|
|
36
|
+
const toRemove = entries.slice(0, entries.length - 1000);
|
|
37
|
+
toRemove.forEach(url => preloadedRef.current.delete(url));
|
|
38
|
+
}
|
|
39
|
+
}, [validUrls]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Preload a single image
|
|
44
|
+
*/
|
|
45
|
+
export function preloadImage(url: string | undefined): void {
|
|
46
|
+
if (!url || !url.startsWith('http')) return;
|
|
47
|
+
|
|
48
|
+
Image.prefetch(url).catch(() => {
|
|
49
|
+
// Silently handle prefetch errors
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared module-scoped cache of image aspect ratios (width / height), keyed by
|
|
3
|
+
* the image source URI.
|
|
4
|
+
*
|
|
5
|
+
* Intended for a thumbnail renderer and a fullscreen viewer (e.g.
|
|
6
|
+
* `@oxyhq/bloom/zoomable-image-gallery`) sharing the SAME image URI to read
|
|
7
|
+
* from and write to this one map, so an image already resolved as a thumbnail
|
|
8
|
+
* has its ratio on hand when it opens "big" — no second cache, no duplicate
|
|
9
|
+
* `Image.getSize` round-trip on the hot path. If the two surfaces render
|
|
10
|
+
* different URI variants of the same image (e.g. a smaller thumbnail variant
|
|
11
|
+
* vs. a larger lightbox variant), pass the known ratio through explicitly
|
|
12
|
+
* (`GalleryImage.aspectRatio`) instead of relying on cache-key reuse.
|
|
13
|
+
*/
|
|
14
|
+
import { Image } from 'react-native';
|
|
15
|
+
|
|
16
|
+
/** Fallback ratio used when a remote image's intrinsic size cannot be read. */
|
|
17
|
+
export const DEFAULT_ASPECT_RATIO = 4 / 3;
|
|
18
|
+
|
|
19
|
+
const aspectRatioCache = new Map<string, number>();
|
|
20
|
+
|
|
21
|
+
export const getAspectRatio = (uri: string): number | undefined => aspectRatioCache.get(uri);
|
|
22
|
+
|
|
23
|
+
export const hasAspectRatio = (uri: string): boolean => aspectRatioCache.has(uri);
|
|
24
|
+
|
|
25
|
+
export const setAspectRatio = (uri: string, ratio: number): void => {
|
|
26
|
+
if (uri && ratio > 0 && Number.isFinite(ratio)) {
|
|
27
|
+
aspectRatioCache.set(uri, ratio);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the aspect ratio for `uri`, returning the cached value immediately on
|
|
33
|
+
* a hit and otherwise fetching the intrinsic size via `Image.getSize`, writing
|
|
34
|
+
* the result back into the shared cache. On failure the shared
|
|
35
|
+
* `DEFAULT_ASPECT_RATIO` is cached and returned so callers never deadlock on a
|
|
36
|
+
* broken image.
|
|
37
|
+
*/
|
|
38
|
+
export const fetchAspectRatio = (uri: string): Promise<number> => {
|
|
39
|
+
const cached = aspectRatioCache.get(uri);
|
|
40
|
+
if (cached !== undefined) {
|
|
41
|
+
return Promise.resolve(cached);
|
|
42
|
+
}
|
|
43
|
+
return new Promise<number>((resolve) => {
|
|
44
|
+
Image.getSize(
|
|
45
|
+
uri,
|
|
46
|
+
(width, height) => {
|
|
47
|
+
if (width > 0 && height > 0) {
|
|
48
|
+
const ratio = width / height;
|
|
49
|
+
aspectRatioCache.set(uri, ratio);
|
|
50
|
+
resolve(ratio);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
aspectRatioCache.set(uri, DEFAULT_ASPECT_RATIO);
|
|
54
|
+
resolve(DEFAULT_ASPECT_RATIO);
|
|
55
|
+
},
|
|
56
|
+
() => {
|
|
57
|
+
aspectRatioCache.set(uri, DEFAULT_ASPECT_RATIO);
|
|
58
|
+
resolve(DEFAULT_ASPECT_RATIO);
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -44,6 +44,7 @@ export { useHaptics, BloomHapticsProvider } from './hooks/useHaptics';
|
|
|
44
44
|
export type { HapticStrength, BloomHapticsProviderProps } from './hooks/useHaptics';
|
|
45
45
|
export { useGutters } from './hooks/useGutters';
|
|
46
46
|
export type { Gutter, Gutters } from './hooks/useGutters';
|
|
47
|
+
export { useImagePreload, preloadImage } from './hooks/useImagePreload';
|
|
47
48
|
|
|
48
49
|
// Icons
|
|
49
50
|
export * as Icons from './icons';
|
package/src/index.web.ts
CHANGED
|
@@ -49,6 +49,7 @@ export { useHaptics, BloomHapticsProvider } from './hooks/useHaptics';
|
|
|
49
49
|
export type { HapticStrength, BloomHapticsProviderProps } from './hooks/useHaptics';
|
|
50
50
|
export { useGutters } from './hooks/useGutters';
|
|
51
51
|
export type { Gutter, Gutters } from './hooks/useGutters';
|
|
52
|
+
export { useImagePreload, preloadImage } from './hooks/useImagePreload';
|
|
52
53
|
|
|
53
54
|
// Icons
|
|
54
55
|
export * as Icons from './icons';
|