@ng-native/expo 0.1.2 → 0.1.3

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/README.md CHANGED
@@ -130,11 +130,10 @@ mount(rootTag, App, getFabricUIManager(), { globalStyles: styleSheetOf(GlobalSty
130
130
  the bundler ships the file - a plain path would be a font that is simply missing on the device
131
131
  with nothing anywhere to say why.
132
132
 
133
- **There is no font matching on a device.** Native looks a family up by name and that is all, so a
134
- bold cut is a family of its own: the second face above is registered as `Inter-700` as well, and
135
- a rule that wants it asks for `font-family: Inter-700`. Writing `font-weight: 700` against a
136
- family with one registered face gets whatever the platform synthesizes, exactly as it would in a
137
- React Native app.
133
+ **Weights and styles match as on the web.** Native looks a family up by name and that is all, so
134
+ the second face above is registered as `Inter-700` as well, and the engine points a text with
135
+ `font-family: Inter; font-weight: 700` at it, by CSS's matching rules, wherever the family and
136
+ weight were set.
138
137
 
139
138
  `loadFonts` takes any number of sheets and does nothing at all when none of them declare a face,
140
139
  so bootstrap can call it unconditionally. Injecting `Fonts` afterwards answers what is loaded.
@@ -250,7 +249,8 @@ AppRegistry.registerRunnable('main', ({ rootTag }) => {
250
249
  ```
251
250
 
252
251
  Mounting does not wait for the fonts; the splash screen does. Text laid out before they arrive is
253
- behind the splash, so nothing is ever seen in the fallback face and nothing reflows.
252
+ laid out again as each face registers, behind the splash, so nothing is ever seen in the fallback
253
+ face.
254
254
  `hideWhenReady` waits one frame after the work, because hiding the instant it resolves uncovers
255
255
  the frame that was on screen while it ran - and it uncovers even when the work fails, rather than
256
256
  leaving a splash screen up forever.
package/dist/fonts.d.ts CHANGED
@@ -43,10 +43,12 @@ export interface NativeFonts {
43
43
  * Loading is by family name, which is also all native matches on.
44
44
  *
45
45
  * There is no weight matching on a device: `font-family: Inter` finds the face registered under
46
- * exactly that name, and a bold cut is a family of its own. A face that declares a weight is
47
- * therefore registered under a composed name as well as its own, so a stylesheet can say
48
- * `font-family: Inter-700` and get the file it declared - and a sheet that only ever registers
49
- * one weight per family needs to know none of this.
46
+ * exactly that name, and a bold cut is a family of its own. A face that declares a weight or a
47
+ * style is therefore registered under a composed name as well (`Inter-700`, `Inter-italic`,
48
+ * `Inter-700-italic`), which is the name the engine points a matching text at.
49
+ *
50
+ * A face with both a weight and a style also answers to each alone, as it did before it had a
51
+ * name of its own, but only where no face declared exactly that.
50
52
  */
51
53
  export declare function registrationsFor(faces: readonly FontFace[]): Record<string, unknown>;
52
54
  export declare function expoFonts(): NativeFonts | null;
@@ -71,9 +73,9 @@ export declare class FontRegistry {
71
73
  /**
72
74
  * Register every `@font-face` a compiled sheet declares.
73
75
  *
74
- * Awaited before `mount` in an app that uses one: text laid out before this resolves is laid
75
- * out in the fallback face. Sheets with no faces resolve immediately, so calling it
76
- * unconditionally at bootstrap costs nothing.
76
+ * Called before `mount` in an app that uses one. Text laid out before a face registers is in
77
+ * the fallback face until it does, and is laid out again then. Sheets with no faces resolve
78
+ * immediately, so calling it unconditionally at bootstrap costs nothing.
77
79
  */
78
80
  loadSheet(...sheets: readonly (SheetWithFonts | null | undefined)[]): Promise<void>;
79
81
  /** Register faces by name, for a font that did not come from a stylesheet. */
package/dist/fonts.js CHANGED
@@ -21,24 +21,30 @@
21
21
  * here would be copying out its internals rather than using it.
22
22
  */
23
23
  import { InjectionToken, computed, signal } from '@angular/core';
24
+ import { faceName, fontsRegistered } from '@ng-native/fabric';
24
25
  import { expoModule } from "./native.js";
25
26
  /**
26
27
  * Loading is by family name, which is also all native matches on.
27
28
  *
28
29
  * There is no weight matching on a device: `font-family: Inter` finds the face registered under
29
- * exactly that name, and a bold cut is a family of its own. A face that declares a weight is
30
- * therefore registered under a composed name as well as its own, so a stylesheet can say
31
- * `font-family: Inter-700` and get the file it declared - and a sheet that only ever registers
32
- * one weight per family needs to know none of this.
30
+ * exactly that name, and a bold cut is a family of its own. A face that declares a weight or a
31
+ * style is therefore registered under a composed name as well (`Inter-700`, `Inter-italic`,
32
+ * `Inter-700-italic`), which is the name the engine points a matching text at.
33
+ *
34
+ * A face with both a weight and a style also answers to each alone, as it did before it had a
35
+ * name of its own, but only where no face declared exactly that.
33
36
  */
34
37
  export function registrationsFor(faces) {
35
38
  const map = {};
36
39
  for (const face of faces) {
37
40
  map[face.family] ??= face.source;
38
- if (face.weight !== undefined)
39
- map[`${face.family}-${face.weight}`] = face.source;
40
- if (face.style)
41
- map[`${face.family}-${face.style}`] = face.source;
41
+ map[faceName(face)] = face.source;
42
+ }
43
+ for (const face of faces) {
44
+ if (face.weight === undefined || !face.style)
45
+ continue;
46
+ map[`${face.family}-${face.weight}`] ??= face.source;
47
+ map[`${face.family}-${face.style}`] ??= face.source;
42
48
  }
43
49
  return map;
44
50
  }
@@ -85,9 +91,9 @@ export class FontRegistry {
85
91
  /**
86
92
  * Register every `@font-face` a compiled sheet declares.
87
93
  *
88
- * Awaited before `mount` in an app that uses one: text laid out before this resolves is laid
89
- * out in the fallback face. Sheets with no faces resolve immediately, so calling it
90
- * unconditionally at bootstrap costs nothing.
94
+ * Called before `mount` in an app that uses one. Text laid out before a face registers is in
95
+ * the fallback face until it does, and is laid out again then. Sheets with no faces resolve
96
+ * immediately, so calling it unconditionally at bootstrap costs nothing.
91
97
  */
92
98
  async loadSheet(...sheets) {
93
99
  const faces = sheets.flatMap((sheet) => [...(sheet?.fonts ?? [])]);
@@ -97,9 +103,17 @@ export class FontRegistry {
97
103
  }
98
104
  /** Register faces by name, for a font that did not come from a stylesheet. */
99
105
  async load(map) {
100
- if (!this.native)
106
+ const native = this.native;
107
+ if (!native)
101
108
  return;
102
- await this.native.loadAsync(map);
109
+ try {
110
+ await native.loadAsync(map);
111
+ }
112
+ finally {
113
+ // Text already laid out in the fallback face keeps it until it is laid out again. Only the
114
+ // faces that registered: one failing fails the whole load, not the others in it.
115
+ fontsRegistered(Object.keys(map).filter((family) => native.isLoaded(family)));
116
+ }
103
117
  this.generation.update((n) => n + 1);
104
118
  }
105
119
  }
package/dist/fonts.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAuBzC;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAA0B;IACzD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC;QACjC,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QAClF,IAAI,IAAI,CAAC,KAAK;YAAE,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACpE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,WAAW,CAA+B,CAAC,CAAC;IAC/F,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,OAAO;QACL,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAA2C,CAAC;QAC/E,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC3C,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE;KAC5C,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,YAAY;IACN,MAAM,CAAqB;IAC5C;;;;;;;OAOG;IACc,UAAU,GAAG,MAAM,CAAC,CAAC;mFAAC,CAAC;IAExC,YAAY,MAA0B;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,8FAA8F;IAC9F,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;IAC9B,CAAC;IAED,uEAAuE;IAC9D,QAAQ,GAA8B,QAAQ,CAAC,GAAG,EAAE;QAC3D,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;IAC7C,CAAC;iFAAC,CAAC;IAEH,sFAAsF;IACtF,GAAG,CAAC,MAAc;QAChB,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC;IAChD,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,SAAS,CAAC,GAAG,MAAsD;QACvE,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,OAAO;QAC1B,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,8EAA8E;IAC9E,KAAK,CAAC,IAAI,CAAC,GAA4B;QACrC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,CAAC;CACF;AAED,yDAAyD;AACzD,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,cAAc,CAAe,sBAAsB,EAAE;IAC5E,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,SAAS,EAAE,CAAC;CAC7C,CAAC,CAAC;AAGH;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,GAAG,MAAsD;IAEzD,OAAO,IAAI,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,CAAC;AAC5D,CAAC","sourcesContent":["/**\n * `Fonts`, bound to `expo-font`.\n *\n * A font on a phone is not fetched when the text engine first needs it. It has to be registered\n * with the platform up front, or the first paint is in the fallback face and reflows when the\n * real one lands - which is why the interesting call here is the one an app makes *before* it\n * mounts:\n *\n * ```ts\n * import { loadFonts } from '@ng-native/expo/fonts';\n *\n * await loadFonts(styleSheetOf(GlobalStyles));\n * mount(rootTag, App, getFabricUIManager(), { globalStyles: styleSheetOf(GlobalStyles) });\n * ```\n *\n * The class takes its platform rather than injecting it, because that call happens before there\n * is an injector to inject from. `inject(Fonts)` is for a screen that wants to ask what loaded.\n *\n * Unlike the other services here this goes through `expo-font`'s JavaScript rather than its\n * native module: the module takes descriptors the JS builds out of an asset, and rebuilding them\n * here would be copying out its internals rather than using it.\n */\nimport { InjectionToken, computed, signal, type Signal } from '@angular/core';\nimport { expoModule } from './native.ts';\n\n/** A face a stylesheet declared, as the compiler collected it. */\nexport interface FontFace {\n readonly family: string;\n /** What `require('./Inter.ttf')` returned: an asset id, or a module the bundler resolved. */\n readonly source: unknown;\n readonly weight?: number;\n readonly style?: string;\n}\n\n/** Just enough of a compiled stylesheet to find the faces in it. */\nexport interface SheetWithFonts {\n readonly fonts?: readonly FontFace[];\n}\n\n/** The slice of `expo-font` this needs. */\nexport interface NativeFonts {\n loadAsync(map: Record<string, unknown>): Promise<void>;\n isLoaded(family: string): boolean;\n getLoadedFonts(): string[];\n}\n\n/**\n * Loading is by family name, which is also all native matches on.\n *\n * There is no weight matching on a device: `font-family: Inter` finds the face registered under\n * exactly that name, and a bold cut is a family of its own. A face that declares a weight is\n * therefore registered under a composed name as well as its own, so a stylesheet can say\n * `font-family: Inter-700` and get the file it declared - and a sheet that only ever registers\n * one weight per family needs to know none of this.\n */\nexport function registrationsFor(faces: readonly FontFace[]): Record<string, unknown> {\n const map: Record<string, unknown> = {};\n for (const face of faces) {\n map[face.family] ??= face.source;\n if (face.weight !== undefined) map[`${face.family}-${face.weight}`] = face.source;\n if (face.style) map[`${face.family}-${face.style}`] = face.source;\n }\n return map;\n}\n\nexport function expoFonts(): NativeFonts | null {\n const expo = expoModule('expo-font', () => require('expo-font') as typeof import('expo-font'));\n if (!expo) return null;\n return {\n loadAsync: (map) => expo.loadAsync(map as Parameters<typeof expo.loadAsync>[0]),\n isLoaded: (family) => expo.isLoaded(family),\n getLoadedFonts: () => expo.getLoadedFonts(),\n };\n}\n\nexport class FontRegistry {\n private readonly native: NativeFonts | null;\n /**\n * Bumped whenever faces are registered.\n *\n * The platform's own list is a plain array that changes underneath us, so a template binding to\n * it would render once and never again - and the one moment it changes is exactly the moment a\n * screen waiting on a font wants to hear about. This makes both readers reactive without\n * mirroring the list.\n */\n private readonly generation = signal(0);\n\n constructor(native: NativeFonts | null) {\n this.native = native;\n }\n\n /** Whether `expo-font` is installed at all. Without it a custom face simply never appears. */\n get available(): boolean {\n return this.native !== null;\n }\n\n /** Every family the platform can currently find, custom or bundled. */\n readonly families: Signal<readonly string[]> = computed(() => {\n this.generation();\n return this.native?.getLoadedFonts() ?? [];\n });\n\n /** Whether a family is registered. Reactive: re-read after a `load` that adds one. */\n has(family: string): boolean {\n this.generation();\n return this.native?.isLoaded(family) ?? false;\n }\n\n /**\n * Register every `@font-face` a compiled sheet declares.\n *\n * Awaited before `mount` in an app that uses one: text laid out before this resolves is laid\n * out in the fallback face. Sheets with no faces resolve immediately, so calling it\n * unconditionally at bootstrap costs nothing.\n */\n async loadSheet(...sheets: readonly (SheetWithFonts | null | undefined)[]): Promise<void> {\n const faces = sheets.flatMap((sheet) => [...(sheet?.fonts ?? [])]);\n if (!faces.length) return;\n await this.load(registrationsFor(faces));\n }\n\n /** Register faces by name, for a font that did not come from a stylesheet. */\n async load(map: Record<string, unknown>): Promise<void> {\n if (!this.native) return;\n await this.native.loadAsync(map);\n this.generation.update((n) => n + 1);\n }\n}\n\n/** Injected where an app wants to ask what is loaded. */\nexport const Fonts = new InjectionToken<FontRegistry>('angular-native.fonts', {\n factory: () => new FontRegistry(expoFonts()),\n});\nexport type Fonts = FontRegistry;\n\n/**\n * Register every face the given sheets declare, before the app mounts.\n *\n * A function rather than a service because that is when it is needed: there is no injector yet.\n */\nexport function loadFonts(\n ...sheets: readonly (SheetWithFonts | null | undefined)[]\n): Promise<void> {\n return new FontRegistry(expoFonts()).loadSheet(...sheets);\n}\n"]}
1
+ {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Fonts`, bound to `expo-font`.\n *\n * A font on a phone is not fetched when the text engine first needs it. It has to be registered\n * with the platform up front, or the first paint is in the fallback face and reflows when the\n * real one lands - which is why the interesting call here is the one an app makes *before* it\n * mounts:\n *\n * ```ts\n * import { loadFonts } from '@ng-native/expo/fonts';\n *\n * await loadFonts(styleSheetOf(GlobalStyles));\n * mount(rootTag, App, getFabricUIManager(), { globalStyles: styleSheetOf(GlobalStyles) });\n * ```\n *\n * The class takes its platform rather than injecting it, because that call happens before there\n * is an injector to inject from. `inject(Fonts)` is for a screen that wants to ask what loaded.\n *\n * Unlike the other services here this goes through `expo-font`'s JavaScript rather than its\n * native module: the module takes descriptors the JS builds out of an asset, and rebuilding them\n * here would be copying out its internals rather than using it.\n */\nimport { InjectionToken, computed, signal, type Signal } from '@angular/core';\nimport { faceName, fontsRegistered } from '@ng-native/fabric';\nimport { expoModule } from './native.ts';\n\n/** A face a stylesheet declared, as the compiler collected it. */\nexport interface FontFace {\n readonly family: string;\n /** What `require('./Inter.ttf')` returned: an asset id, or a module the bundler resolved. */\n readonly source: unknown;\n readonly weight?: number;\n readonly style?: string;\n}\n\n/** Just enough of a compiled stylesheet to find the faces in it. */\nexport interface SheetWithFonts {\n readonly fonts?: readonly FontFace[];\n}\n\n/** The slice of `expo-font` this needs. */\nexport interface NativeFonts {\n loadAsync(map: Record<string, unknown>): Promise<void>;\n isLoaded(family: string): boolean;\n getLoadedFonts(): string[];\n}\n\n/**\n * Loading is by family name, which is also all native matches on.\n *\n * There is no weight matching on a device: `font-family: Inter` finds the face registered under\n * exactly that name, and a bold cut is a family of its own. A face that declares a weight or a\n * style is therefore registered under a composed name as well (`Inter-700`, `Inter-italic`,\n * `Inter-700-italic`), which is the name the engine points a matching text at.\n *\n * A face with both a weight and a style also answers to each alone, as it did before it had a\n * name of its own, but only where no face declared exactly that.\n */\nexport function registrationsFor(faces: readonly FontFace[]): Record<string, unknown> {\n const map: Record<string, unknown> = {};\n for (const face of faces) {\n map[face.family] ??= face.source;\n map[faceName(face)] = face.source;\n }\n for (const face of faces) {\n if (face.weight === undefined || !face.style) continue;\n map[`${face.family}-${face.weight}`] ??= face.source;\n map[`${face.family}-${face.style}`] ??= face.source;\n }\n return map;\n}\n\nexport function expoFonts(): NativeFonts | null {\n const expo = expoModule('expo-font', () => require('expo-font') as typeof import('expo-font'));\n if (!expo) return null;\n return {\n loadAsync: (map) => expo.loadAsync(map as Parameters<typeof expo.loadAsync>[0]),\n isLoaded: (family) => expo.isLoaded(family),\n getLoadedFonts: () => expo.getLoadedFonts(),\n };\n}\n\nexport class FontRegistry {\n private readonly native: NativeFonts | null;\n /**\n * Bumped whenever faces are registered.\n *\n * The platform's own list is a plain array that changes underneath us, so a template binding to\n * it would render once and never again - and the one moment it changes is exactly the moment a\n * screen waiting on a font wants to hear about. This makes both readers reactive without\n * mirroring the list.\n */\n private readonly generation = signal(0);\n\n constructor(native: NativeFonts | null) {\n this.native = native;\n }\n\n /** Whether `expo-font` is installed at all. Without it a custom face simply never appears. */\n get available(): boolean {\n return this.native !== null;\n }\n\n /** Every family the platform can currently find, custom or bundled. */\n readonly families: Signal<readonly string[]> = computed(() => {\n this.generation();\n return this.native?.getLoadedFonts() ?? [];\n });\n\n /** Whether a family is registered. Reactive: re-read after a `load` that adds one. */\n has(family: string): boolean {\n this.generation();\n return this.native?.isLoaded(family) ?? false;\n }\n\n /**\n * Register every `@font-face` a compiled sheet declares.\n *\n * Called before `mount` in an app that uses one. Text laid out before a face registers is in\n * the fallback face until it does, and is laid out again then. Sheets with no faces resolve\n * immediately, so calling it unconditionally at bootstrap costs nothing.\n */\n async loadSheet(...sheets: readonly (SheetWithFonts | null | undefined)[]): Promise<void> {\n const faces = sheets.flatMap((sheet) => [...(sheet?.fonts ?? [])]);\n if (!faces.length) return;\n await this.load(registrationsFor(faces));\n }\n\n /** Register faces by name, for a font that did not come from a stylesheet. */\n async load(map: Record<string, unknown>): Promise<void> {\n const native = this.native;\n if (!native) return;\n try {\n await native.loadAsync(map);\n } finally {\n // Text already laid out in the fallback face keeps it until it is laid out again. Only the\n // faces that registered: one failing fails the whole load, not the others in it.\n fontsRegistered(Object.keys(map).filter((family) => native.isLoaded(family)));\n }\n this.generation.update((n) => n + 1);\n }\n}\n\n/** Injected where an app wants to ask what is loaded. */\nexport const Fonts = new InjectionToken<FontRegistry>('angular-native.fonts', {\n factory: () => new FontRegistry(expoFonts()),\n});\nexport type Fonts = FontRegistry;\n\n/**\n * Register every face the given sheets declare, before the app mounts.\n *\n * A function rather than a service because that is when it is needed: there is no injector yet.\n */\nexport function loadFonts(\n ...sheets: readonly (SheetWithFonts | null | undefined)[]\n): Promise<void> {\n return new FontRegistry(expoFonts()).loadSheet(...sheets);\n}\n"]}
@@ -21,7 +21,8 @@
21
21
  * ```
22
22
  *
23
23
  * Mounting does not wait for the fonts; the splash screen does. Text laid out before they arrive
24
- * is behind the splash, so nothing is ever seen in the fallback face and nothing reflows.
24
+ * is laid out again as each face registers, behind the splash, so nothing is ever seen in the
25
+ * fallback face.
25
26
  *
26
27
  * `hideWhenReady` is that shape as one call, which is the only thing worth wrapping here: every
27
28
  * function underneath is already a plain promise with no React in it. The class takes its
@@ -21,7 +21,8 @@
21
21
  * ```
22
22
  *
23
23
  * Mounting does not wait for the fonts; the splash screen does. Text laid out before they arrive
24
- * is behind the splash, so nothing is ever seen in the fallback face and nothing reflows.
24
+ * is laid out again as each face registers, behind the splash, so nothing is ever seen in the
25
+ * fallback face.
25
26
  *
26
27
  * `hideWhenReady` is that shape as one call, which is the only thing worth wrapping here: every
27
28
  * function underneath is already a plain promise with no React in it. The class takes its
@@ -1 +1 @@
1
- {"version":3,"file":"splash-screen.js","sourceRoot":"","sources":["../src/splash-screen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQzC,MAAM,UAAU,gBAAgB;IAC9B,MAAM,IAAI,GAAG,UAAU,CACrB,oBAAoB,EACpB,GAAG,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAwC,CAC3E,CAAC;IACF,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,OAAO;QACL,oBAAoB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE;QACvD,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE;KAClC,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,MAAM;IACA,MAAM,CAAgE;IAC/E,QAAQ,GAA0D,IAAI,CAAC;IACvE,IAAI,GAAG,KAAK,CAAC;IAErB;;;OAGG;IACH,YAAY,MAAqE;QAC/E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,IAAY,MAAM;QAChB,IAAI,CAAC,QAAQ,KAAK;YAChB,MAAM,EAAE,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM;SACxE,CAAC;QACF,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,8FAA8F;IAC9F,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;IAC9B,CAAC;IAED;;;;;OAKG;IACH,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QACtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,KAAK,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,+DAA+D;IAC/D,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;QAClB,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAChD,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,aAAa,CAAC,IAAsB,EAAE,SAAS,GAAG,YAAY;QAClE,IAAI,CAAC;YACH,MAAM,IAAI,CAAC;QACb,CAAC;gBAAS,CAAC;YACT,MAAM,SAAS,EAAE,CAAC;YAClB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;CACF;AAED,4FAA4F;AAC5F,MAAM,YAAY,GAAG,GAAkB,EAAE,CACvC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;IACtB,MAAM,GAAG,GAAI,UAAmE;SAC7E,qBAAqB,CAAC;IACzB,IAAI,GAAG;QAAE,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;;QACzB,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC;AAEL;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAEzD,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,cAAc,CAAS,6BAA6B,EAAE;IACpF,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY;CAC5B,CAAC,CAAC","sourcesContent":["/**\n * `SplashScreen`, bound to `expo-splash-screen`. Held until the app has something worth showing.\n *\n * The whole point is the ordering, and it is easy to get subtly wrong: the native screen hides\n * itself as soon as the first frame is drawn, so an app that loads fonts before mounting shows a\n * blank window for as long as that takes. Preventing the auto-hide has to happen at module scope,\n * before any of that, and hiding has to happen after the first real frame:\n *\n * ```ts\n * import { splashScreen } from '@ng-native/expo/splash-screen';\n * import { loadFonts } from '@ng-native/expo/fonts';\n *\n * // At module scope, before anything renders.\n * splashScreen.hold();\n *\n * AppRegistry.registerRunnable('main', ({ rootTag }) => {\n * const fonts = loadFonts(styleSheetOf(GlobalStyles));\n * mount(Number(rootTag), App, getFabricUIManager(), { globalStyles: styleSheetOf(GlobalStyles) });\n * void splashScreen.hideWhenReady(fonts);\n * });\n * ```\n *\n * Mounting does not wait for the fonts; the splash screen does. Text laid out before they arrive\n * is behind the splash, so nothing is ever seen in the fallback face and nothing reflows.\n *\n * `hideWhenReady` is that shape as one call, which is the only thing worth wrapping here: every\n * function underneath is already a plain promise with no React in it. The class takes its\n * platform rather than injecting it, because the first call happens before there is an injector.\n */\nimport { InjectionToken } from '@angular/core';\nimport { expoModule } from './native.ts';\n\n/** The slice of `expo-splash-screen` this needs. */\nexport interface NativeSplashScreen {\n preventAutoHideAsync(): Promise<boolean>;\n hideAsync(): Promise<void>;\n}\n\nexport function expoSplashScreen(): NativeSplashScreen | null {\n const expo = expoModule(\n 'expo-splash-screen',\n () => require('expo-splash-screen') as typeof import('expo-splash-screen'),\n );\n if (!expo) return null;\n return {\n preventAutoHideAsync: () => expo.preventAutoHideAsync(),\n hideAsync: () => expo.hideAsync(),\n };\n}\n\nexport class Splash {\n private readonly source: NativeSplashScreen | null | (() => NativeSplashScreen | null);\n private resolved: { readonly native: NativeSplashScreen | null } | null = null;\n private held = false;\n\n /**\n * The module, or a function that reaches it: the shared instance is created when its file is\n * imported, and reaching the module then would fail an import rather than the first call.\n */\n constructor(native: NativeSplashScreen | null | (() => NativeSplashScreen | null)) {\n this.source = native;\n }\n\n private get native(): NativeSplashScreen | null {\n this.resolved ??= {\n native: typeof this.source === 'function' ? this.source() : this.source,\n };\n return this.resolved.native;\n }\n\n /** Whether the module is installed. Without it the screen hides itself on the first frame. */\n get available(): boolean {\n return this.native !== null;\n }\n\n /**\n * Keep the splash up. Called at module scope, before anything is mounted.\n *\n * Failures are swallowed: the only ones are races with the screen already having gone, and an\n * app that cannot hold its splash should still start.\n */\n hold(): void {\n if (!this.native || this.held) return;\n this.held = true;\n void this.native.preventAutoHideAsync().catch(() => {});\n }\n\n /** Let it go. Safe to call whether or not it was ever held. */\n async hide(): Promise<void> {\n if (!this.native) return;\n this.held = false;\n await this.native.hideAsync().catch(() => {});\n }\n\n /**\n * Hide once `work` is done *and* the first frame after it has been drawn.\n *\n * Hiding the moment the work resolves uncovers the frame that was on screen while it ran, which\n * is the empty one. Waiting a frame is what makes the handover invisible, and is why this is a\n * method rather than two calls at a call site.\n */\n async hideWhenReady(work: Promise<unknown>, nextFrame = defaultFrame): Promise<void> {\n try {\n await work;\n } finally {\n await nextFrame();\n await this.hide();\n }\n }\n}\n\n/** One turn of the frame loop, or the macrotask queue where there is no loop to wait on. */\nconst defaultFrame = (): Promise<void> =>\n new Promise((resolve) => {\n const raf = (globalThis as { requestAnimationFrame?: (cb: () => void) => void })\n .requestAnimationFrame;\n if (raf) raf(() => resolve());\n else setTimeout(resolve, 0);\n });\n\n/**\n * The one instance, exported directly because it is needed before an injector exists: holding the\n * splash is the first thing an entry file does.\n */\nexport const splashScreen = new Splash(expoSplashScreen);\n\nexport const SplashScreen = new InjectionToken<Splash>('angular-native.splashScreen', {\n factory: () => splashScreen,\n});\nexport type SplashScreen = Splash;\n"]}
1
+ {"version":3,"file":"splash-screen.js","sourceRoot":"","sources":["../src/splash-screen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQzC,MAAM,UAAU,gBAAgB;IAC9B,MAAM,IAAI,GAAG,UAAU,CACrB,oBAAoB,EACpB,GAAG,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAwC,CAC3E,CAAC;IACF,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,OAAO;QACL,oBAAoB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE;QACvD,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE;KAClC,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,MAAM;IACA,MAAM,CAAgE;IAC/E,QAAQ,GAA0D,IAAI,CAAC;IACvE,IAAI,GAAG,KAAK,CAAC;IAErB;;;OAGG;IACH,YAAY,MAAqE;QAC/E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,IAAY,MAAM;QAChB,IAAI,CAAC,QAAQ,KAAK;YAChB,MAAM,EAAE,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM;SACxE,CAAC;QACF,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,8FAA8F;IAC9F,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;IAC9B,CAAC;IAED;;;;;OAKG;IACH,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QACtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,KAAK,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,+DAA+D;IAC/D,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;QAClB,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAChD,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,aAAa,CAAC,IAAsB,EAAE,SAAS,GAAG,YAAY;QAClE,IAAI,CAAC;YACH,MAAM,IAAI,CAAC;QACb,CAAC;gBAAS,CAAC;YACT,MAAM,SAAS,EAAE,CAAC;YAClB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;CACF;AAED,4FAA4F;AAC5F,MAAM,YAAY,GAAG,GAAkB,EAAE,CACvC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;IACtB,MAAM,GAAG,GAAI,UAAmE;SAC7E,qBAAqB,CAAC;IACzB,IAAI,GAAG;QAAE,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;;QACzB,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC;AAEL;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAEzD,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,cAAc,CAAS,6BAA6B,EAAE;IACpF,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY;CAC5B,CAAC,CAAC","sourcesContent":["/**\n * `SplashScreen`, bound to `expo-splash-screen`. Held until the app has something worth showing.\n *\n * The whole point is the ordering, and it is easy to get subtly wrong: the native screen hides\n * itself as soon as the first frame is drawn, so an app that loads fonts before mounting shows a\n * blank window for as long as that takes. Preventing the auto-hide has to happen at module scope,\n * before any of that, and hiding has to happen after the first real frame:\n *\n * ```ts\n * import { splashScreen } from '@ng-native/expo/splash-screen';\n * import { loadFonts } from '@ng-native/expo/fonts';\n *\n * // At module scope, before anything renders.\n * splashScreen.hold();\n *\n * AppRegistry.registerRunnable('main', ({ rootTag }) => {\n * const fonts = loadFonts(styleSheetOf(GlobalStyles));\n * mount(Number(rootTag), App, getFabricUIManager(), { globalStyles: styleSheetOf(GlobalStyles) });\n * void splashScreen.hideWhenReady(fonts);\n * });\n * ```\n *\n * Mounting does not wait for the fonts; the splash screen does. Text laid out before they arrive\n * is laid out again as each face registers, behind the splash, so nothing is ever seen in the\n * fallback face.\n *\n * `hideWhenReady` is that shape as one call, which is the only thing worth wrapping here: every\n * function underneath is already a plain promise with no React in it. The class takes its\n * platform rather than injecting it, because the first call happens before there is an injector.\n */\nimport { InjectionToken } from '@angular/core';\nimport { expoModule } from './native.ts';\n\n/** The slice of `expo-splash-screen` this needs. */\nexport interface NativeSplashScreen {\n preventAutoHideAsync(): Promise<boolean>;\n hideAsync(): Promise<void>;\n}\n\nexport function expoSplashScreen(): NativeSplashScreen | null {\n const expo = expoModule(\n 'expo-splash-screen',\n () => require('expo-splash-screen') as typeof import('expo-splash-screen'),\n );\n if (!expo) return null;\n return {\n preventAutoHideAsync: () => expo.preventAutoHideAsync(),\n hideAsync: () => expo.hideAsync(),\n };\n}\n\nexport class Splash {\n private readonly source: NativeSplashScreen | null | (() => NativeSplashScreen | null);\n private resolved: { readonly native: NativeSplashScreen | null } | null = null;\n private held = false;\n\n /**\n * The module, or a function that reaches it: the shared instance is created when its file is\n * imported, and reaching the module then would fail an import rather than the first call.\n */\n constructor(native: NativeSplashScreen | null | (() => NativeSplashScreen | null)) {\n this.source = native;\n }\n\n private get native(): NativeSplashScreen | null {\n this.resolved ??= {\n native: typeof this.source === 'function' ? this.source() : this.source,\n };\n return this.resolved.native;\n }\n\n /** Whether the module is installed. Without it the screen hides itself on the first frame. */\n get available(): boolean {\n return this.native !== null;\n }\n\n /**\n * Keep the splash up. Called at module scope, before anything is mounted.\n *\n * Failures are swallowed: the only ones are races with the screen already having gone, and an\n * app that cannot hold its splash should still start.\n */\n hold(): void {\n if (!this.native || this.held) return;\n this.held = true;\n void this.native.preventAutoHideAsync().catch(() => {});\n }\n\n /** Let it go. Safe to call whether or not it was ever held. */\n async hide(): Promise<void> {\n if (!this.native) return;\n this.held = false;\n await this.native.hideAsync().catch(() => {});\n }\n\n /**\n * Hide once `work` is done *and* the first frame after it has been drawn.\n *\n * Hiding the moment the work resolves uncovers the frame that was on screen while it ran, which\n * is the empty one. Waiting a frame is what makes the handover invisible, and is why this is a\n * method rather than two calls at a call site.\n */\n async hideWhenReady(work: Promise<unknown>, nextFrame = defaultFrame): Promise<void> {\n try {\n await work;\n } finally {\n await nextFrame();\n await this.hide();\n }\n }\n}\n\n/** One turn of the frame loop, or the macrotask queue where there is no loop to wait on. */\nconst defaultFrame = (): Promise<void> =>\n new Promise((resolve) => {\n const raf = (globalThis as { requestAnimationFrame?: (cb: () => void) => void })\n .requestAnimationFrame;\n if (raf) raf(() => resolve());\n else setTimeout(resolve, 0);\n });\n\n/**\n * The one instance, exported directly because it is needed before an injector exists: holding the\n * splash is the first thing an entry file does.\n */\nexport const splashScreen = new Splash(expoSplashScreen);\n\nexport const SplashScreen = new InjectionToken<Splash>('angular-native.splashScreen', {\n factory: () => splashScreen,\n});\nexport type SplashScreen = Splash;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-native/expo",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Expo modules as Angular services for Angular Native, plus registerExpoView for a module's native view.",
5
5
  "keywords": [
6
6
  "angular",
@@ -174,7 +174,7 @@
174
174
  "access": "public"
175
175
  },
176
176
  "dependencies": {
177
- "@ng-native/fabric": "0.1.2"
177
+ "@ng-native/fabric": "0.1.3"
178
178
  },
179
179
  "nx": {
180
180
  "tags": [