@ng-native/expo 0.18.1 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -44,6 +44,11 @@ export interface NativeKeyboard {
44
44
  addListener(event: 'onKeyboardMove', listener: (frame: NativeKeyboardFrame) => void): {
45
45
  remove(): void;
46
46
  };
47
+ /**
48
+ * How far below the top of the screen the app's window starts, in points. A build made before
49
+ * the module had it has no such method.
50
+ */
51
+ windowTopOnScreen?(): number;
47
52
  }
48
53
  /**
49
54
  * The keyboard as the native module reports it, as it starts to move and at every frame of the
package/dist/keyboard.js CHANGED
@@ -92,6 +92,9 @@ export function nativeKeyboardSource(native, fallback = keyboardSource()) {
92
92
  get windowTop() {
93
93
  return fallback.windowTop;
94
94
  },
95
+ get screenTop() {
96
+ return native.windowTopOnScreen?.() ?? 0;
97
+ },
95
98
  };
96
99
  }
97
100
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"keyboard.js","sourceRoot":"","sources":["../src/keyboard.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `provideNativeKeyboard()`: the keyboard's motion on Android, from this package's native half.\n *\n * React Native on Android reports the keyboard once the window's insets have changed, as it starts\n * to move, with no duration: a `<keyboard-avoiding-view>` jumps to where the keyboard is going and a\n * `<keyboard-dock>` jumps onto it, while the keyboard slides up underneath. iOS reports the motion\n * as it starts, with its duration and curve, and the same layouts move with the keyboard there.\n *\n * With this, Android does too. The `Keyboard` service hears of the motion as it starts, with\n * Android's duration, and its `position` follows every frame of it, which a\n * `<keyboard-avoiding-view>` moves with, since Android animates no layout alongside the keyboard by\n * default; and a `<keyboard-dock>` follows it frame by frame on the native side, as one does on iOS\n * with react-native-keyboard-controller. Off\n * Android, and in a build without this package's native half, such as Expo Go, it provides nothing\n * and React Native's own events are what there is.\n *\n * ```ts\n * bootstrap(App, { providers: [provideNativeKeyboard()] });\n * ```\n */\nimport { type Provider } from '@angular/core';\nimport {\n KEYBOARD_MOTION,\n Keyboard,\n keyboardSource,\n type KeyboardMetrics,\n type KeyboardSource,\n} from '@ng-native/device';\nimport { currentPlatform } from './native.ts';\nimport { expoViewName } from './register-expo-view.ts';\n\nconst MODULE = 'NgNativeKeyboard';\n\n/** What the native module sends as the keyboard starts to move. */\nexport interface NativeKeyboardChange {\n /** Points above the system bars; zero for a keyboard going away. */\n readonly height: number;\n /** The keyboard's top edge on the screen, in points. Absent for a keyboard going away. */\n readonly screenY?: number;\n readonly duration: number;\n readonly easing: string;\n}\n\n/** Where the keyboard is at one frame of its motion, or that the motion is over. */\nexport type NativeKeyboardFrame =\n { readonly height: number; readonly screenY?: number } | { readonly end: true };\n\n/** This package's native keyboard module. A test provides a stand-in. */\nexport interface NativeKeyboard {\n addListener(\n event: 'onKeyboardChange',\n listener: (change: NativeKeyboardChange) => void,\n ): { remove(): void };\n addListener(\n event: 'onKeyboardMove',\n listener: (frame: NativeKeyboardFrame) => void,\n ): { remove(): void };\n}\n\n/** How long React Native's own event waits for the native one that says the same, in ms. */\nconst OVERTAKEN_MS = 100;\n\n/** Heights closer than this, in points, are one: the two report it through different roundings. */\nconst SAME_HEIGHT = 0.5;\n\n/** The module where the build has it, on Android. */\nfunction nativeKeyboard(): NativeKeyboard | null {\n if (currentPlatform() !== 'android') return null;\n return (\n (globalThis as { expo?: { modules?: Record<string, NativeKeyboard | undefined> } }).expo\n ?.modules?.[MODULE] ?? null\n );\n}\n\n/** A native height or edge as the metrics carry it: only the fields that are there. */\nconst metricsOf = (at: {\n readonly height: number;\n readonly screenY?: number;\n}): KeyboardMetrics => ({\n height: at.height,\n ...(at.screenY !== undefined && { screenY: at.screenY }),\n});\n\n/**\n * The keyboard as the native module reports it, as it starts to move and at every frame of the\n * motion, and as React Native's own events report it: they still come, and they are what a window\n * of its own, a modal's, has. They arrive at about the moment the native one does, sometimes just\n * before it, so one waits a moment and is dropped when the native one says the same; reported\n * first, it would hand a layout following the frames the destination before the motion began.\n */\nexport function nativeKeyboardSource(\n native: NativeKeyboard,\n fallback: KeyboardSource = keyboardSource(),\n): KeyboardSource {\n return {\n subscribe: (listener) => {\n let last: number | null = null;\n let waiting: ReturnType<typeof setTimeout> | undefined;\n const report = (metrics: KeyboardMetrics) => {\n last = metrics.height;\n listener(metrics);\n };\n const subscription = native.addListener('onKeyboardChange', (change) => {\n clearTimeout(waiting);\n report({ ...metricsOf(change), duration: change.duration, easing: change.easing });\n });\n // A motion watching began part way through sends its frames and end but not its change,\n // which went before: where its last frame left the keyboard is reported at the end, as a\n // jump, unless something already said so.\n let reached: KeyboardMetrics | null = null;\n const frames = native.addListener('onKeyboardMove', (frame) => {\n if (!('end' in frame)) reached = metricsOf(frame);\n else if (reached && (last === null || Math.abs(reached.height - last) >= SAME_HEIGHT))\n report({ ...reached, duration: 0 });\n });\n const stop = fallback.subscribe((metrics) => {\n clearTimeout(waiting);\n waiting = setTimeout(() => {\n if (last === null || Math.abs(metrics.height - last) >= SAME_HEIGHT) report(metrics);\n }, OVERTAKEN_MS);\n });\n return () => {\n clearTimeout(waiting);\n subscription.remove();\n frames.remove();\n stop();\n };\n },\n subscribeMotion: (listener) => {\n const subscription = native.addListener('onKeyboardMove', (frame) =>\n listener('end' in frame ? null : metricsOf(frame)),\n );\n return () => subscription.remove();\n },\n dismiss: () => fallback.dismiss(),\n // The module's `screenY` is from the top of the screen as React Native's is.\n get windowTop() {\n return fallback.windowTop;\n },\n };\n}\n\n/**\n * Follow the keyboard on Android as it moves rather than once it has: see this file. Nothing to\n * install beyond this package; a build made before it had this native module, and Expo Go, keep\n * React Native's own events.\n */\nexport function provideNativeKeyboard(): Provider[] {\n return [\n {\n provide: Keyboard.SOURCE,\n useFactory: () => {\n const native = nativeKeyboard();\n return native ? nativeKeyboardSource(native) : keyboardSource();\n },\n },\n {\n provide: KEYBOARD_MOTION,\n useFactory: () => (nativeKeyboard() ? { viewName: expoViewName(MODULE) } : null),\n },\n ];\n}\n"]}
1
+ {"version":3,"file":"keyboard.js","sourceRoot":"","sources":["../src/keyboard.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `provideNativeKeyboard()`: the keyboard's motion on Android, from this package's native half.\n *\n * React Native on Android reports the keyboard once the window's insets have changed, as it starts\n * to move, with no duration: a `<keyboard-avoiding-view>` jumps to where the keyboard is going and a\n * `<keyboard-dock>` jumps onto it, while the keyboard slides up underneath. iOS reports the motion\n * as it starts, with its duration and curve, and the same layouts move with the keyboard there.\n *\n * With this, Android does too. The `Keyboard` service hears of the motion as it starts, with\n * Android's duration, and its `position` follows every frame of it, which a\n * `<keyboard-avoiding-view>` moves with, since Android animates no layout alongside the keyboard by\n * default; and a `<keyboard-dock>` follows it frame by frame on the native side, as one does on iOS\n * with react-native-keyboard-controller. Off\n * Android, and in a build without this package's native half, such as Expo Go, it provides nothing\n * and React Native's own events are what there is.\n *\n * ```ts\n * bootstrap(App, { providers: [provideNativeKeyboard()] });\n * ```\n */\nimport { type Provider } from '@angular/core';\nimport {\n KEYBOARD_MOTION,\n Keyboard,\n keyboardSource,\n type KeyboardMetrics,\n type KeyboardSource,\n} from '@ng-native/device';\nimport { currentPlatform } from './native.ts';\nimport { expoViewName } from './register-expo-view.ts';\n\nconst MODULE = 'NgNativeKeyboard';\n\n/** What the native module sends as the keyboard starts to move. */\nexport interface NativeKeyboardChange {\n /** Points above the system bars; zero for a keyboard going away. */\n readonly height: number;\n /** The keyboard's top edge on the screen, in points. Absent for a keyboard going away. */\n readonly screenY?: number;\n readonly duration: number;\n readonly easing: string;\n}\n\n/** Where the keyboard is at one frame of its motion, or that the motion is over. */\nexport type NativeKeyboardFrame =\n { readonly height: number; readonly screenY?: number } | { readonly end: true };\n\n/** This package's native keyboard module. A test provides a stand-in. */\nexport interface NativeKeyboard {\n addListener(\n event: 'onKeyboardChange',\n listener: (change: NativeKeyboardChange) => void,\n ): { remove(): void };\n addListener(\n event: 'onKeyboardMove',\n listener: (frame: NativeKeyboardFrame) => void,\n ): { remove(): void };\n /**\n * How far below the top of the screen the app's window starts, in points. A build made before\n * the module had it has no such method.\n */\n windowTopOnScreen?(): number;\n}\n\n/** How long React Native's own event waits for the native one that says the same, in ms. */\nconst OVERTAKEN_MS = 100;\n\n/** Heights closer than this, in points, are one: the two report it through different roundings. */\nconst SAME_HEIGHT = 0.5;\n\n/** The module where the build has it, on Android. */\nfunction nativeKeyboard(): NativeKeyboard | null {\n if (currentPlatform() !== 'android') return null;\n return (\n (globalThis as { expo?: { modules?: Record<string, NativeKeyboard | undefined> } }).expo\n ?.modules?.[MODULE] ?? null\n );\n}\n\n/** A native height or edge as the metrics carry it: only the fields that are there. */\nconst metricsOf = (at: {\n readonly height: number;\n readonly screenY?: number;\n}): KeyboardMetrics => ({\n height: at.height,\n ...(at.screenY !== undefined && { screenY: at.screenY }),\n});\n\n/**\n * The keyboard as the native module reports it, as it starts to move and at every frame of the\n * motion, and as React Native's own events report it: they still come, and they are what a window\n * of its own, a modal's, has. They arrive at about the moment the native one does, sometimes just\n * before it, so one waits a moment and is dropped when the native one says the same; reported\n * first, it would hand a layout following the frames the destination before the motion began.\n */\nexport function nativeKeyboardSource(\n native: NativeKeyboard,\n fallback: KeyboardSource = keyboardSource(),\n): KeyboardSource {\n return {\n subscribe: (listener) => {\n let last: number | null = null;\n let waiting: ReturnType<typeof setTimeout> | undefined;\n const report = (metrics: KeyboardMetrics) => {\n last = metrics.height;\n listener(metrics);\n };\n const subscription = native.addListener('onKeyboardChange', (change) => {\n clearTimeout(waiting);\n report({ ...metricsOf(change), duration: change.duration, easing: change.easing });\n });\n // A motion watching began part way through sends its frames and end but not its change,\n // which went before: where its last frame left the keyboard is reported at the end, as a\n // jump, unless something already said so.\n let reached: KeyboardMetrics | null = null;\n const frames = native.addListener('onKeyboardMove', (frame) => {\n if (!('end' in frame)) reached = metricsOf(frame);\n else if (reached && (last === null || Math.abs(reached.height - last) >= SAME_HEIGHT))\n report({ ...reached, duration: 0 });\n });\n const stop = fallback.subscribe((metrics) => {\n clearTimeout(waiting);\n waiting = setTimeout(() => {\n if (last === null || Math.abs(metrics.height - last) >= SAME_HEIGHT) report(metrics);\n }, OVERTAKEN_MS);\n });\n return () => {\n clearTimeout(waiting);\n subscription.remove();\n frames.remove();\n stop();\n };\n },\n subscribeMotion: (listener) => {\n const subscription = native.addListener('onKeyboardMove', (frame) =>\n listener('end' in frame ? null : metricsOf(frame)),\n );\n return () => subscription.remove();\n },\n dismiss: () => fallback.dismiss(),\n // The module's `screenY` is from the top of the screen as React Native's is.\n get windowTop() {\n return fallback.windowTop;\n },\n get screenTop() {\n return native.windowTopOnScreen?.() ?? 0;\n },\n };\n}\n\n/**\n * Follow the keyboard on Android as it moves rather than once it has: see this file. Nothing to\n * install beyond this package; a build made before it had this native module, and Expo Go, keep\n * React Native's own events.\n */\nexport function provideNativeKeyboard(): Provider[] {\n return [\n {\n provide: Keyboard.SOURCE,\n useFactory: () => {\n const native = nativeKeyboard();\n return native ? nativeKeyboardSource(native) : keyboardSource();\n },\n },\n {\n provide: KEYBOARD_MOTION,\n useFactory: () => (nativeKeyboard() ? { viewName: expoViewName(MODULE) } : null),\n },\n ];\n}\n"]}
package/dist/native.js CHANGED
@@ -85,6 +85,7 @@ const NATIVE_MODULES = {
85
85
  'expo-background-task': 'ExpoBackgroundTask',
86
86
  'expo-battery': 'ExpoBattery',
87
87
  'expo-brightness': 'ExpoBrightness',
88
+ 'expo-calendar': 'CalendarNext',
88
89
  'expo-clipboard': 'ExpoClipboard',
89
90
  'expo-crypto': 'ExpoCrypto',
90
91
  'expo-device': 'ExpoDevice',
@@ -1 +1 @@
1
- {"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAmB,EAAE,MAAkC;IAC9E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC;QACpB,IAAI,IAAI,YAAY,OAAO;YAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,OAAO,EAAE,CAAC;QACjB,MAAM,CAAC,OAAO,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAI,IAAa;IACvC,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAKD;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACzB,IAAI,GAAG,oBAAoB,CAAC;IAC9C,uDAAuD;IAC9C,MAAM,CAAS;IAExB,YAAY,MAAc,EAAE,QAAwB,EAAE,KAAe,EAAE,OAAgB;QACrF,KAAK,CACH,OAAO;YACL,CAAC,QAAQ,KAAK,KAAK;gBACjB,CAAC,CAAC,GAAG,MAAM,wDAAwD,MAAM,iCAAiC;gBAC1G,CAAC,CAAC,GAAG,MAAM,uEAAuE,MAAM,KAAK;oBAC3F,uCAAuC,QAAQ,wCAAwC;oBACvF,2EAA2E,CAAC,EAClF,EAAE,KAAK,EAAE,CACV,CAAC;QACF,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,MAAc;IACxD,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;IACnC,MAAM,OAAO,GACX,QAAQ,KAAK,IAAI;QACf,CAAC,CAAC,oBAAoB,MAAM,6DAA6D,MAAM,EAAE;QACjG,CAAC,CAAC,oBAAoB,MAAM,wBAAwB,QAAQ,GAAG,CAAC;IACpE,0EAA0E;IAC1E,OAAO,IAAI,kBAAkB,CAAC,MAAM,EAAE,QAAQ,IAAI,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAC/E,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,eAAe;IAC7B,OAAO,QAAQ,CACb,GAAG,EAAE,CAAE,OAAO,CAAC,cAAc,CAAkC,CAAC,QAAQ,CAAC,EAAoB,CAC9F,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,cAAc,GAAqC;IACvD,kBAAkB,EAAE,iBAAiB;IACrC,YAAY,EAAE,WAAW;IACzB,YAAY,EAAE,WAAW;IACzB,sBAAsB,EAAE,oBAAoB;IAC5C,cAAc,EAAE,aAAa;IAC7B,iBAAiB,EAAE,gBAAgB;IACnC,gBAAgB,EAAE,eAAe;IACjC,aAAa,EAAE,YAAY;IAC3B,aAAa,EAAE,YAAY;IAC3B,sBAAsB,EAAE,oBAAoB;IAC5C,kBAAkB,EAAE,YAAY;IAChC,gBAAgB,EAAE,eAAe;IACjC,WAAW,EAAE,gBAAgB;IAC7B,wBAAwB,EAAE,sBAAsB;IAChD,mBAAmB,EAAE,qBAAqB;IAC1C,iBAAiB,EAAE,eAAe;IAClC,2BAA2B,EAAE,yBAAyB;IACtD,gBAAgB,EAAE,cAAc;IAChC,mBAAmB,EAAE,kBAAkB;IACvC,eAAe,EAAE,cAAc;IAC/B,oBAAoB,EAAE,kBAAkB;IACxC,cAAc,EAAE,aAAa;IAC7B,oBAAoB,EAAE,2BAA2B;IACjD,qBAAqB,EAAE,mBAAmB;IAC1C,yBAAyB,EAAE,uBAAuB;IAClD,mBAAmB,EAAE,iBAAiB;IACtC,cAAc,EAAE,uBAAuB;IACvC,aAAa,EAAE,YAAY;IAC3B,mBAAmB,EAAE,iBAAiB;IACtC,4BAA4B,EAAE,0BAA0B;IACxD,cAAc,EAAE,aAAa;IAC7B,YAAY,EAAE,WAAW;IACzB,kBAAkB,EAAE,gBAAgB;CACrC,CAAC;AAEF;;;;;;GAMG;AACH,SAAS,iBAAiB,CAAC,MAAc,EAAE,QAA+B;IACxE,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACnE,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAuC,CAAC,CAAC;IAChG,OAAO,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,UAAU,CACxB,MAAc,EACd,IAAgC,EAChC,YAAuC,CAAC,KAAK,EAAE,SAAS,CAAC;IAEzD,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;IACnC,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACnE,IAAI,MAAM,IAAI,IAAI;YAAE,OAAO,MAAM,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,GAAG,KAAK,CAAC;IAClB,CAAC;IACD,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACpE,OAAO,OAAO,CAAC,IAAI,kBAAkB,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,6FAA6F;AAC7F,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;AAE9D;;;;;;;;;GASG;AACH,SAAS,OAAO,CAAI,KAAyB;IAC3C,MAAM,IAAI,GAAG,GAAU,EAAE;QACvB,MAAM,KAAK,CAAC;IACd,CAAC,CAAC;IACF,iFAAiF;IACjF,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,SAAS,OAAO,KAAI,CAAC,EAAE;QAC/C,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACxF,GAAG,EAAE,IAAI;QACT,GAAG,EAAE,IAAI;QACT,KAAK,EAAE,IAAI;QACX,SAAS,EAAE,IAAI;QACf,OAAO,EAAE,IAAI;QACb,cAAc,EAAE,IAAI;QACpB,cAAc,EAAE,IAAI;QACpB,wBAAwB,EAAE,IAAI;KAC/B,CAAC,CAAC;IACH,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAClC,OAAO,OAAY,CAAC;AACtB,CAAC;AAED,2EAA2E;AAC3E,MAAM,aAAa,GAAG,IAAI,OAAO,EAA8B,CAAC;AAEhE,2EAA2E;AAC3E,MAAM,SAAS,GAAG,CAAC,KAAc,EAAW,EAAE,CAC5C,OAAO,KAAK,KAAK,UAAU,IAAI,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAI,KAA2B;IACpD,OAAO,KAAK,IAAI,IAAI,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AAC1D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAc,EACd,IAAgC,EAChC,SAAqC;IAErC,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,KAAK,IAAI,aAAa,CAAC,GAAG,CAAC,KAAe,CAAC,CAAC;IAC1D,IAAI,KAAK;QAAE,MAAM,KAAK,CAAC;IACvB,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["/**\n * Reaching an Expo module without importing one.\n *\n * A static `import ... from 'expo-battery'` makes the file unloadable by Node, which is what used\n * to force every service into two files: the behaviour somewhere the test suite could reach, and\n * the binding somewhere it could not. A `require` inside a function has neither problem. Metro\n * still resolves the string literal at build time, so the module is bundled exactly as before and\n * an app that has not installed it still gets a build error rather than a surprise at runtime;\n * Node never evaluates the call at all, because nothing in a test asks for the real platform.\n *\n * The `catch` is doing two jobs, and only one of them is Node. In an ESM test `require` is not\n * defined, so calling it throws `ReferenceError` and the service goes inert - the same state as\n * a device that has no such sensor. On a device the throw is a module that is present but not\n * linked, which is the same answer for the same reason.\n */\n/**\n * Calls a listener the app gave. What it throws, or rejects with when it is asynchronous, goes to\n * `report`, so one failing listener does not stop what calls it.\n */\nexport function isolated(call: () => unknown, report: (failure: unknown) => void): void {\n try {\n const done = call();\n if (done instanceof Promise) done.catch(report);\n } catch (failure) {\n report(failure);\n }\n}\n\nexport function optional<T>(load: () => T): T | null {\n try {\n return load();\n } catch {\n return null;\n }\n}\n\n/** The platforms an Expo module is written for. */\nexport type ModulePlatform = 'ios' | 'android' | 'web';\n\n/**\n * A module the app needs and does not have: the package is not installed, or it is and the app was\n * not rebuilt since, so its native half is not in the binary. Development builds and Expo Go carry\n * only the native modules they were built with.\n */\nexport class MissingModuleError extends Error {\n override readonly name = 'MissingModuleError';\n /** The package, as it is installed: `expo-haptics`. */\n readonly module: string;\n\n constructor(module: string, platform: ModulePlatform, cause?: unknown, message?: string) {\n super(\n message ??\n (platform === 'web'\n ? `${module} is not installed. Install it with \"npx expo install ${module}\", then restart the dev server.`\n : `${module} is not in this build of the app. Install it with \"npx expo install ${module}\", ` +\n `then rebuild the app (\"npx expo run:${platform}\", or a new EAS build): a development ` +\n `build, and Expo Go, contain only the native modules they were built with.`),\n { cause },\n );\n this.module = module;\n }\n}\n\n/**\n * The `MissingModuleError` to throw where `expoModule` answered null: the module is installed or not, but there is\n * nothing to load it on. In Node, where a test runs, that is every native module, and \"install it\"\n * would be advice to do what is already done; `inTest` says what a test does instead. On a platform\n * the module does not support, it says that.\n */\nexport function unavailable(module: string, inTest: string): MissingModuleError {\n const platform = currentPlatform();\n const message =\n platform === null\n ? `[angular-native] ${module} has no native module to load in Node, where a test runs. ${inTest}`\n : `[angular-native] ${module} is not available on ${platform}.`;\n // The platform is only read for the default message, which this replaces.\n return new MissingModuleError(module, platform ?? 'web', undefined, message);\n}\n\n/** The platform the app is running on, or null in Node, where there is none to ask. */\nexport function currentPlatform(): ModulePlatform | null {\n return optional(\n () => (require('react-native') as { Platform: { OS: string } }).Platform.OS as ModulePlatform,\n );\n}\n\n/**\n * For each Expo package whose JavaScript requires a native module as it is evaluated, and so\n * throws when its native half is not in the build, one of those native modules. Any one of them\n * says whether the package's native half is there, and each of these is registered on both iOS and\n * Android.\n */\nconst NATIVE_MODULES: Readonly<Record<string, string>> = {\n 'expo-application': 'ExpoApplication',\n 'expo-asset': 'ExpoAsset',\n 'expo-audio': 'ExpoAudio',\n 'expo-background-task': 'ExpoBackgroundTask',\n 'expo-battery': 'ExpoBattery',\n 'expo-brightness': 'ExpoBrightness',\n 'expo-clipboard': 'ExpoClipboard',\n 'expo-crypto': 'ExpoCrypto',\n 'expo-device': 'ExpoDevice',\n 'expo-document-picker': 'ExpoDocumentPicker',\n 'expo-file-system': 'FileSystem',\n 'expo-foldables': 'ExpoFoldables',\n 'expo-font': 'ExpoFontLoader',\n 'expo-image-manipulator': 'ExpoImageManipulator',\n 'expo-image-picker': 'ExponentImagePicker',\n 'expo-keep-awake': 'ExpoKeepAwake',\n 'expo-local-authentication': 'ExpoLocalAuthentication',\n 'expo-local-llm': 'ExpoLocalLlm',\n 'expo-localization': 'ExpoLocalization',\n 'expo-location': 'ExpoLocation',\n 'expo-media-library': 'ExpoMediaLibrary',\n 'expo-network': 'ExpoNetwork',\n 'expo-notifications': 'ExpoNotificationPresenter',\n 'expo-screen-capture': 'ExpoScreenCapture',\n 'expo-screen-orientation': 'ExpoScreenOrientation',\n 'expo-secure-store': 'ExpoSecureStore',\n 'expo-sensors': 'ExponentAccelerometer',\n 'expo-sqlite': 'ExpoSQLite',\n 'expo-store-review': 'ExpoStoreReview',\n 'expo-tracking-transparency': 'ExpoTrackingTransparency',\n 'expo-updates': 'ExpoUpdates',\n 'expo-video': 'ExpoVideo',\n 'expo-web-browser': 'ExpoWebBrowser',\n};\n\n/**\n * Whether `module`'s native half is missing from the build on `platform`, asked of Expo before its\n * JavaScript is evaluated. That JavaScript throws while it is being evaluated when its native module\n * is not there, and Metro reports that as fatal, before any `catch` here, whenever it is not inside\n * another module's load: in a service's factory, say. Not on the web, where a package registers its\n * module only once it is evaluated.\n */\nfunction nativeHalfMissing(module: string, platform: ModulePlatform | null): boolean {\n const name = NATIVE_MODULES[module];\n if (!name || platform === null || platform === 'web') return false;\n const core = optional(() => require('expo-modules-core') as typeof import('expo-modules-core'));\n return core !== null && !core.requireOptionalNativeModule(name);\n}\n\n/**\n * The Expo module a service needs.\n *\n * Where the module exists for the platform the app is on, a module that is missing is a mistake\n * the developer can fix, so using what this answers throws a `MissingModuleError` that says how -\n * rather than leave a feature that silently does nothing. Using it, not asking for it: see\n * `missing`. Anywhere it cannot exist - an iOS-only module on Android, the\n * web, and Node, where a test runs with no platform at all - this answers null, and the service\n * goes inert, as code shared across platforms needs it to.\n *\n * `load` answering null or undefined counts as missing too: `requireOptionalNativeModule` answers\n * null rather than throwing. So does a package whose native half is not in the build, which `load`\n * is not called for: see `nativeHalfMissing`.\n */\nexport function expoModule<T>(\n module: string,\n load: () => T | null | undefined,\n platforms: readonly ModulePlatform[] = ['ios', 'android'],\n): T | null {\n const platform = currentPlatform();\n let failure: unknown;\n try {\n const loaded = nativeHalfMissing(module, platform) ? null : load();\n if (loaded != null) return loaded;\n } catch (error) {\n failure = error;\n }\n if (platform === null || !platforms.includes(platform)) return null;\n return missing(new MissingModuleError(module, platform, failure));\n}\n\n/** What Angular asks of any injected value, and `await` of any value: never the module's. */\nconst ASKED_OF_ANY = new Set(['then', 'ngOnDestroy', 'name']);\n\n/**\n * A module that is not there, which throws `error` when it is used rather than when it is asked\n * for. Thrown from a token's factory or a service's constructor, the error would be the one the\n * first injection sees: Angular leaves the token marked as being made, and every injection after\n * it fails with NG0200, \"Circular dependency\", which names nothing to install.\n *\n * Not thenable, and quiet to a symbol, so awaiting it or logging it does not throw, nor Angular\n * asking an injected value whether it has an `ngOnDestroy` or is its own not-found marker, by\n * `name`.\n */\nfunction missing<T>(error: MissingModuleError): T {\n const fail = (): never => {\n throw error;\n };\n // A function, so calling it, or a member read off it, is a use that says so too.\n const standIn = new Proxy(function standIn() {}, {\n get: (_, key) => (typeof key === 'symbol' || ASKED_OF_ANY.has(key) ? undefined : fail()),\n set: fail,\n has: fail,\n apply: fail,\n construct: fail,\n ownKeys: fail,\n deleteProperty: fail,\n defineProperty: fail,\n getOwnPropertyDescriptor: fail,\n });\n missingErrors.set(standIn, error);\n return standIn as T;\n}\n\n/** The error each stand-in for a missing module throws when it is used. */\nconst missingErrors = new WeakMap<object, MissingModuleError>();\n\n/** Whether `value` is the stand-in for a module missing from the build. */\nconst isMissing = (value: unknown): boolean =>\n typeof value === 'function' && missingErrors.has(value);\n\n/**\n * The module, or null where it is missing from the build: for what a service does as it is made,\n * a listener or a first read, which is skipped then rather than thrown, so the service is made and\n * what the app calls on it is what says the module is missing. See `missing`. A factory that\n * builds over the module hands the stand-in on where it is missing (`if (!present(expo)) return\n * expo as never`), for the same reason.\n */\nexport function present<T>(value: T | null | undefined): T | null {\n return value == null || isMissing(value) ? null : value;\n}\n\n/**\n * As `expoModule`, throwing at once where the module is missing: for a function an app calls\n * itself, a player, a database or the splash screen, where no token is left marked and a failure\n * after the call would be caught and lost.\n */\nexport function expoModuleNow<T>(\n module: string,\n load: () => T | null | undefined,\n platforms?: readonly ModulePlatform[],\n): T | null {\n const found = expoModule(module, load, platforms);\n const error = found && missingErrors.get(found as object);\n if (error) throw error;\n return found;\n}\n"]}
1
+ {"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Reaching an Expo module without importing one.\n *\n * A static `import ... from 'expo-battery'` makes the file unloadable by Node, which is what used\n * to force every service into two files: the behaviour somewhere the test suite could reach, and\n * the binding somewhere it could not. A `require` inside a function has neither problem. Metro\n * still resolves the string literal at build time, so the module is bundled exactly as before and\n * an app that has not installed it still gets a build error rather than a surprise at runtime;\n * Node never evaluates the call at all, because nothing in a test asks for the real platform.\n *\n * The `catch` is doing two jobs, and only one of them is Node. In an ESM test `require` is not\n * defined, so calling it throws `ReferenceError` and the service goes inert - the same state as\n * a device that has no such sensor. On a device the throw is a module that is present but not\n * linked, which is the same answer for the same reason.\n */\n/**\n * Calls a listener the app gave. What it throws, or rejects with when it is asynchronous, goes to\n * `report`, so one failing listener does not stop what calls it.\n */\nexport function isolated(call: () => unknown, report: (failure: unknown) => void): void {\n try {\n const done = call();\n if (done instanceof Promise) done.catch(report);\n } catch (failure) {\n report(failure);\n }\n}\n\nexport function optional<T>(load: () => T): T | null {\n try {\n return load();\n } catch {\n return null;\n }\n}\n\n/** The platforms an Expo module is written for. */\nexport type ModulePlatform = 'ios' | 'android' | 'web';\n\n/**\n * A module the app needs and does not have: the package is not installed, or it is and the app was\n * not rebuilt since, so its native half is not in the binary. Development builds and Expo Go carry\n * only the native modules they were built with.\n */\nexport class MissingModuleError extends Error {\n override readonly name = 'MissingModuleError';\n /** The package, as it is installed: `expo-haptics`. */\n readonly module: string;\n\n constructor(module: string, platform: ModulePlatform, cause?: unknown, message?: string) {\n super(\n message ??\n (platform === 'web'\n ? `${module} is not installed. Install it with \"npx expo install ${module}\", then restart the dev server.`\n : `${module} is not in this build of the app. Install it with \"npx expo install ${module}\", ` +\n `then rebuild the app (\"npx expo run:${platform}\", or a new EAS build): a development ` +\n `build, and Expo Go, contain only the native modules they were built with.`),\n { cause },\n );\n this.module = module;\n }\n}\n\n/**\n * The `MissingModuleError` to throw where `expoModule` answered null: the module is installed or not, but there is\n * nothing to load it on. In Node, where a test runs, that is every native module, and \"install it\"\n * would be advice to do what is already done; `inTest` says what a test does instead. On a platform\n * the module does not support, it says that.\n */\nexport function unavailable(module: string, inTest: string): MissingModuleError {\n const platform = currentPlatform();\n const message =\n platform === null\n ? `[angular-native] ${module} has no native module to load in Node, where a test runs. ${inTest}`\n : `[angular-native] ${module} is not available on ${platform}.`;\n // The platform is only read for the default message, which this replaces.\n return new MissingModuleError(module, platform ?? 'web', undefined, message);\n}\n\n/** The platform the app is running on, or null in Node, where there is none to ask. */\nexport function currentPlatform(): ModulePlatform | null {\n return optional(\n () => (require('react-native') as { Platform: { OS: string } }).Platform.OS as ModulePlatform,\n );\n}\n\n/**\n * For each Expo package whose JavaScript requires a native module as it is evaluated, and so\n * throws when its native half is not in the build, one of those native modules. Any one of them\n * says whether the package's native half is there, and each of these is registered on both iOS and\n * Android.\n */\nconst NATIVE_MODULES: Readonly<Record<string, string>> = {\n 'expo-application': 'ExpoApplication',\n 'expo-asset': 'ExpoAsset',\n 'expo-audio': 'ExpoAudio',\n 'expo-background-task': 'ExpoBackgroundTask',\n 'expo-battery': 'ExpoBattery',\n 'expo-brightness': 'ExpoBrightness',\n 'expo-calendar': 'CalendarNext',\n 'expo-clipboard': 'ExpoClipboard',\n 'expo-crypto': 'ExpoCrypto',\n 'expo-device': 'ExpoDevice',\n 'expo-document-picker': 'ExpoDocumentPicker',\n 'expo-file-system': 'FileSystem',\n 'expo-foldables': 'ExpoFoldables',\n 'expo-font': 'ExpoFontLoader',\n 'expo-image-manipulator': 'ExpoImageManipulator',\n 'expo-image-picker': 'ExponentImagePicker',\n 'expo-keep-awake': 'ExpoKeepAwake',\n 'expo-local-authentication': 'ExpoLocalAuthentication',\n 'expo-local-llm': 'ExpoLocalLlm',\n 'expo-localization': 'ExpoLocalization',\n 'expo-location': 'ExpoLocation',\n 'expo-media-library': 'ExpoMediaLibrary',\n 'expo-network': 'ExpoNetwork',\n 'expo-notifications': 'ExpoNotificationPresenter',\n 'expo-screen-capture': 'ExpoScreenCapture',\n 'expo-screen-orientation': 'ExpoScreenOrientation',\n 'expo-secure-store': 'ExpoSecureStore',\n 'expo-sensors': 'ExponentAccelerometer',\n 'expo-sqlite': 'ExpoSQLite',\n 'expo-store-review': 'ExpoStoreReview',\n 'expo-tracking-transparency': 'ExpoTrackingTransparency',\n 'expo-updates': 'ExpoUpdates',\n 'expo-video': 'ExpoVideo',\n 'expo-web-browser': 'ExpoWebBrowser',\n};\n\n/**\n * Whether `module`'s native half is missing from the build on `platform`, asked of Expo before its\n * JavaScript is evaluated. That JavaScript throws while it is being evaluated when its native module\n * is not there, and Metro reports that as fatal, before any `catch` here, whenever it is not inside\n * another module's load: in a service's factory, say. Not on the web, where a package registers its\n * module only once it is evaluated.\n */\nfunction nativeHalfMissing(module: string, platform: ModulePlatform | null): boolean {\n const name = NATIVE_MODULES[module];\n if (!name || platform === null || platform === 'web') return false;\n const core = optional(() => require('expo-modules-core') as typeof import('expo-modules-core'));\n return core !== null && !core.requireOptionalNativeModule(name);\n}\n\n/**\n * The Expo module a service needs.\n *\n * Where the module exists for the platform the app is on, a module that is missing is a mistake\n * the developer can fix, so using what this answers throws a `MissingModuleError` that says how -\n * rather than leave a feature that silently does nothing. Using it, not asking for it: see\n * `missing`. Anywhere it cannot exist - an iOS-only module on Android, the\n * web, and Node, where a test runs with no platform at all - this answers null, and the service\n * goes inert, as code shared across platforms needs it to.\n *\n * `load` answering null or undefined counts as missing too: `requireOptionalNativeModule` answers\n * null rather than throwing. So does a package whose native half is not in the build, which `load`\n * is not called for: see `nativeHalfMissing`.\n */\nexport function expoModule<T>(\n module: string,\n load: () => T | null | undefined,\n platforms: readonly ModulePlatform[] = ['ios', 'android'],\n): T | null {\n const platform = currentPlatform();\n let failure: unknown;\n try {\n const loaded = nativeHalfMissing(module, platform) ? null : load();\n if (loaded != null) return loaded;\n } catch (error) {\n failure = error;\n }\n if (platform === null || !platforms.includes(platform)) return null;\n return missing(new MissingModuleError(module, platform, failure));\n}\n\n/** What Angular asks of any injected value, and `await` of any value: never the module's. */\nconst ASKED_OF_ANY = new Set(['then', 'ngOnDestroy', 'name']);\n\n/**\n * A module that is not there, which throws `error` when it is used rather than when it is asked\n * for. Thrown from a token's factory or a service's constructor, the error would be the one the\n * first injection sees: Angular leaves the token marked as being made, and every injection after\n * it fails with NG0200, \"Circular dependency\", which names nothing to install.\n *\n * Not thenable, and quiet to a symbol, so awaiting it or logging it does not throw, nor Angular\n * asking an injected value whether it has an `ngOnDestroy` or is its own not-found marker, by\n * `name`.\n */\nfunction missing<T>(error: MissingModuleError): T {\n const fail = (): never => {\n throw error;\n };\n // A function, so calling it, or a member read off it, is a use that says so too.\n const standIn = new Proxy(function standIn() {}, {\n get: (_, key) => (typeof key === 'symbol' || ASKED_OF_ANY.has(key) ? undefined : fail()),\n set: fail,\n has: fail,\n apply: fail,\n construct: fail,\n ownKeys: fail,\n deleteProperty: fail,\n defineProperty: fail,\n getOwnPropertyDescriptor: fail,\n });\n missingErrors.set(standIn, error);\n return standIn as T;\n}\n\n/** The error each stand-in for a missing module throws when it is used. */\nconst missingErrors = new WeakMap<object, MissingModuleError>();\n\n/** Whether `value` is the stand-in for a module missing from the build. */\nconst isMissing = (value: unknown): boolean =>\n typeof value === 'function' && missingErrors.has(value);\n\n/**\n * The module, or null where it is missing from the build: for what a service does as it is made,\n * a listener or a first read, which is skipped then rather than thrown, so the service is made and\n * what the app calls on it is what says the module is missing. See `missing`. A factory that\n * builds over the module hands the stand-in on where it is missing (`if (!present(expo)) return\n * expo as never`), for the same reason.\n */\nexport function present<T>(value: T | null | undefined): T | null {\n return value == null || isMissing(value) ? null : value;\n}\n\n/**\n * As `expoModule`, throwing at once where the module is missing: for a function an app calls\n * itself, a player, a database or the splash screen, where no token is left marked and a failure\n * after the call would be caught and lost.\n */\nexport function expoModuleNow<T>(\n module: string,\n load: () => T | null | undefined,\n platforms?: readonly ModulePlatform[],\n): T | null {\n const found = expoModule(module, load, platforms);\n const error = found && missingErrors.get(found as object);\n if (error) throw error;\n return found;\n}\n"]}
@@ -13,13 +13,27 @@ public final class ExpoUIModifiersModule: Module {
13
13
 
14
14
  OnCreate {
15
15
  #if canImport(ExpoUI)
16
- ViewModifierRegistry.register("ngNativeInlineNavigationTitle") { _, _, _ in
17
- InlineNavigationTitle()
18
- }
19
- ViewModifierRegistry.register("ngNativeSegmentedFont") { params, _, _ in
20
- SegmentedFont(params: params)
21
- }
16
+ _ = Self.registered
22
17
  #endif
23
18
  }
24
19
  }
20
+
21
+ #if canImport(ExpoUI)
22
+ /**
23
+ Registers the modifiers once per process, as `expo-widgets` registers its own. `@expo/ui`'s
24
+ registry is one dictionary for the whole process, outlives a reload, and has no lock: Expo creates
25
+ this module again on every reload, on the JavaScript thread, while the main thread can still be
26
+ drawing the outgoing screens' SwiftUI views and reading that dictionary. Writing it again then
27
+ is a data race, and these factories capture nothing, so there is nothing to replace. A static's
28
+ initializer runs once, behind a lock, and the first one runs before any SwiftUI view is drawn.
29
+ */
30
+ private static let registered: Void = {
31
+ ViewModifierRegistry.register("ngNativeInlineNavigationTitle") { _, _, _ in
32
+ InlineNavigationTitle()
33
+ }
34
+ ViewModifierRegistry.register("ngNativeSegmentedFont") { params, _, _ in
35
+ SegmentedFont(params: params)
36
+ }
37
+ }()
38
+ #endif
25
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-native/expo",
3
- "version": "0.18.1",
3
+ "version": "0.19.0",
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",
@@ -63,6 +63,10 @@
63
63
  "types": "./dist/browser.d.ts",
64
64
  "default": "./dist/browser.js"
65
65
  },
66
+ "./calendar": {
67
+ "types": "./dist/calendar.d.ts",
68
+ "default": "./dist/calendar.js"
69
+ },
66
70
  "./camera": {
67
71
  "types": "./dist/camera.d.ts",
68
72
  "default": "./dist/camera.js"
@@ -221,8 +225,8 @@
221
225
  "access": "public"
222
226
  },
223
227
  "dependencies": {
224
- "@ng-native/device": "0.18.1",
225
- "@ng-native/fabric": "0.18.1"
228
+ "@ng-native/device": "0.19.0",
229
+ "@ng-native/fabric": "0.19.0"
226
230
  },
227
231
  "nx": {
228
232
  "tags": [
@@ -247,6 +251,7 @@
247
251
  "expo-background-task": "^57.0.0 || ^58.0.0",
248
252
  "expo-battery": "^57.0.0 || ^58.0.0",
249
253
  "expo-brightness": "^57.0.0 || ^58.0.0",
254
+ "expo-calendar": "^57.0.0 || ^58.0.0",
250
255
  "expo-camera": "^57.0.0 || ^58.0.0",
251
256
  "expo-clipboard": "^57.0.0 || ^58.0.0",
252
257
  "expo-crypto": "^57.0.0 || ^58.0.0",
@@ -297,6 +302,7 @@
297
302
  "expo-background-task": "^58.0.11",
298
303
  "expo-battery": "^58.0.3",
299
304
  "expo-brightness": "^58.0.2",
305
+ "expo-calendar": "^58.0.6",
300
306
  "expo-camera": "^58.0.11",
301
307
  "expo-crypto": "^58.0.7",
302
308
  "expo-device": "^58.0.5",
@@ -356,6 +362,9 @@
356
362
  "expo-brightness": {
357
363
  "optional": true
358
364
  },
365
+ "expo-calendar": {
366
+ "optional": true
367
+ },
359
368
  "expo-camera": {
360
369
  "optional": true
361
370
  },