@oxyhq/bloom 0.51.0 → 0.51.1
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/frosted-icon-button/shared.js +1 -1
- package/lib/commonjs/tab-bar/surface-solid.js +43 -0
- package/lib/commonjs/tab-bar/surface-solid.js.map +1 -0
- package/lib/commonjs/tab-bar/surface.js +5 -42
- package/lib/commonjs/tab-bar/surface.js.map +1 -1
- package/lib/commonjs/tab-bar/surface.native.js +65 -8
- package/lib/commonjs/tab-bar/surface.native.js.map +1 -1
- package/lib/module/frosted-icon-button/shared.js +1 -1
- package/lib/module/tab-bar/surface-solid.js +37 -0
- package/lib/module/tab-bar/surface-solid.js.map +1 -0
- package/lib/module/tab-bar/surface.js +10 -23
- package/lib/module/tab-bar/surface.js.map +1 -1
- package/lib/module/tab-bar/surface.native.js +68 -9
- package/lib/module/tab-bar/surface.native.js.map +1 -1
- package/lib/typescript/commonjs/benefit-list/types.d.ts +2 -1
- package/lib/typescript/commonjs/benefit-list/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/fab/types.d.ts +4 -2
- package/lib/typescript/commonjs/fab/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/frosted-icon-button/shared.d.ts +1 -1
- package/lib/typescript/commonjs/frosted-icon-button/types.d.ts +2 -1
- package/lib/typescript/commonjs/frosted-icon-button/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/tab-bar/surface-solid.d.ts +6 -0
- package/lib/typescript/commonjs/tab-bar/surface-solid.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/surface.d.ts +28 -5
- package/lib/typescript/commonjs/tab-bar/surface.d.ts.map +1 -1
- package/lib/typescript/commonjs/tab-bar/surface.native.d.ts.map +1 -1
- package/lib/typescript/commonjs/tab-bar/types.d.ts +6 -3
- package/lib/typescript/commonjs/tab-bar/types.d.ts.map +1 -1
- package/lib/typescript/module/benefit-list/types.d.ts +2 -1
- package/lib/typescript/module/benefit-list/types.d.ts.map +1 -1
- package/lib/typescript/module/fab/types.d.ts +4 -2
- package/lib/typescript/module/fab/types.d.ts.map +1 -1
- package/lib/typescript/module/frosted-icon-button/shared.d.ts +1 -1
- package/lib/typescript/module/frosted-icon-button/types.d.ts +2 -1
- package/lib/typescript/module/frosted-icon-button/types.d.ts.map +1 -1
- package/lib/typescript/module/tab-bar/surface-solid.d.ts +6 -0
- package/lib/typescript/module/tab-bar/surface-solid.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/surface.d.ts +28 -5
- package/lib/typescript/module/tab-bar/surface.d.ts.map +1 -1
- package/lib/typescript/module/tab-bar/surface.native.d.ts.map +1 -1
- package/lib/typescript/module/tab-bar/types.d.ts +6 -3
- package/lib/typescript/module/tab-bar/types.d.ts.map +1 -1
- package/package.json +2 -8
- package/src/__tests__/TabBarGlassFallback.test.tsx +234 -0
- package/src/__tests__/TabBarWebFork.test.ts +46 -0
- package/src/__tests__/icon-references.test.ts +243 -0
- package/src/benefit-list/types.ts +2 -1
- package/src/fab/types.ts +4 -2
- package/src/frosted-icon-button/shared.ts +1 -1
- package/src/frosted-icon-button/types.ts +2 -1
- package/src/tab-bar/surface-solid.tsx +40 -0
- package/src/tab-bar/surface.native.tsx +77 -15
- package/src/tab-bar/surface.tsx +10 -29
- package/src/tab-bar/types.ts +6 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9D,MAAM,MAAM,UAAU,GAAG;IACvB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9D,MAAM,MAAM,UAAU,GAAG;IACvB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;OASG;IACH,IAAI,EAAE,SAAS,CAAC;IAChB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG;IACpC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG;IAC/C,IAAI,EAAE,UAAU,CAAC;IACjB,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC"}
|
|
@@ -3,7 +3,8 @@ import type { StyleProp, TextStyle, ViewStyle } from 'react-native';
|
|
|
3
3
|
export interface BenefitRowProps {
|
|
4
4
|
/**
|
|
5
5
|
* Leading icon — rendered inside a rounded `fill-secondary` square. Pass a
|
|
6
|
-
* Bloom icon element (e.g. `<Icons.
|
|
6
|
+
* Bloom icon element (e.g. `<Icons.Lock_Stroke2_Corner0_Rounded size="sm" />`,
|
|
7
|
+
* suffix included — there is no shortened alias) or any node.
|
|
7
8
|
*/
|
|
8
9
|
icon: ReactNode;
|
|
9
10
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/benefit-list/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,WAAW,eAAe;IAC9B
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/benefit-list/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,IAAI,EAAE,SAAS,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,QAAQ,EAAE,SAAS,CAAC;IACpB,wCAAwC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
|
|
@@ -53,8 +53,10 @@ export interface FabProps {
|
|
|
53
53
|
onPress?: () => void;
|
|
54
54
|
/**
|
|
55
55
|
* The icon to render inside the FAB. Typically a Bloom icon element
|
|
56
|
-
* (`<Icons.
|
|
57
|
-
*
|
|
56
|
+
* (`<Icons.PlusLarge_Stroke2_Corner0_Rounded />` — icon exports carry their full
|
|
57
|
+
* style and corner suffix, and there is no shortened alias). For an icon-only
|
|
58
|
+
* FAB this is the whole content; for the extended variant it sits to the left of
|
|
59
|
+
* the `label`.
|
|
58
60
|
*
|
|
59
61
|
* `children` is accepted as an alias/override of `icon` for ergonomics — if
|
|
60
62
|
* both are given, `icon` wins and `children` is ignored.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/fab/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;AAE7D;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,YAAY,GACpB,cAAc,GACd,aAAa,GACb,WAAW,GACX,UAAU,GACV,QAAQ,CAAC;AAEb,MAAM,WAAW,QAAQ;IACvB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/fab/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;AAE7D;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,YAAY,GACpB,cAAc,GACd,aAAa,GACb,WAAW,GACX,UAAU,GACV,QAAQ,CAAC;AAEb,MAAM,WAAW,QAAQ;IACvB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAExB;;;;OAIG;IACH,SAAS,CAAC,EAAE,YAAY,CAAC;IAEzB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,8CAA8C;IAC9C,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,8CAA8C;IAC9C,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAUhB,wDAAwD;IACxD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;IAC/D,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;CACtC"}
|
|
@@ -52,7 +52,7 @@ export declare function resolveFrostedPalette(colors: ThemeColors, isDark: boole
|
|
|
52
52
|
* Inject `color` as the icon's `fill` — but only as a FALLBACK: if the caller
|
|
53
53
|
* already set an explicit `fill` on the icon element, it wins and the node is
|
|
54
54
|
* returned untouched. Bloom icons resolve their paint as `fill ?? style.color`,
|
|
55
|
-
* so
|
|
55
|
+
* so any bare Bloom icon element gets the theme-aware color for free while an
|
|
56
56
|
* explicitly-colored icon is never overridden. Non-element children (plain
|
|
57
57
|
* text/strings) pass through unchanged.
|
|
58
58
|
*/
|
|
@@ -15,7 +15,8 @@ export interface FrostedIconButtonProps {
|
|
|
15
15
|
onPress?: () => void;
|
|
16
16
|
/**
|
|
17
17
|
* The icon to render, centered inside the circle. Typically a Bloom icon
|
|
18
|
-
* element (`<Icons.
|
|
18
|
+
* element (`<Icons.ArrowLeft_Stroke2_Corner0_Rounded />` — icon exports carry
|
|
19
|
+
* their full style and corner suffix). If the icon does not set its own `fill`,
|
|
19
20
|
* the button injects the theme-aware icon color (foreground when frosted,
|
|
20
21
|
* `primaryForeground` when `active`) as a fallback so a bare icon is colored
|
|
21
22
|
* correctly out of the box; an explicit `fill` on the icon always wins.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/frosted-icon-button/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD;;;;;;;;;GASG;AACH,MAAM,MAAM,qBAAqB,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACrC,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/frosted-icon-button/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD;;;;;;;;;GASG;AACH,MAAM,MAAM,qBAAqB,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACrC,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,IAAI,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAAC;IAEtC,4EAA4E;IAC5E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,8CAA8C;IAC9C,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAUvE,wDAAwD;IACxD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;IAC/D,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;CACtC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { TabBarSurfaceProps } from './shared';
|
|
2
|
+
export declare function SolidTabBarSurface({ theme, style }: TabBarSurfaceProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare namespace SolidTabBarSurface {
|
|
4
|
+
var displayName: string;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=surface-solid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface-solid.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/surface-solid.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,kBAAkB,2CAWtE;yBAXe,kBAAkB"}
|
|
@@ -1,6 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Ported from expo-glass-tabs v0.1.1 — src/glass-tab-bar.tsx
|
|
3
|
+
* (MIT © 2026 David Mokos). See the top-level NOTICE.
|
|
4
|
+
*
|
|
5
|
+
* NEUTRAL default variant of the tab bar's capsule surface: the non-glass one.
|
|
6
|
+
*
|
|
7
|
+
* This is the file resolved by everything that does NOT do React Native's
|
|
8
|
+
* platform-extension resolution — a consumer's `tsc` (which follows Bloom's
|
|
9
|
+
* `"react-native"` source export condition) and any non-Metro bundler reading
|
|
10
|
+
* the published `lib/`. It therefore imports NOTHING platform-specific:
|
|
11
|
+
* `expo-glass-effect` is native-only, and a static import of it from a file a
|
|
12
|
+
* web bundler or a consumer's type-checker resolves would break module
|
|
13
|
+
* resolution outright. Same three-way split, for the same reason, as
|
|
14
|
+
* `theme/native-root-vars{,.native,.web}.ts`.
|
|
15
|
+
*
|
|
16
|
+
* The surface itself lives in `surface-solid.tsx`, because `surface.native.tsx`
|
|
17
|
+
* renders it as its own fallback and cannot import this file to get it — Metro
|
|
18
|
+
* resolves `./surface` back to `surface.native.tsx`, i.e. to itself. This module's
|
|
19
|
+
* job is to give that one implementation the name every entry module imports.
|
|
20
|
+
*
|
|
21
|
+
* The variants — all three implement `TabBarSurfaceProps` exactly, so the entry
|
|
22
|
+
* modules can swap them freely:
|
|
23
|
+
* - `surface.native.tsx` — iOS/Android (Metro), real `UIGlassEffect` glass, with
|
|
24
|
+
* the solid surface as its fallback;
|
|
25
|
+
* - `surface.web.tsx` — web, a CSS `backdrop-filter`;
|
|
26
|
+
* - this file — everywhere else, the opaque fallback alone.
|
|
27
|
+
*/
|
|
28
|
+
export { SolidTabBarSurface as TabBarSurface } from './surface-solid';
|
|
6
29
|
//# sourceMappingURL=surface.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/surface.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/surface.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,kBAAkB,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface.native.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/surface.native.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"surface.native.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/surface.native.tsx"],"names":[],"mappings":"AAmDA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AA8CnD,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,kBAAkB,2CAgBjE;yBAhBe,aAAa"}
|
|
@@ -18,9 +18,12 @@ export type TabBarItem = {
|
|
|
18
18
|
/** Text under the icon; fades out and is clipped when the bar minimizes. */
|
|
19
19
|
label: string;
|
|
20
20
|
/**
|
|
21
|
-
* The glyph, as a Bloom icon element (`<Icons.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* The glyph, as a Bloom icon element (`<Icons.Home_Stroke2_Corner0_Rounded />`)
|
|
22
|
+
* or any node. Every `@oxyhq/bloom/icons` export carries its full style and
|
|
23
|
+
* corner suffix; a shortened `Home` is not an export, and a name that does not
|
|
24
|
+
* exist is simply `undefined`, which React then rejects as an invalid element
|
|
25
|
+
* type. This is the primary icon API — same convention as `Tabs`/`TabsTrigger`
|
|
26
|
+
* and `FrostedIconButton`. The bar renders it TWICE (an inactive layer with the
|
|
24
27
|
* active layer crossfading on top), injecting the layer's tint as the icon's
|
|
25
28
|
* `fill` unless the element already sets one.
|
|
26
29
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9D,MAAM,MAAM,UAAU,GAAG;IACvB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9D,MAAM,MAAM,UAAU,GAAG;IACvB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;OASG;IACH,IAAI,EAAE,SAAS,CAAC;IAChB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG;IACpC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG;IAC/C,IAAI,EAAE,UAAU,CAAC;IACjB,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/bloom",
|
|
3
|
-
"version": "0.51.
|
|
3
|
+
"version": "0.51.1",
|
|
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",
|
|
@@ -1189,7 +1189,7 @@
|
|
|
1189
1189
|
"expo": "*",
|
|
1190
1190
|
"expo-blur": "*",
|
|
1191
1191
|
"expo-font": "*",
|
|
1192
|
-
"expo-glass-effect": ">=0.1.
|
|
1192
|
+
"expo-glass-effect": ">=0.1.9",
|
|
1193
1193
|
"expo-haptics": "*",
|
|
1194
1194
|
"expo-image": "*",
|
|
1195
1195
|
"expo-router": ">=3.0.0",
|
|
@@ -1211,18 +1211,12 @@
|
|
|
1211
1211
|
"expo-font": {
|
|
1212
1212
|
"optional": true
|
|
1213
1213
|
},
|
|
1214
|
-
"expo-glass-effect": {
|
|
1215
|
-
"optional": true
|
|
1216
|
-
},
|
|
1217
1214
|
"expo-haptics": {
|
|
1218
1215
|
"optional": true
|
|
1219
1216
|
},
|
|
1220
1217
|
"expo-router": {
|
|
1221
1218
|
"optional": true
|
|
1222
1219
|
},
|
|
1223
|
-
"expo-symbols": {
|
|
1224
|
-
"optional": true
|
|
1225
|
-
},
|
|
1226
1220
|
"react-dom": {
|
|
1227
1221
|
"optional": true
|
|
1228
1222
|
},
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ReactTestInstance } from 'react-test-renderer';
|
|
3
|
+
import { render } from '@testing-library/react-native';
|
|
4
|
+
|
|
5
|
+
import type { TabBarSurfaceProps } from '../tab-bar/shared';
|
|
6
|
+
import type { TabBarTheme } from '../tab-bar/types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The native capsule surface must DEGRADE, never crash, when `expo-glass-effect`
|
|
10
|
+
* is not really usable.
|
|
11
|
+
*
|
|
12
|
+
* This is the one thing the rest of the tab-bar suite structurally cannot see.
|
|
13
|
+
* `TabBar.test.tsx` renders the native surface against the manual mock in
|
|
14
|
+
* `__mocks__/expo-glass-effect.ts`, which is a HEALTHY install reporting
|
|
15
|
+
* `isLiquidGlassAvailable() === false` — so it only ever exercises the branch a
|
|
16
|
+
* correctly-packaged Android device takes. Every failure mode below is a
|
|
17
|
+
* different install: the module resolved but handed back no exports, or the JS
|
|
18
|
+
* package is there while the native module is not.
|
|
19
|
+
*
|
|
20
|
+
* Each case is re-mocked into its own module registry because both hazards live at
|
|
21
|
+
* MODULE scope. `Animated.createAnimatedComponent(GlassView)` runs on import, and
|
|
22
|
+
* on iOS `isLiquidGlassAvailable()` reaches a native module through
|
|
23
|
+
* `requireNativeModule`, which throws by contract when it is absent. Mocking after
|
|
24
|
+
* the module under test was imported would test a render branch and miss both.
|
|
25
|
+
*
|
|
26
|
+
* The two counter-tests at the end are what keep the rest honest: a guard that
|
|
27
|
+
* always fell through to the solid surface would satisfy every other assertion
|
|
28
|
+
* here while silently deleting the glass look on every iOS 26 device.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/** Every export may be absent — that is the whole point of these fixtures. */
|
|
32
|
+
interface GlassEffectModule {
|
|
33
|
+
GlassView?: unknown;
|
|
34
|
+
isLiquidGlassAvailable?: () => boolean;
|
|
35
|
+
isGlassEffectAPIAvailable?: () => boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A host tag stands in for the real `GlassView` component: under the reanimated
|
|
40
|
+
* mock `createAnimatedComponent` is the identity, so whatever the package exports
|
|
41
|
+
* IS the element type, and a string host is the cheapest valid one to assert on.
|
|
42
|
+
* It matches the tag the manual mock renders.
|
|
43
|
+
*/
|
|
44
|
+
const GLASS_HOST = 'GlassView';
|
|
45
|
+
|
|
46
|
+
const THEME: TabBarTheme = {
|
|
47
|
+
activeTint: 'rgb(20 20 20)',
|
|
48
|
+
inactiveTint: 'rgb(120 120 120)',
|
|
49
|
+
highlight: 'rgb(230 230 230)',
|
|
50
|
+
glassTint: 'rgba(255, 255, 255, 0.55)',
|
|
51
|
+
solidFallback: 'rgb(246 246 246)',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const STYLE = { borderRadius: 29 };
|
|
55
|
+
|
|
56
|
+
function flattenStyle(style: unknown): Record<string, unknown> {
|
|
57
|
+
const out: Record<string, unknown> = {};
|
|
58
|
+
const visit = (s: unknown): void => {
|
|
59
|
+
if (Array.isArray(s)) s.forEach(visit);
|
|
60
|
+
else if (s && typeof s === 'object') Object.assign(out, s);
|
|
61
|
+
};
|
|
62
|
+
visit(style);
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Every host node rendered as `tag`. */
|
|
67
|
+
function hosts(root: ReactTestInstance, tag: string): ReactTestInstance[] {
|
|
68
|
+
return root.findAll((node) => typeof node.type === 'string' && node.type === tag);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Render `surface.native.tsx` against a given `expo-glass-effect`.
|
|
73
|
+
*
|
|
74
|
+
* Only the surface comes from the isolated registry — testing-library registers
|
|
75
|
+
* suite-level hooks when it loads, so it has to be imported at the top of the file
|
|
76
|
+
* rather than inside a test. Nothing in the rendered tree calls a hook, and the
|
|
77
|
+
* surface renders host strings from the reanimated mock, so the two registries
|
|
78
|
+
* never have to agree on a React instance. `times` re-renders within one registry,
|
|
79
|
+
* for the assertion about module-scoped memoization.
|
|
80
|
+
*/
|
|
81
|
+
function renderNativeSurface(glassEffect: GlassEffectModule, times = 1): ReactTestInstance {
|
|
82
|
+
let root: ReactTestInstance | undefined;
|
|
83
|
+
|
|
84
|
+
jest.isolateModules(() => {
|
|
85
|
+
jest.doMock('expo-glass-effect', () => glassEffect);
|
|
86
|
+
|
|
87
|
+
const { TabBarSurface } = require('../tab-bar/surface.native') as {
|
|
88
|
+
TabBarSurface: React.ComponentType<TabBarSurfaceProps>;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
for (let i = 0; i < times; i += 1) {
|
|
92
|
+
root = render(<TabBarSurface theme={THEME} style={STYLE} />).UNSAFE_root;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (!root) throw new Error('the native surface did not render');
|
|
97
|
+
return root;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Assert the tree is the opaque non-glass surface, filled from the theme. */
|
|
101
|
+
function expectSolidFallback(root: ReactTestInstance): void {
|
|
102
|
+
expect(hosts(root, GLASS_HOST)).toHaveLength(0);
|
|
103
|
+
const surface = hosts(root, 'Animated.View')[0];
|
|
104
|
+
expect(surface).toBeTruthy();
|
|
105
|
+
expect(flattenStyle(surface?.props.style).backgroundColor).toBe(THEME.solidFallback);
|
|
106
|
+
// The animated capsule radius must survive the fallback — the pill still
|
|
107
|
+
// reshapes as the bar minimizes.
|
|
108
|
+
expect(flattenStyle(surface?.props.style).borderRadius).toBe(STYLE.borderRadius);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
describe('TabBarSurface (native) — degrading when glass is unusable', () => {
|
|
112
|
+
let warn: jest.SpyInstance<void, Parameters<typeof console.warn>>;
|
|
113
|
+
|
|
114
|
+
beforeEach(() => {
|
|
115
|
+
jest.resetModules();
|
|
116
|
+
warn = jest.spyOn(console, 'warn').mockImplementation(() => undefined);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
afterEach(() => {
|
|
120
|
+
warn.mockRestore();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('falls back when the module resolved without GlassView', () => {
|
|
124
|
+
// `createAnimatedComponent(undefined)` produces an element type React rejects
|
|
125
|
+
// with "Element type is invalid … but got: undefined" — and it happens at
|
|
126
|
+
// import, so the whole screen the bar is on goes down with it.
|
|
127
|
+
expectSolidFallback(
|
|
128
|
+
renderNativeSurface({ GlassView: undefined, isLiquidGlassAvailable: () => true }),
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('falls back when the module delivered no exports at all', () => {
|
|
133
|
+
// A stub, an alias, or a resolver that landed on a platform-specific file:
|
|
134
|
+
// there is no `isLiquidGlassAvailable` to call either, so calling it blindly
|
|
135
|
+
// is a TypeError.
|
|
136
|
+
expectSolidFallback(renderNativeSurface({}));
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('falls back when the native module is missing from the build', () => {
|
|
140
|
+
// On iOS `isLiquidGlassAvailable()` resolves `ExpoGlassEffect` through
|
|
141
|
+
// `requireNativeModule`, which THROWS by contract when the module is not in
|
|
142
|
+
// the binary — an OTA JS update landing on a binary built before the package
|
|
143
|
+
// was linked reaches exactly this state.
|
|
144
|
+
expectSolidFallback(
|
|
145
|
+
renderNativeSurface({
|
|
146
|
+
GlassView: GLASS_HOST,
|
|
147
|
+
isLiquidGlassAvailable: () => {
|
|
148
|
+
throw new Error("Cannot find native module 'ExpoGlassEffect'");
|
|
149
|
+
},
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('reports the missing native module instead of swallowing it', () => {
|
|
155
|
+
renderNativeSurface({
|
|
156
|
+
GlassView: GLASS_HOST,
|
|
157
|
+
isLiquidGlassAvailable: () => {
|
|
158
|
+
throw new Error("Cannot find native module 'ExpoGlassEffect'");
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
expect(warn).toHaveBeenCalledTimes(1);
|
|
163
|
+
const [message, detail] = warn.mock.calls[0] ?? [];
|
|
164
|
+
expect(String(message)).toContain('expo-glass-effect');
|
|
165
|
+
expect(String(detail)).toContain("Cannot find native module 'ExpoGlassEffect'");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('falls back when the platform has no glass API (iOS 26 betas)', () => {
|
|
169
|
+
// Upstream's own guard: on those builds `isLiquidGlassAvailable()` answers
|
|
170
|
+
// true while touching `GlassView` crashes NATIVELY, below JS.
|
|
171
|
+
expectSolidFallback(
|
|
172
|
+
renderNativeSurface({
|
|
173
|
+
GlassView: GLASS_HOST,
|
|
174
|
+
isLiquidGlassAvailable: () => true,
|
|
175
|
+
isGlassEffectAPIAvailable: () => false,
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('renders real glass when the package is fully there', () => {
|
|
181
|
+
const root = renderNativeSurface({
|
|
182
|
+
GlassView: GLASS_HOST,
|
|
183
|
+
isLiquidGlassAvailable: () => true,
|
|
184
|
+
isGlassEffectAPIAvailable: () => true,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const glass = hosts(root, GLASS_HOST)[0];
|
|
188
|
+
expect(glass).toBeTruthy();
|
|
189
|
+
expect(glass?.props.glassEffectStyle).toBe('regular');
|
|
190
|
+
expect(flattenStyle(glass?.props.style).backgroundColor).toBe(THEME.glassTint);
|
|
191
|
+
expect(warn).not.toHaveBeenCalled();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('renders glass on a version that predates isGlassEffectAPIAvailable', () => {
|
|
195
|
+
// The probe first shipped in 0.1.9, so the `>=0.1.9` peer floor guarantees it
|
|
196
|
+
// — but a peer range only WARNS, so a consumer can still resolve an older
|
|
197
|
+
// copy. A missing probe means "not applicable", never "unavailable"; treating
|
|
198
|
+
// it as the latter would delete the glass look on every working device.
|
|
199
|
+
expect(
|
|
200
|
+
hosts(
|
|
201
|
+
renderNativeSurface({ GlassView: GLASS_HOST, isLiquidGlassAvailable: () => true }),
|
|
202
|
+
GLASS_HOST,
|
|
203
|
+
),
|
|
204
|
+
).toHaveLength(1);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('probes availability once per session, not once per render', () => {
|
|
208
|
+
let probes = 0;
|
|
209
|
+
renderNativeSurface(
|
|
210
|
+
{
|
|
211
|
+
GlassView: GLASS_HOST,
|
|
212
|
+
isLiquidGlassAvailable: () => {
|
|
213
|
+
probes += 1;
|
|
214
|
+
return true;
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
3,
|
|
218
|
+
);
|
|
219
|
+
expect(probes).toBe(1);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('reuses the neutral surface rather than a second implementation', () => {
|
|
223
|
+
// Two copies of the fallback would drift: the neutral variant is what web
|
|
224
|
+
// bundlers and a consumer's `tsc` resolve, and it must paint exactly what an
|
|
225
|
+
// Android device paints.
|
|
226
|
+
jest.isolateModules(() => {
|
|
227
|
+
const { TabBarSurface } = require('../tab-bar/surface') as { TabBarSurface: unknown };
|
|
228
|
+
const { SolidTabBarSurface } = require('../tab-bar/surface-solid') as {
|
|
229
|
+
SolidTabBarSurface: unknown;
|
|
230
|
+
};
|
|
231
|
+
expect(TabBarSurface).toBe(SolidTabBarSurface);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
});
|
|
@@ -50,6 +50,7 @@ const NEUTRAL_AND_WEB_FILES = [
|
|
|
50
50
|
'tab-bar/TabBarBase.tsx',
|
|
51
51
|
'tab-bar/minimize-context.tsx',
|
|
52
52
|
'tab-bar/surface.tsx',
|
|
53
|
+
'tab-bar/surface-solid.tsx',
|
|
53
54
|
'tab-bar/surface.web.tsx',
|
|
54
55
|
'tab-bar/glyph.tsx',
|
|
55
56
|
'progressive-blur/index.web.tsx',
|
|
@@ -128,6 +129,17 @@ describe('tab-bar platform split', () => {
|
|
|
128
129
|
expect(moduleSpecifiers(read('tab-bar/glyph.native.tsx'))).toContain('expo-symbols');
|
|
129
130
|
});
|
|
130
131
|
|
|
132
|
+
it('the native surface takes its fallback from surface-solid, never ./surface', () => {
|
|
133
|
+
// Metro resolves a bare `./surface` to `surface.native.tsx` from ANY caller,
|
|
134
|
+
// so importing the neutral sibling by that name would be a self-import — which
|
|
135
|
+
// is the entire reason `surface-solid.tsx` exists as a separate name.
|
|
136
|
+
const specifiers = moduleSpecifiers(read('tab-bar/surface.native.tsx'));
|
|
137
|
+
expect(specifiers).toContain('./surface-solid');
|
|
138
|
+
expect(specifiers).not.toContain('./surface');
|
|
139
|
+
// And the neutral variant must be that same component, not a second copy.
|
|
140
|
+
expect(moduleSpecifiers(read('tab-bar/surface.tsx'))).toContain('./surface-solid');
|
|
141
|
+
});
|
|
142
|
+
|
|
131
143
|
it('the router packages live only under tab-bar/expo-router', () => {
|
|
132
144
|
const router = moduleSpecifiers(read('tab-bar/expo-router/RouterTabBar.tsx'));
|
|
133
145
|
expect(router.some((s) => isFrom(s, 'expo-router'))).toBe(true);
|
|
@@ -160,6 +172,8 @@ describe('tab-bar platform split', () => {
|
|
|
160
172
|
describe('tab-bar published surface', () => {
|
|
161
173
|
const pkg = JSON.parse(readFileSync(join(REPO_ROOT, 'package.json'), 'utf8')) as {
|
|
162
174
|
exports: Record<string, { browser?: { import?: string; require?: string } }>;
|
|
175
|
+
peerDependencies: Record<string, string>;
|
|
176
|
+
peerDependenciesMeta: Record<string, { optional?: boolean }>;
|
|
163
177
|
};
|
|
164
178
|
|
|
165
179
|
it('exports ./tab-bar with a browser condition → index.web.js', () => {
|
|
@@ -184,6 +198,38 @@ describe('tab-bar published surface', () => {
|
|
|
184
198
|
expect(entry?.browser).toBeUndefined();
|
|
185
199
|
});
|
|
186
200
|
|
|
201
|
+
it('declares the two Apple-only native peers REQUIRED, keeping their ranges', () => {
|
|
202
|
+
// `expo-glass-effect` and `expo-symbols` are imported STATICALLY, by
|
|
203
|
+
// `tab-bar/surface.native` and `tab-bar/glyph.native`. That is what settles
|
|
204
|
+
// this: optional means "you may omit this", but omitting it does not degrade
|
|
205
|
+
// — the native bundle fails to RESOLVE the specifier and the build dies. An
|
|
206
|
+
// optional peer plus a static import is an incoherent pair, and the only
|
|
207
|
+
// thing marking them optional would achieve is silencing the one warning
|
|
208
|
+
// that tells a consumer their build is about to break. Declared the same way
|
|
209
|
+
// `expo-blur` already is, for the same reason.
|
|
210
|
+
//
|
|
211
|
+
// The RANGE carries the second half: both files import symbols that arrived
|
|
212
|
+
// after 1.0 — `isGlassEffectAPIAvailable` first shipped in
|
|
213
|
+
// expo-glass-effect 0.1.9 — so an older installed copy is a real
|
|
214
|
+
// incompatibility, not merely an absent one.
|
|
215
|
+
for (const peer of ['expo-glass-effect', 'expo-symbols']) {
|
|
216
|
+
expect(pkg.peerDependencies[peer]).toMatch(/^>=\d/);
|
|
217
|
+
expect(pkg.peerDependenciesMeta[peer]).toBeUndefined();
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('keeps the cross-platform native peers REQUIRED', () => {
|
|
222
|
+
// `expo-blur` and `expo-image` are apple+android native modules imported from
|
|
223
|
+
// platform-NEUTRAL files (`progressive-blur/index.tsx`,
|
|
224
|
+
// `frosted-icon-button/FrostedIconButton.tsx`, `zoomable-image-gallery/`), so
|
|
225
|
+
// every native platform genuinely needs them. Marking them optional would
|
|
226
|
+
// silence the one warning that tells a consumer their build is about to fail.
|
|
227
|
+
for (const peer of ['expo-blur', 'expo-image']) {
|
|
228
|
+
expect(pkg.peerDependencies[peer]).toBeDefined();
|
|
229
|
+
expect(pkg.peerDependenciesMeta[peer]).toBeUndefined();
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
187
233
|
it('the generator lists exactly the right subpaths as web-forked', () => {
|
|
188
234
|
const gen = readFileSync(join(REPO_ROOT, 'scripts/generate-platform-exports.mjs'), 'utf8');
|
|
189
235
|
const block = /const WEB_FORKED_SUBPATHS = new Set\(\[([\s\S]*?)\]\);/.exec(gen)?.[1];
|