@oxyhq/bloom 0.59.0 → 0.61.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/context-menu/index.web.js +2 -1
- package/lib/commonjs/context-menu/index.web.js.map +1 -1
- package/lib/commonjs/image-aspect-ratio-cache/imageAspectRatioCache.js +18 -1
- package/lib/commonjs/image-aspect-ratio-cache/imageAspectRatioCache.js.map +1 -1
- package/lib/commonjs/image-aspect-ratio-cache/index.js +6 -0
- package/lib/commonjs/image-aspect-ratio-cache/index.js.map +1 -1
- package/lib/commonjs/overlay/index.js +2 -2
- package/lib/commonjs/popover/index.web.js +2 -1
- package/lib/commonjs/popover/index.web.js.map +1 -1
- package/lib/commonjs/select/index.web.js +2 -1
- package/lib/commonjs/select/index.web.js.map +1 -1
- package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js +15 -6
- package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js.map +1 -1
- package/lib/module/context-menu/index.web.js +2 -1
- package/lib/module/context-menu/index.web.js.map +1 -1
- package/lib/module/image-aspect-ratio-cache/imageAspectRatioCache.js +16 -0
- package/lib/module/image-aspect-ratio-cache/imageAspectRatioCache.js.map +1 -1
- package/lib/module/image-aspect-ratio-cache/index.js +1 -1
- package/lib/module/image-aspect-ratio-cache/index.js.map +1 -1
- package/lib/module/overlay/index.js +2 -2
- package/lib/module/popover/index.web.js +3 -2
- package/lib/module/popover/index.web.js.map +1 -1
- package/lib/module/select/index.web.js +2 -1
- package/lib/module/select/index.web.js.map +1 -1
- package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js +16 -7
- package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js.map +1 -1
- package/lib/typescript/commonjs/context-menu/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/image-aspect-ratio-cache/imageAspectRatioCache.d.ts +12 -0
- package/lib/typescript/commonjs/image-aspect-ratio-cache/imageAspectRatioCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/image-aspect-ratio-cache/index.d.ts +2 -1
- package/lib/typescript/commonjs/image-aspect-ratio-cache/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/overlay/index.d.ts +2 -2
- package/lib/typescript/commonjs/popover/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/select/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +1 -1
- package/lib/typescript/module/context-menu/index.web.d.ts.map +1 -1
- package/lib/typescript/module/image-aspect-ratio-cache/imageAspectRatioCache.d.ts +12 -0
- package/lib/typescript/module/image-aspect-ratio-cache/imageAspectRatioCache.d.ts.map +1 -1
- package/lib/typescript/module/image-aspect-ratio-cache/index.d.ts +2 -1
- package/lib/typescript/module/image-aspect-ratio-cache/index.d.ts.map +1 -1
- package/lib/typescript/module/overlay/index.d.ts +2 -2
- package/lib/typescript/module/popover/index.web.d.ts.map +1 -1
- package/lib/typescript/module/select/index.web.d.ts.map +1 -1
- package/lib/typescript/module/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/imageIntrinsicSize.test.ts +47 -0
- package/src/__tests__/pointer-events-style-form.test.ts +24 -0
- package/src/context-menu/index.web.tsx +2 -1
- package/src/image-aspect-ratio-cache/imageAspectRatioCache.ts +18 -0
- package/src/image-aspect-ratio-cache/index.ts +2 -0
- package/src/overlay/index.tsx +2 -2
- package/src/popover/index.web.tsx +2 -1
- package/src/select/index.web.tsx +2 -1
- package/src/zoomable-image-gallery/ZoomableImageGallery.tsx +19 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/select/index.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8D,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/select/index.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8D,MAAM,OAAO,CAAC;AAgBnF,OAAO,EAA0C,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACxF,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,WAAW,EAEX,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,oBAAoB,EAAE,CAAC;AA6BhC,wBAAgB,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAgB/E;AAMD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,kBAAkB,yTAuDpE;AAMD,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EAAE,YAAY,EACtB,WAAW,EACX,KAAK,GACN,EAAE,gBAAgB,2CAkBlB;AAMD,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,eAAe,2CAGpD;AAMD,wBAAgB,aAAa,CAAC,CAAC,EAAE,EAC/B,KAAK,EACL,UAAU,EACV,KAA0B,EAC1B,cAA0C,GAC3C,EAAE,kBAAkB,CAAC,CAAC,CAAC,kDAmCvB;AAMD,wBAAgB,UAAU,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,eAAe,2CAyC1E;AAMD,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,mBAAmB,2CAMtE;AAMD,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,aAAyB,EAAE,EAAE,wBAAwB,2CAYhG;AAMD,wBAAgB,eAAe,4CAW9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ZoomableImageGallery.d.ts","sourceRoot":"","sources":["../../../../src/zoomable-image-gallery/ZoomableImageGallery.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ZoomableImageGallery.d.ts","sourceRoot":"","sources":["../../../../src/zoomable-image-gallery/ZoomableImageGallery.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAoEjF,OAAO,KAAK,EAGV,0BAA0B,EAC1B,yBAAyB,EAC1B,MAAM,SAAS,CAAC;AAg7BjB,eAAO,MAAM,oBAAoB,8GAA4B,CAAC;AAyK9D,eAAe,oBAAoB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/context-menu/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAQN,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/context-menu/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAQN,MAAM,OAAO,CAAC;AAYf,OAAO,KAAK,EAEV,uBAAuB,EACvB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,EAExB,MAAM,SAAS,CAAC;AA+BjB,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CA2BtE;AAWD,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,uBAAuB,2CA4C3F;AAMD,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,uBAAuB,kDA6C9E;AAMD,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,KAAK,EACL,OAAO,EACP,QAAgB,EAChB,KAAK,GACN,EAAE,oBAAoB,2CA+CtB;AAMD,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,wBAAwB,2CAiBhF;AAMD,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,wBAAwB,2CAU3E;AAMD,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,EAAE,qBAAqB,2CAEnE;AAMD,wBAAgB,kBAAkB,4CAWjC"}
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
/** Fallback ratio used when a remote image's intrinsic size cannot be read. */
|
|
2
2
|
export declare const DEFAULT_ASPECT_RATIO: number;
|
|
3
|
+
/**
|
|
4
|
+
* Intrinsic pixel size, recorded whenever we had to read it anyway (the
|
|
5
|
+
* `Image.getSize` behind `fetchAspectRatio`). A viewer needs it to avoid
|
|
6
|
+
* blowing a small image up past its own resolution — a ratio alone cannot say
|
|
7
|
+
* how big "big" is allowed to be.
|
|
8
|
+
*/
|
|
9
|
+
export interface IntrinsicSize {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}
|
|
13
|
+
/** Intrinsic size for `uri`, when it has already been measured. */
|
|
14
|
+
export declare const getIntrinsicSize: (uri: string) => IntrinsicSize | undefined;
|
|
3
15
|
export declare const getAspectRatio: (uri: string) => number | undefined;
|
|
4
16
|
export declare const hasAspectRatio: (uri: string) => boolean;
|
|
5
17
|
export declare const setAspectRatio: (uri: string, ratio: number) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageAspectRatioCache.d.ts","sourceRoot":"","sources":["../../../../src/image-aspect-ratio-cache/imageAspectRatioCache.ts"],"names":[],"mappings":"AAeA,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,QAAQ,CAAC;AAI1C,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAAsC,CAAC;AAE7F,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,OAAoC,CAAC;AAElF,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,IAI3D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,OAAO,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"imageAspectRatioCache.d.ts","sourceRoot":"","sources":["../../../../src/image-aspect-ratio-cache/imageAspectRatioCache.ts"],"names":[],"mappings":"AAeA,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,QAAQ,CAAC;AAI1C;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,mEAAmE;AACnE,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,aAAa,GAAG,SAClC,CAAC;AAE9B,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAAsC,CAAC;AAE7F,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,OAAoC,CAAC;AAElF,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,IAI3D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,OAAO,CAAC,MAAM,CAyB5D,CAAC"}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export type { IntrinsicSize } from './imageAspectRatioCache';
|
|
2
|
+
export { getIntrinsicSize, getAspectRatio, hasAspectRatio, setAspectRatio, fetchAspectRatio, DEFAULT_ASPECT_RATIO, } from './imageAspectRatioCache';
|
|
2
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/image-aspect-ratio-cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/image-aspect-ratio-cache/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC"}
|
|
@@ -45,9 +45,9 @@ import { View, type StyleProp, type ViewStyle } from 'react-native';
|
|
|
45
45
|
* One blur radius for every Bloom overlay. Surfaces differ in what they show,
|
|
46
46
|
* not in how the app behind them recedes.
|
|
47
47
|
*/
|
|
48
|
-
export declare const BACKDROP_BLUR_INTENSITY =
|
|
48
|
+
export declare const BACKDROP_BLUR_INTENSITY = 80;
|
|
49
49
|
/** Dim laid over the blur — the blur alone does not give enough contrast. */
|
|
50
|
-
export declare const BACKDROP_DIM_OPACITY = 0.
|
|
50
|
+
export declare const BACKDROP_DIM_OPACITY = 0.55;
|
|
51
51
|
export interface OverlayRootProps {
|
|
52
52
|
children?: ReactNode;
|
|
53
53
|
style?: StyleProp<ViewStyle>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/popover/index.web.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/popover/index.web.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAkB,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EAEnB,YAAY,EACZ,mBAAmB,EACpB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,YAAY,EACZ,gBAAgB,EAChB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAI7B;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,mBAAmB,CAoBvD;AASD,wBAAgB,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,YAAY,2CAkD1D;AAED,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,mBAAmB,2CA+BtE;AAuCD,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,KAAiB,EACjB,SAA0B,EAC1B,MAAU,EACV,QAAQ,EACR,QAAc,EACd,WAAkB,EAClB,KAAK,EACL,MAAM,GACP,EAAE,mBAAmB,kDAmGrB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/select/index.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8D,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/select/index.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8D,MAAM,OAAO,CAAC;AAgBnF,OAAO,EAA0C,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACxF,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,WAAW,EAEX,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,oBAAoB,EAAE,CAAC;AA6BhC,wBAAgB,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAgB/E;AAMD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,kBAAkB,yTAuDpE;AAMD,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EAAE,YAAY,EACtB,WAAW,EACX,KAAK,GACN,EAAE,gBAAgB,2CAkBlB;AAMD,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,eAAe,2CAGpD;AAMD,wBAAgB,aAAa,CAAC,CAAC,EAAE,EAC/B,KAAK,EACL,UAAU,EACV,KAA0B,EAC1B,cAA0C,GAC3C,EAAE,kBAAkB,CAAC,CAAC,CAAC,kDAmCvB;AAMD,wBAAgB,UAAU,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,eAAe,2CAyC1E;AAMD,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,mBAAmB,2CAMtE;AAMD,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,aAAyB,EAAE,EAAE,wBAAwB,2CAYhG;AAMD,wBAAgB,eAAe,4CAW9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ZoomableImageGallery.d.ts","sourceRoot":"","sources":["../../../../src/zoomable-image-gallery/ZoomableImageGallery.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ZoomableImageGallery.d.ts","sourceRoot":"","sources":["../../../../src/zoomable-image-gallery/ZoomableImageGallery.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAoEjF,OAAO,KAAK,EAGV,0BAA0B,EAC1B,yBAAyB,EAC1B,MAAM,SAAS,CAAC;AAg7BjB,eAAO,MAAM,oBAAoB,8GAA4B,CAAC;AAyK9D,eAAe,oBAAoB,CAAC"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// The fullscreen viewer must not blow an image up past its own resolution — a
|
|
2
|
+
// 512px avatar or a small thumbnail filling the viewport just renders soft.
|
|
3
|
+
// A ratio alone cannot express that ceiling, so the shared cache records the
|
|
4
|
+
// intrinsic size it had to read anyway (the `Image.getSize` behind
|
|
5
|
+
// `fetchAspectRatio`) and the viewer clamps its fit box to it.
|
|
6
|
+
|
|
7
|
+
const getSize = jest.fn();
|
|
8
|
+
|
|
9
|
+
jest.mock('react-native', () => ({
|
|
10
|
+
Image: { getSize: (...args: unknown[]) => getSize(...args) },
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_ASPECT_RATIO,
|
|
15
|
+
fetchAspectRatio,
|
|
16
|
+
getAspectRatio,
|
|
17
|
+
getIntrinsicSize,
|
|
18
|
+
} from '../image-aspect-ratio-cache';
|
|
19
|
+
|
|
20
|
+
describe('intrinsic size cache', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
getSize.mockReset();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('records the measured pixel size alongside the ratio', async () => {
|
|
26
|
+
const uri = 'https://cdn.example/avatar-512.jpg';
|
|
27
|
+
getSize.mockImplementation((_uri: string, ok: (w: number, h: number) => void) => ok(512, 512));
|
|
28
|
+
|
|
29
|
+
const ratio = await fetchAspectRatio(uri);
|
|
30
|
+
|
|
31
|
+
expect(ratio).toBe(1);
|
|
32
|
+
expect(getAspectRatio(uri)).toBe(1);
|
|
33
|
+
expect(getIntrinsicSize(uri)).toEqual({ width: 512, height: 512 });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('leaves no size behind when the image cannot be measured', async () => {
|
|
37
|
+
const uri = 'https://cdn.example/broken.jpg';
|
|
38
|
+
getSize.mockImplementation((_uri: string, _ok: unknown, fail: () => void) => fail());
|
|
39
|
+
|
|
40
|
+
const ratio = await fetchAspectRatio(uri);
|
|
41
|
+
|
|
42
|
+
expect(ratio).toBe(DEFAULT_ASPECT_RATIO);
|
|
43
|
+
// No measurement means no ceiling — the viewer falls back to its fit box
|
|
44
|
+
// rather than clamping to a guess.
|
|
45
|
+
expect(getIntrinsicSize(uri)).toBeUndefined();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -43,6 +43,30 @@ describe('pointerEvents style-object form', () => {
|
|
|
43
43
|
expect(files.some((f) => f.endsWith(join('overlay', 'index.tsx')))).toBe(true);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
// Every dismiss scrim is the shared `Backdrop`. The dropdown surfaces used to
|
|
47
|
+
// render a bare full-screen `Pressable` instead — a fourth copy of the idea,
|
|
48
|
+
// and a fully transparent one, so menus dismissed but never dimmed or blurred
|
|
49
|
+
// while dialogs and sheets did.
|
|
50
|
+
it('routes every backdrop-styled element through <Backdrop>', () => {
|
|
51
|
+
const offenders: string[] = [];
|
|
52
|
+
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
const source = readFileSync(file, 'utf8');
|
|
55
|
+
// Every JSX element that takes the file's `styles.backdrop`, resolved back
|
|
56
|
+
// to the tag that opens it.
|
|
57
|
+
for (const match of source.matchAll(/style=\{(?:\[)?styles\.backdrop\b/g)) {
|
|
58
|
+
const opener = source.slice(0, match.index).lastIndexOf('<');
|
|
59
|
+
const tag = /^<([A-Za-z.]+)/.exec(source.slice(opener))?.[1] ?? '?';
|
|
60
|
+
if (tag !== 'Backdrop' && tag !== 'AnimatedBackdrop') {
|
|
61
|
+
const line = source.slice(0, match.index).split('\n').length;
|
|
62
|
+
offenders.push(`${file.slice(SRC.length + 1)}:${line} <${tag}> uses styles.backdrop`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
expect(offenders).toEqual([]);
|
|
68
|
+
});
|
|
69
|
+
|
|
46
70
|
it('never uses the RN-only values as styles — pass them as the prop', () => {
|
|
47
71
|
const offenders = files
|
|
48
72
|
.flatMap((file) =>
|
|
@@ -17,6 +17,7 @@ import { Pressable, StyleSheet, View } from 'react-native';
|
|
|
17
17
|
|
|
18
18
|
import { useTheme } from '../theme/use-theme';
|
|
19
19
|
import { Text } from '../typography';
|
|
20
|
+
import { Backdrop } from '../overlay';
|
|
20
21
|
import { Portal } from '../portal/index.web';
|
|
21
22
|
import { useInteractionState } from '../hooks/useInteractionState';
|
|
22
23
|
import { createOverlayZIndex } from '../styles/z-index';
|
|
@@ -172,7 +173,7 @@ export function ContextMenuContent({ children, style }: ContextMenuContentProps)
|
|
|
172
173
|
|
|
173
174
|
return (
|
|
174
175
|
<Portal>
|
|
175
|
-
<
|
|
176
|
+
<Backdrop
|
|
176
177
|
style={styles.backdrop}
|
|
177
178
|
onPress={close}
|
|
178
179
|
accessibilityLabel="Close context menu"
|
|
@@ -18,6 +18,23 @@ export const DEFAULT_ASPECT_RATIO = 4 / 3;
|
|
|
18
18
|
|
|
19
19
|
const aspectRatioCache = new Map<string, number>();
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Intrinsic pixel size, recorded whenever we had to read it anyway (the
|
|
23
|
+
* `Image.getSize` behind `fetchAspectRatio`). A viewer needs it to avoid
|
|
24
|
+
* blowing a small image up past its own resolution — a ratio alone cannot say
|
|
25
|
+
* how big "big" is allowed to be.
|
|
26
|
+
*/
|
|
27
|
+
export interface IntrinsicSize {
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const intrinsicSizeCache = new Map<string, IntrinsicSize>();
|
|
33
|
+
|
|
34
|
+
/** Intrinsic size for `uri`, when it has already been measured. */
|
|
35
|
+
export const getIntrinsicSize = (uri: string): IntrinsicSize | undefined =>
|
|
36
|
+
intrinsicSizeCache.get(uri);
|
|
37
|
+
|
|
21
38
|
export const getAspectRatio = (uri: string): number | undefined => aspectRatioCache.get(uri);
|
|
22
39
|
|
|
23
40
|
export const hasAspectRatio = (uri: string): boolean => aspectRatioCache.has(uri);
|
|
@@ -47,6 +64,7 @@ export const fetchAspectRatio = (uri: string): Promise<number> => {
|
|
|
47
64
|
if (width > 0 && height > 0) {
|
|
48
65
|
const ratio = width / height;
|
|
49
66
|
aspectRatioCache.set(uri, ratio);
|
|
67
|
+
intrinsicSizeCache.set(uri, { width, height });
|
|
50
68
|
resolve(ratio);
|
|
51
69
|
return;
|
|
52
70
|
}
|
package/src/overlay/index.tsx
CHANGED
|
@@ -56,9 +56,9 @@ import { WEB_POSITION_FIXED } from '../styles/web-view-style';
|
|
|
56
56
|
* One blur radius for every Bloom overlay. Surfaces differ in what they show,
|
|
57
57
|
* not in how the app behind them recedes.
|
|
58
58
|
*/
|
|
59
|
-
export const BACKDROP_BLUR_INTENSITY =
|
|
59
|
+
export const BACKDROP_BLUR_INTENSITY = 80;
|
|
60
60
|
/** Dim laid over the blur — the blur alone does not give enough contrast. */
|
|
61
|
-
export const BACKDROP_DIM_OPACITY = 0.
|
|
61
|
+
export const BACKDROP_DIM_OPACITY = 0.55;
|
|
62
62
|
|
|
63
63
|
export interface OverlayRootProps {
|
|
64
64
|
children?: ReactNode;
|
|
@@ -10,6 +10,7 @@ import React, {
|
|
|
10
10
|
import { Pressable, StyleSheet, View } from 'react-native';
|
|
11
11
|
|
|
12
12
|
import { useTheme } from '../theme/use-theme';
|
|
13
|
+
import { Backdrop } from '../overlay';
|
|
13
14
|
import { Portal } from '../portal/index.web';
|
|
14
15
|
import { createOverlayZIndex } from '../styles/z-index';
|
|
15
16
|
import { WEB_POSITION_FIXED } from '../styles/web-view-style';
|
|
@@ -262,7 +263,7 @@ export function PopoverContent({
|
|
|
262
263
|
return (
|
|
263
264
|
<Portal>
|
|
264
265
|
{dismissible ? (
|
|
265
|
-
<
|
|
266
|
+
<Backdrop
|
|
266
267
|
style={styles.backdrop}
|
|
267
268
|
onPress={() => control.close()}
|
|
268
269
|
accessibilityLabel="Dismiss popover"
|
package/src/select/index.web.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { Pressable, StyleSheet, View } from 'react-native';
|
|
|
3
3
|
|
|
4
4
|
import { useTheme } from '../theme/use-theme';
|
|
5
5
|
import { Text } from '../typography';
|
|
6
|
+
import { Backdrop } from '../overlay';
|
|
6
7
|
import { Portal } from '../portal/index.web';
|
|
7
8
|
import { createOverlayZIndex } from '../styles/z-index';
|
|
8
9
|
import { WEB_POSITION_FIXED } from '../styles/web-view-style';
|
|
@@ -188,7 +189,7 @@ export function SelectContent<T>({
|
|
|
188
189
|
|
|
189
190
|
return (
|
|
190
191
|
<Portal>
|
|
191
|
-
<
|
|
192
|
+
<Backdrop
|
|
192
193
|
style={styles.backdrop}
|
|
193
194
|
onPress={ctx.close}
|
|
194
195
|
accessibilityLabel="Close selection"
|
|
@@ -40,6 +40,7 @@ import { Z_INDEX } from '../styles/z-index';
|
|
|
40
40
|
import { WEB_POSITION_FIXED, type WebCssStyle } from '../styles/web-view-style';
|
|
41
41
|
import {
|
|
42
42
|
getAspectRatio,
|
|
43
|
+
getIntrinsicSize,
|
|
43
44
|
fetchAspectRatio,
|
|
44
45
|
DEFAULT_ASPECT_RATIO,
|
|
45
46
|
} from '../image-aspect-ratio-cache';
|
|
@@ -216,9 +217,12 @@ const ZoomableImageGalleryInner = React.forwardRef<ZoomableImageGalleryHandle, Z
|
|
|
216
217
|
[SCREEN_WIDTH, SCREEN_HEIGHT]
|
|
217
218
|
);
|
|
218
219
|
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
|
|
220
|
+
// Largest width/height for `ratio` that fits inside `fitBox` without cropping
|
|
221
|
+
// (contain), never exceeding the image's own resolution: filling the viewport
|
|
222
|
+
// with a 512px avatar or a small thumbnail just renders it soft. `uri` is what
|
|
223
|
+
// lets us look the intrinsic size up; without it (not measured yet) the fit
|
|
224
|
+
// box wins and the clamp applies on the next frame.
|
|
225
|
+
const fitForRatio = useCallback((ratio: number, uri?: string): FittedSize => {
|
|
222
226
|
const safeRatio = ratio > 0 && Number.isFinite(ratio) ? ratio : DEFAULT_ASPECT_RATIO;
|
|
223
227
|
let width = fitBox.width;
|
|
224
228
|
let height = width / safeRatio;
|
|
@@ -226,6 +230,11 @@ const ZoomableImageGalleryInner = React.forwardRef<ZoomableImageGalleryHandle, Z
|
|
|
226
230
|
height = fitBox.height;
|
|
227
231
|
width = height * safeRatio;
|
|
228
232
|
}
|
|
233
|
+
const intrinsic = uri ? getIntrinsicSize(uri) : undefined;
|
|
234
|
+
if (intrinsic && intrinsic.width > 0 && width > intrinsic.width) {
|
|
235
|
+
width = intrinsic.width;
|
|
236
|
+
height = width / safeRatio;
|
|
237
|
+
}
|
|
229
238
|
return { width, height };
|
|
230
239
|
}, [fitBox]);
|
|
231
240
|
|
|
@@ -234,7 +243,11 @@ const ZoomableImageGalleryInner = React.forwardRef<ZoomableImageGalleryHandle, Z
|
|
|
234
243
|
// after swiping it tracks the viewed image so the collapse-on-dismiss renders
|
|
235
244
|
// and flies back the image actually on screen.
|
|
236
245
|
const activeRatio = pageRatios[activeIndex] ?? openRatio;
|
|
237
|
-
const
|
|
246
|
+
const activeUri = images[activeIndex]?.uri;
|
|
247
|
+
const activeFit = useMemo(
|
|
248
|
+
() => fitForRatio(activeRatio, activeUri),
|
|
249
|
+
[fitForRatio, activeRatio, activeUri],
|
|
250
|
+
);
|
|
238
251
|
|
|
239
252
|
// Alt text (accessibility description) of the image currently on screen, shown
|
|
240
253
|
// as a caption at the bottom of the viewer (Bluesky-style lightbox footer).
|
|
@@ -391,7 +404,7 @@ const ZoomableImageGalleryInner = React.forwardRef<ZoomableImageGalleryHandle, Z
|
|
|
391
404
|
// animation grows it from the thumbnail's footprint (scale < 1) up to 1,
|
|
392
405
|
// mirroring the avatar's small→big scale. The initial scale is the ratio
|
|
393
406
|
// of the thumbnail width to the fitted width.
|
|
394
|
-
const fitted = fitForRatio(ratio);
|
|
407
|
+
const fitted = fitForRatio(ratio, target.uri);
|
|
395
408
|
const centerX = SCREEN_WIDTH / 2;
|
|
396
409
|
const centerY = SCREEN_HEIGHT / 2;
|
|
397
410
|
if (rect && rect.width > 0) {
|
|
@@ -829,7 +842,7 @@ const ZoomableImageGalleryInner = React.forwardRef<ZoomableImageGalleryHandle, Z
|
|
|
829
842
|
>
|
|
830
843
|
{images.map((img, idx) => {
|
|
831
844
|
const ratio = pageRatios[idx] ?? getAspectRatio(img.uri) ?? DEFAULT_ASPECT_RATIO;
|
|
832
|
-
const fit = fitForRatio(ratio);
|
|
845
|
+
const fit = fitForRatio(ratio, img.uri);
|
|
833
846
|
// Only the active page is zoomable + gesture-wrapped (double-tap
|
|
834
847
|
// / single-tap dismiss / pinch / pan). Off-screen pages stay a
|
|
835
848
|
// plain image; the active image handles its own tap-to-dismiss
|