@ng-native/expo 0.17.1 → 0.18.1
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/src/main/java/dev/ngnative/expo/KeyboardModule.kt +10 -1
- package/dist/audio.d.ts +37 -0
- package/dist/audio.js +90 -6
- package/dist/audio.js.map +1 -1
- package/dist/brightness.js +50 -9
- package/dist/brightness.js.map +1 -1
- package/dist/expo-ui-components.d.ts +45 -2
- package/dist/expo-ui-components.js +96 -5
- package/dist/expo-ui-components.js.map +1 -1
- package/dist/foldable.d.ts +36 -3
- package/dist/foldable.js +66 -13
- package/dist/foldable.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keep-awake.js +2 -2
- package/dist/keep-awake.js.map +1 -1
- package/dist/keyboard.js +15 -0
- package/dist/keyboard.js.map +1 -1
- package/dist/orientation.js +4 -3
- package/dist/orientation.js.map +1 -1
- package/dist/screen-capture.js +2 -2
- package/dist/screen-capture.js.map +1 -1
- package/dist/search-field.js +1 -1
- package/dist/search-field.js.map +1 -1
- package/dist/symbol.d.ts +3 -1
- package/dist/symbol.js +26 -7
- package/dist/symbol.js.map +1 -1
- package/dist/ui-bottom-sheet-view.d.ts +10 -1
- package/dist/ui-bottom-sheet-view.js +10 -2
- package/dist/ui-bottom-sheet-view.js.map +1 -1
- package/expo-module.config.json +6 -1
- package/ios/SymbolImageSize.swift +53 -0
- package/package.json +42 -38
|
@@ -84,7 +84,16 @@ class KeyboardModule : Module() {
|
|
|
84
84
|
val activity = appContext.currentActivity ?: return
|
|
85
85
|
activity.runOnUiThread {
|
|
86
86
|
if (watcher == null && observing) {
|
|
87
|
-
|
|
87
|
+
val current =
|
|
88
|
+
KeyboardWatcher.watch(activity.window.decorView, listener) ?: return@runOnUiThread
|
|
89
|
+
watcher = current
|
|
90
|
+
// A keyboard already up, and still, sends nothing until it next moves: report it as one
|
|
91
|
+
// that starts and ends at once, as the watcher reports a jump. One that is moving reports
|
|
92
|
+
// itself through the rest of its motion.
|
|
93
|
+
if (current.inset > 0 && !current.moving) {
|
|
94
|
+
listener.onKeyboardStart(current.inset, 0)
|
|
95
|
+
listener.onKeyboardEnd(current.inset)
|
|
96
|
+
}
|
|
88
97
|
}
|
|
89
98
|
}
|
|
90
99
|
}
|
package/dist/audio.d.ts
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `audioPlayer` and `audioRecorder`, an `expo-audio` player and recorder owned by the component
|
|
3
|
+
* that makes each.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* protected readonly player = audioPlayer(this.source, { timeUpdate: 0.5 });
|
|
7
|
+
* protected readonly recorder = audioRecorder(RecordingPresets.HIGH_QUALITY);
|
|
8
|
+
*
|
|
9
|
+
* // {{ player.state().currentTime }} {{ recorder.state().durationMillis }}
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
import { type Signal } from '@angular/core';
|
|
1
13
|
import { type Player } from './player.ts';
|
|
14
|
+
type RecordingOptions = import('expo-audio').RecordingOptions;
|
|
15
|
+
type RecorderState = import('expo-audio').RecorderState;
|
|
16
|
+
type AudioRecorder = import('expo-audio').AudioRecorder;
|
|
2
17
|
/**
|
|
3
18
|
* An audio player owned by the current component, released when it is destroyed.
|
|
4
19
|
*
|
|
@@ -8,3 +23,25 @@ import { type Player } from './player.ts';
|
|
|
8
23
|
export declare function audioPlayer(source: import('expo-audio').AudioSource, options?: {
|
|
9
24
|
timeUpdate?: number;
|
|
10
25
|
}): Player<import('expo-audio').AudioPlayer>;
|
|
26
|
+
/** A component's own recorder: the module's, and its status as a signal. */
|
|
27
|
+
export interface Recorder<T> {
|
|
28
|
+
/** The module's own recorder, for `prepareToRecordAsync()`, `record()`, `stop()` and the rest. */
|
|
29
|
+
readonly native: T;
|
|
30
|
+
/** `getStatus()`, asked every `statusInterval` seconds while the component lives. */
|
|
31
|
+
readonly state: Signal<RecorderState>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* An audio recorder owned by the current component, released when it is destroyed: what
|
|
35
|
+
* `useAudioRecorder` and `useAudioRecorderState` give a React component.
|
|
36
|
+
*
|
|
37
|
+
* The recorder is made with `options` merged for the platform, as `useAudioRecorder` makes it, so
|
|
38
|
+
* call `prepareToRecordAsync()` with none. Android reads `isMeteringEnabled` from these alone: a
|
|
39
|
+
* recorder made without it and prepared with it has no `metering`.
|
|
40
|
+
*
|
|
41
|
+
* `statusInterval` is in seconds, as `audioPlayer`'s `timeUpdate` is, where
|
|
42
|
+
* `useAudioRecorderState` takes milliseconds.
|
|
43
|
+
*/
|
|
44
|
+
export declare function audioRecorder(options: RecordingOptions, { statusInterval }?: {
|
|
45
|
+
statusInterval?: number;
|
|
46
|
+
}): Recorder<AudioRecorder>;
|
|
47
|
+
export {};
|
package/dist/audio.js
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `audioPlayer`, an `expo-audio` player owned by the component
|
|
2
|
+
* `audioPlayer` and `audioRecorder`, an `expo-audio` player and recorder owned by the component
|
|
3
|
+
* that makes each.
|
|
3
4
|
*
|
|
4
5
|
* ```ts
|
|
5
6
|
* protected readonly player = audioPlayer(this.source, { timeUpdate: 0.5 });
|
|
7
|
+
* protected readonly recorder = audioRecorder(RecordingPresets.HIGH_QUALITY);
|
|
6
8
|
*
|
|
7
|
-
* // {{ player.state().currentTime }}
|
|
9
|
+
* // {{ player.state().currentTime }} {{ recorder.state().durationMillis }}
|
|
8
10
|
* ```
|
|
9
11
|
*/
|
|
10
|
-
import {
|
|
12
|
+
import { DestroyRef, inject, signal } from '@angular/core';
|
|
13
|
+
import { currentPlatform, expoModuleNow, unavailable } from "./native.js";
|
|
11
14
|
import { ownPlayer, watchAudioPlayer } from "./player.js";
|
|
15
|
+
/** `expo-audio` where the platform has it, or the error that says why not. */
|
|
16
|
+
function expoAudio() {
|
|
17
|
+
const expo = expoModuleNow('expo-audio', () => require('expo-audio'), [
|
|
18
|
+
'ios',
|
|
19
|
+
'android',
|
|
20
|
+
'web',
|
|
21
|
+
]);
|
|
22
|
+
if (!expo)
|
|
23
|
+
throw unavailable('expo-audio', 'Stand in for the player or recorder in the test.');
|
|
24
|
+
return expo;
|
|
25
|
+
}
|
|
12
26
|
/**
|
|
13
27
|
* An audio player owned by the current component, released when it is destroyed.
|
|
14
28
|
*
|
|
@@ -16,11 +30,81 @@ import { ownPlayer, watchAudioPlayer } from "./player.js";
|
|
|
16
30
|
* an option to `createAudioPlayer` itself, in milliseconds rather than `videoPlayer`'s seconds.
|
|
17
31
|
*/
|
|
18
32
|
export function audioPlayer(source, options = {}) {
|
|
19
|
-
const expo =
|
|
20
|
-
if (!expo)
|
|
21
|
-
throw unavailable('expo-audio', 'Stand in for the player in the test.');
|
|
33
|
+
const expo = expoAudio();
|
|
22
34
|
const updateInterval = options.timeUpdate === undefined ? undefined : options.timeUpdate * 1000;
|
|
23
35
|
const native = expo.createAudioPlayer(source, { updateInterval });
|
|
24
36
|
return ownPlayer(native, () => watchAudioPlayer(native));
|
|
25
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* An audio recorder owned by the current component, released when it is destroyed: what
|
|
40
|
+
* `useAudioRecorder` and `useAudioRecorderState` give a React component.
|
|
41
|
+
*
|
|
42
|
+
* The recorder is made with `options` merged for the platform, as `useAudioRecorder` makes it, so
|
|
43
|
+
* call `prepareToRecordAsync()` with none. Android reads `isMeteringEnabled` from these alone: a
|
|
44
|
+
* recorder made without it and prepared with it has no `metering`.
|
|
45
|
+
*
|
|
46
|
+
* `statusInterval` is in seconds, as `audioPlayer`'s `timeUpdate` is, where
|
|
47
|
+
* `useAudioRecorderState` takes milliseconds.
|
|
48
|
+
*/
|
|
49
|
+
export function audioRecorder(options, { statusInterval = 0.5 } = {}) {
|
|
50
|
+
// First, so a call outside an injection context throws before there is a recorder to leak.
|
|
51
|
+
const destroyRef = inject(DestroyRef);
|
|
52
|
+
const expo = expoAudio();
|
|
53
|
+
const native = makeRecorder(expo, forPlatform(options));
|
|
54
|
+
const state = signal(native.getStatus(), { ...(ngDevMode ? { debugName: "state" } : /* istanbul ignore next */ {}), equal: sameStatus });
|
|
55
|
+
const polling = setInterval(() => state.set(native.getStatus()), statusInterval * 1000);
|
|
56
|
+
destroyRef.onDestroy(() => {
|
|
57
|
+
clearInterval(polling);
|
|
58
|
+
releaseRecorder(native);
|
|
59
|
+
});
|
|
60
|
+
return { native, state: state.asReadonly() };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The platform's recorder class. On the web `expo-audio`'s `AudioModule` is a plain module whose
|
|
64
|
+
* recorder is `AudioRecorderWeb`; elsewhere it is the native module's `AudioRecorder`.
|
|
65
|
+
*/
|
|
66
|
+
function makeRecorder(expo, options) {
|
|
67
|
+
const module = expo.AudioModule;
|
|
68
|
+
const Recorder = currentPlatform() === 'web' ? module['AudioRecorderWeb'] : module['AudioRecorder'];
|
|
69
|
+
return new Recorder(options);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* What `useAudioRecorder` does when its component goes: the web recorder, which has no
|
|
73
|
+
* `release()`, clears its timeouts instead.
|
|
74
|
+
*/
|
|
75
|
+
function releaseRecorder(native) {
|
|
76
|
+
const recorder = native;
|
|
77
|
+
if (currentPlatform() === 'web')
|
|
78
|
+
recorder.clearTimeouts?.();
|
|
79
|
+
else
|
|
80
|
+
recorder.release?.();
|
|
81
|
+
}
|
|
82
|
+
/** `expo-audio`'s own `createRecordingOptions`, which it does not export: the platform's merged in. */
|
|
83
|
+
function forPlatform(options) {
|
|
84
|
+
const common = {
|
|
85
|
+
extension: options.extension,
|
|
86
|
+
sampleRate: options.sampleRate,
|
|
87
|
+
numberOfChannels: options.numberOfChannels,
|
|
88
|
+
bitRate: options.bitRate,
|
|
89
|
+
isMeteringEnabled: options.isMeteringEnabled ?? false,
|
|
90
|
+
};
|
|
91
|
+
const platform = currentPlatform();
|
|
92
|
+
if (platform === 'ios')
|
|
93
|
+
return { ...common, directory: options.directory, ...options.ios };
|
|
94
|
+
if (platform === 'android')
|
|
95
|
+
return { ...common, directory: options.directory, ...options.android };
|
|
96
|
+
return { ...common, ...options.web };
|
|
97
|
+
}
|
|
98
|
+
/** What `useAudioRecorderState` counts as no change: up to 50 ms of duration and up to 0.1 dB of level, both included. */
|
|
99
|
+
function sameStatus(a, b) {
|
|
100
|
+
const metering = a.metering === undefined || b.metering === undefined
|
|
101
|
+
? a.metering === b.metering
|
|
102
|
+
: Math.abs(a.metering - b.metering) <= 0.1;
|
|
103
|
+
return (a.canRecord === b.canRecord &&
|
|
104
|
+
a.isRecording === b.isRecording &&
|
|
105
|
+
a.mediaServicesDidReset === b.mediaServicesDidReset &&
|
|
106
|
+
a.url === b.url &&
|
|
107
|
+
Math.abs(a.durationMillis - b.durationMillis) <= 50 &&
|
|
108
|
+
metering);
|
|
109
|
+
}
|
|
26
110
|
//# sourceMappingURL=audio.js.map
|
package/dist/audio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAe,MAAM,aAAa,CAAC;AAOvE,8EAA8E;AAC9E,SAAS,SAAS;IAChB,MAAM,IAAI,GAAG,aAAa,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAc,EAAE;QACjF,KAAK;QACL,SAAS;QACT,KAAK;KACN,CAAC,CAAC;IACH,IAAI,CAAC,IAAI;QAAE,MAAM,WAAW,CAAC,YAAY,EAAE,kDAAkD,CAAC,CAAC;IAC/F,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,MAAwC,EACxC,UAAmC,EAAE;IAErC,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IACzB,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAChG,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC;IAClE,OAAO,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3D,CAAC;AAUD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAyB,EACzB,EAAE,cAAc,GAAG,GAAG,KAAkC,EAAE;IAE1D,2FAA2F;IAC3F,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,6EAAI,KAAK,EAAE,UAAU,GAAG,CAAC;IAChE,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC,CAAC;IACxF,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE;QACxB,aAAa,CAAC,OAAO,CAAC,CAAC;QACvB,eAAe,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,IAAe,EAAE,OAAgC;IACrE,MAAM,MAAM,GAAG,IAAI,CAAC,WAA2E,CAAC;IAChG,MAAM,QAAQ,GACZ,eAAe,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACrF,OAAO,IAAI,QAAS,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,MAAqB;IAC5C,MAAM,QAAQ,GAAG,MAAwD,CAAC;IAC1E,IAAI,eAAe,EAAE,KAAK,KAAK;QAAE,QAAQ,CAAC,aAAa,EAAE,EAAE,CAAC;;QACvD,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;AAC5B,CAAC;AAED,uGAAuG;AACvG,SAAS,WAAW,CAAC,OAAyB;IAC5C,MAAM,MAAM,GAAG;QACb,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;QAC1C,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,IAAI,KAAK;KACtD,CAAC;IACF,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;IACnC,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC3F,IAAI,QAAQ,KAAK,SAAS;QACxB,OAAO,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IACzE,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;AACvC,CAAC;AAED,0HAA0H;AAC1H,SAAS,UAAU,CAAC,CAAgB,EAAE,CAAgB;IACpD,MAAM,QAAQ,GACZ,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS;QAClD,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ;QAC3B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC;IAC/C,OAAO,CACL,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,SAAS;QAC3B,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW;QAC/B,CAAC,CAAC,qBAAqB,KAAK,CAAC,CAAC,qBAAqB;QACnD,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG;QACf,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE;QACnD,QAAQ,CACT,CAAC;AACJ,CAAC","sourcesContent":["/**\n * `audioPlayer` and `audioRecorder`, an `expo-audio` player and recorder owned by the component\n * that makes each.\n *\n * ```ts\n * protected readonly player = audioPlayer(this.source, { timeUpdate: 0.5 });\n * protected readonly recorder = audioRecorder(RecordingPresets.HIGH_QUALITY);\n *\n * // {{ player.state().currentTime }} {{ recorder.state().durationMillis }}\n * ```\n */\nimport { DestroyRef, inject, signal, type Signal } from '@angular/core';\nimport { currentPlatform, expoModuleNow, unavailable } from './native.ts';\nimport { ownPlayer, watchAudioPlayer, type Player } from './player.ts';\n\ntype ExpoAudio = typeof import('expo-audio');\ntype RecordingOptions = import('expo-audio').RecordingOptions;\ntype RecorderState = import('expo-audio').RecorderState;\ntype AudioRecorder = import('expo-audio').AudioRecorder;\n\n/** `expo-audio` where the platform has it, or the error that says why not. */\nfunction expoAudio(): ExpoAudio {\n const expo = expoModuleNow('expo-audio', () => require('expo-audio') as ExpoAudio, [\n 'ios',\n 'android',\n 'web',\n ]);\n if (!expo) throw unavailable('expo-audio', 'Stand in for the player or recorder in the test.');\n return expo;\n}\n\n/**\n * An audio player owned by the current component, released when it is destroyed.\n *\n * `expo-audio` has no `timeUpdateEventInterval` property to set after the fact - the interval is\n * an option to `createAudioPlayer` itself, in milliseconds rather than `videoPlayer`'s seconds.\n */\nexport function audioPlayer(\n source: import('expo-audio').AudioSource,\n options: { timeUpdate?: number } = {},\n): Player<import('expo-audio').AudioPlayer> {\n const expo = expoAudio();\n const updateInterval = options.timeUpdate === undefined ? undefined : options.timeUpdate * 1000;\n const native = expo.createAudioPlayer(source, { updateInterval });\n return ownPlayer(native, () => watchAudioPlayer(native));\n}\n\n/** A component's own recorder: the module's, and its status as a signal. */\nexport interface Recorder<T> {\n /** The module's own recorder, for `prepareToRecordAsync()`, `record()`, `stop()` and the rest. */\n readonly native: T;\n /** `getStatus()`, asked every `statusInterval` seconds while the component lives. */\n readonly state: Signal<RecorderState>;\n}\n\n/**\n * An audio recorder owned by the current component, released when it is destroyed: what\n * `useAudioRecorder` and `useAudioRecorderState` give a React component.\n *\n * The recorder is made with `options` merged for the platform, as `useAudioRecorder` makes it, so\n * call `prepareToRecordAsync()` with none. Android reads `isMeteringEnabled` from these alone: a\n * recorder made without it and prepared with it has no `metering`.\n *\n * `statusInterval` is in seconds, as `audioPlayer`'s `timeUpdate` is, where\n * `useAudioRecorderState` takes milliseconds.\n */\nexport function audioRecorder(\n options: RecordingOptions,\n { statusInterval = 0.5 }: { statusInterval?: number } = {},\n): Recorder<AudioRecorder> {\n // First, so a call outside an injection context throws before there is a recorder to leak.\n const destroyRef = inject(DestroyRef);\n const expo = expoAudio();\n const native = makeRecorder(expo, forPlatform(options));\n const state = signal(native.getStatus(), { equal: sameStatus });\n const polling = setInterval(() => state.set(native.getStatus()), statusInterval * 1000);\n destroyRef.onDestroy(() => {\n clearInterval(polling);\n releaseRecorder(native);\n });\n return { native, state: state.asReadonly() };\n}\n\n/**\n * The platform's recorder class. On the web `expo-audio`'s `AudioModule` is a plain module whose\n * recorder is `AudioRecorderWeb`; elsewhere it is the native module's `AudioRecorder`.\n */\nfunction makeRecorder(expo: ExpoAudio, options: Record<string, unknown>): AudioRecorder {\n const module = expo.AudioModule as unknown as Record<string, new (o: unknown) => AudioRecorder>;\n const Recorder =\n currentPlatform() === 'web' ? module['AudioRecorderWeb'] : module['AudioRecorder'];\n return new Recorder!(options);\n}\n\n/**\n * What `useAudioRecorder` does when its component goes: the web recorder, which has no\n * `release()`, clears its timeouts instead.\n */\nfunction releaseRecorder(native: AudioRecorder): void {\n const recorder = native as AudioRecorder & { clearTimeouts?: () => void };\n if (currentPlatform() === 'web') recorder.clearTimeouts?.();\n else recorder.release?.();\n}\n\n/** `expo-audio`'s own `createRecordingOptions`, which it does not export: the platform's merged in. */\nfunction forPlatform(options: RecordingOptions): Record<string, unknown> {\n const common = {\n extension: options.extension,\n sampleRate: options.sampleRate,\n numberOfChannels: options.numberOfChannels,\n bitRate: options.bitRate,\n isMeteringEnabled: options.isMeteringEnabled ?? false,\n };\n const platform = currentPlatform();\n if (platform === 'ios') return { ...common, directory: options.directory, ...options.ios };\n if (platform === 'android')\n return { ...common, directory: options.directory, ...options.android };\n return { ...common, ...options.web };\n}\n\n/** What `useAudioRecorderState` counts as no change: up to 50 ms of duration and up to 0.1 dB of level, both included. */\nfunction sameStatus(a: RecorderState, b: RecorderState): boolean {\n const metering =\n a.metering === undefined || b.metering === undefined\n ? a.metering === b.metering\n : Math.abs(a.metering - b.metering) <= 0.1;\n return (\n a.canRecord === b.canRecord &&\n a.isRecording === b.isRecording &&\n a.mediaServicesDidReset === b.mediaServicesDidReset &&\n a.url === b.url &&\n Math.abs(a.durationMillis - b.durationMillis) <= 50 &&\n metering\n );\n}\n"]}
|
package/dist/brightness.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { DestroyRef, InjectionToken, Service, inject, signal, untracked, } from '@angular/core';
|
|
11
11
|
import { TopClaim, onWindowFront, sharedBySource } from '@ng-native/device';
|
|
12
|
+
import { nativePlatform } from '@ng-native/fabric';
|
|
12
13
|
import { expoModule, present } from "./native.js";
|
|
13
14
|
import * as i0 from "@angular/core";
|
|
14
15
|
export class Brightness {
|
|
@@ -22,17 +23,51 @@ export class Brightness {
|
|
|
22
23
|
if (!present(expo))
|
|
23
24
|
return expo;
|
|
24
25
|
// One per module, so every app the runtime mounts sets the same screen.
|
|
25
|
-
return sharedBySource(expo, () =>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
return sharedBySource(Brightness, expo, () => {
|
|
27
|
+
if (nativePlatform() === 'android') {
|
|
28
|
+
return {
|
|
29
|
+
get: () => expo.getBrightnessAsync(),
|
|
30
|
+
set: (level) => expo.setBrightnessAsync(level),
|
|
31
|
+
restore: () => expo.restoreSystemBrightnessAsync(),
|
|
32
|
+
onWindowFront,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// iOS has no level of the app's own: `setBrightnessAsync` sets the screen's, which stays
|
|
36
|
+
// after the app until the device locks, and `restoreSystemBrightnessAsync` does nothing
|
|
37
|
+
// there. So the level the screen had before the first set is read, and set again. One
|
|
38
|
+
// queue, so a set asked for while a restore runs reads that level after it is back.
|
|
39
|
+
let before = null;
|
|
40
|
+
let holding = false;
|
|
41
|
+
let queue = Promise.resolve();
|
|
42
|
+
const inTurn = (step) => {
|
|
43
|
+
const turn = queue.then(step);
|
|
44
|
+
queue = turn.catch(() => { });
|
|
45
|
+
return turn;
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
get: () => expo.getBrightnessAsync(),
|
|
49
|
+
set: (level) => inTurn(async () => {
|
|
50
|
+
if (!holding) {
|
|
51
|
+
before = await expo.getBrightnessAsync().catch(() => null);
|
|
52
|
+
holding = true;
|
|
53
|
+
}
|
|
54
|
+
await expo.setBrightnessAsync(level);
|
|
55
|
+
}),
|
|
56
|
+
restore: () => inTurn(async () => {
|
|
57
|
+
if (!holding)
|
|
58
|
+
return;
|
|
59
|
+
holding = false;
|
|
60
|
+
if (before !== null)
|
|
61
|
+
await expo.setBrightnessAsync(before);
|
|
62
|
+
}),
|
|
63
|
+
onWindowFront,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
31
66
|
},
|
|
32
67
|
});
|
|
33
68
|
native = inject(Brightness.SOURCE);
|
|
34
69
|
/** Every app's levels: the screen is the runtime's, not an app's. */
|
|
35
|
-
window = sharedBySource(this.native, () => new BrightnessWindow(this.native));
|
|
70
|
+
window = sharedBySource(BrightnessWindow, this.native, () => new BrightnessWindow(this.native));
|
|
36
71
|
current = this.window.current;
|
|
37
72
|
/** This app, among the apps setting the level. */
|
|
38
73
|
owner = {};
|
|
@@ -100,9 +135,15 @@ class BrightnessWindow {
|
|
|
100
135
|
}, () => { });
|
|
101
136
|
}
|
|
102
137
|
restore() {
|
|
103
|
-
|
|
138
|
+
const native = this.native;
|
|
139
|
+
void native
|
|
104
140
|
?.restore()
|
|
105
|
-
.then(() =>
|
|
141
|
+
.then(() => native.get())
|
|
142
|
+
.then((level) => {
|
|
143
|
+
// A level set while the restore ran is the one showing, not the one read back.
|
|
144
|
+
if (untracked(this.levels.claims.all).length === 0)
|
|
145
|
+
this.current.set(level);
|
|
146
|
+
}, () => { });
|
|
106
147
|
}
|
|
107
148
|
}
|
|
108
149
|
//# sourceMappingURL=brightness.js.map
|
package/dist/brightness.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brightness.js","sourceRoot":"","sources":["../src/brightness.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Brightness`, bound to `expo-brightness`.\n *\n * For the screens that have a reason to change it: a boarding pass, a QR code.\n *\n * The app's own brightness only. The system one is a global setting an app can leave changed\n * after it is gone, and needs a permission on Android - a decision to make deliberately against\n * Expo's own API rather than to reach through a convenience here.\n */\nimport {\n DestroyRef,\n InjectionToken,\n Service,\n inject,\n signal,\n type Signal,\n untracked,\n type WritableSignal,\n} from '@angular/core';\nimport { TopClaim, onWindowFront, sharedBySource } from '@ng-native/device';\nimport { expoModule, present } from './native.ts';\n\nexport interface NativeBrightness {\n get(): Promise<number>;\n set(level: number): Promise<void>;\n restore(): Promise<void>;\n /** Calls `listener` when a window comes to the front, to be given the level again. */\n onWindowFront?(listener: () => void): () => void;\n}\n\n@Service()\nexport class Brightness {\n /** Overridden in a test to watch the level without changing a screen. */\n static readonly SOURCE = new InjectionToken<NativeBrightness | null>(\n 'angular-native.brightnessSource',\n {\n factory: () => {\n const expo = expoModule(\n 'expo-brightness',\n () => require('expo-brightness') as typeof import('expo-brightness'),\n );\n if (!expo) return null;\n // Missing from the build: the stand-in, to say so when it is used.\n if (!present(expo)) return expo as never;\n // One per module, so every app the runtime mounts sets the same screen.\n return sharedBySource(expo, () => ({\n get: () => expo.getBrightnessAsync(),\n set: (level) => expo.setBrightnessAsync(level),\n restore: () => expo.restoreSystemBrightnessAsync(),\n onWindowFront,\n }));\n },\n },\n );\n\n private readonly native = inject(Brightness.SOURCE);\n /** Every app's levels: the screen is the runtime's, not an app's. */\n private readonly window = sharedBySource(this.native, () => new BrightnessWindow(this.native));\n private readonly current = this.window.current;\n /** This app, among the apps setting the level. */\n private readonly owner = {};\n\n /** Nought to one. One until the platform answers, since that is what a screen already shows. */\n readonly level: Signal<number> = this.current.asReadonly();\n\n constructor() {\n inject(DestroyRef).onDestroy(() => {\n // An app that goes takes its levels with it, and leaves the screen to the apps still here.\n this.window.levels.claims.drop(this.owner);\n this.window.levels.apply();\n });\n }\n\n /**\n * Set it, and return the function that puts it back.\n *\n * Always paired, because the alternative is an app that leaves the screen at full brightness\n * after the boarding pass has been shown - which the user experiences as a battery fault.\n */\n set(level: number): () => void {\n const clamped = Math.min(1, Math.max(0, level));\n // A stack, as the status bar's is: a screen pushed over one that set a level of its own puts\n // back the level beneath it, not the system's, when it goes.\n const drop = this.window.levels.claims.push(this.owner, clamped);\n this.window.levels.apply();\n return () => {\n drop();\n this.window.levels.apply();\n };\n }\n\n restore(): void {\n // This app's levels are put back, so a release still to come has nothing left to undo. Another\n // app's stay: the screen is the runtime's.\n this.window.levels.claims.drop(this.owner);\n if (untracked(this.window.levels.claims.all).length > 0) {\n this.window.levels.apply();\n return;\n }\n this.window.levels.clear();\n this.window.restore();\n }\n}\n\n/** The screen's level, as every app on one module sets it. */\nclass BrightnessWindow {\n readonly current: WritableSignal<number> = signal(1);\n readonly levels: TopClaim<number>;\n private readonly native: NativeBrightness | null;\n\n constructor(native: NativeBrightness | null) {\n this.native = native;\n this.levels = new TopClaim(\n (level) => {\n this.current.set(level);\n void this.native?.set(level);\n },\n () => this.restore(),\n present(native)?.onWindowFront,\n );\n // What the screen shows, unless a level was set before the platform answered.\n void present(native)\n ?.get()\n .then(\n (level) => {\n if (untracked(this.levels.claims.all).length === 0) this.current.set(level);\n },\n () => {},\n );\n }\n\n restore(): void {\n void this.native\n ?.restore()\n .then(() => this.native?.get().then((level) => this.current.set(level)));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"brightness.js","sourceRoot":"","sources":["../src/brightness.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Brightness`, bound to `expo-brightness`.\n *\n * For the screens that have a reason to change it: a boarding pass, a QR code.\n *\n * The app's own brightness only. The system one is a global setting an app can leave changed\n * after it is gone, and needs a permission on Android - a decision to make deliberately against\n * Expo's own API rather than to reach through a convenience here.\n */\nimport {\n DestroyRef,\n InjectionToken,\n Service,\n inject,\n signal,\n type Signal,\n untracked,\n type WritableSignal,\n} from '@angular/core';\nimport { TopClaim, onWindowFront, sharedBySource } from '@ng-native/device';\nimport { nativePlatform } from '@ng-native/fabric';\nimport { expoModule, present } from './native.ts';\n\nexport interface NativeBrightness {\n get(): Promise<number>;\n set(level: number): Promise<void>;\n restore(): Promise<void>;\n /** Calls `listener` when a window comes to the front, to be given the level again. */\n onWindowFront?(listener: () => void): () => void;\n}\n\n@Service()\nexport class Brightness {\n /** Overridden in a test to watch the level without changing a screen. */\n static readonly SOURCE = new InjectionToken<NativeBrightness | null>(\n 'angular-native.brightnessSource',\n {\n factory: () => {\n const expo = expoModule(\n 'expo-brightness',\n () => require('expo-brightness') as typeof import('expo-brightness'),\n );\n if (!expo) return null;\n // Missing from the build: the stand-in, to say so when it is used.\n if (!present(expo)) return expo as never;\n // One per module, so every app the runtime mounts sets the same screen.\n return sharedBySource(Brightness, expo, () => {\n if (nativePlatform() === 'android') {\n return {\n get: () => expo.getBrightnessAsync(),\n set: (level) => expo.setBrightnessAsync(level),\n restore: () => expo.restoreSystemBrightnessAsync(),\n onWindowFront,\n };\n }\n // iOS has no level of the app's own: `setBrightnessAsync` sets the screen's, which stays\n // after the app until the device locks, and `restoreSystemBrightnessAsync` does nothing\n // there. So the level the screen had before the first set is read, and set again. One\n // queue, so a set asked for while a restore runs reads that level after it is back.\n let before: number | null = null;\n let holding = false;\n let queue: Promise<unknown> = Promise.resolve();\n const inTurn = (step: () => Promise<void>): Promise<void> => {\n const turn = queue.then(step);\n queue = turn.catch(() => {});\n return turn;\n };\n return {\n get: () => expo.getBrightnessAsync(),\n set: (level) =>\n inTurn(async () => {\n if (!holding) {\n before = await expo.getBrightnessAsync().catch(() => null);\n holding = true;\n }\n await expo.setBrightnessAsync(level);\n }),\n restore: () =>\n inTurn(async () => {\n if (!holding) return;\n holding = false;\n if (before !== null) await expo.setBrightnessAsync(before);\n }),\n onWindowFront,\n };\n });\n },\n },\n );\n\n private readonly native = inject(Brightness.SOURCE);\n /** Every app's levels: the screen is the runtime's, not an app's. */\n private readonly window = sharedBySource(\n BrightnessWindow,\n this.native,\n () => new BrightnessWindow(this.native),\n );\n private readonly current = this.window.current;\n /** This app, among the apps setting the level. */\n private readonly owner = {};\n\n /** Nought to one. One until the platform answers, since that is what a screen already shows. */\n readonly level: Signal<number> = this.current.asReadonly();\n\n constructor() {\n inject(DestroyRef).onDestroy(() => {\n // An app that goes takes its levels with it, and leaves the screen to the apps still here.\n this.window.levels.claims.drop(this.owner);\n this.window.levels.apply();\n });\n }\n\n /**\n * Set it, and return the function that puts it back.\n *\n * Always paired, because the alternative is an app that leaves the screen at full brightness\n * after the boarding pass has been shown - which the user experiences as a battery fault.\n */\n set(level: number): () => void {\n const clamped = Math.min(1, Math.max(0, level));\n // A stack, as the status bar's is: a screen pushed over one that set a level of its own puts\n // back the level beneath it, not the system's, when it goes.\n const drop = this.window.levels.claims.push(this.owner, clamped);\n this.window.levels.apply();\n return () => {\n drop();\n this.window.levels.apply();\n };\n }\n\n restore(): void {\n // This app's levels are put back, so a release still to come has nothing left to undo. Another\n // app's stay: the screen is the runtime's.\n this.window.levels.claims.drop(this.owner);\n if (untracked(this.window.levels.claims.all).length > 0) {\n this.window.levels.apply();\n return;\n }\n this.window.levels.clear();\n this.window.restore();\n }\n}\n\n/** The screen's level, as every app on one module sets it. */\nclass BrightnessWindow {\n readonly current: WritableSignal<number> = signal(1);\n readonly levels: TopClaim<number>;\n private readonly native: NativeBrightness | null;\n\n constructor(native: NativeBrightness | null) {\n this.native = native;\n this.levels = new TopClaim(\n (level) => {\n this.current.set(level);\n void this.native?.set(level);\n },\n () => this.restore(),\n present(native)?.onWindowFront,\n );\n // What the screen shows, unless a level was set before the platform answered.\n void present(native)\n ?.get()\n .then(\n (level) => {\n if (untracked(this.levels.claims.all).length === 0) this.current.set(level);\n },\n () => {},\n );\n }\n\n restore(): void {\n const native = this.native;\n void native\n ?.restore()\n .then(() => native.get())\n .then(\n (level) => {\n // A level set while the restore ran is the one showing, not the one read back.\n if (untracked(this.levels.claims.all).length === 0) this.current.set(level);\n },\n () => {},\n );\n }\n}\n"]}
|
|
@@ -165,11 +165,13 @@ export declare class UiList {
|
|
|
165
165
|
/**
|
|
166
166
|
* The system's swipe actions on a list row, iOS only. The first child is the row; the actions go
|
|
167
167
|
* in `<ui-slot name="actions" [extraProps]="{ edge: 'trailing', allowsFullSwipe: true }">`, one
|
|
168
|
-
* slot per edge, as `ui-button`s.
|
|
168
|
+
* slot per edge, as `ui-button`s. A sideways drag on the row is the swipe's, so a `pressable` in a
|
|
169
|
+
* `ui-view-host` in the row does not press for it.
|
|
169
170
|
*/
|
|
170
171
|
export declare class UiSwipeActions {
|
|
171
172
|
protected readonly nativeView: true;
|
|
172
173
|
readonly modifiers: import("@angular/core").InputSignal<readonly UiModifier[] | undefined>;
|
|
174
|
+
constructor();
|
|
173
175
|
static ɵfac: i0.ɵɵFactoryDeclaration<UiSwipeActions, never>;
|
|
174
176
|
static ɵcmp: i0.ɵɵComponentDeclaration<UiSwipeActions, "ui-swipe-actions", never, { "modifiers": { "alias": "modifiers"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
175
177
|
}
|
|
@@ -852,6 +854,13 @@ export type UiPresentationDetent = 'medium' | 'large' | {
|
|
|
852
854
|
} | {
|
|
853
855
|
readonly height: number;
|
|
854
856
|
};
|
|
857
|
+
/**
|
|
858
|
+
* Whether the screen behind a SwiftUI sheet takes touches, as `presentationBackgroundInteraction`
|
|
859
|
+
* says it: as the system decides, always, never, or while the sheet rests at or below a detent.
|
|
860
|
+
*/
|
|
861
|
+
export type UiBottomSheetBackgroundInteraction = 'automatic' | 'enabled' | 'disabled' | {
|
|
862
|
+
readonly upThrough: UiPresentationDetent;
|
|
863
|
+
};
|
|
855
864
|
/** The functions `@expo/ui` defines on Compose's sheet, called with the view's tag as `this`. */
|
|
856
865
|
export interface UiBottomSheetViewFunctions {
|
|
857
866
|
/** Slides the sheet away, answering once it has gone. */
|
|
@@ -939,6 +948,30 @@ export declare class UiBottomSheet {
|
|
|
939
948
|
* way. Neither is the sheet as the system draws it.
|
|
940
949
|
*/
|
|
941
950
|
readonly backgroundColor: import("@angular/core").InputSignal<string | undefined>;
|
|
951
|
+
/**
|
|
952
|
+
* The user cannot dismiss the sheet; only `open` closes it. On iOS it is SwiftUI's
|
|
953
|
+
* `interactiveDismissDisabled`: a swipe down takes the sheet to its smallest detent and no
|
|
954
|
+
* further, and a tap outside it does nothing. On Android a swipe, the back button and a tap
|
|
955
|
+
* outside the sheet are all turned off, as `@expo/ui`'s own sheet does; Compose's sheet has no
|
|
956
|
+
* way to keep the drag between its heights without the swipe that dismisses it, so that drag is
|
|
957
|
+
* off too.
|
|
958
|
+
*/
|
|
959
|
+
readonly interactiveDismissDisabled: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
|
|
960
|
+
/**
|
|
961
|
+
* Whether the screen behind the sheet takes touches, iOS 16.4 and later only: SwiftUI's
|
|
962
|
+
* `presentationBackgroundInteraction`. `'enabled'` always, `'disabled'` never, `'automatic'` as
|
|
963
|
+
* the system decides, and `{ upThrough: 'medium' }` while the sheet rests at that detent or
|
|
964
|
+
* below it, which should be one of its `detents`. Unset, SwiftUI decides. Android has no
|
|
965
|
+
* equivalent: Compose's sheet is in a window of its own over the screen, which takes no touch
|
|
966
|
+
* while it shows.
|
|
967
|
+
*/
|
|
968
|
+
readonly backgroundInteraction: import("@angular/core").InputSignal<UiBottomSheetBackgroundInteraction | undefined>;
|
|
969
|
+
/**
|
|
970
|
+
* More SwiftUI modifiers, iOS only, after the sheet's own on the `ui-group` around its content,
|
|
971
|
+
* where SwiftUI reads presentation modifiers from: `presentationCornerRadius`, for one. They
|
|
972
|
+
* apply to the content too. Android takes no notice of them.
|
|
973
|
+
*/
|
|
974
|
+
readonly modifiers: import("@angular/core").InputSignal<readonly UiModifier[] | undefined>;
|
|
942
975
|
/**
|
|
943
976
|
* The sheet has finished closing, whether the user dismissed it or `open` was set false: the
|
|
944
977
|
* moment another sheet or a dialog can be presented.
|
|
@@ -985,6 +1018,16 @@ export declare class UiBottomSheet {
|
|
|
985
1018
|
* detents of a sheet not sized to its content are followed while it is open.
|
|
986
1019
|
*/
|
|
987
1020
|
protected readonly presentation: Signal<readonly UiModifier[]>;
|
|
1021
|
+
/** Whether the screen behind the sheet takes touches, in `@expo/ui`'s shape, where one is set. */
|
|
1022
|
+
private readonly interaction;
|
|
1023
|
+
/**
|
|
1024
|
+
* How Compose's sheet may be dismissed other than by a swipe. Always committed, so that turning
|
|
1025
|
+
* `interactiveDismissDisabled` off again reaches native.
|
|
1026
|
+
*/
|
|
1027
|
+
protected readonly dismissal: Signal<{
|
|
1028
|
+
shouldDismissOnBackPress: boolean;
|
|
1029
|
+
shouldDismissOnClickOutside: boolean;
|
|
1030
|
+
}>;
|
|
988
1031
|
/** The `background-color` the element has from CSS, which the sheet draws rather than it. */
|
|
989
1032
|
private readonly painted;
|
|
990
1033
|
/** The sheet's colour: the input's, or else the element's from CSS. */
|
|
@@ -1036,6 +1079,6 @@ export declare class UiBottomSheet {
|
|
|
1036
1079
|
/** The sheet has gone: its content leaves the tree. */
|
|
1037
1080
|
private finish;
|
|
1038
1081
|
static ɵfac: i0.ɵɵFactoryDeclaration<UiBottomSheet, never>;
|
|
1039
|
-
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>;
|
|
1082
|
+
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; }; "interactiveDismissDisabled": { "alias": "interactiveDismissDisabled"; "required": false; "isSignal": true; }; "backgroundInteraction": { "alias": "backgroundInteraction"; "required": false; "isSignal": true; }; "modifiers": { "alias": "modifiers"; "required": false; "isSignal": true; }; }, { "open": "openChange"; "dismissed": "dismissed"; }, ["toolbarItems"], ["*", "ui-toolbar-item"], true, never>;
|
|
1040
1083
|
}
|
|
1041
1084
|
export {};
|
|
@@ -289,15 +289,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
|
|
|
289
289
|
host: { '[modifiers]': 'modifiers()' },
|
|
290
290
|
}]
|
|
291
291
|
}], propDecorators: { modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }] } });
|
|
292
|
+
/**
|
|
293
|
+
* How far a finger goes sideways, and further than it goes down, before a row's swipe actions take
|
|
294
|
+
* the touch. SwiftUI's own row button still taps at about 20 points and its actions come out by
|
|
295
|
+
* about 30.
|
|
296
|
+
*/
|
|
297
|
+
const SWIPE_SLOP = 20;
|
|
298
|
+
/**
|
|
299
|
+
* Take a sideways drag from what holds the touch inside the row, as SwiftUI does: its list moves
|
|
300
|
+
* the row and never taps it. Native sends the app's views inside the row no cancel, since React
|
|
301
|
+
* Native's touch handler runs beside the list's gestures rather than giving way to them, so the
|
|
302
|
+
* row's own responder asks for the drag, and a `pressable` that gives it up does not press.
|
|
303
|
+
*/
|
|
304
|
+
function takeSidewaysDrag() {
|
|
305
|
+
const node = inject(ElementRef).nativeElement;
|
|
306
|
+
const engine = node.engine ?? inject(HostEngine);
|
|
307
|
+
let from = null;
|
|
308
|
+
const stop = engine.setResponder(node, {
|
|
309
|
+
onStartShouldSetResponderCapture: (event) => {
|
|
310
|
+
from = { x: event.nativeEvent.pageX ?? 0, y: event.nativeEvent.pageY ?? 0 };
|
|
311
|
+
return false;
|
|
312
|
+
},
|
|
313
|
+
onMoveShouldSetResponderCapture: (event) => {
|
|
314
|
+
if (!from)
|
|
315
|
+
return false;
|
|
316
|
+
const across = Math.abs((event.nativeEvent.pageX ?? from.x) - from.x);
|
|
317
|
+
const down = Math.abs((event.nativeEvent.pageY ?? from.y) - from.y);
|
|
318
|
+
return across >= SWIPE_SLOP && across > down;
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
inject(DestroyRef).onDestroy(stop);
|
|
322
|
+
}
|
|
292
323
|
/**
|
|
293
324
|
* The system's swipe actions on a list row, iOS only. The first child is the row; the actions go
|
|
294
325
|
* in `<ui-slot name="actions" [extraProps]="{ edge: 'trailing', allowsFullSwipe: true }">`, one
|
|
295
|
-
* slot per edge, as `ui-button`s.
|
|
326
|
+
* slot per edge, as `ui-button`s. A sideways drag on the row is the swipe's, so a `pressable` in a
|
|
327
|
+
* `ui-view-host` in the row does not press for it.
|
|
296
328
|
*/
|
|
297
329
|
export class UiSwipeActions {
|
|
298
330
|
nativeView = nativeView();
|
|
299
331
|
modifiers = input(/* @ts-ignore */
|
|
300
332
|
...(ngDevMode ? [undefined, { debugName: "modifiers" }] : /* istanbul ignore next */ []));
|
|
333
|
+
constructor() {
|
|
334
|
+
if (nativePlatform() !== 'android')
|
|
335
|
+
takeSidewaysDrag();
|
|
336
|
+
}
|
|
301
337
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiSwipeActions, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
302
338
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiSwipeActions, isStandalone: true, selector: "ui-swipe-actions", inputs: { modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "modifiers": "modifiers()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
303
339
|
}
|
|
@@ -308,7 +344,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
|
|
|
308
344
|
template: '<ng-content />',
|
|
309
345
|
host: { '[modifiers]': 'modifiers()' },
|
|
310
346
|
}]
|
|
311
|
-
}], propDecorators: { modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }] } });
|
|
347
|
+
}], ctorParameters: () => [], propDecorators: { modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }] } });
|
|
312
348
|
/**
|
|
313
349
|
* A SwiftUI `Slider`, or Compose's on Android. `valueChanged` reports each new value as the thumb
|
|
314
350
|
* moves; note the d, which SwiftUI's event has and `@expo/ui`'s React prop does not.
|
|
@@ -1789,6 +1825,32 @@ export class UiBottomSheet {
|
|
|
1789
1825
|
*/
|
|
1790
1826
|
backgroundColor = input(/* @ts-ignore */
|
|
1791
1827
|
...(ngDevMode ? [undefined, { debugName: "backgroundColor" }] : /* istanbul ignore next */ []));
|
|
1828
|
+
/**
|
|
1829
|
+
* The user cannot dismiss the sheet; only `open` closes it. On iOS it is SwiftUI's
|
|
1830
|
+
* `interactiveDismissDisabled`: a swipe down takes the sheet to its smallest detent and no
|
|
1831
|
+
* further, and a tap outside it does nothing. On Android a swipe, the back button and a tap
|
|
1832
|
+
* outside the sheet are all turned off, as `@expo/ui`'s own sheet does; Compose's sheet has no
|
|
1833
|
+
* way to keep the drag between its heights without the swipe that dismisses it, so that drag is
|
|
1834
|
+
* off too.
|
|
1835
|
+
*/
|
|
1836
|
+
interactiveDismissDisabled = input(false, { ...(ngDevMode ? { debugName: "interactiveDismissDisabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1837
|
+
/**
|
|
1838
|
+
* Whether the screen behind the sheet takes touches, iOS 16.4 and later only: SwiftUI's
|
|
1839
|
+
* `presentationBackgroundInteraction`. `'enabled'` always, `'disabled'` never, `'automatic'` as
|
|
1840
|
+
* the system decides, and `{ upThrough: 'medium' }` while the sheet rests at that detent or
|
|
1841
|
+
* below it, which should be one of its `detents`. Unset, SwiftUI decides. Android has no
|
|
1842
|
+
* equivalent: Compose's sheet is in a window of its own over the screen, which takes no touch
|
|
1843
|
+
* while it shows.
|
|
1844
|
+
*/
|
|
1845
|
+
backgroundInteraction = input(/* @ts-ignore */
|
|
1846
|
+
...(ngDevMode ? [undefined, { debugName: "backgroundInteraction" }] : /* istanbul ignore next */ []));
|
|
1847
|
+
/**
|
|
1848
|
+
* More SwiftUI modifiers, iOS only, after the sheet's own on the `ui-group` around its content,
|
|
1849
|
+
* where SwiftUI reads presentation modifiers from: `presentationCornerRadius`, for one. They
|
|
1850
|
+
* apply to the content too. Android takes no notice of them.
|
|
1851
|
+
*/
|
|
1852
|
+
modifiers = input(/* @ts-ignore */
|
|
1853
|
+
...(ngDevMode ? [undefined, { debugName: "modifiers" }] : /* istanbul ignore next */ []));
|
|
1792
1854
|
/**
|
|
1793
1855
|
* The sheet has finished closing, whether the user dismissed it or `open` was set false: the
|
|
1794
1856
|
* moment another sheet or a dialog can be presented.
|
|
@@ -1857,8 +1919,33 @@ export class UiBottomSheet {
|
|
|
1857
1919
|
visibility: this.showDragIndicator() ? 'visible' : 'hidden',
|
|
1858
1920
|
},
|
|
1859
1921
|
...this.background(),
|
|
1922
|
+
...(this.interactiveDismissDisabled()
|
|
1923
|
+
? [{ $type: 'interactiveDismissDisabled', isDisabled: true }]
|
|
1924
|
+
: []),
|
|
1925
|
+
...this.interaction(),
|
|
1926
|
+
...(this.modifiers() ?? []),
|
|
1860
1927
|
], /* @ts-ignore */
|
|
1861
1928
|
...(ngDevMode ? [{ debugName: "presentation" }] : /* istanbul ignore next */ []));
|
|
1929
|
+
/** Whether the screen behind the sheet takes touches, in `@expo/ui`'s shape, where one is set. */
|
|
1930
|
+
interaction = computed(() => {
|
|
1931
|
+
const given = this.backgroundInteraction();
|
|
1932
|
+
if (given === undefined)
|
|
1933
|
+
return [];
|
|
1934
|
+
const $type = 'presentationBackgroundInteraction';
|
|
1935
|
+
return typeof given === 'string'
|
|
1936
|
+
? [{ $type, interactionType: given }]
|
|
1937
|
+
: [{ $type, interactionType: 'enabledUpThrough', detent: given.upThrough }];
|
|
1938
|
+
}, /* @ts-ignore */
|
|
1939
|
+
...(ngDevMode ? [{ debugName: "interaction" }] : /* istanbul ignore next */ []));
|
|
1940
|
+
/**
|
|
1941
|
+
* How Compose's sheet may be dismissed other than by a swipe. Always committed, so that turning
|
|
1942
|
+
* `interactiveDismissDisabled` off again reaches native.
|
|
1943
|
+
*/
|
|
1944
|
+
dismissal = computed(() => {
|
|
1945
|
+
const dismissible = !this.interactiveDismissDisabled();
|
|
1946
|
+
return { shouldDismissOnBackPress: dismissible, shouldDismissOnClickOutside: dismissible };
|
|
1947
|
+
}, /* @ts-ignore */
|
|
1948
|
+
...(ngDevMode ? [{ debugName: "dismissal" }] : /* istanbul ignore next */ []));
|
|
1862
1949
|
/** The `background-color` the element has from CSS, which the sheet draws rather than it. */
|
|
1863
1950
|
painted = signal(undefined, /* @ts-ignore */
|
|
1864
1951
|
...(ngDevMode ? [{ debugName: "painted" }] : /* istanbul ignore next */ []));
|
|
@@ -1988,7 +2075,7 @@ export class UiBottomSheet {
|
|
|
1988
2075
|
afterNextRender(() => this.mounted.set(this.open()), { injector: this.injector });
|
|
1989
2076
|
}
|
|
1990
2077
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiBottomSheet, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1991
|
-
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: `
|
|
2078
|
+
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 }, interactiveDismissDisabled: { classPropertyName: "interactiveDismissDisabled", publicName: "interactiveDismissDisabled", isSignal: true, isRequired: false, transformFunction: null }, backgroundInteraction: { classPropertyName: "backgroundInteraction", publicName: "backgroundInteraction", isSignal: true, isRequired: false, transformFunction: null }, modifiers: { classPropertyName: "modifiers", publicName: "modifiers", 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: `
|
|
1992
2079
|
<ng-template #content><ng-content /></ng-template>
|
|
1993
2080
|
@if (swiftUI) {
|
|
1994
2081
|
<ui-host style="position: absolute" pointerEvents="none" [style.width.px]="width()">
|
|
@@ -2029,6 +2116,8 @@ export class UiBottomSheet {
|
|
|
2029
2116
|
[showDragHandle]="showDragIndicator()"
|
|
2030
2117
|
[containerColor]="color()"
|
|
2031
2118
|
[skipPartiallyExpanded]="fits()"
|
|
2119
|
+
[sheetGesturesEnabled]="!interactiveDismissDisabled()"
|
|
2120
|
+
[properties]="dismissal()"
|
|
2032
2121
|
(dismissRequest)="dismissRequested($event)"
|
|
2033
2122
|
>
|
|
2034
2123
|
<ui-view-host layoutRoot [matchContents]="fits() || undefined">
|
|
@@ -2037,7 +2126,7 @@ export class UiBottomSheet {
|
|
|
2037
2126
|
</ui-bottom-sheet-view>
|
|
2038
2127
|
</ui-host>
|
|
2039
2128
|
}
|
|
2040
|
-
`, 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"] }] });
|
|
2129
|
+
`, 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", "sheetGesturesEnabled", "properties"], 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"] }] });
|
|
2041
2130
|
}
|
|
2042
2131
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiBottomSheet, decorators: [{
|
|
2043
2132
|
type: Component,
|
|
@@ -2096,6 +2185,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
|
|
|
2096
2185
|
[showDragHandle]="showDragIndicator()"
|
|
2097
2186
|
[containerColor]="color()"
|
|
2098
2187
|
[skipPartiallyExpanded]="fits()"
|
|
2188
|
+
[sheetGesturesEnabled]="!interactiveDismissDisabled()"
|
|
2189
|
+
[properties]="dismissal()"
|
|
2099
2190
|
(dismissRequest)="dismissRequested($event)"
|
|
2100
2191
|
>
|
|
2101
2192
|
<ui-view-host layoutRoot [matchContents]="fits() || undefined">
|
|
@@ -2109,5 +2200,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
|
|
|
2109
2200
|
}]
|
|
2110
2201
|
}], ctorParameters: () => [], propDecorators: { sheet: [{ type: i0.ViewChild, args: ['sheet', { ...{
|
|
2111
2202
|
read: ElementRef,
|
|
2112
|
-
}, 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 }] }] } });
|
|
2203
|
+
}, 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 }] }], interactiveDismissDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactiveDismissDisabled", required: false }] }], backgroundInteraction: [{ type: i0.Input, args: [{ isSignal: true, alias: "backgroundInteraction", required: false }] }], modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }], dismissed: [{ type: i0.Output, args: ["dismissed"] }], toolbarItems: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => UiToolbarItem), { isSignal: true }] }] } });
|
|
2113
2204
|
//# sourceMappingURL=expo-ui-components.js.map
|