@ng-native/expo 0.2.0 → 0.4.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/README.md +32 -382
- package/dist/async-storage.d.ts +15 -0
- package/dist/async-storage.js +29 -0
- package/dist/async-storage.js.map +1 -0
- package/dist/audio.d.ts +10 -0
- package/dist/audio.js +26 -0
- package/dist/audio.js.map +1 -0
- package/dist/community-views.js +1 -1
- package/dist/community-views.js.map +1 -1
- package/dist/crypto.js +2 -2
- package/dist/crypto.js.map +1 -1
- package/dist/database.d.ts +18 -3
- package/dist/database.js +26 -7
- package/dist/database.js.map +1 -1
- package/dist/date-time-picker.d.ts +33 -0
- package/dist/date-time-picker.js +77 -0
- package/dist/date-time-picker.js.map +1 -0
- package/dist/dom-component.d.ts +5 -0
- package/dist/dom-component.js +57 -2
- package/dist/dom-component.js.map +1 -1
- package/dist/expo-ui-components.d.ts +292 -12
- package/dist/expo-ui-components.js +531 -41
- package/dist/expo-ui-components.js.map +1 -1
- package/dist/expo-ui.js +19 -6
- package/dist/expo-ui.js.map +1 -1
- package/dist/file-system.d.ts +7 -0
- package/dist/file-system.js +16 -3
- package/dist/file-system.js.map +1 -1
- package/dist/foldable.d.ts +78 -0
- package/dist/foldable.js +87 -0
- package/dist/foldable.js.map +1 -0
- package/dist/fonts.d.ts +3 -9
- package/dist/fonts.js +26 -16
- package/dist/fonts.js.map +1 -1
- package/dist/glass.d.ts +1 -1
- package/dist/image-picker.js +3 -9
- package/dist/image-picker.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/live-activity.d.ts +88 -0
- package/dist/live-activity.js +156 -0
- package/dist/live-activity.js.map +1 -0
- package/dist/location.d.ts +17 -0
- package/dist/location.js +20 -7
- package/dist/location.js.map +1 -1
- package/dist/media-library.js +1 -3
- package/dist/media-library.js.map +1 -1
- package/dist/native.d.ts +12 -2
- package/dist/native.js +81 -10
- package/dist/native.js.map +1 -1
- package/dist/notifications.js +2 -4
- package/dist/notifications.js.map +1 -1
- package/dist/permissions.d.ts +2 -0
- package/dist/permissions.js +6 -0
- package/dist/permissions.js.map +1 -1
- package/dist/player.d.ts +24 -11
- package/dist/player.js +7 -26
- package/dist/player.js.map +1 -1
- package/dist/register-expo-view.d.ts +7 -0
- package/dist/register-expo-view.js +6 -1
- package/dist/register-expo-view.js.map +1 -1
- package/dist/screen-capture.js +2 -4
- package/dist/screen-capture.js.map +1 -1
- package/dist/secure-store.d.ts +15 -0
- package/dist/secure-store.js +30 -0
- package/dist/secure-store.js.map +1 -0
- package/dist/store.d.ts +5 -10
- package/dist/store.js +5 -36
- package/dist/store.js.map +1 -1
- package/dist/tracking.d.ts +1 -1
- package/dist/tracking.js +3 -5
- package/dist/tracking.js.map +1 -1
- package/dist/video.d.ts +5 -0
- package/dist/video.js +21 -0
- package/dist/video.js.map +1 -0
- package/dist/watch.d.ts +103 -0
- package/dist/watch.js +228 -0
- package/dist/watch.js.map +1 -0
- package/dist/widget.d.ts +63 -0
- package/dist/widget.js +98 -0
- package/dist/widget.js.map +1 -0
- package/package.json +53 -4
package/dist/foldable.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Foldable`, bound to `expo-foldables`: the hinge of a foldable device, an iPhone Duo or an
|
|
3
|
+
* Android foldable.
|
|
4
|
+
*
|
|
5
|
+
* On a phone that does not fold, on the web and in a test, it answers like a phone: not available,
|
|
6
|
+
* `'unknown'`, no fold and no angle.
|
|
7
|
+
*/
|
|
8
|
+
import { DestroyRef, InjectionToken, Service, computed, inject, signal, } from '@angular/core';
|
|
9
|
+
import { expoModule } from "./native.js";
|
|
10
|
+
import * as i0 from "@angular/core";
|
|
11
|
+
export class Foldable {
|
|
12
|
+
static SOURCE = new InjectionToken('angular-native.hingeSource', {
|
|
13
|
+
factory: () => expoModule('expo-foldables', () => require('expo-foldables').Hinge),
|
|
14
|
+
});
|
|
15
|
+
native = inject(Foldable.SOURCE);
|
|
16
|
+
state = signal(undefined, /* @ts-ignore */
|
|
17
|
+
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
18
|
+
angleState = signal(null, /* @ts-ignore */
|
|
19
|
+
...(ngDevMode ? [{ debugName: "angleState" }] : /* istanbul ignore next */ []));
|
|
20
|
+
angleSubscription = null;
|
|
21
|
+
/** Whether the device reports a hinge. It can turn true shortly after launch. */
|
|
22
|
+
available = computed(() => this.state() !== undefined, /* @ts-ignore */
|
|
23
|
+
...(ngDevMode ? [{ debugName: "available" }] : /* istanbul ignore next */ []));
|
|
24
|
+
posture = computed(() => this.state()?.posture ?? 'unknown', /* @ts-ignore */
|
|
25
|
+
...(ngDevMode ? [{ debugName: "posture" }] : /* istanbul ignore next */ []));
|
|
26
|
+
/**
|
|
27
|
+
* Where the fold crosses the window, in window coordinates: a view that does not start at the
|
|
28
|
+
* window's left edge subtracts its own offset. Null while it crosses no window, as on a closed
|
|
29
|
+
* phone's cover display.
|
|
30
|
+
*/
|
|
31
|
+
fold = computed(() => this.state()?.fold ?? null, /* @ts-ignore */
|
|
32
|
+
...(ngDevMode ? [{ debugName: "fold" }] : /* istanbul ignore next */ []));
|
|
33
|
+
/** The hinge angle in degrees, 0 shut and 180 flat. Null without an angle sensor. */
|
|
34
|
+
angle = this.angleState.asReadonly();
|
|
35
|
+
/** Whether the fold splits the window, so content should stay off it. */
|
|
36
|
+
separating = computed(() => this.fold()?.isSeparating ?? false, /* @ts-ignore */
|
|
37
|
+
...(ngDevMode ? [{ debugName: "separating" }] : /* istanbul ignore next */ []));
|
|
38
|
+
/** Half open with the fold running down the middle, like a book. */
|
|
39
|
+
book = computed(() => this.halfOpen('vertical'), /* @ts-ignore */
|
|
40
|
+
...(ngDevMode ? [{ debugName: "book" }] : /* istanbul ignore next */ []));
|
|
41
|
+
/** Half open with the fold running across, like a laptop. */
|
|
42
|
+
tabletop = computed(() => this.halfOpen('horizontal'), /* @ts-ignore */
|
|
43
|
+
...(ngDevMode ? [{ debugName: "tabletop" }] : /* istanbul ignore next */ []));
|
|
44
|
+
constructor() {
|
|
45
|
+
const native = this.native;
|
|
46
|
+
if (!native)
|
|
47
|
+
return;
|
|
48
|
+
const stateSubscription = native.addOnStateChangeListener((state) => this.update(state));
|
|
49
|
+
this.update(native.getState());
|
|
50
|
+
inject(DestroyRef).onDestroy(() => {
|
|
51
|
+
stateSubscription.remove();
|
|
52
|
+
this.angleSubscription?.remove();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
update(state) {
|
|
56
|
+
this.state.set(state);
|
|
57
|
+
if (state === undefined) {
|
|
58
|
+
this.stopFollowingAngle();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.followAngle();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
stopFollowingAngle() {
|
|
65
|
+
this.angleSubscription?.remove();
|
|
66
|
+
this.angleSubscription = null;
|
|
67
|
+
this.angleState.set(null);
|
|
68
|
+
}
|
|
69
|
+
followAngle() {
|
|
70
|
+
const native = this.native;
|
|
71
|
+
if (!native || this.angleSubscription || !native.isAngleAvailable)
|
|
72
|
+
return;
|
|
73
|
+
this.angleSubscription = native.addOnAngleChangeListener((angle) => this.angleState.set(angle), { minDeltaDegrees: 0.5 });
|
|
74
|
+
}
|
|
75
|
+
halfOpen(orientation) {
|
|
76
|
+
const fold = this.fold();
|
|
77
|
+
return (this.posture() === 'partially-open' &&
|
|
78
|
+
fold?.isSeparating === true &&
|
|
79
|
+
fold.orientation === orientation);
|
|
80
|
+
}
|
|
81
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Foldable, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
82
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: Foldable });
|
|
83
|
+
}
|
|
84
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Foldable, decorators: [{
|
|
85
|
+
type: Service
|
|
86
|
+
}], ctorParameters: () => [] });
|
|
87
|
+
//# sourceMappingURL=foldable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foldable.js","sourceRoot":"","sources":["../src/foldable.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Foldable`, bound to `expo-foldables`: the hinge of a foldable device, an iPhone Duo or an\n * Android foldable.\n *\n * On a phone that does not fold, on the web and in a test, it answers like a phone: not available,\n * `'unknown'`, no fold and no angle.\n */\nimport {\n DestroyRef,\n InjectionToken,\n Service,\n computed,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { expoModule } from './native.ts';\n\n/** How far the hinge is open. `'unknown'` without a hinge, or while the platform cannot say. */\nexport type HingePosture = 'closed' | 'partially-open' | 'fully-open' | 'unknown';\n\n/** A rectangle in window coordinates, in points: measured from the window's top left corner. */\nexport interface FoldRect {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\n/** Where the fold crosses the app's window. */\nexport interface Fold {\n /** The fold's area, in window coordinates. Zero wide or tall where the fold is a line. */\n readonly bounds: FoldRect;\n readonly orientation: 'horizontal' | 'vertical';\n /** Whether the fold splits the window in two, so content should stay off it. */\n readonly isSeparating: boolean;\n /** `'full'` where the fold area shows nothing, as with a physical gap between two screens. */\n readonly occlusion: 'none' | 'full';\n}\n\n/** What `expo-foldables` reports: the posture, and the fold while it crosses the window. */\nexport interface HingeState {\n readonly posture: HingePosture;\n readonly fold?: Fold;\n}\n\ninterface Subscription {\n remove(): void;\n}\n\n/** `expo-foldables`' `Hinge`, as `Foldable.SOURCE` provides it. A test passes a stand-in. */\nexport interface NativeHinge {\n readonly isAvailable: boolean;\n readonly isAngleAvailable: boolean;\n getState(): HingeState | undefined;\n addOnStateChangeListener(listener: (state: HingeState | undefined) => void): Subscription;\n addOnAngleChangeListener(\n listener: (angle: number) => void,\n options?: { minDeltaDegrees?: number },\n ): Subscription;\n}\n\n@Service()\nexport class Foldable {\n static readonly SOURCE = new InjectionToken<NativeHinge | null>('angular-native.hingeSource', {\n factory: () =>\n expoModule(\n 'expo-foldables',\n () => (require('expo-foldables') as { Hinge: NativeHinge }).Hinge,\n ),\n });\n\n private readonly native = inject(Foldable.SOURCE);\n private readonly state = signal<HingeState | undefined>(undefined);\n private readonly angleState = signal<number | null>(null);\n private angleSubscription: Subscription | null = null;\n\n /** Whether the device reports a hinge. It can turn true shortly after launch. */\n readonly available: Signal<boolean> = computed(() => this.state() !== undefined);\n readonly posture: Signal<HingePosture> = computed(() => this.state()?.posture ?? 'unknown');\n /**\n * Where the fold crosses the window, in window coordinates: a view that does not start at the\n * window's left edge subtracts its own offset. Null while it crosses no window, as on a closed\n * phone's cover display.\n */\n readonly fold: Signal<Fold | null> = computed(() => this.state()?.fold ?? null);\n /** The hinge angle in degrees, 0 shut and 180 flat. Null without an angle sensor. */\n readonly angle: Signal<number | null> = this.angleState.asReadonly();\n /** Whether the fold splits the window, so content should stay off it. */\n readonly separating: Signal<boolean> = computed(() => this.fold()?.isSeparating ?? false);\n /** Half open with the fold running down the middle, like a book. */\n readonly book: Signal<boolean> = computed(() => this.halfOpen('vertical'));\n /** Half open with the fold running across, like a laptop. */\n readonly tabletop: Signal<boolean> = computed(() => this.halfOpen('horizontal'));\n\n constructor() {\n const native = this.native;\n if (!native) return;\n const stateSubscription = native.addOnStateChangeListener((state) => this.update(state));\n this.update(native.getState());\n inject(DestroyRef).onDestroy(() => {\n stateSubscription.remove();\n this.angleSubscription?.remove();\n });\n }\n\n private update(state: HingeState | undefined): void {\n this.state.set(state);\n if (state === undefined) {\n this.stopFollowingAngle();\n } else {\n this.followAngle();\n }\n }\n\n private stopFollowingAngle(): void {\n this.angleSubscription?.remove();\n this.angleSubscription = null;\n this.angleState.set(null);\n }\n\n private followAngle(): void {\n const native = this.native;\n if (!native || this.angleSubscription || !native.isAngleAvailable) return;\n this.angleSubscription = native.addOnAngleChangeListener(\n (angle) => this.angleState.set(angle),\n { minDeltaDegrees: 0.5 },\n );\n }\n\n private halfOpen(orientation: Fold['orientation']): boolean {\n const fold = this.fold();\n return (\n this.posture() === 'partially-open' &&\n fold?.isSeparating === true &&\n fold.orientation === orientation\n );\n }\n}\n"]}
|
package/dist/fonts.d.ts
CHANGED
|
@@ -54,15 +54,6 @@ export declare function registrationsFor(faces: readonly FontFace[]): Record<str
|
|
|
54
54
|
export declare function expoFonts(): NativeFonts | null;
|
|
55
55
|
export declare class FontRegistry {
|
|
56
56
|
private readonly native;
|
|
57
|
-
/**
|
|
58
|
-
* Bumped whenever faces are registered.
|
|
59
|
-
*
|
|
60
|
-
* The platform's own list is a plain array that changes underneath us, so a template binding to
|
|
61
|
-
* it would render once and never again - and the one moment it changes is exactly the moment a
|
|
62
|
-
* screen waiting on a font wants to hear about. This makes both readers reactive without
|
|
63
|
-
* mirroring the list.
|
|
64
|
-
*/
|
|
65
|
-
private readonly generation;
|
|
66
57
|
constructor(native: NativeFonts | null);
|
|
67
58
|
/** Whether `expo-font` is installed at all. Without it a custom face simply never appears. */
|
|
68
59
|
get available(): boolean;
|
|
@@ -88,5 +79,8 @@ export type Fonts = FontRegistry;
|
|
|
88
79
|
* Register every face the given sheets declare, before the app mounts.
|
|
89
80
|
*
|
|
90
81
|
* A function rather than a service because that is when it is needed: there is no injector yet.
|
|
82
|
+
*
|
|
83
|
+
* A missing `expo-font` rejects the promise rather than throwing, so a caller's `.catch` can mount
|
|
84
|
+
* the app in the fallback face. Sheets that declare no face resolve without reaching for it.
|
|
91
85
|
*/
|
|
92
86
|
export declare function loadFonts(...sheets: readonly (SheetWithFonts | null | undefined)[]): Promise<void>;
|
package/dist/fonts.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* here would be copying out its internals rather than using it.
|
|
22
22
|
*/
|
|
23
23
|
import { InjectionToken, computed, signal } from '@angular/core';
|
|
24
|
-
import { faceName, fontsRegistered } from '@ng-native/fabric';
|
|
24
|
+
import { faceName, fontsLoading, fontsRegistered, fontsSettled, onFontsRegistered, } from '@ng-native/fabric';
|
|
25
25
|
import { expoModule } from "./native.js";
|
|
26
26
|
/**
|
|
27
27
|
* Loading is by family name, which is also all native matches on.
|
|
@@ -58,18 +58,20 @@ export function expoFonts() {
|
|
|
58
58
|
getLoadedFonts: () => expo.getLoadedFonts(),
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Bumped whenever faces are registered, by any registry.
|
|
63
|
+
*
|
|
64
|
+
* The platform's own list is a plain array that changes underneath us, so a template binding to
|
|
65
|
+
* it would render once and never again - and the one moment it changes is exactly the moment a
|
|
66
|
+
* screen waiting on a font wants to hear about. This makes both readers reactive without
|
|
67
|
+
* mirroring the list. Shared by every registry, because `loadFonts()` registers through its own
|
|
68
|
+
* and a screen reads through the injected one.
|
|
69
|
+
*/
|
|
70
|
+
const generation = signal(0, /* @ts-ignore */
|
|
71
|
+
...(ngDevMode ? [{ debugName: "generation" }] : /* istanbul ignore next */ []));
|
|
72
|
+
onFontsRegistered(() => generation.update((n) => n + 1));
|
|
61
73
|
export class FontRegistry {
|
|
62
74
|
native;
|
|
63
|
-
/**
|
|
64
|
-
* Bumped whenever faces are registered.
|
|
65
|
-
*
|
|
66
|
-
* The platform's own list is a plain array that changes underneath us, so a template binding to
|
|
67
|
-
* it would render once and never again - and the one moment it changes is exactly the moment a
|
|
68
|
-
* screen waiting on a font wants to hear about. This makes both readers reactive without
|
|
69
|
-
* mirroring the list.
|
|
70
|
-
*/
|
|
71
|
-
generation = signal(0, /* @ts-ignore */
|
|
72
|
-
...(ngDevMode ? [{ debugName: "generation" }] : /* istanbul ignore next */ []));
|
|
73
75
|
constructor(native) {
|
|
74
76
|
this.native = native;
|
|
75
77
|
}
|
|
@@ -79,13 +81,13 @@ export class FontRegistry {
|
|
|
79
81
|
}
|
|
80
82
|
/** Every family the platform can currently find, custom or bundled. */
|
|
81
83
|
families = computed(() => {
|
|
82
|
-
|
|
84
|
+
generation();
|
|
83
85
|
return this.native?.getLoadedFonts() ?? [];
|
|
84
86
|
}, /* @ts-ignore */
|
|
85
87
|
...(ngDevMode ? [{ debugName: "families" }] : /* istanbul ignore next */ []));
|
|
86
88
|
/** Whether a family is registered. Reactive: re-read after a `load` that adds one. */
|
|
87
89
|
has(family) {
|
|
88
|
-
|
|
90
|
+
generation();
|
|
89
91
|
return this.native?.isLoaded(family) ?? false;
|
|
90
92
|
}
|
|
91
93
|
/**
|
|
@@ -106,15 +108,18 @@ export class FontRegistry {
|
|
|
106
108
|
const native = this.native;
|
|
107
109
|
if (!native)
|
|
108
110
|
return;
|
|
111
|
+
// Before the first await, so text the app lays out meanwhile waits for the face by name.
|
|
112
|
+
const families = Object.keys(map);
|
|
113
|
+
fontsLoading(families);
|
|
109
114
|
try {
|
|
110
115
|
await native.loadAsync(map);
|
|
111
116
|
}
|
|
112
117
|
finally {
|
|
118
|
+
fontsSettled(families);
|
|
113
119
|
// Text already laid out in the fallback face keeps it until it is laid out again. Only the
|
|
114
120
|
// faces that registered: one failing fails the whole load, not the others in it.
|
|
115
121
|
fontsRegistered(Object.keys(map).filter((family) => native.isLoaded(family)));
|
|
116
122
|
}
|
|
117
|
-
this.generation.update((n) => n + 1);
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
/** Injected where an app wants to ask what is loaded. */
|
|
@@ -125,8 +130,13 @@ export const Fonts = new InjectionToken('angular-native.fonts', {
|
|
|
125
130
|
* Register every face the given sheets declare, before the app mounts.
|
|
126
131
|
*
|
|
127
132
|
* A function rather than a service because that is when it is needed: there is no injector yet.
|
|
133
|
+
*
|
|
134
|
+
* A missing `expo-font` rejects the promise rather than throwing, so a caller's `.catch` can mount
|
|
135
|
+
* the app in the fallback face. Sheets that declare no face resolve without reaching for it.
|
|
128
136
|
*/
|
|
129
|
-
export function loadFonts(...sheets) {
|
|
130
|
-
|
|
137
|
+
export async function loadFonts(...sheets) {
|
|
138
|
+
if (!sheets.some((sheet) => sheet?.fonts?.length))
|
|
139
|
+
return;
|
|
140
|
+
await new FontRegistry(expoFonts()).loadSheet(...sheets);
|
|
131
141
|
}
|
|
132
142
|
//# sourceMappingURL=fonts.js.map
|
package/dist/fonts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Fonts`, bound to `expo-font`.\n *\n * A font on a phone is not fetched when the text engine first needs it. It has to be registered\n * with the platform up front, or the first paint is in the fallback face and reflows when the\n * real one lands - which is why the interesting call here is the one an app makes *before* it\n * mounts:\n *\n * ```ts\n * import { loadFonts } from '@ng-native/expo/fonts';\n *\n * await loadFonts(styleSheetOf(GlobalStyles));\n * mount(rootTag, App, getFabricUIManager(), { globalStyles: styleSheetOf(GlobalStyles) });\n * ```\n *\n * The class takes its platform rather than injecting it, because that call happens before there\n * is an injector to inject from. `inject(Fonts)` is for a screen that wants to ask what loaded.\n *\n * Unlike the other services here this goes through `expo-font`'s JavaScript rather than its\n * native module: the module takes descriptors the JS builds out of an asset, and rebuilding them\n * here would be copying out its internals rather than using it.\n */\nimport { InjectionToken, computed, signal, type Signal } from '@angular/core';\nimport { faceName, fontsRegistered } from '@ng-native/fabric';\nimport { expoModule } from './native.ts';\n\n/** A face a stylesheet declared, as the compiler collected it. */\nexport interface FontFace {\n readonly family: string;\n /** What `require('./Inter.ttf')` returned: an asset id, or a module the bundler resolved. */\n readonly source: unknown;\n readonly weight?: number;\n readonly style?: string;\n}\n\n/** Just enough of a compiled stylesheet to find the faces in it. */\nexport interface SheetWithFonts {\n readonly fonts?: readonly FontFace[];\n}\n\n/** The slice of `expo-font` this needs. */\nexport interface NativeFonts {\n loadAsync(map: Record<string, unknown>): Promise<void>;\n isLoaded(family: string): boolean;\n getLoadedFonts(): string[];\n}\n\n/**\n * Loading is by family name, which is also all native matches on.\n *\n * There is no weight matching on a device: `font-family: Inter` finds the face registered under\n * exactly that name, and a bold cut is a family of its own. A face that declares a weight or a\n * style is therefore registered under a composed name as well (`Inter-700`, `Inter-italic`,\n * `Inter-700-italic`), which is the name the engine points a matching text at.\n *\n * A face with both a weight and a style also answers to each alone, as it did before it had a\n * name of its own, but only where no face declared exactly that.\n */\nexport function registrationsFor(faces: readonly FontFace[]): Record<string, unknown> {\n const map: Record<string, unknown> = {};\n for (const face of faces) {\n map[face.family] ??= face.source;\n map[faceName(face)] = face.source;\n }\n for (const face of faces) {\n if (face.weight === undefined || !face.style) continue;\n map[`${face.family}-${face.weight}`] ??= face.source;\n map[`${face.family}-${face.style}`] ??= face.source;\n }\n return map;\n}\n\nexport function expoFonts(): NativeFonts | null {\n const expo = expoModule('expo-font', () => require('expo-font') as typeof import('expo-font'));\n if (!expo) return null;\n return {\n loadAsync: (map) => expo.loadAsync(map as Parameters<typeof expo.loadAsync>[0]),\n isLoaded: (family) => expo.isLoaded(family),\n getLoadedFonts: () => expo.getLoadedFonts(),\n };\n}\n\nexport class FontRegistry {\n private readonly native: NativeFonts | null;\n /**\n * Bumped whenever faces are registered.\n *\n * The platform's own list is a plain array that changes underneath us, so a template binding to\n * it would render once and never again - and the one moment it changes is exactly the moment a\n * screen waiting on a font wants to hear about. This makes both readers reactive without\n * mirroring the list.\n */\n private readonly generation = signal(0);\n\n constructor(native: NativeFonts | null) {\n this.native = native;\n }\n\n /** Whether `expo-font` is installed at all. Without it a custom face simply never appears. */\n get available(): boolean {\n return this.native !== null;\n }\n\n /** Every family the platform can currently find, custom or bundled. */\n readonly families: Signal<readonly string[]> = computed(() => {\n this.generation();\n return this.native?.getLoadedFonts() ?? [];\n });\n\n /** Whether a family is registered. Reactive: re-read after a `load` that adds one. */\n has(family: string): boolean {\n this.generation();\n return this.native?.isLoaded(family) ?? false;\n }\n\n /**\n * Register every `@font-face` a compiled sheet declares.\n *\n * Called before `mount` in an app that uses one. Text laid out before a face registers is in\n * the fallback face until it does, and is laid out again then. Sheets with no faces resolve\n * immediately, so calling it unconditionally at bootstrap costs nothing.\n */\n async loadSheet(...sheets: readonly (SheetWithFonts | null | undefined)[]): Promise<void> {\n const faces = sheets.flatMap((sheet) => [...(sheet?.fonts ?? [])]);\n if (!faces.length) return;\n await this.load(registrationsFor(faces));\n }\n\n /** Register faces by name, for a font that did not come from a stylesheet. */\n async load(map: Record<string, unknown>): Promise<void> {\n const native = this.native;\n if (!native) return;\n try {\n await native.loadAsync(map);\n } finally {\n // Text already laid out in the fallback face keeps it until it is laid out again. Only the\n // faces that registered: one failing fails the whole load, not the others in it.\n fontsRegistered(Object.keys(map).filter((family) => native.isLoaded(family)));\n }\n this.generation.update((n) => n + 1);\n }\n}\n\n/** Injected where an app wants to ask what is loaded. */\nexport const Fonts = new InjectionToken<FontRegistry>('angular-native.fonts', {\n factory: () => new FontRegistry(expoFonts()),\n});\nexport type Fonts = FontRegistry;\n\n/**\n * Register every face the given sheets declare, before the app mounts.\n *\n * A function rather than a service because that is when it is needed: there is no injector yet.\n */\nexport function loadFonts(\n ...sheets: readonly (SheetWithFonts | null | undefined)[]\n): Promise<void> {\n return new FontRegistry(expoFonts()).loadSheet(...sheets);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Fonts`, bound to `expo-font`.\n *\n * A font on a phone is not fetched when the text engine first needs it. It has to be registered\n * with the platform up front, or the first paint is in the fallback face and reflows when the\n * real one lands - which is why the interesting call here is the one an app makes *before* it\n * mounts:\n *\n * ```ts\n * import { loadFonts } from '@ng-native/expo/fonts';\n *\n * await loadFonts(styleSheetOf(GlobalStyles));\n * mount(rootTag, App, getFabricUIManager(), { globalStyles: styleSheetOf(GlobalStyles) });\n * ```\n *\n * The class takes its platform rather than injecting it, because that call happens before there\n * is an injector to inject from. `inject(Fonts)` is for a screen that wants to ask what loaded.\n *\n * Unlike the other services here this goes through `expo-font`'s JavaScript rather than its\n * native module: the module takes descriptors the JS builds out of an asset, and rebuilding them\n * here would be copying out its internals rather than using it.\n */\nimport { InjectionToken, computed, signal, type Signal } from '@angular/core';\nimport {\n faceName,\n fontsLoading,\n fontsRegistered,\n fontsSettled,\n onFontsRegistered,\n} from '@ng-native/fabric';\nimport { expoModule } from './native.ts';\n\n/** A face a stylesheet declared, as the compiler collected it. */\nexport interface FontFace {\n readonly family: string;\n /** What `require('./Inter.ttf')` returned: an asset id, or a module the bundler resolved. */\n readonly source: unknown;\n readonly weight?: number;\n readonly style?: string;\n}\n\n/** Just enough of a compiled stylesheet to find the faces in it. */\nexport interface SheetWithFonts {\n readonly fonts?: readonly FontFace[];\n}\n\n/** The slice of `expo-font` this needs. */\nexport interface NativeFonts {\n loadAsync(map: Record<string, unknown>): Promise<void>;\n isLoaded(family: string): boolean;\n getLoadedFonts(): string[];\n}\n\n/**\n * Loading is by family name, which is also all native matches on.\n *\n * There is no weight matching on a device: `font-family: Inter` finds the face registered under\n * exactly that name, and a bold cut is a family of its own. A face that declares a weight or a\n * style is therefore registered under a composed name as well (`Inter-700`, `Inter-italic`,\n * `Inter-700-italic`), which is the name the engine points a matching text at.\n *\n * A face with both a weight and a style also answers to each alone, as it did before it had a\n * name of its own, but only where no face declared exactly that.\n */\nexport function registrationsFor(faces: readonly FontFace[]): Record<string, unknown> {\n const map: Record<string, unknown> = {};\n for (const face of faces) {\n map[face.family] ??= face.source;\n map[faceName(face)] = face.source;\n }\n for (const face of faces) {\n if (face.weight === undefined || !face.style) continue;\n map[`${face.family}-${face.weight}`] ??= face.source;\n map[`${face.family}-${face.style}`] ??= face.source;\n }\n return map;\n}\n\nexport function expoFonts(): NativeFonts | null {\n const expo = expoModule('expo-font', () => require('expo-font') as typeof import('expo-font'));\n if (!expo) return null;\n return {\n loadAsync: (map) => expo.loadAsync(map as Parameters<typeof expo.loadAsync>[0]),\n isLoaded: (family) => expo.isLoaded(family),\n getLoadedFonts: () => expo.getLoadedFonts(),\n };\n}\n\n/**\n * Bumped whenever faces are registered, by any registry.\n *\n * The platform's own list is a plain array that changes underneath us, so a template binding to\n * it would render once and never again - and the one moment it changes is exactly the moment a\n * screen waiting on a font wants to hear about. This makes both readers reactive without\n * mirroring the list. Shared by every registry, because `loadFonts()` registers through its own\n * and a screen reads through the injected one.\n */\nconst generation = signal(0);\nonFontsRegistered(() => generation.update((n) => n + 1));\n\nexport class FontRegistry {\n private readonly native: NativeFonts | null;\n\n constructor(native: NativeFonts | null) {\n this.native = native;\n }\n\n /** Whether `expo-font` is installed at all. Without it a custom face simply never appears. */\n get available(): boolean {\n return this.native !== null;\n }\n\n /** Every family the platform can currently find, custom or bundled. */\n readonly families: Signal<readonly string[]> = computed(() => {\n generation();\n return this.native?.getLoadedFonts() ?? [];\n });\n\n /** Whether a family is registered. Reactive: re-read after a `load` that adds one. */\n has(family: string): boolean {\n generation();\n return this.native?.isLoaded(family) ?? false;\n }\n\n /**\n * Register every `@font-face` a compiled sheet declares.\n *\n * Called before `mount` in an app that uses one. Text laid out before a face registers is in\n * the fallback face until it does, and is laid out again then. Sheets with no faces resolve\n * immediately, so calling it unconditionally at bootstrap costs nothing.\n */\n async loadSheet(...sheets: readonly (SheetWithFonts | null | undefined)[]): Promise<void> {\n const faces = sheets.flatMap((sheet) => [...(sheet?.fonts ?? [])]);\n if (!faces.length) return;\n await this.load(registrationsFor(faces));\n }\n\n /** Register faces by name, for a font that did not come from a stylesheet. */\n async load(map: Record<string, unknown>): Promise<void> {\n const native = this.native;\n if (!native) return;\n // Before the first await, so text the app lays out meanwhile waits for the face by name.\n const families = Object.keys(map);\n fontsLoading(families);\n try {\n await native.loadAsync(map);\n } finally {\n fontsSettled(families);\n // Text already laid out in the fallback face keeps it until it is laid out again. Only the\n // faces that registered: one failing fails the whole load, not the others in it.\n fontsRegistered(Object.keys(map).filter((family) => native.isLoaded(family)));\n }\n }\n}\n\n/** Injected where an app wants to ask what is loaded. */\nexport const Fonts = new InjectionToken<FontRegistry>('angular-native.fonts', {\n factory: () => new FontRegistry(expoFonts()),\n});\nexport type Fonts = FontRegistry;\n\n/**\n * Register every face the given sheets declare, before the app mounts.\n *\n * A function rather than a service because that is when it is needed: there is no injector yet.\n *\n * A missing `expo-font` rejects the promise rather than throwing, so a caller's `.catch` can mount\n * the app in the fallback face. Sheets that declare no face resolve without reaching for it.\n */\nexport async function loadFonts(\n ...sheets: readonly (SheetWithFonts | null | undefined)[]\n): Promise<void> {\n if (!sheets.some((sheet) => sheet?.fonts?.length)) return;\n await new FontRegistry(expoFonts()).loadSheet(...sheets);\n}\n"]}
|
package/dist/glass.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare class ExpoGlass {
|
|
|
15
15
|
/** Reacts to touch, as a control made of glass does. */
|
|
16
16
|
readonly isInteractive: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
17
17
|
/** The appearance the glass takes, when it should not follow the app's. */
|
|
18
|
-
readonly colorScheme: import("@angular/core").InputSignal<"
|
|
18
|
+
readonly colorScheme: import("@angular/core").InputSignal<"dark" | "light" | "auto" | undefined>;
|
|
19
19
|
static ɵfac: i0.ɵɵFactoryDeclaration<ExpoGlass, never>;
|
|
20
20
|
static ɵcmp: i0.ɵɵComponentDeclaration<ExpoGlass, "expo-glass", never, { "glassEffectStyle": { "alias": "glassEffectStyle"; "required": false; "isSignal": true; }; "tintColor": { "alias": "tintColor"; "required": false; "isSignal": true; }; "isInteractive": { "alias": "isInteractive"; "required": false; "isSignal": true; }; "colorScheme": { "alias": "colorScheme"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
21
21
|
}
|
package/dist/image-picker.js
CHANGED
|
@@ -12,14 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { InjectionToken, Service, inject } from '@angular/core';
|
|
14
14
|
import { expoModule } from "./native.js";
|
|
15
|
-
import { Permission } from "./permissions.js";
|
|
15
|
+
import { Permission, UNAVAILABLE } from "./permissions.js";
|
|
16
16
|
import * as i0 from "@angular/core";
|
|
17
|
-
/** What a permission is without the module: refused, and never to be asked about. */
|
|
18
|
-
const UNAVAILABLE = async () => ({
|
|
19
|
-
status: 'denied',
|
|
20
|
-
granted: false,
|
|
21
|
-
canAskAgain: false,
|
|
22
|
-
});
|
|
23
17
|
export class ImagePicker {
|
|
24
18
|
/** Overridden in a test to pick photos without a photo library. */
|
|
25
19
|
static SOURCE = new InjectionToken('angular-native.imagePickerSource', {
|
|
@@ -31,9 +25,9 @@ export class ImagePicker {
|
|
|
31
25
|
* The photo library. The system picker needs no permission to pick from, so `pick()` never
|
|
32
26
|
* asks; this is for an app that reads the library itself.
|
|
33
27
|
*/
|
|
34
|
-
libraryPermission = Permission.of(this.native?.getMediaLibraryPermissionsAsync ?? UNAVAILABLE, this.native?.requestMediaLibraryPermissionsAsync ?? UNAVAILABLE);
|
|
28
|
+
libraryPermission = Permission.of(() => this.native?.getMediaLibraryPermissionsAsync?.() ?? Promise.resolve(UNAVAILABLE), () => this.native?.requestMediaLibraryPermissionsAsync?.() ?? Promise.resolve(UNAVAILABLE));
|
|
35
29
|
/** The camera, which `capture()` asks for itself. */
|
|
36
|
-
cameraPermission = Permission.of(this.native?.getCameraPermissionsAsync ?? UNAVAILABLE, this.native?.requestCameraPermissionsAsync ?? UNAVAILABLE);
|
|
30
|
+
cameraPermission = Permission.of(() => this.native?.getCameraPermissionsAsync?.() ?? Promise.resolve(UNAVAILABLE), () => this.native?.requestCameraPermissionsAsync?.() ?? Promise.resolve(UNAVAILABLE));
|
|
37
31
|
/** Pick from the photo library. Empty if they cancelled. */
|
|
38
32
|
async pick(options) {
|
|
39
33
|
if (!this.native)
|
package/dist/image-picker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-picker.js","sourceRoot":"","sources":["../src/image-picker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAA2B,MAAM,kBAAkB,CAAC;;
|
|
1
|
+
{"version":3,"file":"image-picker.js","sourceRoot":"","sources":["../src/image-picker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,WAAW,EAA2B,MAAM,kBAAkB,CAAC;;AAqBpF,MAAM,OAAO,WAAW;IACtB,mEAAmE;IACnE,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,kCAAkC,EAClC;QACE,OAAO,EAAE,GAAG,EAAE,CACZ,UAAU,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAsB,CAAC;YACxF,IAAI;KACP,CACF,CAAC;IAEe,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAErD;;;OAGG;IACM,iBAAiB,GAAG,UAAU,CAAC,EAAE,CACxC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,+BAA+B,EAAE,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,EACtF,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,mCAAmC,EAAE,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAC3F,CAAC;IAEF,qDAAqD;IAC5C,gBAAgB,GAAG,UAAU,CAAC,EAAE,CACvC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,yBAAyB,EAAE,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,EAChF,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,6BAA6B,EAAE,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CACrF,CAAC;IAEF,4DAA4D;IAC5D,KAAK,CAAC,IAAI,CAAC,OAAuB;QAChC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,CAAC;QAC5B,OAAO,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,2FAA2F;IAC3F,KAAK,CAAC,OAAO,CAAC,OAAuB;QACnC,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC;YAAE,OAAO,EAAE,CAAC;QACvE,OAAO,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;IAChE,CAAC;uGAtCU,WAAW;wGAAX,WAAW;;2FAAX,WAAW;kBADvB,OAAO;;AA0CR,SAAS,QAAQ,CAAC,MAAoB;IACpC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC","sourcesContent":["/**\n * `ImagePicker`, bound to `expo-image-picker`.\n *\n * ```ts\n * private readonly picker = inject(ImagePicker);\n * const [photo] = await this.picker.pick({ mediaTypes: ['images'] });\n * ```\n *\n * The options are Expo's own, passed through unchanged. What this adds is the two things every\n * call site otherwise writes: a cancelled picker answers with no assets rather than a result to\n * unwrap, and `capture()` asks for the camera before opening it, where Expo's would reject.\n */\nimport { InjectionToken, Service, inject } from '@angular/core';\nimport { expoModule } from './native.ts';\nimport { Permission, UNAVAILABLE, type PermissionResponse } from './permissions.ts';\n\nexport type PickerOptions = import('expo-image-picker').ImagePickerOptions;\nexport type PickedAsset = import('expo-image-picker').ImagePickerAsset;\n\n/** The slice of `expo-image-picker` this needs. */\nexport interface NativeImagePicker {\n launchImageLibraryAsync(options?: PickerOptions): Promise<PickerResult>;\n launchCameraAsync(options?: PickerOptions): Promise<PickerResult>;\n getMediaLibraryPermissionsAsync(): Promise<PermissionResponse>;\n requestMediaLibraryPermissionsAsync(): Promise<PermissionResponse>;\n getCameraPermissionsAsync(): Promise<PermissionResponse>;\n requestCameraPermissionsAsync(): Promise<PermissionResponse>;\n}\n\ninterface PickerResult {\n readonly canceled: boolean;\n readonly assets: readonly PickedAsset[] | null;\n}\n\n@Service()\nexport class ImagePicker {\n /** Overridden in a test to pick photos without a photo library. */\n static readonly SOURCE = new InjectionToken<NativeImagePicker | null>(\n 'angular-native.imagePickerSource',\n {\n factory: () =>\n expoModule('expo-image-picker', () => require('expo-image-picker') as NativeImagePicker) ??\n null,\n },\n );\n\n private readonly native = inject(ImagePicker.SOURCE);\n\n /**\n * The photo library. The system picker needs no permission to pick from, so `pick()` never\n * asks; this is for an app that reads the library itself.\n */\n readonly libraryPermission = Permission.of(\n () => this.native?.getMediaLibraryPermissionsAsync?.() ?? Promise.resolve(UNAVAILABLE),\n () => this.native?.requestMediaLibraryPermissionsAsync?.() ?? Promise.resolve(UNAVAILABLE),\n );\n\n /** The camera, which `capture()` asks for itself. */\n readonly cameraPermission = Permission.of(\n () => this.native?.getCameraPermissionsAsync?.() ?? Promise.resolve(UNAVAILABLE),\n () => this.native?.requestCameraPermissionsAsync?.() ?? Promise.resolve(UNAVAILABLE),\n );\n\n /** Pick from the photo library. Empty if they cancelled. */\n async pick(options?: PickerOptions): Promise<readonly PickedAsset[]> {\n if (!this.native) return [];\n return assetsOf(await this.native.launchImageLibraryAsync(options));\n }\n\n /** Take a photo or video with the system camera. Empty if they cancelled or refused it. */\n async capture(options?: PickerOptions): Promise<readonly PickedAsset[]> {\n if (!this.native || !(await this.cameraPermission.ensure())) return [];\n return assetsOf(await this.native.launchCameraAsync(options));\n }\n}\n\nfunction assetsOf(result: PickerResult): readonly PickedAsset[] {\n return result.canceled ? [] : (result.assets ?? []);\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export { observed, type Observed } from './observed.ts';
|
|
10
10
|
export { Permission, type PermissionApi, type PermissionResponse } from './permissions.ts';
|
|
11
|
-
export { MissingModuleError, expoModule, optional, type ModulePlatform } from './native.ts';
|
|
11
|
+
export { MissingModuleError, expoModule, optional, unavailable, type ModulePlatform, } from './native.ts';
|
|
12
12
|
export { NATIVE_VIEWS, registerNativeViews } from './community-views.ts';
|
|
13
13
|
export { registerExpoUiViews } from './expo-ui.ts';
|
|
14
14
|
export { nativeState, type NativeState } from './native-state.ts';
|
|
15
15
|
export { EXPO_VIEWS, expoViewName, registerExpoView, registerExpoViews, type ExpoViewOptions, } from './register-expo-view.ts';
|
|
16
|
-
export { UiButton, UiDatePicker, UiDivider, UiHost, UiImage, UiMenu, UiPicker, UiColorPicker, type UiColorChangeEvent, UiForm, UiGauge, UiHStack, UiLabeledContent, UiList, UiProgress, UiSection, UiSpacer, UiStepper, type UiStepperChangeEvent, UiTextField, type UiTextFieldChangeEvent, UiToggle, type UiToggleChangeEvent, UiSlider, type UiSliderChangeEvent, UiSlot, UiSwipeActions, UiText, UiVStack, type UiDateChangeEvent, type UiPickerOption, type UiModifier, } from './expo-ui-components.ts';
|
|
16
|
+
export { UiAccessoryWidgetBackground, UiButton, UiCapsule, UiCircle, UiEllipse, UiLabel, UiLink, UiRectangle, UiRoundedRectangle, UiUnevenRoundedRectangle, UiZStack, type UiZStackAlignment, UiDatePicker, UiDivider, UiHost, UiImage, UiMenu, UiPicker, UiColorPicker, type UiColorChangeEvent, UiForm, UiChart, type UiChartAreaStyle, type UiChartBarStyle, type UiChartDataPoint, type UiChartLineStyle, type UiChartPieStyle, type UiChartPointStyle, type UiChartPointSymbol, type UiChartRectangleStyle, type UiChartRuleStyle, type UiChartType, UiGauge, UiHStack, UiLabeledContent, UiList, UiProgress, UiSection, UiSpacer, UiStepper, type UiStepperChangeEvent, UiTextField, type UiTextFieldChangeEvent, UiToggle, type UiToggleChangeEvent, UiSlider, type UiSliderChangeEvent, UiSlot, UiSwipeActions, UiText, UiVStack, type UiDateChangeEvent, type UiPickerOption, type UiModifier, } from './expo-ui-components.ts';
|
|
17
17
|
export { ExpoImage, type ExpoImageContentFit, type ExpoImageSource } from './expo-image.ts';
|
|
18
18
|
export { ExpoGlass, ExpoGlassContainer, liquidGlassAvailable, type GlassEffectStyleConfig, type GlassStyle, } from './glass.ts';
|
|
19
19
|
export { ExpoSymbol, type SymbolAnimationSpec, type SymbolResizeMode, type SymbolScale, type SymbolType, type SymbolWeight, } from './symbol.ts';
|
|
20
|
+
export { DateTimePicker, type DateTimePickerChangeEvent } from './date-time-picker.ts';
|
|
20
21
|
export { SegmentedControl, type SegmentedControlChangeEvent, type SegmentedControlFont, } from './segmented-control.ts';
|
package/dist/index.js
CHANGED
|
@@ -8,14 +8,15 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export { observed } from "./observed.js";
|
|
10
10
|
export { Permission } from "./permissions.js";
|
|
11
|
-
export { MissingModuleError, expoModule, optional } from "./native.js";
|
|
11
|
+
export { MissingModuleError, expoModule, optional, unavailable, } from "./native.js";
|
|
12
12
|
export { NATIVE_VIEWS, registerNativeViews } from "./community-views.js";
|
|
13
13
|
export { registerExpoUiViews } from "./expo-ui.js";
|
|
14
14
|
export { nativeState } from "./native-state.js";
|
|
15
15
|
export { EXPO_VIEWS, expoViewName, registerExpoView, registerExpoViews, } from "./register-expo-view.js";
|
|
16
|
-
export { UiButton, UiDatePicker, UiDivider, UiHost, UiImage, UiMenu, UiPicker, UiColorPicker, UiForm, UiGauge, UiHStack, UiLabeledContent, UiList, UiProgress, UiSection, UiSpacer, UiStepper, UiTextField, UiToggle, UiSlider, UiSlot, UiSwipeActions, UiText, UiVStack, } from "./expo-ui-components.js";
|
|
16
|
+
export { UiAccessoryWidgetBackground, UiButton, UiCapsule, UiCircle, UiEllipse, UiLabel, UiLink, UiRectangle, UiRoundedRectangle, UiUnevenRoundedRectangle, UiZStack, UiDatePicker, UiDivider, UiHost, UiImage, UiMenu, UiPicker, UiColorPicker, UiForm, UiChart, UiGauge, UiHStack, UiLabeledContent, UiList, UiProgress, UiSection, UiSpacer, UiStepper, UiTextField, UiToggle, UiSlider, UiSlot, UiSwipeActions, UiText, UiVStack, } from "./expo-ui-components.js";
|
|
17
17
|
export { ExpoImage } from "./expo-image.js";
|
|
18
18
|
export { ExpoGlass, ExpoGlassContainer, liquidGlassAvailable, } from "./glass.js";
|
|
19
19
|
export { ExpoSymbol, } from "./symbol.js";
|
|
20
|
+
export { DateTimePicker } from "./date-time-picker.js";
|
|
20
21
|
export { SegmentedControl, } from "./segmented-control.js";
|
|
21
22
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAiB,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,UAAU,EAA+C,MAAM,kBAAkB,CAAC;AAC3F,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAiB,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,UAAU,EAA+C,MAAM,kBAAkB,CAAC;AAC3F,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,QAAQ,EACR,WAAW,GAEZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAClE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAElB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,2BAA2B,EAC3B,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,SAAS,EACT,OAAO,EACP,MAAM,EACN,WAAW,EACX,kBAAkB,EAClB,wBAAwB,EACxB,QAAQ,EAER,YAAY,EACZ,SAAS,EACT,MAAM,EACN,OAAO,EACP,MAAM,EACN,QAAQ,EACR,aAAa,EAEb,MAAM,EACN,OAAO,EAWP,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,MAAM,EACN,UAAU,EACV,SAAS,EACT,QAAQ,EACR,SAAS,EAET,WAAW,EAEX,QAAQ,EAER,QAAQ,EAER,MAAM,EACN,cAAc,EACd,MAAM,EACN,QAAQ,GAIT,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAkD,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GAGrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,UAAU,GAMX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAkC,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,gBAAgB,GAGjB,MAAM,wBAAwB,CAAC","sourcesContent":["/**\n * What the package exports without naming a module.\n *\n * Everything with an Expo module behind it has its own entry point - `@ng-native/expo/\n * battery.ts` and friends - so that importing haptics does not make an app install the video\n * player. What is left here is the shared machinery: the permission shape every module spells\n * identically, the signal-from-a-listener those services are built out of, and the view names.\n */\nexport { observed, type Observed } from './observed.ts';\nexport { Permission, type PermissionApi, type PermissionResponse } from './permissions.ts';\nexport {\n MissingModuleError,\n expoModule,\n optional,\n unavailable,\n type ModulePlatform,\n} from './native.ts';\nexport { NATIVE_VIEWS, registerNativeViews } from './community-views.ts';\nexport { registerExpoUiViews } from './expo-ui.ts';\nexport { nativeState, type NativeState } from './native-state.ts';\nexport {\n EXPO_VIEWS,\n expoViewName,\n registerExpoView,\n registerExpoViews,\n type ExpoViewOptions,\n} from './register-expo-view.ts';\nexport {\n UiAccessoryWidgetBackground,\n UiButton,\n UiCapsule,\n UiCircle,\n UiEllipse,\n UiLabel,\n UiLink,\n UiRectangle,\n UiRoundedRectangle,\n UiUnevenRoundedRectangle,\n UiZStack,\n type UiZStackAlignment,\n UiDatePicker,\n UiDivider,\n UiHost,\n UiImage,\n UiMenu,\n UiPicker,\n UiColorPicker,\n type UiColorChangeEvent,\n UiForm,\n UiChart,\n type UiChartAreaStyle,\n type UiChartBarStyle,\n type UiChartDataPoint,\n type UiChartLineStyle,\n type UiChartPieStyle,\n type UiChartPointStyle,\n type UiChartPointSymbol,\n type UiChartRectangleStyle,\n type UiChartRuleStyle,\n type UiChartType,\n UiGauge,\n UiHStack,\n UiLabeledContent,\n UiList,\n UiProgress,\n UiSection,\n UiSpacer,\n UiStepper,\n type UiStepperChangeEvent,\n UiTextField,\n type UiTextFieldChangeEvent,\n UiToggle,\n type UiToggleChangeEvent,\n UiSlider,\n type UiSliderChangeEvent,\n UiSlot,\n UiSwipeActions,\n UiText,\n UiVStack,\n type UiDateChangeEvent,\n type UiPickerOption,\n type UiModifier,\n} from './expo-ui-components.ts';\nexport { ExpoImage, type ExpoImageContentFit, type ExpoImageSource } from './expo-image.ts';\nexport {\n ExpoGlass,\n ExpoGlassContainer,\n liquidGlassAvailable,\n type GlassEffectStyleConfig,\n type GlassStyle,\n} from './glass.ts';\nexport {\n ExpoSymbol,\n type SymbolAnimationSpec,\n type SymbolResizeMode,\n type SymbolScale,\n type SymbolType,\n type SymbolWeight,\n} from './symbol.ts';\nexport { DateTimePicker, type DateTimePickerChangeEvent } from './date-time-picker.ts';\nexport {\n SegmentedControl,\n type SegmentedControlChangeEvent,\n type SegmentedControlFont,\n} from './segmented-control.ts';\n"]}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* iOS Live Activities and home-screen widgets, through `expo-widgets`: `createLiveActivity` and
|
|
3
|
+
* `createWidget` draw one from an Angular component's template, and `liveActivity` keeps an
|
|
4
|
+
* activity in step with a signal, on the lock screen and in the Dynamic Island. Off iOS,
|
|
5
|
+
* `expo-widgets` starts a stand-in with no id, and `start()` answers false.
|
|
6
|
+
*/
|
|
7
|
+
import { type InputSignal, type Signal, type Type } from '@angular/core';
|
|
8
|
+
/** A widget layout: an Angular component whose `props` input the layout draws from. */
|
|
9
|
+
export type WidgetLayout<T extends object> = Type<{
|
|
10
|
+
readonly props: InputSignal<T>;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* A Live Activity drawn by `layout`, an Angular component, as `expo-widgets`' `createLiveActivity`
|
|
14
|
+
* makes one. `name` matches the activity's name in the `expo-widgets` plugin's config.
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* export const scoreActivity = createLiveActivity('Score', ScoreLayout);
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* `@ng-native/metro`'s transformer compiles the layout's template, at build time, to the source the
|
|
21
|
+
* widget extension evaluates, and drops the class, so the app never runs it: the extension does,
|
|
22
|
+
* with no Angular. So the class holds only its inputs, members holding a modifier from
|
|
23
|
+
* `@expo/ui/swift-ui/modifiers`, and members holding a literal, and its template is inline.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createLiveActivity<T extends object>(name: string, layout: WidgetLayout<T>): import('expo-widgets').LiveActivityFactory<T>;
|
|
26
|
+
/**
|
|
27
|
+
* A home-screen widget drawn by `layout`, as `expo-widgets`' `createWidget` makes one. The layout
|
|
28
|
+
* reads what the widget is drawn in, such as its `widgetFamily`, from an `environment` input beside
|
|
29
|
+
* `props`. Compiled as `createLiveActivity`'s layout is.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createWidget<T extends object>(name: string, layout: WidgetLayout<T>): import('expo-widgets').Widget<T>;
|
|
32
|
+
/**
|
|
33
|
+
* How an ended activity leaves the lock screen: `'default'` keeps it, with its final props, for up
|
|
34
|
+
* to four hours; `'immediate'` removes it; `{ after }` keeps it until then.
|
|
35
|
+
*/
|
|
36
|
+
export type LiveActivityDismissal = 'default' | 'immediate' | {
|
|
37
|
+
readonly after: Date;
|
|
38
|
+
};
|
|
39
|
+
interface Subscription {
|
|
40
|
+
remove(): void;
|
|
41
|
+
}
|
|
42
|
+
/** One running activity, as `expo-widgets`' `LiveActivity` is. */
|
|
43
|
+
export interface NativeLiveActivity<T extends object> {
|
|
44
|
+
getId(): string;
|
|
45
|
+
update(props: T, staleDate?: Date): Promise<void>;
|
|
46
|
+
end(dismissalPolicy?: LiveActivityDismissal, props?: T, contentDate?: Date): Promise<void>;
|
|
47
|
+
getPushToken(): Promise<string | null>;
|
|
48
|
+
addPushTokenListener(listener: (event: {
|
|
49
|
+
pushToken: string;
|
|
50
|
+
}) => void): Subscription;
|
|
51
|
+
}
|
|
52
|
+
/** What `createLiveActivity` answers: starts activities of one kind, and lists the running ones. */
|
|
53
|
+
export interface LiveActivityFactory<T extends object> {
|
|
54
|
+
start(props: T, url?: string, staleDate?: Date): NativeLiveActivity<T>;
|
|
55
|
+
getInstances(): NativeLiveActivity<T>[];
|
|
56
|
+
}
|
|
57
|
+
/** A Live Activity kept in step with a signal. */
|
|
58
|
+
export interface LiveActivityRef {
|
|
59
|
+
/** Whether an activity of this kind is running and following the signal. */
|
|
60
|
+
readonly active: Signal<boolean>;
|
|
61
|
+
/** The running activity's id, or null. */
|
|
62
|
+
readonly id: Signal<string | null>;
|
|
63
|
+
/** The token a server sends the activity's updates to over APNs, once iOS issues one. */
|
|
64
|
+
readonly pushToken: Signal<string | null>;
|
|
65
|
+
/**
|
|
66
|
+
* Why the last start or update did not happen. A start the system refuses, as when Live
|
|
67
|
+
* Activities are turned off in Settings, is only kept here; a failed update also goes to the
|
|
68
|
+
* `ErrorHandler`.
|
|
69
|
+
*/
|
|
70
|
+
readonly error: Signal<unknown>;
|
|
71
|
+
/**
|
|
72
|
+
* Shows the activity with the signal's value, or picks up one already running. Answers whether it
|
|
73
|
+
* is running now; `url` opens the app at that link when the activity is tapped.
|
|
74
|
+
*/
|
|
75
|
+
start(options?: {
|
|
76
|
+
url?: string;
|
|
77
|
+
}): boolean;
|
|
78
|
+
/** Ends every activity of this kind with the signal's value. */
|
|
79
|
+
end(dismissal?: LiveActivityDismissal): Promise<void>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Keeps a Live Activity of the factory's kind in step with `props`: once started, every change to the
|
|
83
|
+
* signal updates it. One left running from before the app started is picked up rather than started
|
|
84
|
+
* again, since iOS caps how many an app runs. Destroying the injector stops the updates and leaves
|
|
85
|
+
* the activity running, as it outlives the app. Call it in an injection context.
|
|
86
|
+
*/
|
|
87
|
+
export declare function liveActivity<T extends object>(factory: LiveActivityFactory<T>, props: Signal<T>): LiveActivityRef;
|
|
88
|
+
export {};
|