@oxyhq/bloom 0.3.3 → 0.3.4

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.
@@ -4,11 +4,54 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.FontLoader = FontLoader;
7
- var _react = _interopRequireDefault(require("react"));
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
8
9
  var _expoFont = require("expo-font");
9
10
  var _fontAssets = require("./font-assets.js");
10
11
  var _jsxRuntime = require("react/jsx-runtime");
11
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
13
+ /**
14
+ * React Native's `Text` exposes a static `defaultProps` for app-wide prop
15
+ * defaults. The public type definition omits it (it's an implementation
16
+ * detail, not part of the stable API), so we describe it locally rather
17
+ * than casting to `any`.
18
+ */
19
+
20
+ /**
21
+ * Mutates `Text.defaultProps.style` so every native `<Text>` in the consuming
22
+ * app inherits Bloom's sans font (Inter). The Bloom family name is prepended
23
+ * to any existing default style so caller-provided `style` (which React
24
+ * applies AFTER `defaultProps.style`) still wins for overrides.
25
+ *
26
+ * Idempotent: re-invoking it after the first apply is a no-op because the
27
+ * first style entry will already be the Bloom default we added.
28
+ */
29
+ function applyDefaultTextFont() {
30
+ const TextWithDefaults = _reactNative.Text;
31
+ const existing = TextWithDefaults.defaultProps?.style;
32
+ const bloomDefault = {
33
+ fontFamily: 'Inter'
34
+ };
35
+
36
+ // Detect whether we've already prepended ourselves. `existing` may be an
37
+ // array (multiple style fragments), a single object, a registered style
38
+ // number, or undefined. Only the array/object cases need inspection — if
39
+ // it's a number, we can't read the contents so we just prepend.
40
+ if (Array.isArray(existing)) {
41
+ const first = existing[0];
42
+ if (first && typeof first === 'object' && !Array.isArray(first) && first.fontFamily === bloomDefault.fontFamily) {
43
+ return;
44
+ }
45
+ } else if (existing && typeof existing === 'object' && existing.fontFamily === bloomDefault.fontFamily) {
46
+ return;
47
+ }
48
+ const nextStyle = existing == null ? bloomDefault : [bloomDefault, existing];
49
+ TextWithDefaults.defaultProps = {
50
+ ...TextWithDefaults.defaultProps,
51
+ style: nextStyle
52
+ };
53
+ }
54
+
12
55
  /**
13
56
  * Native font loader. Calls `expo-font`'s `useFonts` with the Bloom font
14
57
  * asset map and gates `children` on the load result. The hook is invoked
@@ -16,6 +59,16 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
16
59
  * fonts get loaded. This keeps Hook order stable across renders and means
17
60
  * `<BloomThemeProvider fonts={false}>` still gets fonts pre-loaded if the
18
61
  * provider tree ever flips `fonts` back on.
62
+ *
63
+ * Once the bundled fonts report `loaded === true`, this component also
64
+ * mutates `Text.defaultProps.style` to prepend `{ fontFamily: 'Inter' }`
65
+ * so every native `<Text>` in the consuming app inherits Bloom's sans
66
+ * family without callers needing to import `<Text>` from `@oxyhq/bloom`.
67
+ * The mutation is gated by a `useRef` so it runs at most once per
68
+ * FontLoader instance, and the underlying apply is idempotent — the
69
+ * Bloom default is prepended to any caller `defaultProps.style`, so
70
+ * per-call `style` overrides (which React applies after `defaultProps`)
71
+ * still take precedence.
19
72
  */
20
73
  function FontLoader({
21
74
  enabled,
@@ -23,6 +76,11 @@ function FontLoader({
23
76
  children
24
77
  }) {
25
78
  const [loaded] = (0, _expoFont.useFonts)(_fontAssets.FONT_ASSETS);
79
+ const defaultsApplied = (0, _react.useRef)(false);
80
+ if (loaded && !defaultsApplied.current) {
81
+ defaultsApplied.current = true;
82
+ applyDefaultTextFont();
83
+ }
26
84
  if (!enabled) return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
27
85
  children: children
28
86
  });
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireDefault","require","_expoFont","_fontAssets","_jsxRuntime","e","__esModule","default","FontLoader","enabled","fallback","children","loaded","useFonts","FONT_ASSETS","jsx","Fragment"],"sourceRoot":"../../../src","sources":["fonts/FontLoader.native.tsx"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,SAAA,GAAAD,OAAA;AACA,IAAAE,WAAA,GAAAF,OAAA;AAA4C,IAAAG,WAAA,GAAAH,OAAA;AAAA,SAAAD,uBAAAK,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAc5C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASG,UAAUA,CAAC;EAAEC,OAAO;EAAEC,QAAQ;EAAEC;AAA0B,CAAC,EAAE;EAC3E,MAAM,CAACC,MAAM,CAAC,GAAG,IAAAC,kBAAQ,EAACC,uBAAW,CAAC;EACtC,IAAI,CAACL,OAAO,EAAE,oBAAO,IAAAL,WAAA,CAAAW,GAAA,EAAAX,WAAA,CAAAY,QAAA;IAAAL,QAAA,EAAGA;EAAQ,CAAG,CAAC;EACpC,IAAI,CAACC,MAAM,EAAE,oBAAO,IAAAR,WAAA,CAAAW,GAAA,EAAAX,WAAA,CAAAY,QAAA;IAAAL,QAAA,EAAGD,QAAQ,IAAI;EAAI,CAAG,CAAC;EAC3C,oBAAO,IAAAN,WAAA,CAAAW,GAAA,EAAAX,WAAA,CAAAY,QAAA;IAAAL,QAAA,EAAGA;EAAQ,CAAG,CAAC;AACxB","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_expoFont","_fontAssets","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","applyDefaultTextFont","TextWithDefaults","Text","existing","defaultProps","style","bloomDefault","fontFamily","Array","isArray","first","nextStyle","FontLoader","enabled","fallback","children","loaded","useFonts","FONT_ASSETS","defaultsApplied","useRef","current","jsx","Fragment"],"sourceRoot":"../../../src","sources":["fonts/FontLoader.native.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,9 +1,52 @@
1
1
  "use strict";
2
2
 
3
- import React from 'react';
3
+ import React, { useRef } from 'react';
4
+ import { Text } from 'react-native';
4
5
  import { useFonts } from 'expo-font';
5
6
  import { FONT_ASSETS } from "./font-assets.js";
7
+
8
+ /**
9
+ * React Native's `Text` exposes a static `defaultProps` for app-wide prop
10
+ * defaults. The public type definition omits it (it's an implementation
11
+ * detail, not part of the stable API), so we describe it locally rather
12
+ * than casting to `any`.
13
+ */
6
14
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
15
+ /**
16
+ * Mutates `Text.defaultProps.style` so every native `<Text>` in the consuming
17
+ * app inherits Bloom's sans font (Inter). The Bloom family name is prepended
18
+ * to any existing default style so caller-provided `style` (which React
19
+ * applies AFTER `defaultProps.style`) still wins for overrides.
20
+ *
21
+ * Idempotent: re-invoking it after the first apply is a no-op because the
22
+ * first style entry will already be the Bloom default we added.
23
+ */
24
+ function applyDefaultTextFont() {
25
+ const TextWithDefaults = Text;
26
+ const existing = TextWithDefaults.defaultProps?.style;
27
+ const bloomDefault = {
28
+ fontFamily: 'Inter'
29
+ };
30
+
31
+ // Detect whether we've already prepended ourselves. `existing` may be an
32
+ // array (multiple style fragments), a single object, a registered style
33
+ // number, or undefined. Only the array/object cases need inspection — if
34
+ // it's a number, we can't read the contents so we just prepend.
35
+ if (Array.isArray(existing)) {
36
+ const first = existing[0];
37
+ if (first && typeof first === 'object' && !Array.isArray(first) && first.fontFamily === bloomDefault.fontFamily) {
38
+ return;
39
+ }
40
+ } else if (existing && typeof existing === 'object' && existing.fontFamily === bloomDefault.fontFamily) {
41
+ return;
42
+ }
43
+ const nextStyle = existing == null ? bloomDefault : [bloomDefault, existing];
44
+ TextWithDefaults.defaultProps = {
45
+ ...TextWithDefaults.defaultProps,
46
+ style: nextStyle
47
+ };
48
+ }
49
+
7
50
  /**
8
51
  * Native font loader. Calls `expo-font`'s `useFonts` with the Bloom font
9
52
  * asset map and gates `children` on the load result. The hook is invoked
@@ -11,6 +54,16 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
11
54
  * fonts get loaded. This keeps Hook order stable across renders and means
12
55
  * `<BloomThemeProvider fonts={false}>` still gets fonts pre-loaded if the
13
56
  * provider tree ever flips `fonts` back on.
57
+ *
58
+ * Once the bundled fonts report `loaded === true`, this component also
59
+ * mutates `Text.defaultProps.style` to prepend `{ fontFamily: 'Inter' }`
60
+ * so every native `<Text>` in the consuming app inherits Bloom's sans
61
+ * family without callers needing to import `<Text>` from `@oxyhq/bloom`.
62
+ * The mutation is gated by a `useRef` so it runs at most once per
63
+ * FontLoader instance, and the underlying apply is idempotent — the
64
+ * Bloom default is prepended to any caller `defaultProps.style`, so
65
+ * per-call `style` overrides (which React applies after `defaultProps`)
66
+ * still take precedence.
14
67
  */
15
68
  export function FontLoader({
16
69
  enabled,
@@ -18,6 +71,11 @@ export function FontLoader({
18
71
  children
19
72
  }) {
20
73
  const [loaded] = useFonts(FONT_ASSETS);
74
+ const defaultsApplied = useRef(false);
75
+ if (loaded && !defaultsApplied.current) {
76
+ defaultsApplied.current = true;
77
+ applyDefaultTextFont();
78
+ }
21
79
  if (!enabled) return /*#__PURE__*/_jsx(_Fragment, {
22
80
  children: children
23
81
  });
@@ -1 +1 @@
1
- {"version":3,"names":["React","useFonts","FONT_ASSETS","Fragment","_Fragment","jsx","_jsx","FontLoader","enabled","fallback","children","loaded"],"sourceRoot":"../../../src","sources":["fonts/FontLoader.native.tsx"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,QAAQ,QAAQ,WAAW;AACpC,SAASC,WAAW,QAAQ,kBAAe;AAAC,SAAAC,QAAA,IAAAC,SAAA,EAAAC,GAAA,IAAAC,IAAA;AAc5C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,UAAUA,CAAC;EAAEC,OAAO;EAAEC,QAAQ;EAAEC;AAA0B,CAAC,EAAE;EAC3E,MAAM,CAACC,MAAM,CAAC,GAAGV,QAAQ,CAACC,WAAW,CAAC;EACtC,IAAI,CAACM,OAAO,EAAE,oBAAOF,IAAA,CAAAF,SAAA;IAAAM,QAAA,EAAGA;EAAQ,CAAG,CAAC;EACpC,IAAI,CAACC,MAAM,EAAE,oBAAOL,IAAA,CAAAF,SAAA;IAAAM,QAAA,EAAGD,QAAQ,IAAI;EAAI,CAAG,CAAC;EAC3C,oBAAOH,IAAA,CAAAF,SAAA;IAAAM,QAAA,EAAGA;EAAQ,CAAG,CAAC;AACxB","ignoreList":[]}
1
+ {"version":3,"names":["React","useRef","Text","useFonts","FONT_ASSETS","Fragment","_Fragment","jsx","_jsx","applyDefaultTextFont","TextWithDefaults","existing","defaultProps","style","bloomDefault","fontFamily","Array","isArray","first","nextStyle","FontLoader","enabled","fallback","children","loaded","defaultsApplied","current"],"sourceRoot":"../../../src","sources":["fonts/FontLoader.native.tsx"],"mappings":";;AAAA,OAAOA,KAAK,IAAIC,MAAM,QAAQ,OAAO;AACrC,SAASC,IAAI,QAAwD,cAAc;AACnF,SAASC,QAAQ,QAAQ,WAAW;AACpC,SAASC,WAAW,QAAQ,kBAAe;;AAc3C;AACA;AACA;AACA;AACA;AACA;AALA,SAAAC,QAAA,IAAAC,SAAA,EAAAC,GAAA,IAAAC,IAAA;AAUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,oBAAoBA,CAAA,EAAS;EACpC,MAAMC,gBAAgB,GAAGR,IAAwB;EACjD,MAAMS,QAAQ,GAAGD,gBAAgB,CAACE,YAAY,EAAEC,KAAK;EACrD,MAAMC,YAAuB,GAAG;IAAEC,UAAU,EAAE;EAAQ,CAAC;;EAEvD;EACA;EACA;EACA;EACA,IAAIC,KAAK,CAACC,OAAO,CAACN,QAAQ,CAAC,EAAE;IAC3B,MAAMO,KAAK,GAAGP,QAAQ,CAAC,CAAC,CAAC;IACzB,IACEO,KAAK,IACL,OAAOA,KAAK,KAAK,QAAQ,IACzB,CAACF,KAAK,CAACC,OAAO,CAACC,KAAK,CAAC,IACpBA,KAAK,CAAeH,UAAU,KAAKD,YAAY,CAACC,UAAU,EAC3D;MACA;IACF;EACF,CAAC,MAAM,IACLJ,QAAQ,IACR,OAAOA,QAAQ,KAAK,QAAQ,IAC3BA,QAAQ,CAAeI,UAAU,KAAKD,YAAY,CAACC,UAAU,EAC9D;IACA;EACF;EAEA,MAAMI,SAA+B,GACnCR,QAAQ,IAAI,IAAI,GAAGG,YAAY,GAAG,CAACA,YAAY,EAAEH,QAAQ,CAAC;EAE5DD,gBAAgB,CAACE,YAAY,GAAG;IAC9B,GAAGF,gBAAgB,CAACE,YAAY;IAChCC,KAAK,EAAEM;EACT,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,UAAUA,CAAC;EAAEC,OAAO;EAAEC,QAAQ;EAAEC;AAA0B,CAAC,EAAE;EAC3E,MAAM,CAACC,MAAM,CAAC,GAAGrB,QAAQ,CAACC,WAAW,CAAC;EACtC,MAAMqB,eAAe,GAAGxB,MAAM,CAAC,KAAK,CAAC;EAErC,IAAIuB,MAAM,IAAI,CAACC,eAAe,CAACC,OAAO,EAAE;IACtCD,eAAe,CAACC,OAAO,GAAG,IAAI;IAC9BjB,oBAAoB,CAAC,CAAC;EACxB;EAEA,IAAI,CAACY,OAAO,EAAE,oBAAOb,IAAA,CAAAF,SAAA;IAAAiB,QAAA,EAAGA;EAAQ,CAAG,CAAC;EACpC,IAAI,CAACC,MAAM,EAAE,oBAAOhB,IAAA,CAAAF,SAAA;IAAAiB,QAAA,EAAGD,QAAQ,IAAI;EAAI,CAAG,CAAC;EAC3C,oBAAOd,IAAA,CAAAF,SAAA;IAAAiB,QAAA,EAAGA;EAAQ,CAAG,CAAC;AACxB","ignoreList":[]}
@@ -17,6 +17,16 @@ export interface FontLoaderProps {
17
17
  * fonts get loaded. This keeps Hook order stable across renders and means
18
18
  * `<BloomThemeProvider fonts={false}>` still gets fonts pre-loaded if the
19
19
  * provider tree ever flips `fonts` back on.
20
+ *
21
+ * Once the bundled fonts report `loaded === true`, this component also
22
+ * mutates `Text.defaultProps.style` to prepend `{ fontFamily: 'Inter' }`
23
+ * so every native `<Text>` in the consuming app inherits Bloom's sans
24
+ * family without callers needing to import `<Text>` from `@oxyhq/bloom`.
25
+ * The mutation is gated by a `useRef` so it runs at most once per
26
+ * FontLoader instance, and the underlying apply is idempotent — the
27
+ * Bloom default is prepended to any caller `defaultProps.style`, so
28
+ * per-call `style` overrides (which React applies after `defaultProps`)
29
+ * still take precedence.
20
30
  */
21
31
  export declare function FontLoader({ enabled, fallback, children }: FontLoaderProps): import("react/jsx-runtime").JSX.Element;
22
32
  //# sourceMappingURL=FontLoader.native.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FontLoader.native.d.ts","sourceRoot":"","sources":["../../../../src/fonts/FontLoader.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAK1E"}
1
+ {"version":3,"file":"FontLoader.native.d.ts","sourceRoot":"","sources":["../../../../src/fonts/FontLoader.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAKtC,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAyDD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAY1E"}
@@ -17,6 +17,16 @@ export interface FontLoaderProps {
17
17
  * fonts get loaded. This keeps Hook order stable across renders and means
18
18
  * `<BloomThemeProvider fonts={false}>` still gets fonts pre-loaded if the
19
19
  * provider tree ever flips `fonts` back on.
20
+ *
21
+ * Once the bundled fonts report `loaded === true`, this component also
22
+ * mutates `Text.defaultProps.style` to prepend `{ fontFamily: 'Inter' }`
23
+ * so every native `<Text>` in the consuming app inherits Bloom's sans
24
+ * family without callers needing to import `<Text>` from `@oxyhq/bloom`.
25
+ * The mutation is gated by a `useRef` so it runs at most once per
26
+ * FontLoader instance, and the underlying apply is idempotent — the
27
+ * Bloom default is prepended to any caller `defaultProps.style`, so
28
+ * per-call `style` overrides (which React applies after `defaultProps`)
29
+ * still take precedence.
20
30
  */
21
31
  export declare function FontLoader({ enabled, fallback, children }: FontLoaderProps): import("react/jsx-runtime").JSX.Element;
22
32
  //# sourceMappingURL=FontLoader.native.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FontLoader.native.d.ts","sourceRoot":"","sources":["../../../../src/fonts/FontLoader.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAK1E"}
1
+ {"version":3,"file":"FontLoader.native.d.ts","sourceRoot":"","sources":["../../../../src/fonts/FontLoader.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAKtC,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAyDD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAY1E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
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",
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Text } from 'react-native';
2
+ import { Text, type TextProps } from 'react-native';
3
3
  import { render } from '@testing-library/react-native';
4
4
 
5
5
  type UseFontsResult = readonly [boolean, Error | null];
@@ -16,9 +16,22 @@ function setUseFontsResult(result: UseFontsResult): void {
16
16
  mockUseFontsResult = result;
17
17
  }
18
18
 
19
+ type TextWithDefaults = typeof Text & {
20
+ defaultProps?: Partial<TextProps>;
21
+ };
22
+
23
+ function resetTextDefaults(): void {
24
+ (Text as TextWithDefaults).defaultProps = undefined;
25
+ }
26
+
19
27
  describe('FontLoader (native)', () => {
28
+ beforeEach(() => {
29
+ resetTextDefaults();
30
+ });
31
+
20
32
  afterEach(() => {
21
33
  setUseFontsResult([true, null]);
34
+ resetTextDefaults();
22
35
  });
23
36
 
24
37
  it('renders children when fonts are loaded and enabled', () => {
@@ -72,4 +85,62 @@ describe('FontLoader (native)', () => {
72
85
  );
73
86
  expect(getByText('content')).toBeTruthy();
74
87
  });
88
+
89
+ it('prepends Inter to Text.defaultProps.style once fonts are loaded', () => {
90
+ setUseFontsResult([true, null]);
91
+ render(
92
+ <FontLoader enabled>
93
+ <Text>content</Text>
94
+ </FontLoader>,
95
+ );
96
+ const defaults = (Text as TextWithDefaults).defaultProps;
97
+ expect(defaults?.style).toEqual({ fontFamily: 'Inter' });
98
+ });
99
+
100
+ it('does not touch Text.defaultProps until fonts are loaded', () => {
101
+ setUseFontsResult([false, null]);
102
+ render(
103
+ <FontLoader enabled>
104
+ <Text>content</Text>
105
+ </FontLoader>,
106
+ );
107
+ expect((Text as TextWithDefaults).defaultProps?.style).toBeUndefined();
108
+ });
109
+
110
+ it('preserves a pre-existing default style by appending it after Inter', () => {
111
+ const preExisting = { color: '#abcdef' } as const;
112
+ (Text as TextWithDefaults).defaultProps = { style: preExisting };
113
+ setUseFontsResult([true, null]);
114
+ render(
115
+ <FontLoader enabled>
116
+ <Text>content</Text>
117
+ </FontLoader>,
118
+ );
119
+ const style = (Text as TextWithDefaults).defaultProps?.style;
120
+ expect(Array.isArray(style)).toBe(true);
121
+ if (Array.isArray(style)) {
122
+ expect(style[0]).toEqual({ fontFamily: 'Inter' });
123
+ expect(style[1]).toBe(preExisting);
124
+ }
125
+ });
126
+
127
+ it('is idempotent across multiple FontLoader mounts', () => {
128
+ setUseFontsResult([true, null]);
129
+ const first = render(
130
+ <FontLoader enabled>
131
+ <Text>first</Text>
132
+ </FontLoader>,
133
+ );
134
+ first.unmount();
135
+ const second = render(
136
+ <FontLoader enabled>
137
+ <Text>second</Text>
138
+ </FontLoader>,
139
+ );
140
+ second.unmount();
141
+ const style = (Text as TextWithDefaults).defaultProps?.style;
142
+ // Should still be the single, plain Inter object — not nested arrays
143
+ // or duplicated entries.
144
+ expect(style).toEqual({ fontFamily: 'Inter' });
145
+ });
75
146
  });
@@ -1,4 +1,5 @@
1
- import React from 'react';
1
+ import React, { useRef } from 'react';
2
+ import { Text, type TextProps, type TextStyle, type StyleProp } from 'react-native';
2
3
  import { useFonts } from 'expo-font';
3
4
  import { FONT_ASSETS } from './font-assets';
4
5
 
@@ -14,6 +15,61 @@ export interface FontLoaderProps {
14
15
  children: React.ReactNode;
15
16
  }
16
17
 
18
+ /**
19
+ * React Native's `Text` exposes a static `defaultProps` for app-wide prop
20
+ * defaults. The public type definition omits it (it's an implementation
21
+ * detail, not part of the stable API), so we describe it locally rather
22
+ * than casting to `any`.
23
+ */
24
+ type TextWithDefaults = typeof Text & {
25
+ defaultProps?: Partial<TextProps>;
26
+ };
27
+
28
+ /**
29
+ * Mutates `Text.defaultProps.style` so every native `<Text>` in the consuming
30
+ * app inherits Bloom's sans font (Inter). The Bloom family name is prepended
31
+ * to any existing default style so caller-provided `style` (which React
32
+ * applies AFTER `defaultProps.style`) still wins for overrides.
33
+ *
34
+ * Idempotent: re-invoking it after the first apply is a no-op because the
35
+ * first style entry will already be the Bloom default we added.
36
+ */
37
+ function applyDefaultTextFont(): void {
38
+ const TextWithDefaults = Text as TextWithDefaults;
39
+ const existing = TextWithDefaults.defaultProps?.style;
40
+ const bloomDefault: TextStyle = { fontFamily: 'Inter' };
41
+
42
+ // Detect whether we've already prepended ourselves. `existing` may be an
43
+ // array (multiple style fragments), a single object, a registered style
44
+ // number, or undefined. Only the array/object cases need inspection — if
45
+ // it's a number, we can't read the contents so we just prepend.
46
+ if (Array.isArray(existing)) {
47
+ const first = existing[0];
48
+ if (
49
+ first &&
50
+ typeof first === 'object' &&
51
+ !Array.isArray(first) &&
52
+ (first as TextStyle).fontFamily === bloomDefault.fontFamily
53
+ ) {
54
+ return;
55
+ }
56
+ } else if (
57
+ existing &&
58
+ typeof existing === 'object' &&
59
+ (existing as TextStyle).fontFamily === bloomDefault.fontFamily
60
+ ) {
61
+ return;
62
+ }
63
+
64
+ const nextStyle: StyleProp<TextStyle> =
65
+ existing == null ? bloomDefault : [bloomDefault, existing];
66
+
67
+ TextWithDefaults.defaultProps = {
68
+ ...TextWithDefaults.defaultProps,
69
+ style: nextStyle,
70
+ };
71
+ }
72
+
17
73
  /**
18
74
  * Native font loader. Calls `expo-font`'s `useFonts` with the Bloom font
19
75
  * asset map and gates `children` on the load result. The hook is invoked
@@ -21,9 +77,26 @@ export interface FontLoaderProps {
21
77
  * fonts get loaded. This keeps Hook order stable across renders and means
22
78
  * `<BloomThemeProvider fonts={false}>` still gets fonts pre-loaded if the
23
79
  * provider tree ever flips `fonts` back on.
80
+ *
81
+ * Once the bundled fonts report `loaded === true`, this component also
82
+ * mutates `Text.defaultProps.style` to prepend `{ fontFamily: 'Inter' }`
83
+ * so every native `<Text>` in the consuming app inherits Bloom's sans
84
+ * family without callers needing to import `<Text>` from `@oxyhq/bloom`.
85
+ * The mutation is gated by a `useRef` so it runs at most once per
86
+ * FontLoader instance, and the underlying apply is idempotent — the
87
+ * Bloom default is prepended to any caller `defaultProps.style`, so
88
+ * per-call `style` overrides (which React applies after `defaultProps`)
89
+ * still take precedence.
24
90
  */
25
91
  export function FontLoader({ enabled, fallback, children }: FontLoaderProps) {
26
92
  const [loaded] = useFonts(FONT_ASSETS);
93
+ const defaultsApplied = useRef(false);
94
+
95
+ if (loaded && !defaultsApplied.current) {
96
+ defaultsApplied.current = true;
97
+ applyDefaultTextFont();
98
+ }
99
+
27
100
  if (!enabled) return <>{children}</>;
28
101
  if (!loaded) return <>{fallback ?? null}</>;
29
102
  return <>{children}</>;