@ng-native/expo 0.14.0 → 0.15.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.
@@ -19,4 +19,6 @@ dependencies {
19
19
  implementation 'androidx.core:core:1.17.0'
20
20
  // `<search-field>`'s SearchView. React Native depends on AppCompat already.
21
21
  implementation 'androidx.appcompat:appcompat:1.7.0'
22
+ // ReactTypefaceUtils, which finds a face expo-font registered for `<search-field>` as a text's.
23
+ implementation 'com.facebook.react:react-android'
22
24
  }
@@ -1,11 +1,15 @@
1
1
  package dev.ngnative.expo
2
2
 
3
3
  import android.content.Context
4
+ import android.util.TypedValue
4
5
  import android.view.inputmethod.InputMethodManager
6
+ import android.widget.TextView
5
7
  import androidx.appcompat.widget.SearchView
8
+ import com.facebook.react.views.text.ReactTypefaceUtils
6
9
  import expo.modules.kotlin.AppContext
7
10
  import expo.modules.kotlin.modules.Module
8
11
  import expo.modules.kotlin.modules.ModuleDefinition
12
+ import expo.modules.kotlin.types.Either
9
13
  import expo.modules.kotlin.viewevent.EventDispatcher
10
14
  import expo.modules.kotlin.views.ExpoView
11
15
 
@@ -29,6 +33,19 @@ class SearchFieldModule : Module() {
29
33
  Prop("placeholder") { view: SearchFieldView, placeholder: String? ->
30
34
  view.search.queryHint = placeholder
31
35
  }
36
+ // The face and size the field's CSS resolves to, as a text's: the face by the name expo-font
37
+ // registers it under.
38
+ Prop("fontFamily") { view: SearchFieldView, family: String? -> view.fontFamily = family }
39
+ Prop("fontSize") { view: SearchFieldView, size: Double? -> view.fontSize = size?.toFloat() }
40
+ // A weight and a slant no matched face already draws: a number, or `normal` and `bold`.
41
+ Prop("fontWeight") { view: SearchFieldView, weight: Either<Double, String>? ->
42
+ view.fontWeight = when {
43
+ weight == null -> null
44
+ weight.`is`(Double::class) -> weight.get(Double::class).toInt().toString()
45
+ else -> weight.get(String::class)
46
+ }
47
+ }
48
+ Prop("fontStyle") { view: SearchFieldView, style: String? -> view.fontStyle = style }
32
49
 
33
50
  AsyncFunction("focus") { view: SearchFieldView -> view.focus() }.runOnQueue(expo.modules.kotlin.functions.Queues.MAIN)
34
51
  AsyncFunction("blur") { view: SearchFieldView -> view.search.clearFocus() }.runOnQueue(expo.modules.kotlin.functions.Queues.MAIN)
@@ -85,6 +102,50 @@ class SearchFieldView(context: Context, appContext: AppContext) : ExpoView(conte
85
102
  addView(search, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
86
103
  }
87
104
 
105
+ /** The text box, and the face and size it starts with, put back when the CSS names none. */
106
+ private val input = search.findViewById<TextView>(androidx.appcompat.R.id.search_src_text)
107
+ private val systemTypeface = input?.typeface
108
+ private val systemTextSize = input?.textSize
109
+
110
+ var fontFamily: String? = null
111
+ set(value) {
112
+ field = value
113
+ applyFont()
114
+ }
115
+ var fontSize: Float? = null
116
+ set(value) {
117
+ field = value
118
+ applyFont()
119
+ }
120
+ var fontWeight: String? = null
121
+ set(value) {
122
+ field = value
123
+ applyFont()
124
+ }
125
+ var fontStyle: String? = null
126
+ set(value) {
127
+ field = value
128
+ applyFont()
129
+ }
130
+
131
+ /**
132
+ * The CSS's face, weight and slant, found as a text's is, at its size in sp, so it scales with the
133
+ * font size setting.
134
+ */
135
+ private fun applyFont() {
136
+ val input = input ?: return
137
+ input.typeface = ReactTypefaceUtils.applyStyles(
138
+ systemTypeface,
139
+ ReactTypefaceUtils.parseFontStyle(fontStyle),
140
+ ReactTypefaceUtils.parseFontWeight(fontWeight),
141
+ fontFamily,
142
+ context.assets,
143
+ )
144
+ val size = fontSize
145
+ if (size != null) input.setTextSize(TypedValue.COMPLEX_UNIT_SP, size)
146
+ else if (systemTextSize != null) input.setTextSize(TypedValue.COMPLEX_UNIT_PX, systemTextSize)
147
+ }
148
+
88
149
  /** Puts JavaScript's text in the field, unless it answers a keystroke older than the last one. */
89
150
  private fun applyText() {
90
151
  if (mostRecentEventCount < eventCount || search.query.toString() == text) return
package/dist/camera.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `Camera`: a picture taken from the `<expo-camera>` view on screen.
2
+ * `Camera`: a picture taken, or a video recorded, from the `<expo-camera>` view on screen.
3
3
  *
4
4
  * ```ts
5
5
  * registerExpoViews('expo-camera'); // once, at startup
@@ -14,11 +14,15 @@
14
14
  * }
15
15
  * ```
16
16
  *
17
+ * A video is `record()`, which resolves once it stops - `stopRecording()`, or a `maxDuration` or
18
+ * `maxFileSize` reached - on a view with `mode="video"`.
19
+ *
17
20
  * Importing it is all it takes: it applies to every `<expo-camera>` in the template. The view is
18
21
  * what takes the picture, so this is a directive on the view rather than a service.
19
22
  *
20
23
  * In React, `takePictureAsync` is a method on the component's ref. Underneath, it is one of the
21
- * functions Expo defines on the view (`AsyncFunction("takePicture")` in the module), which native
24
+ * functions Expo defines on the view (`AsyncFunction("takePicture")` in the module, and `record`
25
+ * and `stopRecording` beside it), which native
22
26
  * finds by reading `nativeTag` off whatever it is called on. So this calls the same function with
23
27
  * the tag the engine committed the view under, and no ref is needed.
24
28
  */
@@ -26,11 +30,23 @@ import { InjectionToken } from '@angular/core';
26
30
  import * as i0 from "@angular/core";
27
31
  export type PictureOptions = Omit<import('expo-camera').CameraPictureOptions, 'onPictureSaved' | 'pictureRef'>;
28
32
  export type CameraPicture = import('expo-camera').CameraCapturedPicture;
33
+ /** `expo-camera`'s own, less `mirror`, which it has deprecated for the view's `mirror` prop. */
34
+ export type RecordingOptions = Omit<import('expo-camera').CameraRecordingOptions, 'mirror'>;
35
+ /** A recorded video: a file in the cache directory. */
36
+ export interface CameraRecording {
37
+ readonly uri: string;
38
+ }
29
39
  /** The functions `expo-camera` defines on its view, called with the view's tag as `this`. */
30
40
  export interface CameraViewFunctions {
31
41
  takePicture(this: {
32
42
  nativeTag: number;
33
43
  }, options: PictureOptions): Promise<CameraPicture>;
44
+ record(this: {
45
+ nativeTag: number;
46
+ }, options: RecordingOptions): Promise<CameraRecording>;
47
+ stopRecording(this: {
48
+ nativeTag: number;
49
+ }): Promise<void> | void;
34
50
  }
35
51
  export declare class Camera {
36
52
  /** Overridden in a test to take pictures without a camera. */
@@ -43,6 +59,16 @@ export declare class Camera {
43
59
  * no camera to take it with yet.
44
60
  */
45
61
  takePicture(options?: PictureOptions): Promise<CameraPicture | null>;
62
+ /**
63
+ * Record a video, resolving once the recording stops: `stopRecording()`, a `maxDuration` (in
64
+ * seconds) or `maxFileSize` (in bytes) reached, or the preview stopping. The view needs
65
+ * `mode="video"`, and the microphone permission unless it has `mute`: without it, Android rejects
66
+ * and iOS records without sound. Null without the module, or before the view has reached the
67
+ * screen.
68
+ */
69
+ record(options?: RecordingOptions): Promise<CameraRecording | null>;
70
+ /** Stop the recording in progress, if there is one: `record()` then resolves with the video. */
71
+ stopRecording(): Promise<void>;
46
72
  static ɵfac: i0.ɵɵFactoryDeclaration<Camera, never>;
47
73
  static ɵdir: i0.ɵɵDirectiveDeclaration<Camera, "expo-camera", ["camera"], {}, {}, never, never, true, never>;
48
74
  }
package/dist/camera.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `Camera`: a picture taken from the `<expo-camera>` view on screen.
2
+ * `Camera`: a picture taken, or a video recorded, from the `<expo-camera>` view on screen.
3
3
  *
4
4
  * ```ts
5
5
  * registerExpoViews('expo-camera'); // once, at startup
@@ -14,11 +14,15 @@
14
14
  * }
15
15
  * ```
16
16
  *
17
+ * A video is `record()`, which resolves once it stops - `stopRecording()`, or a `maxDuration` or
18
+ * `maxFileSize` reached - on a view with `mode="video"`.
19
+ *
17
20
  * Importing it is all it takes: it applies to every `<expo-camera>` in the template. The view is
18
21
  * what takes the picture, so this is a directive on the view rather than a service.
19
22
  *
20
23
  * In React, `takePictureAsync` is a method on the component's ref. Underneath, it is one of the
21
- * functions Expo defines on the view (`AsyncFunction("takePicture")` in the module), which native
24
+ * functions Expo defines on the view (`AsyncFunction("takePicture")` in the module, and `record`
25
+ * and `stopRecording` beside it), which native
22
26
  * finds by reading `nativeTag` off whatever it is called on. So this calls the same function with
23
27
  * the tag the engine committed the view under, and no ref is needed.
24
28
  */
@@ -52,6 +56,26 @@ export class Camera {
52
56
  return null;
53
57
  return this.functions.takePicture.call({ nativeTag }, options);
54
58
  }
59
+ /**
60
+ * Record a video, resolving once the recording stops: `stopRecording()`, a `maxDuration` (in
61
+ * seconds) or `maxFileSize` (in bytes) reached, or the preview stopping. The view needs
62
+ * `mode="video"`, and the microphone permission unless it has `mute`: without it, Android rejects
63
+ * and iOS records without sound. Null without the module, or before the view has reached the
64
+ * screen.
65
+ */
66
+ async record(options = {}) {
67
+ const nativeTag = this.engine.tagOf(this.node);
68
+ if (!this.functions || nativeTag === null)
69
+ return null;
70
+ return this.functions.record.call({ nativeTag }, options);
71
+ }
72
+ /** Stop the recording in progress, if there is one: `record()` then resolves with the video. */
73
+ async stopRecording() {
74
+ const nativeTag = this.engine.tagOf(this.node);
75
+ if (!this.functions || nativeTag === null)
76
+ return;
77
+ await this.functions.stopRecording.call({ nativeTag });
78
+ }
55
79
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Camera, deps: [], target: i0.ɵɵFactoryTarget.Directive });
56
80
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.2.0", type: Camera, isStandalone: true, selector: "expo-camera", exportAs: ["camera"], ngImport: i0 });
57
81
  }
@@ -1 +1 @@
1
- {"version":3,"file":"camera.js","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAmB,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;;AAc5D,MAAM,OAAO,MAAM;IACjB,8DAA8D;IAC9D,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,6BAA6B,EAC7B;QACE,OAAO,EAAE,GAAG,EAAE;YACZ,MAAM,IAAI,GAAG,QAAQ,CACnB,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAuC,CACzE,CAAC;YACF,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,EAAE,GAAG,EAAE,CAC5C,IAAI,EAAE,2BAA2B,CAE9B,YAAY,CAAC,CACjB,CAAC;YACF,iFAAiF;YACjF,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAwC,CAAC;YAChF,uFAAuF;YACvF,OAAO,MAAM,EAAE,cAAc,EAAE,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC;QACxD,CAAC;KACF,CACF,CAAC;IAEe,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACxB,IAAI,GAAG,MAAM,CAAyB,UAAU,CAAC,CAAC,aAAa,CAAC;IAEjF;;;OAGG;IACH,KAAK,CAAC,WAAW,CAAC,UAA0B,EAAE;QAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACvD,OAAO,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;uGAlCU,MAAM;2FAAN,MAAM;;2FAAN,MAAM;kBADlB,SAAS;mBAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE","sourcesContent":["/**\n * `Camera`: a picture taken from the `<expo-camera>` view on screen.\n *\n * ```ts\n * registerExpoViews('expo-camera'); // once, at startup\n *\n * @Component({\n * imports: [Camera],\n * template: `<expo-camera facing=\"back\" class=\"flex-1\" />`,\n * })\n * export class Scanner {\n * private readonly camera = viewChild.required(Camera);\n * async shoot() { const picture = await this.camera().takePicture({ quality: 0.8 }); }\n * }\n * ```\n *\n * Importing it is all it takes: it applies to every `<expo-camera>` in the template. The view is\n * what takes the picture, so this is a directive on the view rather than a service.\n *\n * In React, `takePictureAsync` is a method on the component's ref. Underneath, it is one of the\n * functions Expo defines on the view (`AsyncFunction(\"takePicture\")` in the module), which native\n * finds by reading `nativeTag` off whatever it is called on. So this calls the same function with\n * the tag the engine committed the view under, and no ref is needed.\n */\nimport { Directive, ElementRef, InjectionToken, inject } from '@angular/core';\nimport { Engine, type EngineNode } from '@ng-native/fabric';\nimport { expoModule, optional, present } from './native.ts';\n\nexport type PictureOptions = Omit<\n import('expo-camera').CameraPictureOptions,\n 'onPictureSaved' | 'pictureRef'\n>;\nexport type CameraPicture = import('expo-camera').CameraCapturedPicture;\n\n/** The functions `expo-camera` defines on its view, called with the view's tag as `this`. */\nexport interface CameraViewFunctions {\n takePicture(this: { nativeTag: number }, options: PictureOptions): Promise<CameraPicture>;\n}\n\n@Directive({ selector: 'expo-camera', exportAs: 'camera' })\nexport class Camera {\n /** Overridden in a test to take pictures without a camera. */\n static readonly SOURCE = new InjectionToken<CameraViewFunctions | null>(\n 'angular-native.cameraSource',\n {\n factory: () => {\n const core = optional(\n () => require('expo-modules-core') as typeof import('expo-modules-core'),\n );\n const module = expoModule('expo-camera', () =>\n core?.requireOptionalNativeModule<{\n ViewPrototypes?: Record<string, CameraViewFunctions>;\n }>('ExpoCamera'),\n );\n // Missing, the stand-in is the functions: taking a picture says what to install.\n if (module && !present(module)) return module as unknown as CameraViewFunctions;\n // The camera is the module's first view, so its functions are under the module's name.\n return module?.ViewPrototypes?.['ExpoCamera'] ?? null;\n },\n },\n );\n\n private readonly functions = inject(Camera.SOURCE);\n private readonly engine = inject(Engine);\n private readonly node = inject<ElementRef<EngineNode>>(ElementRef).nativeElement;\n\n /**\n * Take a picture. Null without the module, or before the view has reached the screen - there is\n * no camera to take it with yet.\n */\n async takePicture(options: PictureOptions = {}): Promise<CameraPicture | null> {\n const nativeTag = this.engine.tagOf(this.node);\n if (!this.functions || nativeTag === null) return null;\n return this.functions.takePicture.call({ nativeTag }, options);\n }\n}\n"]}
1
+ {"version":3,"file":"camera.js","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAmB,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;;AAsB5D,MAAM,OAAO,MAAM;IACjB,8DAA8D;IAC9D,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,6BAA6B,EAC7B;QACE,OAAO,EAAE,GAAG,EAAE;YACZ,MAAM,IAAI,GAAG,QAAQ,CACnB,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAuC,CACzE,CAAC;YACF,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,EAAE,GAAG,EAAE,CAC5C,IAAI,EAAE,2BAA2B,CAE9B,YAAY,CAAC,CACjB,CAAC;YACF,iFAAiF;YACjF,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAwC,CAAC;YAChF,uFAAuF;YACvF,OAAO,MAAM,EAAE,cAAc,EAAE,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC;QACxD,CAAC;KACF,CACF,CAAC;IAEe,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACxB,IAAI,GAAG,MAAM,CAAyB,UAAU,CAAC,CAAC,aAAa,CAAC;IAEjF;;;OAGG;IACH,KAAK,CAAC,WAAW,CAAC,UAA0B,EAAE;QAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACvD,OAAO,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,CAAC,UAA4B,EAAE;QACzC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACvD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC;IAED,gGAAgG;IAChG,KAAK,CAAC,aAAa;QACjB,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI;YAAE,OAAO;QAClD,MAAM,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;IACzD,CAAC;uGAtDU,MAAM;2FAAN,MAAM;;2FAAN,MAAM;kBADlB,SAAS;mBAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE","sourcesContent":["/**\n * `Camera`: a picture taken, or a video recorded, from the `<expo-camera>` view on screen.\n *\n * ```ts\n * registerExpoViews('expo-camera'); // once, at startup\n *\n * @Component({\n * imports: [Camera],\n * template: `<expo-camera facing=\"back\" class=\"flex-1\" />`,\n * })\n * export class Scanner {\n * private readonly camera = viewChild.required(Camera);\n * async shoot() { const picture = await this.camera().takePicture({ quality: 0.8 }); }\n * }\n * ```\n *\n * A video is `record()`, which resolves once it stops - `stopRecording()`, or a `maxDuration` or\n * `maxFileSize` reached - on a view with `mode=\"video\"`.\n *\n * Importing it is all it takes: it applies to every `<expo-camera>` in the template. The view is\n * what takes the picture, so this is a directive on the view rather than a service.\n *\n * In React, `takePictureAsync` is a method on the component's ref. Underneath, it is one of the\n * functions Expo defines on the view (`AsyncFunction(\"takePicture\")` in the module, and `record`\n * and `stopRecording` beside it), which native\n * finds by reading `nativeTag` off whatever it is called on. So this calls the same function with\n * the tag the engine committed the view under, and no ref is needed.\n */\nimport { Directive, ElementRef, InjectionToken, inject } from '@angular/core';\nimport { Engine, type EngineNode } from '@ng-native/fabric';\nimport { expoModule, optional, present } from './native.ts';\n\nexport type PictureOptions = Omit<\n import('expo-camera').CameraPictureOptions,\n 'onPictureSaved' | 'pictureRef'\n>;\nexport type CameraPicture = import('expo-camera').CameraCapturedPicture;\n/** `expo-camera`'s own, less `mirror`, which it has deprecated for the view's `mirror` prop. */\nexport type RecordingOptions = Omit<import('expo-camera').CameraRecordingOptions, 'mirror'>;\n/** A recorded video: a file in the cache directory. */\nexport interface CameraRecording {\n readonly uri: string;\n}\n\n/** The functions `expo-camera` defines on its view, called with the view's tag as `this`. */\nexport interface CameraViewFunctions {\n takePicture(this: { nativeTag: number }, options: PictureOptions): Promise<CameraPicture>;\n record(this: { nativeTag: number }, options: RecordingOptions): Promise<CameraRecording>;\n stopRecording(this: { nativeTag: number }): Promise<void> | void;\n}\n\n@Directive({ selector: 'expo-camera', exportAs: 'camera' })\nexport class Camera {\n /** Overridden in a test to take pictures without a camera. */\n static readonly SOURCE = new InjectionToken<CameraViewFunctions | null>(\n 'angular-native.cameraSource',\n {\n factory: () => {\n const core = optional(\n () => require('expo-modules-core') as typeof import('expo-modules-core'),\n );\n const module = expoModule('expo-camera', () =>\n core?.requireOptionalNativeModule<{\n ViewPrototypes?: Record<string, CameraViewFunctions>;\n }>('ExpoCamera'),\n );\n // Missing, the stand-in is the functions: taking a picture says what to install.\n if (module && !present(module)) return module as unknown as CameraViewFunctions;\n // The camera is the module's first view, so its functions are under the module's name.\n return module?.ViewPrototypes?.['ExpoCamera'] ?? null;\n },\n },\n );\n\n private readonly functions = inject(Camera.SOURCE);\n private readonly engine = inject(Engine);\n private readonly node = inject<ElementRef<EngineNode>>(ElementRef).nativeElement;\n\n /**\n * Take a picture. Null without the module, or before the view has reached the screen - there is\n * no camera to take it with yet.\n */\n async takePicture(options: PictureOptions = {}): Promise<CameraPicture | null> {\n const nativeTag = this.engine.tagOf(this.node);\n if (!this.functions || nativeTag === null) return null;\n return this.functions.takePicture.call({ nativeTag }, options);\n }\n\n /**\n * Record a video, resolving once the recording stops: `stopRecording()`, a `maxDuration` (in\n * seconds) or `maxFileSize` (in bytes) reached, or the preview stopping. The view needs\n * `mode=\"video\"`, and the microphone permission unless it has `mute`: without it, Android rejects\n * and iOS records without sound. Null without the module, or before the view has reached the\n * screen.\n */\n async record(options: RecordingOptions = {}): Promise<CameraRecording | null> {\n const nativeTag = this.engine.tagOf(this.node);\n if (!this.functions || nativeTag === null) return null;\n return this.functions.record.call({ nativeTag }, options);\n }\n\n /** Stop the recording in progress, if there is one: `record()` then resolves with the video. */\n async stopRecording(): Promise<void> {\n const nativeTag = this.engine.tagOf(this.node);\n if (!this.functions || nativeTag === null) return;\n await this.functions.stopRecording.call({ nativeTag });\n }\n}\n"]}
@@ -730,13 +730,19 @@ export declare class UiForm {
730
730
  static ɵfac: i0.ɵɵFactoryDeclaration<UiForm, never>;
731
731
  static ɵcmp: i0.ɵɵComponentDeclaration<UiForm, "ui-form", never, { "modifiers": { "alias": "modifiers"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
732
732
  }
733
- /** A SwiftUI `Section` of a form or a list, under `title`. */
733
+ /**
734
+ * A SwiftUI `Section` of a form or a list, under `title`, iOS only. A `<ui-slot name="header">`
735
+ * is its header in place of a `title`, and a `<ui-slot name="footer">` is the note under its rows.
736
+ * When both a non-empty `title` and a header slot are set, `@expo/ui`'s `SectionView` draws the
737
+ * `title` and drops the slot; an empty `title` counts as unset. The slot's `name` is written as it is,
738
+ * not bound, as that is what picks it out of the rows.
739
+ */
734
740
  export declare class UiSection {
735
741
  protected readonly nativeView: true;
736
742
  readonly title: import("@angular/core").InputSignal<string | undefined>;
737
743
  readonly modifiers: import("@angular/core").InputSignal<readonly UiModifier[] | undefined>;
738
744
  static ɵfac: i0.ɵɵFactoryDeclaration<UiSection, never>;
739
- static ɵcmp: i0.ɵɵComponentDeclaration<UiSection, "ui-section", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "modifiers": { "alias": "modifiers"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
745
+ static ɵcmp: i0.ɵɵComponentDeclaration<UiSection, "ui-section", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "modifiers": { "alias": "modifiers"; "required": false; "isSignal": true; }; }, {}, never, ["ui-slot[name=header]", "*", "ui-slot[name=footer]"], true, never>;
740
746
  }
741
747
  /**
742
748
  * A SwiftUI `Group`, iOS only: its children as they are, with no layout of its own. What it is for
@@ -882,6 +888,13 @@ export declare class UiBottomSheet {
882
888
  readonly detents: import("@angular/core").InputSignal<readonly UiPresentationDetent[] | undefined>;
883
889
  /** Whether the grabber shows at the top of the sheet. */
884
890
  readonly showDragIndicator: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
891
+ /**
892
+ * The colour of the sheet behind its content: SwiftUI's `presentationBackground` on iOS, where
893
+ * a sheet below its full height is otherwise drawn as glass, and Compose's `containerColor` on
894
+ * Android. Over a `background-color` the element has from CSS, which colours the sheet the same
895
+ * way. Neither is the sheet as the system draws it.
896
+ */
897
+ readonly backgroundColor: import("@angular/core").InputSignal<string | undefined>;
885
898
  /**
886
899
  * The sheet has finished closing, whether the user dismissed it or `open` was set false: the
887
900
  * moment another sheet or a dialog can be presented.
@@ -928,6 +941,16 @@ export declare class UiBottomSheet {
928
941
  * detents of a sheet not sized to its content are followed while it is open.
929
942
  */
930
943
  protected readonly presentation: Signal<readonly UiModifier[]>;
944
+ /** The `background-color` the element has from CSS, which the sheet draws rather than it. */
945
+ private readonly painted;
946
+ /** The sheet's colour: the input's, or else the element's from CSS. */
947
+ protected readonly color: Signal<{} | undefined>;
948
+ /**
949
+ * The sheet's own background, a colour converted as any colour is, where one is given. As a
950
+ * shape style for `@expo/ui` 57.0.17 and later, and as the plain colour the versions before it
951
+ * read, Expo Go's among them. Each ignores the other's field.
952
+ */
953
+ private readonly background;
931
954
  /**
932
955
  * The one view a view host holds. Sized to its content it needs a width of its own, the
933
956
  * window's; otherwise it fills the height the sheet gives it.
@@ -969,6 +992,6 @@ export declare class UiBottomSheet {
969
992
  /** The sheet has gone: its content leaves the tree. */
970
993
  private finish;
971
994
  static ɵfac: i0.ɵɵFactoryDeclaration<UiBottomSheet, never>;
972
- static ɵcmp: i0.ɵɵComponentDeclaration<UiBottomSheet, "ui-bottom-sheet", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "fitToContents": { "alias": "fitToContents"; "required": false; "isSignal": true; }; "detents": { "alias": "detents"; "required": false; "isSignal": true; }; "showDragIndicator": { "alias": "showDragIndicator"; "required": false; "isSignal": true; }; }, { "open": "openChange"; "dismissed": "dismissed"; }, ["toolbarItems"], ["*", "ui-toolbar-item"], true, never>;
995
+ static ɵcmp: i0.ɵɵComponentDeclaration<UiBottomSheet, "ui-bottom-sheet", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "fitToContents": { "alias": "fitToContents"; "required": false; "isSignal": true; }; "detents": { "alias": "detents"; "required": false; "isSignal": true; }; "showDragIndicator": { "alias": "showDragIndicator"; "required": false; "isSignal": true; }; "backgroundColor": { "alias": "backgroundColor"; "required": false; "isSignal": true; }; }, { "open": "openChange"; "dismissed": "dismissed"; }, ["toolbarItems"], ["*", "ui-toolbar-item"], true, never>;
973
996
  }
974
997
  export {};
@@ -1454,7 +1454,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
1454
1454
  host: { '[modifiers]': 'modifiers()' },
1455
1455
  }]
1456
1456
  }], propDecorators: { modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }] } });
1457
- /** A SwiftUI `Section` of a form or a list, under `title`. */
1457
+ /**
1458
+ * A SwiftUI `Section` of a form or a list, under `title`, iOS only. A `<ui-slot name="header">`
1459
+ * is its header in place of a `title`, and a `<ui-slot name="footer">` is the note under its rows.
1460
+ * When both a non-empty `title` and a header slot are set, `@expo/ui`'s `SectionView` draws the
1461
+ * `title` and drops the slot; an empty `title` counts as unset. The slot's `name` is written as it is,
1462
+ * not bound, as that is what picks it out of the rows.
1463
+ */
1458
1464
  export class UiSection {
1459
1465
  nativeView = nativeView();
1460
1466
  title = input(/* @ts-ignore */
@@ -1462,14 +1468,18 @@ export class UiSection {
1462
1468
  modifiers = input(/* @ts-ignore */
1463
1469
  ...(ngDevMode ? [undefined, { debugName: "modifiers" }] : /* istanbul ignore next */ []));
1464
1470
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
1465
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiSection, isStandalone: true, selector: "ui-section", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "title": "title()", "modifiers": "modifiers()" } }, ngImport: i0, template: '<ui-slot name="content"><ng-content /></ui-slot>', isInline: true, dependencies: [{ kind: "component", type: UiSlot, selector: "ui-slot", inputs: ["name", "extraProps"] }] });
1471
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiSection, isStandalone: true, selector: "ui-section", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "title": "title()", "modifiers": "modifiers()" } }, ngImport: i0, template: `<ng-content select="ui-slot[name=header]" />
1472
+ <ui-slot name="content"><ng-content /></ui-slot>
1473
+ <ng-content select="ui-slot[name=footer]" />`, isInline: true, dependencies: [{ kind: "component", type: UiSlot, selector: "ui-slot", inputs: ["name", "extraProps"] }] });
1466
1474
  }
1467
1475
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiSection, decorators: [{
1468
1476
  type: Component,
1469
1477
  args: [{
1470
1478
  selector: 'ui-section',
1471
1479
  imports: [UiSlot],
1472
- template: '<ui-slot name="content"><ng-content /></ui-slot>',
1480
+ template: `<ng-content select="ui-slot[name=header]" />
1481
+ <ui-slot name="content"><ng-content /></ui-slot>
1482
+ <ng-content select="ui-slot[name=footer]" />`,
1473
1483
  host: { '[title]': 'title()', '[modifiers]': 'modifiers()' },
1474
1484
  }]
1475
1485
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }] } });
@@ -1667,6 +1677,14 @@ export class UiBottomSheet {
1667
1677
  ...(ngDevMode ? [undefined, { debugName: "detents" }] : /* istanbul ignore next */ []));
1668
1678
  /** Whether the grabber shows at the top of the sheet. */
1669
1679
  showDragIndicator = input(true, { ...(ngDevMode ? { debugName: "showDragIndicator" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1680
+ /**
1681
+ * The colour of the sheet behind its content: SwiftUI's `presentationBackground` on iOS, where
1682
+ * a sheet below its full height is otherwise drawn as glass, and Compose's `containerColor` on
1683
+ * Android. Over a `background-color` the element has from CSS, which colours the sheet the same
1684
+ * way. Neither is the sheet as the system draws it.
1685
+ */
1686
+ backgroundColor = input(/* @ts-ignore */
1687
+ ...(ngDevMode ? [undefined, { debugName: "backgroundColor" }] : /* istanbul ignore next */ []));
1670
1688
  /**
1671
1689
  * The sheet has finished closing, whether the user dismissed it or `open` was set false: the
1672
1690
  * moment another sheet or a dialog can be presented.
@@ -1734,8 +1752,28 @@ export class UiBottomSheet {
1734
1752
  $type: 'presentationDragIndicator',
1735
1753
  visibility: this.showDragIndicator() ? 'visible' : 'hidden',
1736
1754
  },
1755
+ ...this.background(),
1737
1756
  ], /* @ts-ignore */
1738
1757
  ...(ngDevMode ? [{ debugName: "presentation" }] : /* istanbul ignore next */ []));
1758
+ /** The `background-color` the element has from CSS, which the sheet draws rather than it. */
1759
+ painted = signal(undefined, /* @ts-ignore */
1760
+ ...(ngDevMode ? [{ debugName: "painted" }] : /* istanbul ignore next */ []));
1761
+ /** The sheet's colour: the input's, or else the element's from CSS. */
1762
+ color = computed(() => this.backgroundColor() || this.painted() || undefined, /* @ts-ignore */
1763
+ ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
1764
+ /**
1765
+ * The sheet's own background, a colour converted as any colour is, where one is given. As a
1766
+ * shape style for `@expo/ui` 57.0.17 and later, and as the plain colour the versions before it
1767
+ * read, Expo Go's among them. Each ignores the other's field.
1768
+ */
1769
+ background = computed(() => {
1770
+ const given = this.color();
1771
+ if (given === undefined)
1772
+ return [];
1773
+ const color = this.engine?.color(given) ?? given;
1774
+ return [{ $type: 'presentationBackground', style: { type: 'color', color }, color }];
1775
+ }, /* @ts-ignore */
1776
+ ...(ngDevMode ? [{ debugName: "background" }] : /* istanbul ignore next */ []));
1739
1777
  /**
1740
1778
  * The one view a view host holds. Sized to its content it needs a width of its own, the
1741
1779
  * window's; otherwise it fills the height the sheet gives it.
@@ -1751,6 +1789,10 @@ export class UiBottomSheet {
1751
1789
  if (!this.open())
1752
1790
  untracked(() => this.close());
1753
1791
  });
1792
+ const host = inject(ElementRef).nativeElement;
1793
+ const stop = this.engine?.takePaint?.(host, (color) => this.painted.set(color));
1794
+ if (stop)
1795
+ inject(DestroyRef).onDestroy(stop);
1754
1796
  if (this.swiftUI && !this.toolbarInBuild) {
1755
1797
  const warned = effect(() => {
1756
1798
  if (!this.toolbarItems().length)
@@ -1842,7 +1884,7 @@ export class UiBottomSheet {
1842
1884
  afterNextRender(() => this.mounted.set(this.open()), { injector: this.injector });
1843
1885
  }
1844
1886
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiBottomSheet, deps: [], target: i0.ɵɵFactoryTarget.Component });
1845
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.0", type: UiBottomSheet, isStandalone: true, selector: "ui-bottom-sheet", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, fitToContents: { classPropertyName: "fitToContents", publicName: "fitToContents", isSignal: true, isRequired: false, transformFunction: null }, detents: { classPropertyName: "detents", publicName: "detents", isSignal: true, isRequired: false, transformFunction: null }, showDragIndicator: { classPropertyName: "showDragIndicator", publicName: "showDragIndicator", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", dismissed: "dismissed" }, host: { properties: { "style.position": "\"absolute\"" } }, queries: [{ propertyName: "toolbarItems", predicate: UiToolbarItem, isSignal: true }], viewQueries: [{ propertyName: "sheet", first: true, predicate: ["sheet"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: `
1887
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.0", type: UiBottomSheet, isStandalone: true, selector: "ui-bottom-sheet", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, fitToContents: { classPropertyName: "fitToContents", publicName: "fitToContents", isSignal: true, isRequired: false, transformFunction: null }, detents: { classPropertyName: "detents", publicName: "detents", isSignal: true, isRequired: false, transformFunction: null }, showDragIndicator: { classPropertyName: "showDragIndicator", publicName: "showDragIndicator", isSignal: true, isRequired: false, transformFunction: null }, backgroundColor: { classPropertyName: "backgroundColor", publicName: "backgroundColor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", dismissed: "dismissed" }, host: { properties: { "style.position": "\"absolute\"" } }, queries: [{ propertyName: "toolbarItems", predicate: UiToolbarItem, isSignal: true }], viewQueries: [{ propertyName: "sheet", first: true, predicate: ["sheet"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: `
1846
1888
  <ng-template #content><ng-content /></ng-template>
1847
1889
  @if (swiftUI) {
1848
1890
  <ui-host style="position: absolute" pointerEvents="none" [style.width.px]="width()">
@@ -1881,6 +1923,7 @@ export class UiBottomSheet {
1881
1923
  <ui-bottom-sheet-view
1882
1924
  #sheet
1883
1925
  [showDragHandle]="showDragIndicator()"
1926
+ [containerColor]="color()"
1884
1927
  [skipPartiallyExpanded]="fits()"
1885
1928
  (dismissRequest)="dismissRequested($event)"
1886
1929
  >
@@ -1890,7 +1933,7 @@ export class UiBottomSheet {
1890
1933
  </ui-bottom-sheet-view>
1891
1934
  </ui-host>
1892
1935
  }
1893
- `, isInline: true, dependencies: [{ kind: "component", type: UiBottomSheetContent, selector: "ui-bottom-sheet-content", inputs: ["content"] }, { kind: "component", type: UiBottomSheetView, selector: "ui-bottom-sheet-view", inputs: ["isPresented", "fitToContents", "showDragHandle", "skipPartiallyExpanded"], outputs: ["isPresentedChange", "dismiss", "dismissRequest"] }, { kind: "component", type: UiGroup, selector: "ui-group", inputs: ["modifiers"] }, { kind: "component", type: UiHost, selector: "ui-host", inputs: ["matchContents", "ignoreSafeArea", "useViewportSizeMeasurement"] }, { kind: "component", type: UiNavigationStack, selector: "ui-navigation-stack", inputs: ["modifiers"] }, { kind: "component", type: UiSlot, selector: "ui-slot", inputs: ["name", "extraProps"] }, { kind: "component", type: UiSpacer, selector: "ui-spacer", inputs: ["modifiers"] }, { kind: "component", type: UiToolbar, selector: "ui-toolbar", inputs: ["modifiers"] }, { kind: "component", type: UiViewHost, selector: "ui-view-host", inputs: ["matchContents", "layoutRoot", "modifiers"] }, { kind: "component", type: UiZStack, selector: "ui-zstack", inputs: ["alignment", "modifiers"] }] });
1936
+ `, isInline: true, dependencies: [{ kind: "component", type: UiBottomSheetContent, selector: "ui-bottom-sheet-content", inputs: ["content"] }, { kind: "component", type: UiBottomSheetView, selector: "ui-bottom-sheet-view", inputs: ["isPresented", "fitToContents", "showDragHandle", "containerColor", "skipPartiallyExpanded"], outputs: ["isPresentedChange", "dismiss", "dismissRequest"] }, { kind: "component", type: UiGroup, selector: "ui-group", inputs: ["modifiers"] }, { kind: "component", type: UiHost, selector: "ui-host", inputs: ["matchContents", "ignoreSafeArea", "useViewportSizeMeasurement"] }, { kind: "component", type: UiNavigationStack, selector: "ui-navigation-stack", inputs: ["modifiers"] }, { kind: "component", type: UiSlot, selector: "ui-slot", inputs: ["name", "extraProps"] }, { kind: "component", type: UiSpacer, selector: "ui-spacer", inputs: ["modifiers"] }, { kind: "component", type: UiToolbar, selector: "ui-toolbar", inputs: ["modifiers"] }, { kind: "component", type: UiViewHost, selector: "ui-view-host", inputs: ["matchContents", "layoutRoot", "modifiers"] }, { kind: "component", type: UiZStack, selector: "ui-zstack", inputs: ["alignment", "modifiers"] }] });
1894
1937
  }
1895
1938
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiBottomSheet, decorators: [{
1896
1939
  type: Component,
@@ -1947,6 +1990,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
1947
1990
  <ui-bottom-sheet-view
1948
1991
  #sheet
1949
1992
  [showDragHandle]="showDragIndicator()"
1993
+ [containerColor]="color()"
1950
1994
  [skipPartiallyExpanded]="fits()"
1951
1995
  (dismissRequest)="dismissRequested($event)"
1952
1996
  >
@@ -1961,5 +2005,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
1961
2005
  }]
1962
2006
  }], ctorParameters: () => [], propDecorators: { sheet: [{ type: i0.ViewChild, args: ['sheet', { ...{
1963
2007
  read: ElementRef,
1964
- }, isSignal: true }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], fitToContents: [{ type: i0.Input, args: [{ isSignal: true, alias: "fitToContents", required: false }] }], detents: [{ type: i0.Input, args: [{ isSignal: true, alias: "detents", required: false }] }], showDragIndicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDragIndicator", required: false }] }], dismissed: [{ type: i0.Output, args: ["dismissed"] }], toolbarItems: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => UiToolbarItem), { isSignal: true }] }] } });
2008
+ }, isSignal: true }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], fitToContents: [{ type: i0.Input, args: [{ isSignal: true, alias: "fitToContents", required: false }] }], detents: [{ type: i0.Input, args: [{ isSignal: true, alias: "detents", required: false }] }], showDragIndicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDragIndicator", required: false }] }], backgroundColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "backgroundColor", required: false }] }], dismissed: [{ type: i0.Output, args: ["dismissed"] }], toolbarItems: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => UiToolbarItem), { isSignal: true }] }] } });
1965
2009
  //# sourceMappingURL=expo-ui-components.js.map