@ng-native/router 0.0.1 → 0.1.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/LICENSE +21 -0
- package/README.md +74 -1
- package/dist/bind-route-inputs.d.ts +20 -0
- package/dist/bind-route-inputs.js +52 -0
- package/dist/bind-route-inputs.js.map +1 -0
- package/dist/full-window-overlay.d.ts +16 -0
- package/dist/full-window-overlay.js +52 -0
- package/dist/full-window-overlay.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/native-back.d.ts +38 -0
- package/dist/native-back.js +88 -0
- package/dist/native-back.js.map +1 -0
- package/dist/native-bar-defaults.d.ts +50 -0
- package/dist/native-bar-defaults.js +17 -0
- package/dist/native-bar-defaults.js.map +1 -0
- package/dist/native-header-item.d.ts +12 -0
- package/dist/native-header-item.js +40 -0
- package/dist/native-header-item.js.map +1 -0
- package/dist/native-header-palette.d.ts +45 -0
- package/dist/native-header-palette.js +42 -0
- package/dist/native-header-palette.js.map +1 -0
- package/dist/native-header.d.ts +103 -0
- package/dist/native-header.js +204 -0
- package/dist/native-header.js.map +1 -0
- package/dist/native-links.d.ts +30 -0
- package/dist/native-links.js +40 -0
- package/dist/native-links.js.map +1 -0
- package/dist/native-navigation.d.ts +84 -0
- package/dist/native-navigation.js +153 -0
- package/dist/native-navigation.js.map +1 -0
- package/dist/native-platform-location.d.ts +62 -0
- package/dist/native-platform-location.js +110 -0
- package/dist/native-platform-location.js.map +1 -0
- package/dist/native-router-link.d.ts +14 -0
- package/dist/native-router-link.js +51 -0
- package/dist/native-router-link.js.map +1 -0
- package/dist/native-search-bar.d.ts +44 -0
- package/dist/native-search-bar.js +132 -0
- package/dist/native-search-bar.js.map +1 -0
- package/dist/native-stack-outlet.d.ts +195 -0
- package/dist/native-stack-outlet.js +564 -0
- package/dist/native-stack-outlet.js.map +1 -0
- package/dist/native-stack-reuse-strategy.d.ts +57 -0
- package/dist/native-stack-reuse-strategy.js +126 -0
- package/dist/native-stack-reuse-strategy.js.map +1 -0
- package/dist/native-tab.d.ts +147 -0
- package/dist/native-tab.js +209 -0
- package/dist/native-tab.js.map +1 -0
- package/dist/native-tabs-outlet.d.ts +138 -0
- package/dist/native-tabs-outlet.js +408 -0
- package/dist/native-tabs-outlet.js.map +1 -0
- package/dist/own-host.d.ts +6 -0
- package/dist/own-host.js +28 -0
- package/dist/own-host.js.map +1 -0
- package/dist/provide-native-router.d.ts +41 -0
- package/dist/provide-native-router.js +151 -0
- package/dist/provide-native-router.js.map +1 -0
- package/dist/screen-presentation.d.ts +69 -0
- package/dist/screen-presentation.js +10 -0
- package/dist/screen-presentation.js.map +1 -0
- package/dist/screens.d.ts +37 -0
- package/dist/screens.js +108 -0
- package/dist/screens.js.map +1 -0
- package/dist/tab-routes.d.ts +16 -0
- package/dist/tab-routes.js +24 -0
- package/dist/tab-routes.js.map +1 -0
- package/dist/tab-safe-area-view.d.ts +15 -0
- package/dist/tab-safe-area-view.js +63 -0
- package/dist/tab-safe-area-view.js.map +1 -0
- package/dist/transforms.d.ts +2 -0
- package/dist/transforms.js +13 -0
- package/dist/transforms.js.map +1 -0
- package/package.json +59 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ashley Hunter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,76 @@
|
|
|
1
1
|
# @ng-native/router
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@angular/router` - the same `Router`, route config, guards, resolvers and deep links - pointed at
|
|
4
|
+
`react-native-screens` instead of the DOM: a real native stack, tabs and header, not something
|
|
5
|
+
drawn to look like one.
|
|
6
|
+
|
|
7
|
+
Alpha: APIs may change before 1.0.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
Most apps start from `npx create-expo-app@latest my-app --template @ng-native/template` and add
|
|
12
|
+
routing on top. Otherwise:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install @ng-native/router
|
|
16
|
+
npm install @angular/common @angular/core @angular/router
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Example
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { AppRegistry } from 'react-native';
|
|
23
|
+
import { withComponentInputBinding } from '@angular/router';
|
|
24
|
+
import { mount } from '@ng-native/platform';
|
|
25
|
+
import { getFabricUIManager } from '@ng-native/fabric';
|
|
26
|
+
import { provideNativeRouter } from '@ng-native/router';
|
|
27
|
+
import { routes } from './routes.ts';
|
|
28
|
+
import { App } from './app.ts';
|
|
29
|
+
|
|
30
|
+
AppRegistry.registerRunnable('main', ({ rootTag }) => {
|
|
31
|
+
mount(Number(rootTag), App, getFabricUIManager(), {
|
|
32
|
+
providers: [provideNativeRouter(routes, withComponentInputBinding())],
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`withComponentInputBinding()` is what turns a route param into a component input. It is opt-in,
|
|
38
|
+
as on the web: leave it out and the outlets leave a page's inputs alone.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
@Component({
|
|
42
|
+
selector: 'app-root',
|
|
43
|
+
imports: [NativeStackOutlet, SafeAreaProvider],
|
|
44
|
+
template: `
|
|
45
|
+
<safe-area-provider>
|
|
46
|
+
<native-stack-outlet />
|
|
47
|
+
</safe-area-provider>
|
|
48
|
+
`,
|
|
49
|
+
host: { '[style]': 'fill' },
|
|
50
|
+
})
|
|
51
|
+
export class App {
|
|
52
|
+
protected readonly fill = { flex: 1 };
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## What's in the package
|
|
57
|
+
|
|
58
|
+
- `provideNativeRouter(routes, ...features)` - Angular's `provideRouter`, with a native
|
|
59
|
+
`PlatformLocation`, a `RouteReuseStrategy` that detaches rather than destroys a popped screen,
|
|
60
|
+
and `NativeNavigation` on top. `withLinkParent` is its one native feature.
|
|
61
|
+
- `NativeStackOutlet`, `NativeTabsOutlet`, `NativeTab` - the outlets, as elements.
|
|
62
|
+
- `NativeHeader`, `NativeHeaderItem`, `NativeHeaderPalette`, `NativeSearchBar` - the native header
|
|
63
|
+
and its slots.
|
|
64
|
+
- `nativeRouterLink` - the native equivalent of `routerLink`.
|
|
65
|
+
- `NativeNavigation` - replacing a screen, presenting a modal or sheet, resetting the stack: what a
|
|
66
|
+
URL alone cannot express.
|
|
67
|
+
|
|
68
|
+
## Docs
|
|
69
|
+
|
|
70
|
+
- [Router](https://ng-native.com/packages/router)
|
|
71
|
+
- [Root README](https://github.com/ng-native/ng-native/blob/main/README.md) and
|
|
72
|
+
[ARCHITECTURE.md](https://github.com/ng-native/ng-native/blob/main/ARCHITECTURE.md)
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route params as component inputs, which is what `withComponentInputBinding()` promises.
|
|
3
|
+
*
|
|
4
|
+
* Angular's own binder is a service behind `INPUT_BINDER`, a token the router does not export, and
|
|
5
|
+
* `RouterOutlet` is the only thing that calls it. An outlet that is not `RouterOutlet` therefore
|
|
6
|
+
* has to do this itself - and both of ours advertise `supportsBindingToComponentInputs`, so until
|
|
7
|
+
* now they were promising something nobody delivered: a `:album` param reached the component as
|
|
8
|
+
* an empty input, with no error anywhere.
|
|
9
|
+
*
|
|
10
|
+
* Like Angular's, it only runs in an app that asked for it: both outlets check
|
|
11
|
+
* `Router.componentInputBindingEnabled` first, which is true exactly when `RouterOutlet` would
|
|
12
|
+
* find a binder behind that token. Without the feature a routed page's inputs are its own.
|
|
13
|
+
*
|
|
14
|
+
* The behaviour matches `RoutedComponentInputBinder`: query params first, then path params, then
|
|
15
|
+
* resolved data, and every declared input is written on every change - including to `undefined`
|
|
16
|
+
* when the key is gone, which is what stops a stale param from surviving a navigation.
|
|
17
|
+
*/
|
|
18
|
+
import { type ComponentRef } from '@angular/core';
|
|
19
|
+
import type { ActivatedRoute } from '@angular/router';
|
|
20
|
+
export declare function bindRouteInputs(ref: ComponentRef<unknown>, route: ActivatedRoute): () => void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route params as component inputs, which is what `withComponentInputBinding()` promises.
|
|
3
|
+
*
|
|
4
|
+
* Angular's own binder is a service behind `INPUT_BINDER`, a token the router does not export, and
|
|
5
|
+
* `RouterOutlet` is the only thing that calls it. An outlet that is not `RouterOutlet` therefore
|
|
6
|
+
* has to do this itself - and both of ours advertise `supportsBindingToComponentInputs`, so until
|
|
7
|
+
* now they were promising something nobody delivered: a `:album` param reached the component as
|
|
8
|
+
* an empty input, with no error anywhere.
|
|
9
|
+
*
|
|
10
|
+
* Like Angular's, it only runs in an app that asked for it: both outlets check
|
|
11
|
+
* `Router.componentInputBindingEnabled` first, which is true exactly when `RouterOutlet` would
|
|
12
|
+
* find a binder behind that token. Without the feature a routed page's inputs are its own.
|
|
13
|
+
*
|
|
14
|
+
* The behaviour matches `RoutedComponentInputBinder`: query params first, then path params, then
|
|
15
|
+
* resolved data, and every declared input is written on every change - including to `undefined`
|
|
16
|
+
* when the key is gone, which is what stops a stale param from surviving a navigation.
|
|
17
|
+
*/
|
|
18
|
+
import { reflectComponentType } from '@angular/core';
|
|
19
|
+
export function bindRouteInputs(ref, route) {
|
|
20
|
+
const mirror = reflectComponentType(ref.componentType);
|
|
21
|
+
if (!mirror || mirror.inputs.length === 0)
|
|
22
|
+
return () => { };
|
|
23
|
+
let queryParams = {};
|
|
24
|
+
let params = {};
|
|
25
|
+
let data = {};
|
|
26
|
+
const apply = () => {
|
|
27
|
+
if (ref.hostView.destroyed)
|
|
28
|
+
return;
|
|
29
|
+
const merged = { ...queryParams, ...params, ...data };
|
|
30
|
+
for (const { templateName } of mirror.inputs) {
|
|
31
|
+
ref.setInput(templateName, merged[templateName]);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
// Three subscriptions rather than a `combineLatest`, so this file needs no rxjs operators. Each
|
|
35
|
+
// emits synchronously on subscribe, so the inputs are set before the first change detection.
|
|
36
|
+
const subscriptions = [
|
|
37
|
+
route.queryParams.subscribe((value) => {
|
|
38
|
+
queryParams = value;
|
|
39
|
+
apply();
|
|
40
|
+
}),
|
|
41
|
+
route.params.subscribe((value) => {
|
|
42
|
+
params = value;
|
|
43
|
+
apply();
|
|
44
|
+
}),
|
|
45
|
+
route.data.subscribe((value) => {
|
|
46
|
+
data = value;
|
|
47
|
+
apply();
|
|
48
|
+
}),
|
|
49
|
+
];
|
|
50
|
+
return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=bind-route-inputs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bind-route-inputs.js","sourceRoot":"","sources":["../src/bind-route-inputs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,oBAAoB,EAAqB,MAAM,eAAe,CAAC;AAKxE,MAAM,UAAU,eAAe,CAAC,GAA0B,EAAE,KAAqB;IAC/E,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACvD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;IAE3D,IAAI,WAAW,GAAW,EAAE,CAAC;IAC7B,IAAI,MAAM,GAAW,EAAE,CAAC;IACxB,IAAI,IAAI,GAAW,EAAE,CAAC;IAEtB,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS;YAAE,OAAO;QACnC,MAAM,MAAM,GAAG,EAAE,GAAG,WAAW,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC;QACtD,KAAK,MAAM,EAAE,YAAY,EAAE,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAC7C,GAAG,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QACnD,CAAC;IACH,CAAC,CAAC;IAEF,gGAAgG;IAChG,6FAA6F;IAC7F,MAAM,aAAa,GAAG;QACpB,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,WAAW,GAAG,KAAK,CAAC;YACpB,KAAK,EAAE,CAAC;QACV,CAAC,CAAC;QACF,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;YAC/B,MAAM,GAAG,KAAK,CAAC;YACf,KAAK,EAAE,CAAC;QACV,CAAC,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;YAC7B,IAAI,GAAG,KAAK,CAAC;YACb,KAAK,EAAE,CAAC;QACV,CAAC,CAAC;KACH,CAAC;IAEF,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;AACnF,CAAC","sourcesContent":["/**\n * Route params as component inputs, which is what `withComponentInputBinding()` promises.\n *\n * Angular's own binder is a service behind `INPUT_BINDER`, a token the router does not export, and\n * `RouterOutlet` is the only thing that calls it. An outlet that is not `RouterOutlet` therefore\n * has to do this itself - and both of ours advertise `supportsBindingToComponentInputs`, so until\n * now they were promising something nobody delivered: a `:album` param reached the component as\n * an empty input, with no error anywhere.\n *\n * Like Angular's, it only runs in an app that asked for it: both outlets check\n * `Router.componentInputBindingEnabled` first, which is true exactly when `RouterOutlet` would\n * find a binder behind that token. Without the feature a routed page's inputs are its own.\n *\n * The behaviour matches `RoutedComponentInputBinder`: query params first, then path params, then\n * resolved data, and every declared input is written on every change - including to `undefined`\n * when the key is gone, which is what stops a stale param from surviving a navigation.\n */\nimport { reflectComponentType, type ComponentRef } from '@angular/core';\nimport type { ActivatedRoute } from '@angular/router';\n\ntype Values = Record<string, unknown>;\n\nexport function bindRouteInputs(ref: ComponentRef<unknown>, route: ActivatedRoute): () => void {\n const mirror = reflectComponentType(ref.componentType);\n if (!mirror || mirror.inputs.length === 0) return () => {};\n\n let queryParams: Values = {};\n let params: Values = {};\n let data: Values = {};\n\n const apply = () => {\n if (ref.hostView.destroyed) return;\n const merged = { ...queryParams, ...params, ...data };\n for (const { templateName } of mirror.inputs) {\n ref.setInput(templateName, merged[templateName]);\n }\n };\n\n // Three subscriptions rather than a `combineLatest`, so this file needs no rxjs operators. Each\n // emits synchronously on subscribe, so the inputs are set before the first change detection.\n const subscriptions = [\n route.queryParams.subscribe((value) => {\n queryParams = value;\n apply();\n }),\n route.params.subscribe((value) => {\n params = value;\n apply();\n }),\n route.data.subscribe((value) => {\n data = value;\n apply();\n }),\n ];\n\n return () => subscriptions.forEach((subscription) => subscription.unsubscribe());\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
export declare class FullWindowOverlay {
|
|
3
|
+
private readonly screen;
|
|
4
|
+
/** iOS: VoiceOver stays inside the overlay while it is shown, as for a modal. */
|
|
5
|
+
readonly modal: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
|
|
6
|
+
protected readonly fill: import("@angular/core").Signal<{
|
|
7
|
+
position: string;
|
|
8
|
+
top: number;
|
|
9
|
+
left: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}>;
|
|
13
|
+
constructor();
|
|
14
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FullWindowOverlay, never>;
|
|
15
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FullWindowOverlay, "full-window-overlay", never, { "modal": { "alias": "modal"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content drawn above every screen the app shows, sheets and modals included.
|
|
3
|
+
*
|
|
4
|
+
* ```html
|
|
5
|
+
* <full-window-overlay>
|
|
6
|
+
* @if (toast(); as message) {
|
|
7
|
+
* <view class="toast"><text>{{ message }}</text></view>
|
|
8
|
+
* }
|
|
9
|
+
* </full-window-overlay>
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* A native sheet or modal is presented above the app's root view, so an absolutely positioned
|
|
13
|
+
* view at the root - the usual toast - is covered by the first sheet the user opens. On iOS this
|
|
14
|
+
* is react-native-screens' `RNSFullWindowOverlay`, a window of its own above the app's, which
|
|
15
|
+
* lets touches through wherever it has no content of its own, so it can stay mounted with
|
|
16
|
+
* nothing in it. It fills the window, as `FullWindowOverlay.tsx` sizes it, and follows it through
|
|
17
|
+
* a rotation. On Android, where a screen's presentation is drawn within the activity's own view,
|
|
18
|
+
* it is a plain view filling the window, last in its parent so it draws on top; put it last in the
|
|
19
|
+
* root component's template for that.
|
|
20
|
+
*/
|
|
21
|
+
import { Component, ElementRef, booleanAttribute, computed, inject, input } from '@angular/core';
|
|
22
|
+
import { Screen } from '@ng-native/device';
|
|
23
|
+
import { ownHost } from "./own-host.js";
|
|
24
|
+
import * as i0 from "@angular/core";
|
|
25
|
+
export class FullWindowOverlay {
|
|
26
|
+
screen = inject(Screen);
|
|
27
|
+
/** iOS: VoiceOver stays inside the overlay while it is shown, as for a modal. */
|
|
28
|
+
modal = input(false, { ...(ngDevMode ? { debugName: "modal" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
29
|
+
fill = computed(() => {
|
|
30
|
+
const { width, height } = this.screen.window();
|
|
31
|
+
return { position: 'absolute', top: 0, left: 0, width, height };
|
|
32
|
+
}, /* @ts-ignore */
|
|
33
|
+
...(ngDevMode ? [{ debugName: "fill" }] : /* istanbul ignore next */ []));
|
|
34
|
+
constructor() {
|
|
35
|
+
ownHost(inject(ElementRef).nativeElement, this.constructor);
|
|
36
|
+
}
|
|
37
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: FullWindowOverlay, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
38
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: FullWindowOverlay, isStandalone: true, selector: "full-window-overlay", inputs: { modal: { classPropertyName: "modal", publicName: "modal", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style": "fill()", "accessibilityContainerViewIsModal": "modal()", "pointerEvents": "'box-none'" } }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
39
|
+
}
|
|
40
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: FullWindowOverlay, decorators: [{
|
|
41
|
+
type: Component,
|
|
42
|
+
args: [{
|
|
43
|
+
selector: 'full-window-overlay',
|
|
44
|
+
template: '<ng-content />',
|
|
45
|
+
host: {
|
|
46
|
+
'[style]': 'fill()',
|
|
47
|
+
'[accessibilityContainerViewIsModal]': 'modal()',
|
|
48
|
+
'[pointerEvents]': "'box-none'",
|
|
49
|
+
},
|
|
50
|
+
}]
|
|
51
|
+
}], ctorParameters: () => [], propDecorators: { modal: [{ type: i0.Input, args: [{ isSignal: true, alias: "modal", required: false }] }] } });
|
|
52
|
+
//# sourceMappingURL=full-window-overlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"full-window-overlay.js","sourceRoot":"","sources":["../src/full-window-overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACjG,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;;AAWxC,MAAM,OAAO,iBAAiB;IACX,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAEzC,iFAAiF;IACxE,KAAK,GAAG,KAAK,CAAC,KAAK,6EAAI,SAAS,EAAE,gBAAgB,GAAG,CAAC;IAE5C,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE;QACtC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAC/C,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAClE,CAAC;6EAAC,CAAC;IAEH;QACE,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,aAA2B,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5E,CAAC;uGAbU,iBAAiB;2FAAjB,iBAAiB,+UAPlB,gBAAgB;;2FAOf,iBAAiB;kBAT7B,SAAS;mBAAC;oBACT,QAAQ,EAAE,qBAAqB;oBAC/B,QAAQ,EAAE,gBAAgB;oBAC1B,IAAI,EAAE;wBACJ,SAAS,EAAE,QAAQ;wBACnB,qCAAqC,EAAE,SAAS;wBAChD,iBAAiB,EAAE,YAAY;qBAChC;iBACF","sourcesContent":["/**\n * Content drawn above every screen the app shows, sheets and modals included.\n *\n * ```html\n * <full-window-overlay>\n * @if (toast(); as message) {\n * <view class=\"toast\"><text>{{ message }}</text></view>\n * }\n * </full-window-overlay>\n * ```\n *\n * A native sheet or modal is presented above the app's root view, so an absolutely positioned\n * view at the root - the usual toast - is covered by the first sheet the user opens. On iOS this\n * is react-native-screens' `RNSFullWindowOverlay`, a window of its own above the app's, which\n * lets touches through wherever it has no content of its own, so it can stay mounted with\n * nothing in it. It fills the window, as `FullWindowOverlay.tsx` sizes it, and follows it through\n * a rotation. On Android, where a screen's presentation is drawn within the activity's own view,\n * it is a plain view filling the window, last in its parent so it draws on top; put it last in the\n * root component's template for that.\n */\nimport { Component, ElementRef, booleanAttribute, computed, inject, input } from '@angular/core';\nimport { Screen } from '@ng-native/device';\nimport type { EngineNode } from '@ng-native/fabric';\nimport { ownHost } from './own-host.ts';\n\n@Component({\n selector: 'full-window-overlay',\n template: '<ng-content />',\n host: {\n '[style]': 'fill()',\n '[accessibilityContainerViewIsModal]': 'modal()',\n '[pointerEvents]': \"'box-none'\",\n },\n})\nexport class FullWindowOverlay {\n private readonly screen = inject(Screen);\n\n /** iOS: VoiceOver stays inside the overlay while it is shown, as for a modal. */\n readonly modal = input(false, { transform: booleanAttribute });\n\n protected readonly fill = computed(() => {\n const { width, height } = this.screen.window();\n return { position: 'absolute', top: 0, left: 0, width, height };\n });\n\n constructor() {\n ownHost(inject(ElementRef).nativeElement as EngineNode, this.constructor);\n }\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@angular/router` on a native stack: the outlet that maps activated routes onto `RNSScreen`,
|
|
3
|
+
* the `PlatformLocation` that stands in for the browser's history, and the reuse strategy that
|
|
4
|
+
* keeps a popped screen's component alive while it animates out.
|
|
5
|
+
*
|
|
6
|
+
* Two of these are decorated, so the same caveat as the components barrel applies: a Node test
|
|
7
|
+
* reaches the source file, not this.
|
|
8
|
+
*/
|
|
9
|
+
export { followLink, type LinkParent } from './native-links.ts';
|
|
10
|
+
export { NativeNavigation, type NativeNavigationOptions, type NavigationCommands, type PresentOptions, } from './native-navigation.ts';
|
|
11
|
+
export { NativePlatformLocation, type LinkSource } from './native-platform-location.ts';
|
|
12
|
+
export { NativeRouterLink } from './native-router-link.ts';
|
|
13
|
+
export { NATIVE_HEADER_DEFAULTS, NATIVE_TAB_DEFAULTS, type HeaderDefaults, type SchemeDefaults, type TabDefaults, } from './native-bar-defaults.ts';
|
|
14
|
+
export { NativeHeader, type BackButtonDisplayMode, type BlurEffect, type HeaderDirection, type HeaderInterfaceStyle, } from './native-header.ts';
|
|
15
|
+
export { DEFAULT_HEADER_PALETTE, NATIVE_HEADER_PALETTE, type HeaderColors, } from './native-header-palette.ts';
|
|
16
|
+
export { NativeHeaderItem, type HeaderItemType } from './native-header-item.ts';
|
|
17
|
+
export { NativeSearchBar, type SearchBarPlacement } from './native-search-bar.ts';
|
|
18
|
+
export { NativeStackOutlet } from './native-stack-outlet.ts';
|
|
19
|
+
export { NativeTab, type TabAppearance, type TabBarBlurEffect, type TabIcon, type TabIconProps, type TabItemAppearance, type TabStateAppearance, type TabSystemItem, } from './native-tab.ts';
|
|
20
|
+
export { NativeTabsOutlet } from './native-tabs-outlet.ts';
|
|
21
|
+
export { TabSafeAreaView, type TabSafeAreaEdge } from './tab-safe-area-view.ts';
|
|
22
|
+
export { NativeStackReuseStrategy, reuseScreen } from './native-stack-reuse-strategy.ts';
|
|
23
|
+
export { provideNativeRouter, withHeaderDefaults, withLinkParent, withTabDefaults, type NativeRouterFeature, } from './provide-native-router.ts';
|
|
24
|
+
export { type GestureResponseDistance, type ReplaceAnimation, type ScreenPresentation, type StackAnimation, type StackPresentation, type SwipeDirection, } from './screen-presentation.ts';
|
|
25
|
+
export { ActivityState, type ActivityStateValue } from './screens.ts';
|
|
26
|
+
export { FullWindowOverlay } from './full-window-overlay.ts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@angular/router` on a native stack: the outlet that maps activated routes onto `RNSScreen`,
|
|
3
|
+
* the `PlatformLocation` that stands in for the browser's history, and the reuse strategy that
|
|
4
|
+
* keeps a popped screen's component alive while it animates out.
|
|
5
|
+
*
|
|
6
|
+
* Two of these are decorated, so the same caveat as the components barrel applies: a Node test
|
|
7
|
+
* reaches the source file, not this.
|
|
8
|
+
*/
|
|
9
|
+
export { followLink } from "./native-links.js";
|
|
10
|
+
export { NativeNavigation, } from "./native-navigation.js";
|
|
11
|
+
export { NativePlatformLocation } from "./native-platform-location.js";
|
|
12
|
+
export { NativeRouterLink } from "./native-router-link.js";
|
|
13
|
+
export { NATIVE_HEADER_DEFAULTS, NATIVE_TAB_DEFAULTS, } from "./native-bar-defaults.js";
|
|
14
|
+
export { NativeHeader, } from "./native-header.js";
|
|
15
|
+
export { DEFAULT_HEADER_PALETTE, NATIVE_HEADER_PALETTE, } from "./native-header-palette.js";
|
|
16
|
+
export { NativeHeaderItem } from "./native-header-item.js";
|
|
17
|
+
export { NativeSearchBar } from "./native-search-bar.js";
|
|
18
|
+
export { NativeStackOutlet } from "./native-stack-outlet.js";
|
|
19
|
+
export { NativeTab, } from "./native-tab.js";
|
|
20
|
+
export { NativeTabsOutlet } from "./native-tabs-outlet.js";
|
|
21
|
+
export { TabSafeAreaView } from "./tab-safe-area-view.js";
|
|
22
|
+
export { NativeStackReuseStrategy, reuseScreen } from "./native-stack-reuse-strategy.js";
|
|
23
|
+
export { provideNativeRouter, withHeaderDefaults, withLinkParent, withTabDefaults, } from "./provide-native-router.js";
|
|
24
|
+
export {} from "./screen-presentation.js";
|
|
25
|
+
export { ActivityState } from "./screens.js";
|
|
26
|
+
export { FullWindowOverlay } from "./full-window-overlay.js";
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,gBAAgB,GAIjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAmB,MAAM,+BAA+B,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,mBAAmB,GAIpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,YAAY,GAKb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GAEtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAuB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,eAAe,EAA2B,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EACL,SAAS,GAQV,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAwB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACzF,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,eAAe,GAEhB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAON,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,aAAa,EAA2B,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["/**\n * `@angular/router` on a native stack: the outlet that maps activated routes onto `RNSScreen`,\n * the `PlatformLocation` that stands in for the browser's history, and the reuse strategy that\n * keeps a popped screen's component alive while it animates out.\n *\n * Two of these are decorated, so the same caveat as the components barrel applies: a Node test\n * reaches the source file, not this.\n */\nexport { followLink, type LinkParent } from './native-links.ts';\nexport {\n NativeNavigation,\n type NativeNavigationOptions,\n type NavigationCommands,\n type PresentOptions,\n} from './native-navigation.ts';\nexport { NativePlatformLocation, type LinkSource } from './native-platform-location.ts';\nexport { NativeRouterLink } from './native-router-link.ts';\nexport {\n NATIVE_HEADER_DEFAULTS,\n NATIVE_TAB_DEFAULTS,\n type HeaderDefaults,\n type SchemeDefaults,\n type TabDefaults,\n} from './native-bar-defaults.ts';\nexport {\n NativeHeader,\n type BackButtonDisplayMode,\n type BlurEffect,\n type HeaderDirection,\n type HeaderInterfaceStyle,\n} from './native-header.ts';\nexport {\n DEFAULT_HEADER_PALETTE,\n NATIVE_HEADER_PALETTE,\n type HeaderColors,\n} from './native-header-palette.ts';\nexport { NativeHeaderItem, type HeaderItemType } from './native-header-item.ts';\nexport { NativeSearchBar, type SearchBarPlacement } from './native-search-bar.ts';\nexport { NativeStackOutlet } from './native-stack-outlet.ts';\nexport {\n NativeTab,\n type TabAppearance,\n type TabBarBlurEffect,\n type TabIcon,\n type TabIconProps,\n type TabItemAppearance,\n type TabStateAppearance,\n type TabSystemItem,\n} from './native-tab.ts';\nexport { NativeTabsOutlet } from './native-tabs-outlet.ts';\nexport { TabSafeAreaView, type TabSafeAreaEdge } from './tab-safe-area-view.ts';\nexport { NativeStackReuseStrategy, reuseScreen } from './native-stack-reuse-strategy.ts';\nexport {\n provideNativeRouter,\n withHeaderDefaults,\n withLinkParent,\n withTabDefaults,\n type NativeRouterFeature,\n} from './provide-native-router.ts';\nexport {\n type GestureResponseDistance,\n type ReplaceAnimation,\n type ScreenPresentation,\n type StackAnimation,\n type StackPresentation,\n type SwipeDirection,\n} from './screen-presentation.ts';\nexport { ActivityState, type ActivityStateValue } from './screens.ts';\nexport { FullWindowOverlay } from './full-window-overlay.ts';\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ActivatedRoute, Router } from '@angular/router';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
/** What a stack offers `NativeNavigation` beyond a back: going down it several screens at once. */
|
|
4
|
+
export interface PoppableStack {
|
|
5
|
+
/** Whether this stack is part of what the router shows now. */
|
|
6
|
+
showing(): boolean;
|
|
7
|
+
/** Pop to its first screen, or null when it is showing only that one. */
|
|
8
|
+
popToRoot(): Promise<boolean> | null;
|
|
9
|
+
/** Pop to the screen showing `url`, or null when no screen below the top shows it. */
|
|
10
|
+
popTo(url: string): Promise<boolean> | null;
|
|
11
|
+
}
|
|
12
|
+
export declare class NativeBack {
|
|
13
|
+
private readonly hardwareBack;
|
|
14
|
+
/** Oldest first, as `HardwareBack` holds them. */
|
|
15
|
+
private readonly answers;
|
|
16
|
+
/** Oldest first, and so outermost first, as the answers are. */
|
|
17
|
+
private readonly stacks;
|
|
18
|
+
/** Offer a stack for popping several screens at once. Returns an unsubscribe. */
|
|
19
|
+
addStack(stack: PoppableStack): () => void;
|
|
20
|
+
/** Pop the innermost stack in front that is deeper than one screen, to its first. */
|
|
21
|
+
popToRoot(): Promise<boolean>;
|
|
22
|
+
/** Pop the innermost stack in front that has a screen at `url` below its top, down to it. */
|
|
23
|
+
popTo(url: string): Promise<boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* Offer an outlet's answer to a back. It returns whether it went back, and false lets the next
|
|
26
|
+
* one out try. Returns an unsubscribe.
|
|
27
|
+
*/
|
|
28
|
+
handle(answer: () => boolean): () => void;
|
|
29
|
+
/** Go back as the button would, innermost outlet first. False when none had anywhere to go. */
|
|
30
|
+
back(): boolean;
|
|
31
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<NativeBack, never>;
|
|
32
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Whether a route is part of what the router shows now, rather than a subtree the reuse strategy
|
|
36
|
+
* detached and is keeping for later: a tab behind, or a screen covered by one pushed over it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isShowing(router: Router, route: ActivatedRoute | null): boolean;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who answers a back: Android's hardware button, and `NativeNavigation.back()`.
|
|
3
|
+
*
|
|
4
|
+
* Every outlet offers an answer, and the one in front takes it. Each answer is also handed to
|
|
5
|
+
* `HardwareBack` directly, where React Native asks the newest first. An outlet always subscribes
|
|
6
|
+
* after the outlet it sits inside, so that order runs from the innermost outlet out, and a
|
|
7
|
+
* screen's own handler, subscribed after its outlet, still outranks it. `back()` asks the outlets
|
|
8
|
+
* in that same order, without the screens' own handlers: those are for the button.
|
|
9
|
+
*
|
|
10
|
+
* Newest first is not enough on its own. A stack in a tab behind the one in front, or under a
|
|
11
|
+
* screen pushed over the whole tab bar, is still subscribed, so each answer first checks that
|
|
12
|
+
* its outlet is showing what the router is showing.
|
|
13
|
+
*/
|
|
14
|
+
import { Service, inject } from '@angular/core';
|
|
15
|
+
import { HardwareBack } from '@ng-native/device';
|
|
16
|
+
import * as i0 from "@angular/core";
|
|
17
|
+
export class NativeBack {
|
|
18
|
+
hardwareBack = inject(HardwareBack);
|
|
19
|
+
/** Oldest first, as `HardwareBack` holds them. */
|
|
20
|
+
answers = [];
|
|
21
|
+
/** Oldest first, and so outermost first, as the answers are. */
|
|
22
|
+
stacks = [];
|
|
23
|
+
/** Offer a stack for popping several screens at once. Returns an unsubscribe. */
|
|
24
|
+
addStack(stack) {
|
|
25
|
+
this.stacks.push(stack);
|
|
26
|
+
return () => {
|
|
27
|
+
const index = this.stacks.indexOf(stack);
|
|
28
|
+
if (index !== -1)
|
|
29
|
+
this.stacks.splice(index, 1);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Pop the innermost stack in front that is deeper than one screen, to its first. */
|
|
33
|
+
popToRoot() {
|
|
34
|
+
for (const stack of [...this.stacks].reverse()) {
|
|
35
|
+
if (!stack.showing())
|
|
36
|
+
continue;
|
|
37
|
+
const popped = stack.popToRoot();
|
|
38
|
+
if (popped)
|
|
39
|
+
return popped;
|
|
40
|
+
}
|
|
41
|
+
return Promise.resolve(false);
|
|
42
|
+
}
|
|
43
|
+
/** Pop the innermost stack in front that has a screen at `url` below its top, down to it. */
|
|
44
|
+
popTo(url) {
|
|
45
|
+
for (const stack of [...this.stacks].reverse()) {
|
|
46
|
+
if (!stack.showing())
|
|
47
|
+
continue;
|
|
48
|
+
const popped = stack.popTo(url);
|
|
49
|
+
if (popped)
|
|
50
|
+
return popped;
|
|
51
|
+
}
|
|
52
|
+
return Promise.resolve(false);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Offer an outlet's answer to a back. It returns whether it went back, and false lets the next
|
|
56
|
+
* one out try. Returns an unsubscribe.
|
|
57
|
+
*/
|
|
58
|
+
handle(answer) {
|
|
59
|
+
this.answers.push(answer);
|
|
60
|
+
const stop = this.hardwareBack.handle(answer);
|
|
61
|
+
return () => {
|
|
62
|
+
stop();
|
|
63
|
+
const index = this.answers.indexOf(answer);
|
|
64
|
+
if (index !== -1)
|
|
65
|
+
this.answers.splice(index, 1);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Go back as the button would, innermost outlet first. False when none had anywhere to go. */
|
|
69
|
+
back() {
|
|
70
|
+
return [...this.answers].reverse().some((answer) => answer());
|
|
71
|
+
}
|
|
72
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeBack, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
73
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: NativeBack });
|
|
74
|
+
}
|
|
75
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeBack, decorators: [{
|
|
76
|
+
type: Service
|
|
77
|
+
}] });
|
|
78
|
+
/**
|
|
79
|
+
* Whether a route is part of what the router shows now, rather than a subtree the reuse strategy
|
|
80
|
+
* detached and is keeping for later: a tab behind, or a screen covered by one pushed over it.
|
|
81
|
+
*/
|
|
82
|
+
export function isShowing(router, route) {
|
|
83
|
+
if (!route)
|
|
84
|
+
return false;
|
|
85
|
+
const visit = (node) => node === route || node.children.some(visit);
|
|
86
|
+
return visit(router.routerState.root);
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=native-back.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-back.js","sourceRoot":"","sources":["../src/native-back.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;;AAajD,MAAM,OAAO,UAAU;IACJ,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IACrD,kDAAkD;IACjC,OAAO,GAAsB,EAAE,CAAC;IACjD,gEAAgE;IAC/C,MAAM,GAAoB,EAAE,CAAC;IAE9C,iFAAiF;IACjF,QAAQ,CAAC,KAAoB;QAC3B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,OAAO,GAAG,EAAE;YACV,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACzC,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACjD,CAAC,CAAC;IACJ,CAAC;IAED,qFAAqF;IACrF,SAAS;QACP,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAC/C,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;gBAAE,SAAS;YAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAC;QAC5B,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAED,6FAA6F;IAC7F,KAAK,CAAC,GAAW;QACf,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAC/C,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;gBAAE,SAAS;YAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAC;QAC5B,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,MAAqB;QAC1B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9C,OAAO,GAAG,EAAE;YACV,IAAI,EAAE,CAAC;YACP,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC3C,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAClD,CAAC,CAAC;IACJ,CAAC;IAED,+FAA+F;IAC/F,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,CAAC;uGArDU,UAAU;wGAAV,UAAU;;2FAAV,UAAU;kBADtB,OAAO;;AAyDR;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,KAA4B;IACpE,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,KAAK,GAAG,CAAC,IAAoB,EAAW,EAAE,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7F,OAAO,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC","sourcesContent":["/**\n * Who answers a back: Android's hardware button, and `NativeNavigation.back()`.\n *\n * Every outlet offers an answer, and the one in front takes it. Each answer is also handed to\n * `HardwareBack` directly, where React Native asks the newest first. An outlet always subscribes\n * after the outlet it sits inside, so that order runs from the innermost outlet out, and a\n * screen's own handler, subscribed after its outlet, still outranks it. `back()` asks the outlets\n * in that same order, without the screens' own handlers: those are for the button.\n *\n * Newest first is not enough on its own. A stack in a tab behind the one in front, or under a\n * screen pushed over the whole tab bar, is still subscribed, so each answer first checks that\n * its outlet is showing what the router is showing.\n */\nimport { Service, inject } from '@angular/core';\nimport type { ActivatedRoute, Router } from '@angular/router';\nimport { HardwareBack } from '@ng-native/device';\n\n/** What a stack offers `NativeNavigation` beyond a back: going down it several screens at once. */\nexport interface PoppableStack {\n /** Whether this stack is part of what the router shows now. */\n showing(): boolean;\n /** Pop to its first screen, or null when it is showing only that one. */\n popToRoot(): Promise<boolean> | null;\n /** Pop to the screen showing `url`, or null when no screen below the top shows it. */\n popTo(url: string): Promise<boolean> | null;\n}\n\n@Service()\nexport class NativeBack {\n private readonly hardwareBack = inject(HardwareBack);\n /** Oldest first, as `HardwareBack` holds them. */\n private readonly answers: (() => boolean)[] = [];\n /** Oldest first, and so outermost first, as the answers are. */\n private readonly stacks: PoppableStack[] = [];\n\n /** Offer a stack for popping several screens at once. Returns an unsubscribe. */\n addStack(stack: PoppableStack): () => void {\n this.stacks.push(stack);\n return () => {\n const index = this.stacks.indexOf(stack);\n if (index !== -1) this.stacks.splice(index, 1);\n };\n }\n\n /** Pop the innermost stack in front that is deeper than one screen, to its first. */\n popToRoot(): Promise<boolean> {\n for (const stack of [...this.stacks].reverse()) {\n if (!stack.showing()) continue;\n const popped = stack.popToRoot();\n if (popped) return popped;\n }\n return Promise.resolve(false);\n }\n\n /** Pop the innermost stack in front that has a screen at `url` below its top, down to it. */\n popTo(url: string): Promise<boolean> {\n for (const stack of [...this.stacks].reverse()) {\n if (!stack.showing()) continue;\n const popped = stack.popTo(url);\n if (popped) return popped;\n }\n return Promise.resolve(false);\n }\n\n /**\n * Offer an outlet's answer to a back. It returns whether it went back, and false lets the next\n * one out try. Returns an unsubscribe.\n */\n handle(answer: () => boolean): () => void {\n this.answers.push(answer);\n const stop = this.hardwareBack.handle(answer);\n return () => {\n stop();\n const index = this.answers.indexOf(answer);\n if (index !== -1) this.answers.splice(index, 1);\n };\n }\n\n /** Go back as the button would, innermost outlet first. False when none had anywhere to go. */\n back(): boolean {\n return [...this.answers].reverse().some((answer) => answer());\n }\n}\n\n/**\n * Whether a route is part of what the router shows now, rather than a subtree the reuse strategy\n * detached and is keeping for later: a tab behind, or a screen covered by one pushed over it.\n */\nexport function isShowing(router: Router, route: ActivatedRoute | null): boolean {\n if (!route) return false;\n const visit = (node: ActivatedRoute): boolean => node === route || node.children.some(visit);\n return visit(router.routerState.root);\n}\n"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How every bar in the app looks when a call site does not say, set once with
|
|
3
|
+
* `withHeaderDefaults` and `withTabDefaults` beside the routes.
|
|
4
|
+
*
|
|
5
|
+
* A bar is configured with props, and a prop cannot read the cascade, so an app whose bars should
|
|
6
|
+
* match its pages would otherwise repeat the same bindings on every `<native-header>`. These fill
|
|
7
|
+
* only what a call site left unset: a header that binds `backgroundColor` keeps it.
|
|
8
|
+
*
|
|
9
|
+
* Either kind can be a function of the colour scheme, which is how a bar follows the system
|
|
10
|
+
* appearance the way a page's `@media (prefers-color-scheme: dark)` rules do. It runs inside a
|
|
11
|
+
* `computed`, so any signal it reads is followed too.
|
|
12
|
+
*/
|
|
13
|
+
import { InjectionToken, type Signal } from '@angular/core';
|
|
14
|
+
import type { Scheme } from '@ng-native/device';
|
|
15
|
+
import type { BackButtonDisplayMode, BlurEffect, HeaderInterfaceStyle } from './native-header.ts';
|
|
16
|
+
import type { TabAppearance } from './native-tab.ts';
|
|
17
|
+
/** A set of defaults, fixed or chosen per colour scheme. */
|
|
18
|
+
export type SchemeDefaults<T> = T | ((scheme: Scheme) => T);
|
|
19
|
+
/** The appearance inputs of `<native-header>` that make sense for a whole app. */
|
|
20
|
+
export interface HeaderDefaults {
|
|
21
|
+
readonly userInterfaceStyle?: HeaderInterfaceStyle;
|
|
22
|
+
readonly backgroundColor?: string | number;
|
|
23
|
+
readonly color?: string | number;
|
|
24
|
+
readonly blurEffect?: BlurEffect;
|
|
25
|
+
readonly hideShadow?: boolean;
|
|
26
|
+
readonly translucent?: boolean;
|
|
27
|
+
readonly titleColor?: string | number;
|
|
28
|
+
readonly titleFontFamily?: string;
|
|
29
|
+
readonly titleFontSize?: number;
|
|
30
|
+
readonly titleFontWeight?: string;
|
|
31
|
+
readonly largeTitleColor?: string | number;
|
|
32
|
+
readonly largeTitleBackgroundColor?: string | number;
|
|
33
|
+
readonly largeTitleFontFamily?: string;
|
|
34
|
+
readonly largeTitleFontSize?: number;
|
|
35
|
+
readonly largeTitleFontWeight?: string;
|
|
36
|
+
readonly largeTitleHideShadow?: boolean;
|
|
37
|
+
readonly backTitleFontFamily?: string;
|
|
38
|
+
readonly backTitleFontSize?: number;
|
|
39
|
+
readonly backButtonDisplayMode?: BackButtonDisplayMode;
|
|
40
|
+
}
|
|
41
|
+
/** The appearance inputs of `<native-tabs-outlet>` and `<native-tab>` that suit a whole app. */
|
|
42
|
+
export interface TabDefaults {
|
|
43
|
+
readonly tintColor?: string | number;
|
|
44
|
+
readonly backgroundColor?: string | number;
|
|
45
|
+
readonly colorScheme?: 'inherit' | 'light' | 'dark';
|
|
46
|
+
readonly standardAppearance?: TabAppearance;
|
|
47
|
+
readonly scrollEdgeAppearance?: TabAppearance;
|
|
48
|
+
}
|
|
49
|
+
export declare const NATIVE_HEADER_DEFAULTS: InjectionToken<Signal<HeaderDefaults>>;
|
|
50
|
+
export declare const NATIVE_TAB_DEFAULTS: InjectionToken<Signal<TabDefaults>>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How every bar in the app looks when a call site does not say, set once with
|
|
3
|
+
* `withHeaderDefaults` and `withTabDefaults` beside the routes.
|
|
4
|
+
*
|
|
5
|
+
* A bar is configured with props, and a prop cannot read the cascade, so an app whose bars should
|
|
6
|
+
* match its pages would otherwise repeat the same bindings on every `<native-header>`. These fill
|
|
7
|
+
* only what a call site left unset: a header that binds `backgroundColor` keeps it.
|
|
8
|
+
*
|
|
9
|
+
* Either kind can be a function of the colour scheme, which is how a bar follows the system
|
|
10
|
+
* appearance the way a page's `@media (prefers-color-scheme: dark)` rules do. It runs inside a
|
|
11
|
+
* `computed`, so any signal it reads is followed too.
|
|
12
|
+
*/
|
|
13
|
+
import { InjectionToken, signal } from '@angular/core';
|
|
14
|
+
const none = signal({}).asReadonly();
|
|
15
|
+
export const NATIVE_HEADER_DEFAULTS = new InjectionToken('angular-native.nativeHeaderDefaults', { factory: () => none });
|
|
16
|
+
export const NATIVE_TAB_DEFAULTS = new InjectionToken('angular-native.nativeTabDefaults', { factory: () => none });
|
|
17
|
+
//# sourceMappingURL=native-bar-defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-bar-defaults.js","sourceRoot":"","sources":["../src/native-bar-defaults.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAwCpE,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC;AAErC,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,cAAc,CACtD,qCAAqC,EACrC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CACxB,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,cAAc,CACnD,kCAAkC,EAClC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CACxB,CAAC","sourcesContent":["/**\n * How every bar in the app looks when a call site does not say, set once with\n * `withHeaderDefaults` and `withTabDefaults` beside the routes.\n *\n * A bar is configured with props, and a prop cannot read the cascade, so an app whose bars should\n * match its pages would otherwise repeat the same bindings on every `<native-header>`. These fill\n * only what a call site left unset: a header that binds `backgroundColor` keeps it.\n *\n * Either kind can be a function of the colour scheme, which is how a bar follows the system\n * appearance the way a page's `@media (prefers-color-scheme: dark)` rules do. It runs inside a\n * `computed`, so any signal it reads is followed too.\n */\nimport { InjectionToken, signal, type Signal } from '@angular/core';\nimport type { Scheme } from '@ng-native/device';\nimport type { BackButtonDisplayMode, BlurEffect, HeaderInterfaceStyle } from './native-header.ts';\nimport type { TabAppearance } from './native-tab.ts';\n\n/** A set of defaults, fixed or chosen per colour scheme. */\nexport type SchemeDefaults<T> = T | ((scheme: Scheme) => T);\n\n/** The appearance inputs of `<native-header>` that make sense for a whole app. */\nexport interface HeaderDefaults {\n readonly userInterfaceStyle?: HeaderInterfaceStyle;\n readonly backgroundColor?: string | number;\n readonly color?: string | number;\n readonly blurEffect?: BlurEffect;\n readonly hideShadow?: boolean;\n readonly translucent?: boolean;\n readonly titleColor?: string | number;\n readonly titleFontFamily?: string;\n readonly titleFontSize?: number;\n readonly titleFontWeight?: string;\n readonly largeTitleColor?: string | number;\n readonly largeTitleBackgroundColor?: string | number;\n readonly largeTitleFontFamily?: string;\n readonly largeTitleFontSize?: number;\n readonly largeTitleFontWeight?: string;\n readonly largeTitleHideShadow?: boolean;\n readonly backTitleFontFamily?: string;\n readonly backTitleFontSize?: number;\n readonly backButtonDisplayMode?: BackButtonDisplayMode;\n}\n\n/** The appearance inputs of `<native-tabs-outlet>` and `<native-tab>` that suit a whole app. */\nexport interface TabDefaults {\n readonly tintColor?: string | number;\n readonly backgroundColor?: string | number;\n readonly colorScheme?: 'inherit' | 'light' | 'dark';\n readonly standardAppearance?: TabAppearance;\n readonly scrollEdgeAppearance?: TabAppearance;\n}\n\nconst none = signal({}).asReadonly();\n\nexport const NATIVE_HEADER_DEFAULTS = new InjectionToken<Signal<HeaderDefaults>>(\n 'angular-native.nativeHeaderDefaults',\n { factory: () => none },\n);\n\nexport const NATIVE_TAB_DEFAULTS = new InjectionToken<Signal<TabDefaults>>(\n 'angular-native.nativeTabDefaults',\n { factory: () => none },\n);\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
/** The bar slots. `title` and `center` are the same place. */
|
|
3
|
+
export type HeaderItemType = 'back' | 'left' | 'center' | 'title' | 'right' | 'searchBar';
|
|
4
|
+
export declare class NativeHeaderItem {
|
|
5
|
+
constructor();
|
|
6
|
+
/** Which slot to sit in. Native defaults to `left`. */
|
|
7
|
+
readonly type: import("@angular/core").InputSignal<HeaderItemType | undefined>;
|
|
8
|
+
/** Opt out of the bar's shared background behind this item. */
|
|
9
|
+
readonly hidesSharedBackground: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
10
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<NativeHeaderItem, never>;
|
|
11
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<NativeHeaderItem, "native-header-item", never, { "type": { "alias": "type"; "required": false; "isSignal": true; }; "hidesSharedBackground": { "alias": "hidesSharedBackground"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anything Angular can render, placed in one of the navigation bar's slots.
|
|
3
|
+
*
|
|
4
|
+
* Commits as `RNSScreenStackHeaderSubview`, whose `type` picks the slot. A `right` item is the
|
|
5
|
+
* usual "Done" or "Edit" button; `back` replaces the chevron, which needs
|
|
6
|
+
* `backButtonInCustomView` on the header; `searchBar` expects a search bar rather than
|
|
7
|
+
* arbitrary content.
|
|
8
|
+
*
|
|
9
|
+
* Press handling is the responder system, so a `pressable` inside one works like anywhere else.
|
|
10
|
+
* Native bar button items would look truer to the platform and bring menus and system symbols
|
|
11
|
+
* with them, but they are data rather than views; they are a later opt-in.
|
|
12
|
+
*/
|
|
13
|
+
import { Component, ElementRef, inject, input } from '@angular/core';
|
|
14
|
+
import { ownHost } from "./own-host.js";
|
|
15
|
+
import { optionalBoolean } from "./transforms.js";
|
|
16
|
+
import * as i0 from "@angular/core";
|
|
17
|
+
export class NativeHeaderItem {
|
|
18
|
+
constructor() {
|
|
19
|
+
ownHost(inject(ElementRef).nativeElement, this.constructor);
|
|
20
|
+
}
|
|
21
|
+
/** Which slot to sit in. Native defaults to `left`. */
|
|
22
|
+
type = input(/* @ts-ignore */
|
|
23
|
+
...(ngDevMode ? [undefined, { debugName: "type" }] : /* istanbul ignore next */ []));
|
|
24
|
+
/** Opt out of the bar's shared background behind this item. */
|
|
25
|
+
hidesSharedBackground = input(undefined, { ...(ngDevMode ? { debugName: "hidesSharedBackground" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
26
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeHeaderItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
27
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: NativeHeaderItem, isStandalone: true, selector: "native-header-item", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, hidesSharedBackground: { classPropertyName: "hidesSharedBackground", publicName: "hidesSharedBackground", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "type": "type()", "hidesSharedBackground": "hidesSharedBackground()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
28
|
+
}
|
|
29
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeHeaderItem, decorators: [{
|
|
30
|
+
type: Component,
|
|
31
|
+
args: [{
|
|
32
|
+
selector: 'native-header-item',
|
|
33
|
+
template: '<ng-content />',
|
|
34
|
+
host: {
|
|
35
|
+
'[type]': 'type()',
|
|
36
|
+
'[hidesSharedBackground]': 'hidesSharedBackground()',
|
|
37
|
+
},
|
|
38
|
+
}]
|
|
39
|
+
}], ctorParameters: () => [], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], hidesSharedBackground: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidesSharedBackground", required: false }] }] } });
|
|
40
|
+
//# sourceMappingURL=native-header-item.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-header-item.js","sourceRoot":"","sources":["../src/native-header-item.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;;AAalD,MAAM,OAAO,gBAAgB;IAC3B;QACE,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,aAA2B,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5E,CAAC;IAED,uDAAuD;IAC9C,IAAI,GAAG,KAAK;wFAAkB,CAAC;IACxC,+DAA+D;IACtD,qBAAqB,GAAG,KAAK,CAAC,SAAS,6FAAI,SAAS,EAAE,eAAe,GAAG,CAAC;uGARvE,gBAAgB;2FAAhB,gBAAgB,udANjB,gBAAgB;;2FAMf,gBAAgB;kBAR5B,SAAS;mBAAC;oBACT,QAAQ,EAAE,oBAAoB;oBAC9B,QAAQ,EAAE,gBAAgB;oBAC1B,IAAI,EAAE;wBACJ,QAAQ,EAAE,QAAQ;wBAClB,yBAAyB,EAAE,yBAAyB;qBACrD;iBACF","sourcesContent":["/**\n * Anything Angular can render, placed in one of the navigation bar's slots.\n *\n * Commits as `RNSScreenStackHeaderSubview`, whose `type` picks the slot. A `right` item is the\n * usual \"Done\" or \"Edit\" button; `back` replaces the chevron, which needs\n * `backButtonInCustomView` on the header; `searchBar` expects a search bar rather than\n * arbitrary content.\n *\n * Press handling is the responder system, so a `pressable` inside one works like anywhere else.\n * Native bar button items would look truer to the platform and bring menus and system symbols\n * with them, but they are data rather than views; they are a later opt-in.\n */\nimport { Component, ElementRef, inject, input } from '@angular/core';\nimport type { EngineNode } from '@ng-native/fabric';\nimport { ownHost } from './own-host.ts';\nimport { optionalBoolean } from './transforms.ts';\n\n/** The bar slots. `title` and `center` are the same place. */\nexport type HeaderItemType = 'back' | 'left' | 'center' | 'title' | 'right' | 'searchBar';\n\n@Component({\n selector: 'native-header-item',\n template: '<ng-content />',\n host: {\n '[type]': 'type()',\n '[hidesSharedBackground]': 'hidesSharedBackground()',\n },\n})\nexport class NativeHeaderItem {\n constructor() {\n ownHost(inject(ElementRef).nativeElement as EngineNode, this.constructor);\n }\n\n /** Which slot to sit in. Native defaults to `left`. */\n readonly type = input<HeaderItemType>();\n /** Opt out of the bar's shared background behind this item. */\n readonly hidesSharedBackground = input(undefined, { transform: optionalBoolean });\n}\n"]}
|