@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.
- package/android/build.gradle +2 -0
- package/android/src/main/java/dev/ngnative/expo/SearchFieldModule.kt +61 -0
- package/dist/camera.d.ts +28 -2
- package/dist/camera.js +26 -2
- package/dist/camera.js.map +1 -1
- package/dist/expo-ui-components.d.ts +26 -3
- package/dist/expo-ui-components.js +50 -6
- package/dist/expo-ui-components.js.map +1 -1
- package/dist/player.d.ts +9 -2
- package/dist/player.js +3 -2
- package/dist/player.js.map +1 -1
- package/dist/register-expo-view.d.ts +7 -3
- package/dist/register-expo-view.js +27 -7
- package/dist/register-expo-view.js.map +1 -1
- package/dist/search-field.d.ts +2 -0
- package/dist/search-field.js +15 -2
- package/dist/search-field.js.map +1 -1
- package/dist/ui-bottom-sheet-view.d.ts +3 -1
- package/dist/ui-bottom-sheet-view.js +6 -2
- package/dist/ui-bottom-sheet-view.js.map +1 -1
- package/dist/video.js +1 -1
- package/dist/video.js.map +1 -1
- package/ios/SearchField.swift +70 -0
- package/package.json +2 -2
package/android/build.gradle
CHANGED
|
@@ -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
|
|
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
|
|
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
|
}
|
package/dist/camera.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"camera.js","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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:
|
|
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:
|
|
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
|