@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
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What colour a header bar is when nothing said.
|
|
3
|
+
*
|
|
4
|
+
* A native header is configured with props, and a prop cannot read the cascade - so the bar has no
|
|
5
|
+
* way to resolve `--background` the way a view does with a class. Left unset, each platform picks
|
|
6
|
+
* for itself: iOS follows the system appearance and looks broadly right, and Android falls back to
|
|
7
|
+
* the app theme's `colorPrimary`, which is the framework's default blue on every screen of every
|
|
8
|
+
* app built here.
|
|
9
|
+
*
|
|
10
|
+
* So the values are written out, one pair per colour scheme, and `NativeHeader` uses them for any
|
|
11
|
+
* colour prop the call site did not bind. They are shadcn's neutral `--background` and
|
|
12
|
+
* `--foreground`, a common default for a screen's own palette, so a bar matches the screen under
|
|
13
|
+
* it more often than not. The cost is that an app with a different palette has to say so here
|
|
14
|
+
* too.
|
|
15
|
+
*
|
|
16
|
+
* That is what the token is for. Provide it once and every header follows:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* providers: [
|
|
20
|
+
* {
|
|
21
|
+
* provide: NATIVE_HEADER_PALETTE,
|
|
22
|
+
* useValue: {
|
|
23
|
+
* light: { background: '#ffffff', foreground: '#0a0a0a' },
|
|
24
|
+
* dark: { background: '#0a0a0a', foreground: '#fafafa' },
|
|
25
|
+
* },
|
|
26
|
+
* },
|
|
27
|
+
* ]
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import { InjectionToken } from '@angular/core';
|
|
31
|
+
import type { Scheme } from '@ng-native/device';
|
|
32
|
+
export interface HeaderColors {
|
|
33
|
+
/** The bar itself. */
|
|
34
|
+
readonly background: string;
|
|
35
|
+
/** The title, the back button and anything else the platform tints. */
|
|
36
|
+
readonly foreground: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* shadcn's neutral palette, as the two colours a header needs.
|
|
40
|
+
*
|
|
41
|
+
* `oklch(1 0 0)` and `oklch(0.145 0 0)` in light, `oklch(0.145 0 0)` and `oklch(0.985 0 0)` in
|
|
42
|
+
* dark, lowered to the sRGB the build produces from them.
|
|
43
|
+
*/
|
|
44
|
+
export declare const DEFAULT_HEADER_PALETTE: Record<Scheme, HeaderColors>;
|
|
45
|
+
export declare const NATIVE_HEADER_PALETTE: InjectionToken<Record<Scheme, HeaderColors>>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What colour a header bar is when nothing said.
|
|
3
|
+
*
|
|
4
|
+
* A native header is configured with props, and a prop cannot read the cascade - so the bar has no
|
|
5
|
+
* way to resolve `--background` the way a view does with a class. Left unset, each platform picks
|
|
6
|
+
* for itself: iOS follows the system appearance and looks broadly right, and Android falls back to
|
|
7
|
+
* the app theme's `colorPrimary`, which is the framework's default blue on every screen of every
|
|
8
|
+
* app built here.
|
|
9
|
+
*
|
|
10
|
+
* So the values are written out, one pair per colour scheme, and `NativeHeader` uses them for any
|
|
11
|
+
* colour prop the call site did not bind. They are shadcn's neutral `--background` and
|
|
12
|
+
* `--foreground`, a common default for a screen's own palette, so a bar matches the screen under
|
|
13
|
+
* it more often than not. The cost is that an app with a different palette has to say so here
|
|
14
|
+
* too.
|
|
15
|
+
*
|
|
16
|
+
* That is what the token is for. Provide it once and every header follows:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* providers: [
|
|
20
|
+
* {
|
|
21
|
+
* provide: NATIVE_HEADER_PALETTE,
|
|
22
|
+
* useValue: {
|
|
23
|
+
* light: { background: '#ffffff', foreground: '#0a0a0a' },
|
|
24
|
+
* dark: { background: '#0a0a0a', foreground: '#fafafa' },
|
|
25
|
+
* },
|
|
26
|
+
* },
|
|
27
|
+
* ]
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import { InjectionToken } from '@angular/core';
|
|
31
|
+
/**
|
|
32
|
+
* shadcn's neutral palette, as the two colours a header needs.
|
|
33
|
+
*
|
|
34
|
+
* `oklch(1 0 0)` and `oklch(0.145 0 0)` in light, `oklch(0.145 0 0)` and `oklch(0.985 0 0)` in
|
|
35
|
+
* dark, lowered to the sRGB the build produces from them.
|
|
36
|
+
*/
|
|
37
|
+
export const DEFAULT_HEADER_PALETTE = {
|
|
38
|
+
light: { background: 'rgb(255, 255, 255)', foreground: 'rgb(10, 10, 10)' },
|
|
39
|
+
dark: { background: 'rgb(10, 10, 10)', foreground: 'rgb(250, 250, 250)' },
|
|
40
|
+
};
|
|
41
|
+
export const NATIVE_HEADER_PALETTE = new InjectionToken('angular-native.nativeHeaderPalette', { factory: () => DEFAULT_HEADER_PALETTE });
|
|
42
|
+
//# sourceMappingURL=native-header-palette.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-header-palette.js","sourceRoot":"","sources":["../src/native-header-palette.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAU/C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAiC;IAClE,KAAK,EAAE,EAAE,UAAU,EAAE,oBAAoB,EAAE,UAAU,EAAE,iBAAiB,EAAE;IAC1E,IAAI,EAAE,EAAE,UAAU,EAAE,iBAAiB,EAAE,UAAU,EAAE,oBAAoB,EAAE;CAC1E,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,cAAc,CACrD,oCAAoC,EACpC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,sBAAsB,EAAE,CAC1C,CAAC","sourcesContent":["/**\n * What colour a header bar is when nothing said.\n *\n * A native header is configured with props, and a prop cannot read the cascade - so the bar has no\n * way to resolve `--background` the way a view does with a class. Left unset, each platform picks\n * for itself: iOS follows the system appearance and looks broadly right, and Android falls back to\n * the app theme's `colorPrimary`, which is the framework's default blue on every screen of every\n * app built here.\n *\n * So the values are written out, one pair per colour scheme, and `NativeHeader` uses them for any\n * colour prop the call site did not bind. They are shadcn's neutral `--background` and\n * `--foreground`, a common default for a screen's own palette, so a bar matches the screen under\n * it more often than not. The cost is that an app with a different palette has to say so here\n * too.\n *\n * That is what the token is for. Provide it once and every header follows:\n *\n * ```ts\n * providers: [\n * {\n * provide: NATIVE_HEADER_PALETTE,\n * useValue: {\n * light: { background: '#ffffff', foreground: '#0a0a0a' },\n * dark: { background: '#0a0a0a', foreground: '#fafafa' },\n * },\n * },\n * ]\n * ```\n */\nimport { InjectionToken } from '@angular/core';\nimport type { Scheme } from '@ng-native/device';\n\nexport interface HeaderColors {\n /** The bar itself. */\n readonly background: string;\n /** The title, the back button and anything else the platform tints. */\n readonly foreground: string;\n}\n\n/**\n * shadcn's neutral palette, as the two colours a header needs.\n *\n * `oklch(1 0 0)` and `oklch(0.145 0 0)` in light, `oklch(0.145 0 0)` and `oklch(0.985 0 0)` in\n * dark, lowered to the sRGB the build produces from them.\n */\nexport const DEFAULT_HEADER_PALETTE: Record<Scheme, HeaderColors> = {\n light: { background: 'rgb(255, 255, 255)', foreground: 'rgb(10, 10, 10)' },\n dark: { background: 'rgb(10, 10, 10)', foreground: 'rgb(250, 250, 250)' },\n};\n\nexport const NATIVE_HEADER_PALETTE = new InjectionToken<Record<Scheme, HeaderColors>>(\n 'angular-native.nativeHeaderPalette',\n { factory: () => DEFAULT_HEADER_PALETTE },\n);\n"]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
/** Where the back button's title comes from, on iOS 14+. */
|
|
3
|
+
export type BackButtonDisplayMode = 'minimal' | 'default' | 'generic';
|
|
4
|
+
/** Direction the bar lays out in, for right-to-left languages. */
|
|
5
|
+
export type HeaderDirection = 'ltr' | 'rtl';
|
|
6
|
+
/** How the bar blurs what scrolls under it. `none` paints `backgroundColor` instead. */
|
|
7
|
+
export type BlurEffect = 'none' | 'extraLight' | 'light' | 'dark' | 'regular' | 'prominent' | 'systemUltraThinMaterial' | 'systemThinMaterial' | 'systemMaterial' | 'systemThickMaterial' | 'systemChromeMaterial' | 'systemUltraThinMaterialLight' | 'systemThinMaterialLight' | 'systemMaterialLight' | 'systemThickMaterialLight' | 'systemChromeMaterialLight' | 'systemUltraThinMaterialDark' | 'systemThinMaterialDark' | 'systemMaterialDark' | 'systemThickMaterialDark' | 'systemChromeMaterialDark';
|
|
8
|
+
/** Overrides the bar's light/dark appearance independently of the app's. */
|
|
9
|
+
export type HeaderInterfaceStyle = 'unspecified' | 'light' | 'dark';
|
|
10
|
+
export declare class NativeHeader {
|
|
11
|
+
private readonly scheme;
|
|
12
|
+
private readonly palette;
|
|
13
|
+
/** The app's `withHeaderDefaults`, for whatever this header does not bind itself. */
|
|
14
|
+
protected readonly defaults: import("@angular/core").Signal<import("./native-bar-defaults.ts").HeaderDefaults>;
|
|
15
|
+
constructor();
|
|
16
|
+
/**
|
|
17
|
+
* The palette for the scheme the app is wearing, for whichever colours were not bound.
|
|
18
|
+
*
|
|
19
|
+
* A header prop cannot read the cascade, so left alone each platform chooses: iOS follows the
|
|
20
|
+
* system appearance and looks about right, Android takes the app theme's `colorPrimary` and is
|
|
21
|
+
* the framework blue on every screen. These fill the gap without overriding anything a call site
|
|
22
|
+
* or `withHeaderDefaults` did say. See `native-header-palette.ts` for retuning them.
|
|
23
|
+
*/
|
|
24
|
+
private readonly colours;
|
|
25
|
+
protected readonly resolvedBackgroundColor: import("@angular/core").Signal<string | number>;
|
|
26
|
+
protected readonly resolvedColor: import("@angular/core").Signal<string | number>;
|
|
27
|
+
protected readonly resolvedTitleColor: import("@angular/core").Signal<string | number>;
|
|
28
|
+
/**
|
|
29
|
+
* A large title on iOS, drawn as UIKit draws one unless the call site says otherwise: over the
|
|
30
|
+
* content with the bar clear and unlined at the top, so the page's scroll view (with
|
|
31
|
+
* `contentInsetAdjustmentBehavior="automatic"`) collapses it into the bar as it scrolls. Since
|
|
32
|
+
* iOS 26 the large title sits in the scroll view, above its content, where a bar with a
|
|
33
|
+
* background paints over it; and over an opaque bar the inline title never fades in.
|
|
34
|
+
*/
|
|
35
|
+
protected readonly iosLargeTitle: import("@angular/core").Signal<true | undefined>;
|
|
36
|
+
protected readonly resolvedLargeTitleBackgroundColor: import("@angular/core").Signal<string | number | undefined>;
|
|
37
|
+
/** The bar's title. Absent leaves whatever the platform would show, which is nothing. */
|
|
38
|
+
readonly title: import("@angular/core").InputSignal<string | undefined>;
|
|
39
|
+
/** Title colour. */
|
|
40
|
+
readonly titleColor: import("@angular/core").InputSignal<string | number | undefined>;
|
|
41
|
+
readonly titleFontFamily: import("@angular/core").InputSignal<string | undefined>;
|
|
42
|
+
readonly titleFontSize: import("@angular/core").InputSignalWithTransform<number | undefined, unknown>;
|
|
43
|
+
/** A CSS font weight, `'600'` or `'bold'`. */
|
|
44
|
+
readonly titleFontWeight: import("@angular/core").InputSignal<string | undefined>;
|
|
45
|
+
/** The bar's background. Ignored unless `blurEffect` is `none`. */
|
|
46
|
+
readonly backgroundColor: import("@angular/core").InputSignal<string | number | undefined>;
|
|
47
|
+
/** Tint colour: the back chevron, and any header item that does not set its own. */
|
|
48
|
+
readonly color: import("@angular/core").InputSignal<string | number | undefined>;
|
|
49
|
+
/** iOS blur material behind the bar. */
|
|
50
|
+
readonly blurEffect: import("@angular/core").InputSignal<BlurEffect | undefined>;
|
|
51
|
+
/** Hide the bar entirely, keeping the screen's gestures and safe areas. */
|
|
52
|
+
readonly hidden: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
53
|
+
/** Drop the hairline under the bar. */
|
|
54
|
+
readonly hideShadow: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
55
|
+
/** Let content scroll under the bar rather than starting below it. */
|
|
56
|
+
readonly translucent: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
57
|
+
/** Lay the bar out right to left. */
|
|
58
|
+
readonly direction: import("@angular/core").InputSignal<HeaderDirection | undefined>;
|
|
59
|
+
/** Android: add the status bar inset to the bar's height. */
|
|
60
|
+
readonly topInsetEnabled: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
61
|
+
/** Force the bar light or dark regardless of the app's appearance. */
|
|
62
|
+
readonly userInterfaceStyle: import("@angular/core").InputSignal<HeaderInterfaceStyle | undefined>;
|
|
63
|
+
/** iOS large title, which collapses into the bar as content scrolls. */
|
|
64
|
+
readonly largeTitle: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
65
|
+
readonly largeTitleColor: import("@angular/core").InputSignal<string | number | undefined>;
|
|
66
|
+
readonly largeTitleBackgroundColor: import("@angular/core").InputSignal<string | number | undefined>;
|
|
67
|
+
readonly largeTitleFontFamily: import("@angular/core").InputSignal<string | undefined>;
|
|
68
|
+
readonly largeTitleFontSize: import("@angular/core").InputSignalWithTransform<number | undefined, unknown>;
|
|
69
|
+
readonly largeTitleFontWeight: import("@angular/core").InputSignal<string | undefined>;
|
|
70
|
+
readonly largeTitleHideShadow: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
71
|
+
/**
|
|
72
|
+
* What *this* screen's back button says. `RNSScreenStackHeaderConfig.mm` writes it onto the
|
|
73
|
+
* previous screen's navigation item, falling back to that screen's title, which is why an
|
|
74
|
+
* unset back title reads as the title of the screen you came from.
|
|
75
|
+
*/
|
|
76
|
+
readonly backTitle: import("@angular/core").InputSignal<string | undefined>;
|
|
77
|
+
readonly backTitleFontFamily: import("@angular/core").InputSignal<string | undefined>;
|
|
78
|
+
readonly backTitleFontSize: import("@angular/core").InputSignalWithTransform<number | undefined, unknown>;
|
|
79
|
+
/** Show the back button's title at all. Native defaults this to true. */
|
|
80
|
+
readonly backTitleVisible: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
81
|
+
/** iOS 14+: how aggressively the back title is shortened when space is tight. */
|
|
82
|
+
readonly backButtonDisplayMode: import("@angular/core").InputSignal<BackButtonDisplayMode | undefined>;
|
|
83
|
+
/** Put the back button in the custom view slot, so a `back` header item can replace it. */
|
|
84
|
+
readonly backButtonInCustomView: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
85
|
+
/** Drop the long-press menu of the screens below. */
|
|
86
|
+
readonly disableBackButtonMenu: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
87
|
+
/** Hide the back button without disabling the swipe-back gesture. */
|
|
88
|
+
readonly hideBackButton: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
89
|
+
/**
|
|
90
|
+
* Android: whether the bar takes the status bar's height as padding, so its title sits below
|
|
91
|
+
* it rather than under it.
|
|
92
|
+
*
|
|
93
|
+
* Defaulted per platform because getting it wrong is invisible in a screenshot until you look
|
|
94
|
+
* for it: react-native-screens leaves it false, Android 15 draws every app edge to edge, and
|
|
95
|
+
* the result is a title behind the clock. iOS has a navigation controller doing this itself.
|
|
96
|
+
*/
|
|
97
|
+
readonly consumeTopInset: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
98
|
+
readonly consumeLeftInset: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
99
|
+
readonly consumeRightInset: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
100
|
+
readonly consumeBottomInset: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
101
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<NativeHeader, never>;
|
|
102
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<NativeHeader, "native-header", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "titleColor": { "alias": "titleColor"; "required": false; "isSignal": true; }; "titleFontFamily": { "alias": "titleFontFamily"; "required": false; "isSignal": true; }; "titleFontSize": { "alias": "titleFontSize"; "required": false; "isSignal": true; }; "titleFontWeight": { "alias": "titleFontWeight"; "required": false; "isSignal": true; }; "backgroundColor": { "alias": "backgroundColor"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "blurEffect": { "alias": "blurEffect"; "required": false; "isSignal": true; }; "hidden": { "alias": "hidden"; "required": false; "isSignal": true; }; "hideShadow": { "alias": "hideShadow"; "required": false; "isSignal": true; }; "translucent": { "alias": "translucent"; "required": false; "isSignal": true; }; "direction": { "alias": "direction"; "required": false; "isSignal": true; }; "topInsetEnabled": { "alias": "topInsetEnabled"; "required": false; "isSignal": true; }; "userInterfaceStyle": { "alias": "userInterfaceStyle"; "required": false; "isSignal": true; }; "largeTitle": { "alias": "largeTitle"; "required": false; "isSignal": true; }; "largeTitleColor": { "alias": "largeTitleColor"; "required": false; "isSignal": true; }; "largeTitleBackgroundColor": { "alias": "largeTitleBackgroundColor"; "required": false; "isSignal": true; }; "largeTitleFontFamily": { "alias": "largeTitleFontFamily"; "required": false; "isSignal": true; }; "largeTitleFontSize": { "alias": "largeTitleFontSize"; "required": false; "isSignal": true; }; "largeTitleFontWeight": { "alias": "largeTitleFontWeight"; "required": false; "isSignal": true; }; "largeTitleHideShadow": { "alias": "largeTitleHideShadow"; "required": false; "isSignal": true; }; "backTitle": { "alias": "backTitle"; "required": false; "isSignal": true; }; "backTitleFontFamily": { "alias": "backTitleFontFamily"; "required": false; "isSignal": true; }; "backTitleFontSize": { "alias": "backTitleFontSize"; "required": false; "isSignal": true; }; "backTitleVisible": { "alias": "backTitleVisible"; "required": false; "isSignal": true; }; "backButtonDisplayMode": { "alias": "backButtonDisplayMode"; "required": false; "isSignal": true; }; "backButtonInCustomView": { "alias": "backButtonInCustomView"; "required": false; "isSignal": true; }; "disableBackButtonMenu": { "alias": "disableBackButtonMenu"; "required": false; "isSignal": true; }; "hideBackButton": { "alias": "hideBackButton"; "required": false; "isSignal": true; }; "consumeTopInset": { "alias": "consumeTopInset"; "required": false; "isSignal": true; }; "consumeLeftInset": { "alias": "consumeLeftInset"; "required": false; "isSignal": true; }; "consumeRightInset": { "alias": "consumeRightInset"; "required": false; "isSignal": true; }; "consumeBottomInset": { "alias": "consumeBottomInset"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
103
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The screen's navigation bar, as a component you write inside a page's template.
|
|
3
|
+
*
|
|
4
|
+
* Commits as `RNSScreenStackHeaderConfig`. It is a configuration node rather than a view: the
|
|
5
|
+
* native side reads its props and builds a `UINavigationItem` from them, and the only children
|
|
6
|
+
* that mean anything are `native-header-item`s. `findHeaderConfig` in `RNSScreen.mm` scans the
|
|
7
|
+
* screen's direct `reactSubviews` without recursing, which is why a page's host element is the
|
|
8
|
+
* screen itself (ADR 0004). It can still be written anywhere in the page, inside a `safe-area-view`
|
|
9
|
+
* or a layout component: `registerScreenComponents` has the engine commit it as a direct child of
|
|
10
|
+
* the screen, just after the part of the page it was written in (`registerHoist`). Keep the page's
|
|
11
|
+
* scroll view ahead of it. UIKit then finds that scroll view and collapses a large title into the
|
|
12
|
+
* inline title as the content scrolls.
|
|
13
|
+
*
|
|
14
|
+
* A header owns the top inset. A page that has one should not also wrap itself in a
|
|
15
|
+
* `safe-area-view`, or the content clears the notch twice.
|
|
16
|
+
*
|
|
17
|
+
* **A presented screen has no header.** `RNSScreenStack.mm` presents a modal or a sheet with
|
|
18
|
+
* `presentViewController:`, outside the stack's `UINavigationController`, so there is no
|
|
19
|
+
* navigation bar for this to configure and nothing adjusting the screen's insets either. A page
|
|
20
|
+
* opened with `present()` owns its own chrome: its title, its way out, and its `safe-area-view`.
|
|
21
|
+
* react-navigation has the same shape, which is why a modal there needs a stack nested inside it
|
|
22
|
+
* before it has a header.
|
|
23
|
+
*
|
|
24
|
+
* Props are the `RNSScreenStackHeaderConfig` codegen spec. `headerLeftBarButtonItems` and
|
|
25
|
+
* `headerRightBarButtonItems` are deliberately absent: native bar button items are data rather
|
|
26
|
+
* than views, and they are a later opt-in rather than the primary API.
|
|
27
|
+
*/
|
|
28
|
+
import { Component, computed, input } from '@angular/core';
|
|
29
|
+
import { ElementRef, inject } from '@angular/core';
|
|
30
|
+
import { ColorScheme } from '@ng-native/device';
|
|
31
|
+
import { NATIVE_HEADER_DEFAULTS } from "./native-bar-defaults.js";
|
|
32
|
+
import { NATIVE_HEADER_PALETTE } from "./native-header-palette.js";
|
|
33
|
+
import { ownHost } from "./own-host.js";
|
|
34
|
+
import { nativePlatform } from '@ng-native/fabric';
|
|
35
|
+
import { optionalBoolean, optionalNumber } from "./transforms.js";
|
|
36
|
+
import * as i0 from "@angular/core";
|
|
37
|
+
export class NativeHeader {
|
|
38
|
+
scheme = inject(ColorScheme);
|
|
39
|
+
palette = inject(NATIVE_HEADER_PALETTE);
|
|
40
|
+
/** The app's `withHeaderDefaults`, for whatever this header does not bind itself. */
|
|
41
|
+
defaults = inject(NATIVE_HEADER_DEFAULTS);
|
|
42
|
+
constructor() {
|
|
43
|
+
ownHost(inject(ElementRef).nativeElement, this.constructor);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The palette for the scheme the app is wearing, for whichever colours were not bound.
|
|
47
|
+
*
|
|
48
|
+
* A header prop cannot read the cascade, so left alone each platform chooses: iOS follows the
|
|
49
|
+
* system appearance and looks about right, Android takes the app theme's `colorPrimary` and is
|
|
50
|
+
* the framework blue on every screen. These fill the gap without overriding anything a call site
|
|
51
|
+
* or `withHeaderDefaults` did say. See `native-header-palette.ts` for retuning them.
|
|
52
|
+
*/
|
|
53
|
+
colours = computed(() => this.palette[this.scheme.current()], /* @ts-ignore */
|
|
54
|
+
...(ngDevMode ? [{ debugName: "colours" }] : /* istanbul ignore next */ []));
|
|
55
|
+
resolvedBackgroundColor = computed(() => this.backgroundColor() ?? this.defaults().backgroundColor ?? this.colours().background, /* @ts-ignore */
|
|
56
|
+
...(ngDevMode ? [{ debugName: "resolvedBackgroundColor" }] : /* istanbul ignore next */ []));
|
|
57
|
+
resolvedColor = computed(() => this.color() ?? this.defaults().color ?? this.colours().foreground, /* @ts-ignore */
|
|
58
|
+
...(ngDevMode ? [{ debugName: "resolvedColor" }] : /* istanbul ignore next */ []));
|
|
59
|
+
resolvedTitleColor = computed(() => this.titleColor() ?? this.defaults().titleColor ?? this.colours().foreground, /* @ts-ignore */
|
|
60
|
+
...(ngDevMode ? [{ debugName: "resolvedTitleColor" }] : /* istanbul ignore next */ []));
|
|
61
|
+
/**
|
|
62
|
+
* A large title on iOS, drawn as UIKit draws one unless the call site says otherwise: over the
|
|
63
|
+
* content with the bar clear and unlined at the top, so the page's scroll view (with
|
|
64
|
+
* `contentInsetAdjustmentBehavior="automatic"`) collapses it into the bar as it scrolls. Since
|
|
65
|
+
* iOS 26 the large title sits in the scroll view, above its content, where a bar with a
|
|
66
|
+
* background paints over it; and over an opaque bar the inline title never fades in.
|
|
67
|
+
*/
|
|
68
|
+
iosLargeTitle = computed(() => this.largeTitle() && nativePlatform() === 'ios' ? true : undefined, /* @ts-ignore */
|
|
69
|
+
...(ngDevMode ? [{ debugName: "iosLargeTitle" }] : /* istanbul ignore next */ []));
|
|
70
|
+
resolvedLargeTitleBackgroundColor = computed(() => this.largeTitleBackgroundColor() ??
|
|
71
|
+
this.defaults().largeTitleBackgroundColor ??
|
|
72
|
+
(this.iosLargeTitle() ? 'transparent' : undefined), /* @ts-ignore */
|
|
73
|
+
...(ngDevMode ? [{ debugName: "resolvedLargeTitleBackgroundColor" }] : /* istanbul ignore next */ []));
|
|
74
|
+
// --- title ---------------------------------------------------------------------------
|
|
75
|
+
/** The bar's title. Absent leaves whatever the platform would show, which is nothing. */
|
|
76
|
+
title = input(/* @ts-ignore */
|
|
77
|
+
...(ngDevMode ? [undefined, { debugName: "title" }] : /* istanbul ignore next */ []));
|
|
78
|
+
/** Title colour. */
|
|
79
|
+
titleColor = input(/* @ts-ignore */
|
|
80
|
+
...(ngDevMode ? [undefined, { debugName: "titleColor" }] : /* istanbul ignore next */ []));
|
|
81
|
+
titleFontFamily = input(/* @ts-ignore */
|
|
82
|
+
...(ngDevMode ? [undefined, { debugName: "titleFontFamily" }] : /* istanbul ignore next */ []));
|
|
83
|
+
titleFontSize = input(undefined, { ...(ngDevMode ? { debugName: "titleFontSize" } : /* istanbul ignore next */ {}), transform: optionalNumber });
|
|
84
|
+
/** A CSS font weight, `'600'` or `'bold'`. */
|
|
85
|
+
titleFontWeight = input(/* @ts-ignore */
|
|
86
|
+
...(ngDevMode ? [undefined, { debugName: "titleFontWeight" }] : /* istanbul ignore next */ []));
|
|
87
|
+
// --- the bar itself ------------------------------------------------------------------
|
|
88
|
+
/** The bar's background. Ignored unless `blurEffect` is `none`. */
|
|
89
|
+
backgroundColor = input(/* @ts-ignore */
|
|
90
|
+
...(ngDevMode ? [undefined, { debugName: "backgroundColor" }] : /* istanbul ignore next */ []));
|
|
91
|
+
/** Tint colour: the back chevron, and any header item that does not set its own. */
|
|
92
|
+
color = input(/* @ts-ignore */
|
|
93
|
+
...(ngDevMode ? [undefined, { debugName: "color" }] : /* istanbul ignore next */ []));
|
|
94
|
+
/** iOS blur material behind the bar. */
|
|
95
|
+
blurEffect = input(/* @ts-ignore */
|
|
96
|
+
...(ngDevMode ? [undefined, { debugName: "blurEffect" }] : /* istanbul ignore next */ []));
|
|
97
|
+
/** Hide the bar entirely, keeping the screen's gestures and safe areas. */
|
|
98
|
+
hidden = input(undefined, { ...(ngDevMode ? { debugName: "hidden" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
99
|
+
/** Drop the hairline under the bar. */
|
|
100
|
+
hideShadow = input(undefined, { ...(ngDevMode ? { debugName: "hideShadow" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
101
|
+
/** Let content scroll under the bar rather than starting below it. */
|
|
102
|
+
translucent = input(undefined, { ...(ngDevMode ? { debugName: "translucent" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
103
|
+
/** Lay the bar out right to left. */
|
|
104
|
+
direction = input(/* @ts-ignore */
|
|
105
|
+
...(ngDevMode ? [undefined, { debugName: "direction" }] : /* istanbul ignore next */ []));
|
|
106
|
+
/** Android: add the status bar inset to the bar's height. */
|
|
107
|
+
topInsetEnabled = input(undefined, { ...(ngDevMode ? { debugName: "topInsetEnabled" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
108
|
+
/** Force the bar light or dark regardless of the app's appearance. */
|
|
109
|
+
userInterfaceStyle = input(/* @ts-ignore */
|
|
110
|
+
...(ngDevMode ? [undefined, { debugName: "userInterfaceStyle" }] : /* istanbul ignore next */ []));
|
|
111
|
+
// --- large title (iOS) ---------------------------------------------------------------
|
|
112
|
+
/** iOS large title, which collapses into the bar as content scrolls. */
|
|
113
|
+
largeTitle = input(undefined, { ...(ngDevMode ? { debugName: "largeTitle" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
114
|
+
largeTitleColor = input(/* @ts-ignore */
|
|
115
|
+
...(ngDevMode ? [undefined, { debugName: "largeTitleColor" }] : /* istanbul ignore next */ []));
|
|
116
|
+
largeTitleBackgroundColor = input(/* @ts-ignore */
|
|
117
|
+
...(ngDevMode ? [undefined, { debugName: "largeTitleBackgroundColor" }] : /* istanbul ignore next */ []));
|
|
118
|
+
largeTitleFontFamily = input(/* @ts-ignore */
|
|
119
|
+
...(ngDevMode ? [undefined, { debugName: "largeTitleFontFamily" }] : /* istanbul ignore next */ []));
|
|
120
|
+
largeTitleFontSize = input(undefined, { ...(ngDevMode ? { debugName: "largeTitleFontSize" } : /* istanbul ignore next */ {}), transform: optionalNumber });
|
|
121
|
+
largeTitleFontWeight = input(/* @ts-ignore */
|
|
122
|
+
...(ngDevMode ? [undefined, { debugName: "largeTitleFontWeight" }] : /* istanbul ignore next */ []));
|
|
123
|
+
largeTitleHideShadow = input(undefined, { ...(ngDevMode ? { debugName: "largeTitleHideShadow" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
124
|
+
// --- the back button -----------------------------------------------------------------
|
|
125
|
+
/**
|
|
126
|
+
* What *this* screen's back button says. `RNSScreenStackHeaderConfig.mm` writes it onto the
|
|
127
|
+
* previous screen's navigation item, falling back to that screen's title, which is why an
|
|
128
|
+
* unset back title reads as the title of the screen you came from.
|
|
129
|
+
*/
|
|
130
|
+
backTitle = input(/* @ts-ignore */
|
|
131
|
+
...(ngDevMode ? [undefined, { debugName: "backTitle" }] : /* istanbul ignore next */ []));
|
|
132
|
+
backTitleFontFamily = input(/* @ts-ignore */
|
|
133
|
+
...(ngDevMode ? [undefined, { debugName: "backTitleFontFamily" }] : /* istanbul ignore next */ []));
|
|
134
|
+
backTitleFontSize = input(undefined, { ...(ngDevMode ? { debugName: "backTitleFontSize" } : /* istanbul ignore next */ {}), transform: optionalNumber });
|
|
135
|
+
/** Show the back button's title at all. Native defaults this to true. */
|
|
136
|
+
backTitleVisible = input(undefined, { ...(ngDevMode ? { debugName: "backTitleVisible" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
137
|
+
/** iOS 14+: how aggressively the back title is shortened when space is tight. */
|
|
138
|
+
backButtonDisplayMode = input(/* @ts-ignore */
|
|
139
|
+
...(ngDevMode ? [undefined, { debugName: "backButtonDisplayMode" }] : /* istanbul ignore next */ []));
|
|
140
|
+
/** Put the back button in the custom view slot, so a `back` header item can replace it. */
|
|
141
|
+
backButtonInCustomView = input(undefined, { ...(ngDevMode ? { debugName: "backButtonInCustomView" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
142
|
+
/** Drop the long-press menu of the screens below. */
|
|
143
|
+
disableBackButtonMenu = input(undefined, { ...(ngDevMode ? { debugName: "disableBackButtonMenu" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
144
|
+
/** Hide the back button without disabling the swipe-back gesture. */
|
|
145
|
+
hideBackButton = input(undefined, { ...(ngDevMode ? { debugName: "hideBackButton" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
146
|
+
// --- insets (experimental in react-native-screens) -------------------------------------
|
|
147
|
+
/**
|
|
148
|
+
* Android: whether the bar takes the status bar's height as padding, so its title sits below
|
|
149
|
+
* it rather than under it.
|
|
150
|
+
*
|
|
151
|
+
* Defaulted per platform because getting it wrong is invisible in a screenshot until you look
|
|
152
|
+
* for it: react-native-screens leaves it false, Android 15 draws every app edge to edge, and
|
|
153
|
+
* the result is a title behind the clock. iOS has a navigation controller doing this itself.
|
|
154
|
+
*/
|
|
155
|
+
consumeTopInset = input(nativePlatform() === 'android', { ...(ngDevMode ? { debugName: "consumeTopInset" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
156
|
+
consumeLeftInset = input(undefined, { ...(ngDevMode ? { debugName: "consumeLeftInset" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
157
|
+
consumeRightInset = input(undefined, { ...(ngDevMode ? { debugName: "consumeRightInset" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
158
|
+
consumeBottomInset = input(undefined, { ...(ngDevMode ? { debugName: "consumeBottomInset" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
159
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
160
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: NativeHeader, isStandalone: true, selector: "native-header", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, titleColor: { classPropertyName: "titleColor", publicName: "titleColor", isSignal: true, isRequired: false, transformFunction: null }, titleFontFamily: { classPropertyName: "titleFontFamily", publicName: "titleFontFamily", isSignal: true, isRequired: false, transformFunction: null }, titleFontSize: { classPropertyName: "titleFontSize", publicName: "titleFontSize", isSignal: true, isRequired: false, transformFunction: null }, titleFontWeight: { classPropertyName: "titleFontWeight", publicName: "titleFontWeight", isSignal: true, isRequired: false, transformFunction: null }, backgroundColor: { classPropertyName: "backgroundColor", publicName: "backgroundColor", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, blurEffect: { classPropertyName: "blurEffect", publicName: "blurEffect", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null }, hideShadow: { classPropertyName: "hideShadow", publicName: "hideShadow", isSignal: true, isRequired: false, transformFunction: null }, translucent: { classPropertyName: "translucent", publicName: "translucent", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, topInsetEnabled: { classPropertyName: "topInsetEnabled", publicName: "topInsetEnabled", isSignal: true, isRequired: false, transformFunction: null }, userInterfaceStyle: { classPropertyName: "userInterfaceStyle", publicName: "userInterfaceStyle", isSignal: true, isRequired: false, transformFunction: null }, largeTitle: { classPropertyName: "largeTitle", publicName: "largeTitle", isSignal: true, isRequired: false, transformFunction: null }, largeTitleColor: { classPropertyName: "largeTitleColor", publicName: "largeTitleColor", isSignal: true, isRequired: false, transformFunction: null }, largeTitleBackgroundColor: { classPropertyName: "largeTitleBackgroundColor", publicName: "largeTitleBackgroundColor", isSignal: true, isRequired: false, transformFunction: null }, largeTitleFontFamily: { classPropertyName: "largeTitleFontFamily", publicName: "largeTitleFontFamily", isSignal: true, isRequired: false, transformFunction: null }, largeTitleFontSize: { classPropertyName: "largeTitleFontSize", publicName: "largeTitleFontSize", isSignal: true, isRequired: false, transformFunction: null }, largeTitleFontWeight: { classPropertyName: "largeTitleFontWeight", publicName: "largeTitleFontWeight", isSignal: true, isRequired: false, transformFunction: null }, largeTitleHideShadow: { classPropertyName: "largeTitleHideShadow", publicName: "largeTitleHideShadow", isSignal: true, isRequired: false, transformFunction: null }, backTitle: { classPropertyName: "backTitle", publicName: "backTitle", isSignal: true, isRequired: false, transformFunction: null }, backTitleFontFamily: { classPropertyName: "backTitleFontFamily", publicName: "backTitleFontFamily", isSignal: true, isRequired: false, transformFunction: null }, backTitleFontSize: { classPropertyName: "backTitleFontSize", publicName: "backTitleFontSize", isSignal: true, isRequired: false, transformFunction: null }, backTitleVisible: { classPropertyName: "backTitleVisible", publicName: "backTitleVisible", isSignal: true, isRequired: false, transformFunction: null }, backButtonDisplayMode: { classPropertyName: "backButtonDisplayMode", publicName: "backButtonDisplayMode", isSignal: true, isRequired: false, transformFunction: null }, backButtonInCustomView: { classPropertyName: "backButtonInCustomView", publicName: "backButtonInCustomView", isSignal: true, isRequired: false, transformFunction: null }, disableBackButtonMenu: { classPropertyName: "disableBackButtonMenu", publicName: "disableBackButtonMenu", isSignal: true, isRequired: false, transformFunction: null }, hideBackButton: { classPropertyName: "hideBackButton", publicName: "hideBackButton", isSignal: true, isRequired: false, transformFunction: null }, consumeTopInset: { classPropertyName: "consumeTopInset", publicName: "consumeTopInset", isSignal: true, isRequired: false, transformFunction: null }, consumeLeftInset: { classPropertyName: "consumeLeftInset", publicName: "consumeLeftInset", isSignal: true, isRequired: false, transformFunction: null }, consumeRightInset: { classPropertyName: "consumeRightInset", publicName: "consumeRightInset", isSignal: true, isRequired: false, transformFunction: null }, consumeBottomInset: { classPropertyName: "consumeBottomInset", publicName: "consumeBottomInset", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "title": "title()", "titleColor": "resolvedTitleColor()", "titleFontFamily": "titleFontFamily() ?? defaults().titleFontFamily", "titleFontSize": "titleFontSize() ?? defaults().titleFontSize", "titleFontWeight": "titleFontWeight() ?? defaults().titleFontWeight", "backgroundColor": "resolvedBackgroundColor()", "color": "resolvedColor()", "blurEffect": "blurEffect() ?? defaults().blurEffect", "hidden": "hidden()", "hideShadow": "hideShadow() ?? defaults().hideShadow", "translucent": "translucent() ?? defaults().translucent ?? iosLargeTitle()", "direction": "direction()", "topInsetEnabled": "topInsetEnabled()", "userInterfaceStyle": "userInterfaceStyle() ?? defaults().userInterfaceStyle", "largeTitle": "largeTitle()", "largeTitleColor": "largeTitleColor() ?? defaults().largeTitleColor", "largeTitleBackgroundColor": "resolvedLargeTitleBackgroundColor()", "largeTitleFontFamily": "largeTitleFontFamily() ?? defaults().largeTitleFontFamily", "largeTitleFontSize": "largeTitleFontSize() ?? defaults().largeTitleFontSize", "largeTitleFontWeight": "largeTitleFontWeight() ?? defaults().largeTitleFontWeight", "largeTitleHideShadow": "largeTitleHideShadow() ?? defaults().largeTitleHideShadow ?? iosLargeTitle()", "backTitle": "backTitle()", "backTitleFontFamily": "backTitleFontFamily() ?? defaults().backTitleFontFamily", "backTitleFontSize": "backTitleFontSize() ?? defaults().backTitleFontSize", "backTitleVisible": "backTitleVisible()", "backButtonDisplayMode": "backButtonDisplayMode() ?? defaults().backButtonDisplayMode", "backButtonInCustomView": "backButtonInCustomView()", "disableBackButtonMenu": "disableBackButtonMenu()", "hideBackButton": "hideBackButton()", "consumeTopInset": "consumeTopInset()", "consumeLeftInset": "consumeLeftInset()", "consumeRightInset": "consumeRightInset()", "consumeBottomInset": "consumeBottomInset()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
161
|
+
}
|
|
162
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeHeader, decorators: [{
|
|
163
|
+
type: Component,
|
|
164
|
+
args: [{
|
|
165
|
+
selector: 'native-header',
|
|
166
|
+
template: '<ng-content />',
|
|
167
|
+
host: {
|
|
168
|
+
'[title]': 'title()',
|
|
169
|
+
'[titleColor]': 'resolvedTitleColor()',
|
|
170
|
+
'[titleFontFamily]': 'titleFontFamily() ?? defaults().titleFontFamily',
|
|
171
|
+
'[titleFontSize]': 'titleFontSize() ?? defaults().titleFontSize',
|
|
172
|
+
'[titleFontWeight]': 'titleFontWeight() ?? defaults().titleFontWeight',
|
|
173
|
+
'[backgroundColor]': 'resolvedBackgroundColor()',
|
|
174
|
+
'[color]': 'resolvedColor()',
|
|
175
|
+
'[blurEffect]': 'blurEffect() ?? defaults().blurEffect',
|
|
176
|
+
'[hidden]': 'hidden()',
|
|
177
|
+
'[hideShadow]': 'hideShadow() ?? defaults().hideShadow',
|
|
178
|
+
'[translucent]': 'translucent() ?? defaults().translucent ?? iosLargeTitle()',
|
|
179
|
+
'[direction]': 'direction()',
|
|
180
|
+
'[topInsetEnabled]': 'topInsetEnabled()',
|
|
181
|
+
'[userInterfaceStyle]': 'userInterfaceStyle() ?? defaults().userInterfaceStyle',
|
|
182
|
+
'[largeTitle]': 'largeTitle()',
|
|
183
|
+
'[largeTitleColor]': 'largeTitleColor() ?? defaults().largeTitleColor',
|
|
184
|
+
'[largeTitleBackgroundColor]': 'resolvedLargeTitleBackgroundColor()',
|
|
185
|
+
'[largeTitleFontFamily]': 'largeTitleFontFamily() ?? defaults().largeTitleFontFamily',
|
|
186
|
+
'[largeTitleFontSize]': 'largeTitleFontSize() ?? defaults().largeTitleFontSize',
|
|
187
|
+
'[largeTitleFontWeight]': 'largeTitleFontWeight() ?? defaults().largeTitleFontWeight',
|
|
188
|
+
'[largeTitleHideShadow]': 'largeTitleHideShadow() ?? defaults().largeTitleHideShadow ?? iosLargeTitle()',
|
|
189
|
+
'[backTitle]': 'backTitle()',
|
|
190
|
+
'[backTitleFontFamily]': 'backTitleFontFamily() ?? defaults().backTitleFontFamily',
|
|
191
|
+
'[backTitleFontSize]': 'backTitleFontSize() ?? defaults().backTitleFontSize',
|
|
192
|
+
'[backTitleVisible]': 'backTitleVisible()',
|
|
193
|
+
'[backButtonDisplayMode]': 'backButtonDisplayMode() ?? defaults().backButtonDisplayMode',
|
|
194
|
+
'[backButtonInCustomView]': 'backButtonInCustomView()',
|
|
195
|
+
'[disableBackButtonMenu]': 'disableBackButtonMenu()',
|
|
196
|
+
'[hideBackButton]': 'hideBackButton()',
|
|
197
|
+
'[consumeTopInset]': 'consumeTopInset()',
|
|
198
|
+
'[consumeLeftInset]': 'consumeLeftInset()',
|
|
199
|
+
'[consumeRightInset]': 'consumeRightInset()',
|
|
200
|
+
'[consumeBottomInset]': 'consumeBottomInset()',
|
|
201
|
+
},
|
|
202
|
+
}]
|
|
203
|
+
}], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], titleColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleColor", required: false }] }], titleFontFamily: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleFontFamily", required: false }] }], titleFontSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleFontSize", required: false }] }], titleFontWeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleFontWeight", required: false }] }], backgroundColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "backgroundColor", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], blurEffect: [{ type: i0.Input, args: [{ isSignal: true, alias: "blurEffect", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], hideShadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideShadow", required: false }] }], translucent: [{ type: i0.Input, args: [{ isSignal: true, alias: "translucent", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], topInsetEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "topInsetEnabled", required: false }] }], userInterfaceStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInterfaceStyle", required: false }] }], largeTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "largeTitle", required: false }] }], largeTitleColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "largeTitleColor", required: false }] }], largeTitleBackgroundColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "largeTitleBackgroundColor", required: false }] }], largeTitleFontFamily: [{ type: i0.Input, args: [{ isSignal: true, alias: "largeTitleFontFamily", required: false }] }], largeTitleFontSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "largeTitleFontSize", required: false }] }], largeTitleFontWeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "largeTitleFontWeight", required: false }] }], largeTitleHideShadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "largeTitleHideShadow", required: false }] }], backTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "backTitle", required: false }] }], backTitleFontFamily: [{ type: i0.Input, args: [{ isSignal: true, alias: "backTitleFontFamily", required: false }] }], backTitleFontSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "backTitleFontSize", required: false }] }], backTitleVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "backTitleVisible", required: false }] }], backButtonDisplayMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "backButtonDisplayMode", required: false }] }], backButtonInCustomView: [{ type: i0.Input, args: [{ isSignal: true, alias: "backButtonInCustomView", required: false }] }], disableBackButtonMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableBackButtonMenu", required: false }] }], hideBackButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideBackButton", required: false }] }], consumeTopInset: [{ type: i0.Input, args: [{ isSignal: true, alias: "consumeTopInset", required: false }] }], consumeLeftInset: [{ type: i0.Input, args: [{ isSignal: true, alias: "consumeLeftInset", required: false }] }], consumeRightInset: [{ type: i0.Input, args: [{ isSignal: true, alias: "consumeRightInset", required: false }] }], consumeBottomInset: [{ type: i0.Input, args: [{ isSignal: true, alias: "consumeBottomInset", required: false }] }] } });
|
|
204
|
+
//# sourceMappingURL=native-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-header.js","sourceRoot":"","sources":["../src/native-header.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;;AA2ElE,MAAM,OAAO,YAAY;IACN,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAC7B,OAAO,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACzD,qFAAqF;IAClE,QAAQ,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAE7D;QACE,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,aAA2B,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5E,CAAC;IAED;;;;;;;OAOG;IACc,OAAO,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gFAAC,CAAC;IAE5D,uBAAuB,GAAG,QAAQ,CACnD,GAAG,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,eAAe,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,UAAU;gGAC7F,CAAC;IACiB,aAAa,GAAG,QAAQ,CACzC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,UAAU;sFACzE,CAAC;IACiB,kBAAkB,GAAG,QAAQ,CAC9C,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,UAAU;2FACnF,CAAC;IAEF;;;;;;OAMG;IACgB,aAAa,GAAG,QAAQ,CAAC,GAAG,EAAE,CAC/C,IAAI,CAAC,UAAU,EAAE,IAAI,cAAc,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;sFACnE,CAAC;IACiB,iCAAiC,GAAG,QAAQ,CAC7D,GAAG,EAAE,CACH,IAAI,CAAC,yBAAyB,EAAE;QAChC,IAAI,CAAC,QAAQ,EAAE,CAAC,yBAAyB;QACzC,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;0GACrD,CAAC;IAEF,wFAAwF;IAExF,yFAAyF;IAChF,KAAK,GAAG,KAAK;yFAAU,CAAC;IACjC,oBAAoB;IACX,UAAU,GAAG,KAAK;8FAAmB,CAAC;IACtC,eAAe,GAAG,KAAK;mGAAU,CAAC;IAClC,aAAa,GAAG,KAAK,CAAC,SAAS,qFAAI,SAAS,EAAE,cAAc,GAAG,CAAC;IACzE,8CAA8C;IACrC,eAAe,GAAG,KAAK;mGAAU,CAAC;IAE3C,wFAAwF;IAExF,mEAAmE;IAC1D,eAAe,GAAG,KAAK;mGAAmB,CAAC;IACpD,oFAAoF;IAC3E,KAAK,GAAG,KAAK;yFAAmB,CAAC;IAC1C,wCAAwC;IAC/B,UAAU,GAAG,KAAK;8FAAc,CAAC;IAC1C,2EAA2E;IAClE,MAAM,GAAG,KAAK,CAAC,SAAS,8EAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IACnE,uCAAuC;IAC9B,UAAU,GAAG,KAAK,CAAC,SAAS,kFAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IACvE,sEAAsE;IAC7D,WAAW,GAAG,KAAK,CAAC,SAAS,mFAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IACxE,qCAAqC;IAC5B,SAAS,GAAG,KAAK;6FAAmB,CAAC;IAC9C,6DAA6D;IACpD,eAAe,GAAG,KAAK,CAAC,SAAS,uFAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IAC5E,sEAAsE;IAC7D,kBAAkB,GAAG,KAAK;sGAAwB,CAAC;IAE5D,wFAAwF;IAExF,wEAAwE;IAC/D,UAAU,GAAG,KAAK,CAAC,SAAS,kFAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IAC9D,eAAe,GAAG,KAAK;mGAAmB,CAAC;IAC3C,yBAAyB,GAAG,KAAK;6GAAmB,CAAC;IACrD,oBAAoB,GAAG,KAAK;wGAAU,CAAC;IACvC,kBAAkB,GAAG,KAAK,CAAC,SAAS,0FAAI,SAAS,EAAE,cAAc,GAAG,CAAC;IACrE,oBAAoB,GAAG,KAAK;wGAAU,CAAC;IACvC,oBAAoB,GAAG,KAAK,CAAC,SAAS,4FAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IAEjF,wFAAwF;IAExF;;;;OAIG;IACM,SAAS,GAAG,KAAK;6FAAU,CAAC;IAC5B,mBAAmB,GAAG,KAAK;uGAAU,CAAC;IACtC,iBAAiB,GAAG,KAAK,CAAC,SAAS,yFAAI,SAAS,EAAE,cAAc,GAAG,CAAC;IAC7E,yEAAyE;IAChE,gBAAgB,GAAG,KAAK,CAAC,SAAS,wFAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IAC7E,iFAAiF;IACxE,qBAAqB,GAAG,KAAK;yGAAyB,CAAC;IAChE,2FAA2F;IAClF,sBAAsB,GAAG,KAAK,CAAC,SAAS,8FAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IACnF,qDAAqD;IAC5C,qBAAqB,GAAG,KAAK,CAAC,SAAS,6FAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IAClF,qEAAqE;IAC5D,cAAc,GAAG,KAAK,CAAC,SAAS,sFAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IAE3E,0FAA0F;IAE1F;;;;;;;OAOG;IACM,eAAe,GAAG,KAAK,CAAC,cAAc,EAAE,KAAK,SAAS,uFAC7D,SAAS,EAAE,eAAe,GAC1B,CAAC;IACM,gBAAgB,GAAG,KAAK,CAAC,SAAS,wFAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IACpE,iBAAiB,GAAG,KAAK,CAAC,SAAS,yFAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IACrE,kBAAkB,GAAG,KAAK,CAAC,SAAS,0FAAI,SAAS,EAAE,eAAe,GAAG,CAAC;uGA9HpE,YAAY;2FAAZ,YAAY,6uNAtCb,gBAAgB;;2FAsCf,YAAY;kBAxCxB,SAAS;mBAAC;oBACT,QAAQ,EAAE,eAAe;oBACzB,QAAQ,EAAE,gBAAgB;oBAC1B,IAAI,EAAE;wBACJ,SAAS,EAAE,SAAS;wBACpB,cAAc,EAAE,sBAAsB;wBACtC,mBAAmB,EAAE,iDAAiD;wBACtE,iBAAiB,EAAE,6CAA6C;wBAChE,mBAAmB,EAAE,iDAAiD;wBACtE,mBAAmB,EAAE,2BAA2B;wBAChD,SAAS,EAAE,iBAAiB;wBAC5B,cAAc,EAAE,uCAAuC;wBACvD,UAAU,EAAE,UAAU;wBACtB,cAAc,EAAE,uCAAuC;wBACvD,eAAe,EAAE,4DAA4D;wBAC7E,aAAa,EAAE,aAAa;wBAC5B,mBAAmB,EAAE,mBAAmB;wBACxC,sBAAsB,EAAE,uDAAuD;wBAC/E,cAAc,EAAE,cAAc;wBAC9B,mBAAmB,EAAE,iDAAiD;wBACtE,6BAA6B,EAAE,qCAAqC;wBACpE,wBAAwB,EAAE,2DAA2D;wBACrF,sBAAsB,EAAE,uDAAuD;wBAC/E,wBAAwB,EAAE,2DAA2D;wBACrF,wBAAwB,EACtB,8EAA8E;wBAChF,aAAa,EAAE,aAAa;wBAC5B,uBAAuB,EAAE,yDAAyD;wBAClF,qBAAqB,EAAE,qDAAqD;wBAC5E,oBAAoB,EAAE,oBAAoB;wBAC1C,yBAAyB,EAAE,6DAA6D;wBACxF,0BAA0B,EAAE,0BAA0B;wBACtD,yBAAyB,EAAE,yBAAyB;wBACpD,kBAAkB,EAAE,kBAAkB;wBACtC,mBAAmB,EAAE,mBAAmB;wBACxC,oBAAoB,EAAE,oBAAoB;wBAC1C,qBAAqB,EAAE,qBAAqB;wBAC5C,sBAAsB,EAAE,sBAAsB;qBAC/C;iBACF","sourcesContent":["/**\n * The screen's navigation bar, as a component you write inside a page's template.\n *\n * Commits as `RNSScreenStackHeaderConfig`. It is a configuration node rather than a view: the\n * native side reads its props and builds a `UINavigationItem` from them, and the only children\n * that mean anything are `native-header-item`s. `findHeaderConfig` in `RNSScreen.mm` scans the\n * screen's direct `reactSubviews` without recursing, which is why a page's host element is the\n * screen itself (ADR 0004). It can still be written anywhere in the page, inside a `safe-area-view`\n * or a layout component: `registerScreenComponents` has the engine commit it as a direct child of\n * the screen, just after the part of the page it was written in (`registerHoist`). Keep the page's\n * scroll view ahead of it. UIKit then finds that scroll view and collapses a large title into the\n * inline title as the content scrolls.\n *\n * A header owns the top inset. A page that has one should not also wrap itself in a\n * `safe-area-view`, or the content clears the notch twice.\n *\n * **A presented screen has no header.** `RNSScreenStack.mm` presents a modal or a sheet with\n * `presentViewController:`, outside the stack's `UINavigationController`, so there is no\n * navigation bar for this to configure and nothing adjusting the screen's insets either. A page\n * opened with `present()` owns its own chrome: its title, its way out, and its `safe-area-view`.\n * react-navigation has the same shape, which is why a modal there needs a stack nested inside it\n * before it has a header.\n *\n * Props are the `RNSScreenStackHeaderConfig` codegen spec. `headerLeftBarButtonItems` and\n * `headerRightBarButtonItems` are deliberately absent: native bar button items are data rather\n * than views, and they are a later opt-in rather than the primary API.\n */\nimport { Component, computed, input } from '@angular/core';\nimport { ElementRef, inject } from '@angular/core';\nimport { ColorScheme } from '@ng-native/device';\nimport { NATIVE_HEADER_DEFAULTS } from './native-bar-defaults.ts';\nimport { NATIVE_HEADER_PALETTE } from './native-header-palette.ts';\nimport type { EngineNode } from '@ng-native/fabric';\nimport { ownHost } from './own-host.ts';\nimport { nativePlatform } from '@ng-native/fabric';\nimport { optionalBoolean, optionalNumber } from './transforms.ts';\n\n/** Where the back button's title comes from, on iOS 14+. */\nexport type BackButtonDisplayMode = 'minimal' | 'default' | 'generic';\n\n/** Direction the bar lays out in, for right-to-left languages. */\nexport type HeaderDirection = 'ltr' | 'rtl';\n\n/** How the bar blurs what scrolls under it. `none` paints `backgroundColor` instead. */\nexport type BlurEffect =\n | 'none'\n | 'extraLight'\n | 'light'\n | 'dark'\n | 'regular'\n | 'prominent'\n | 'systemUltraThinMaterial'\n | 'systemThinMaterial'\n | 'systemMaterial'\n | 'systemThickMaterial'\n | 'systemChromeMaterial'\n | 'systemUltraThinMaterialLight'\n | 'systemThinMaterialLight'\n | 'systemMaterialLight'\n | 'systemThickMaterialLight'\n | 'systemChromeMaterialLight'\n | 'systemUltraThinMaterialDark'\n | 'systemThinMaterialDark'\n | 'systemMaterialDark'\n | 'systemThickMaterialDark'\n | 'systemChromeMaterialDark';\n\n/** Overrides the bar's light/dark appearance independently of the app's. */\nexport type HeaderInterfaceStyle = 'unspecified' | 'light' | 'dark';\n\n@Component({\n selector: 'native-header',\n template: '<ng-content />',\n host: {\n '[title]': 'title()',\n '[titleColor]': 'resolvedTitleColor()',\n '[titleFontFamily]': 'titleFontFamily() ?? defaults().titleFontFamily',\n '[titleFontSize]': 'titleFontSize() ?? defaults().titleFontSize',\n '[titleFontWeight]': 'titleFontWeight() ?? defaults().titleFontWeight',\n '[backgroundColor]': 'resolvedBackgroundColor()',\n '[color]': 'resolvedColor()',\n '[blurEffect]': 'blurEffect() ?? defaults().blurEffect',\n '[hidden]': 'hidden()',\n '[hideShadow]': 'hideShadow() ?? defaults().hideShadow',\n '[translucent]': 'translucent() ?? defaults().translucent ?? iosLargeTitle()',\n '[direction]': 'direction()',\n '[topInsetEnabled]': 'topInsetEnabled()',\n '[userInterfaceStyle]': 'userInterfaceStyle() ?? defaults().userInterfaceStyle',\n '[largeTitle]': 'largeTitle()',\n '[largeTitleColor]': 'largeTitleColor() ?? defaults().largeTitleColor',\n '[largeTitleBackgroundColor]': 'resolvedLargeTitleBackgroundColor()',\n '[largeTitleFontFamily]': 'largeTitleFontFamily() ?? defaults().largeTitleFontFamily',\n '[largeTitleFontSize]': 'largeTitleFontSize() ?? defaults().largeTitleFontSize',\n '[largeTitleFontWeight]': 'largeTitleFontWeight() ?? defaults().largeTitleFontWeight',\n '[largeTitleHideShadow]':\n 'largeTitleHideShadow() ?? defaults().largeTitleHideShadow ?? iosLargeTitle()',\n '[backTitle]': 'backTitle()',\n '[backTitleFontFamily]': 'backTitleFontFamily() ?? defaults().backTitleFontFamily',\n '[backTitleFontSize]': 'backTitleFontSize() ?? defaults().backTitleFontSize',\n '[backTitleVisible]': 'backTitleVisible()',\n '[backButtonDisplayMode]': 'backButtonDisplayMode() ?? defaults().backButtonDisplayMode',\n '[backButtonInCustomView]': 'backButtonInCustomView()',\n '[disableBackButtonMenu]': 'disableBackButtonMenu()',\n '[hideBackButton]': 'hideBackButton()',\n '[consumeTopInset]': 'consumeTopInset()',\n '[consumeLeftInset]': 'consumeLeftInset()',\n '[consumeRightInset]': 'consumeRightInset()',\n '[consumeBottomInset]': 'consumeBottomInset()',\n },\n})\nexport class NativeHeader {\n private readonly scheme = inject(ColorScheme);\n private readonly palette = inject(NATIVE_HEADER_PALETTE);\n /** The app's `withHeaderDefaults`, for whatever this header does not bind itself. */\n protected readonly defaults = inject(NATIVE_HEADER_DEFAULTS);\n\n constructor() {\n ownHost(inject(ElementRef).nativeElement as EngineNode, this.constructor);\n }\n\n /**\n * The palette for the scheme the app is wearing, for whichever colours were not bound.\n *\n * A header prop cannot read the cascade, so left alone each platform chooses: iOS follows the\n * system appearance and looks about right, Android takes the app theme's `colorPrimary` and is\n * the framework blue on every screen. These fill the gap without overriding anything a call site\n * or `withHeaderDefaults` did say. See `native-header-palette.ts` for retuning them.\n */\n private readonly colours = computed(() => this.palette[this.scheme.current()]);\n\n protected readonly resolvedBackgroundColor = computed(\n () => this.backgroundColor() ?? this.defaults().backgroundColor ?? this.colours().background,\n );\n protected readonly resolvedColor = computed(\n () => this.color() ?? this.defaults().color ?? this.colours().foreground,\n );\n protected readonly resolvedTitleColor = computed(\n () => this.titleColor() ?? this.defaults().titleColor ?? this.colours().foreground,\n );\n\n /**\n * A large title on iOS, drawn as UIKit draws one unless the call site says otherwise: over the\n * content with the bar clear and unlined at the top, so the page's scroll view (with\n * `contentInsetAdjustmentBehavior=\"automatic\"`) collapses it into the bar as it scrolls. Since\n * iOS 26 the large title sits in the scroll view, above its content, where a bar with a\n * background paints over it; and over an opaque bar the inline title never fades in.\n */\n protected readonly iosLargeTitle = computed(() =>\n this.largeTitle() && nativePlatform() === 'ios' ? true : undefined,\n );\n protected readonly resolvedLargeTitleBackgroundColor = computed(\n () =>\n this.largeTitleBackgroundColor() ??\n this.defaults().largeTitleBackgroundColor ??\n (this.iosLargeTitle() ? 'transparent' : undefined),\n );\n\n // --- title ---------------------------------------------------------------------------\n\n /** The bar's title. Absent leaves whatever the platform would show, which is nothing. */\n readonly title = input<string>();\n /** Title colour. */\n readonly titleColor = input<string | number>();\n readonly titleFontFamily = input<string>();\n readonly titleFontSize = input(undefined, { transform: optionalNumber });\n /** A CSS font weight, `'600'` or `'bold'`. */\n readonly titleFontWeight = input<string>();\n\n // --- the bar itself ------------------------------------------------------------------\n\n /** The bar's background. Ignored unless `blurEffect` is `none`. */\n readonly backgroundColor = input<string | number>();\n /** Tint colour: the back chevron, and any header item that does not set its own. */\n readonly color = input<string | number>();\n /** iOS blur material behind the bar. */\n readonly blurEffect = input<BlurEffect>();\n /** Hide the bar entirely, keeping the screen's gestures and safe areas. */\n readonly hidden = input(undefined, { transform: optionalBoolean });\n /** Drop the hairline under the bar. */\n readonly hideShadow = input(undefined, { transform: optionalBoolean });\n /** Let content scroll under the bar rather than starting below it. */\n readonly translucent = input(undefined, { transform: optionalBoolean });\n /** Lay the bar out right to left. */\n readonly direction = input<HeaderDirection>();\n /** Android: add the status bar inset to the bar's height. */\n readonly topInsetEnabled = input(undefined, { transform: optionalBoolean });\n /** Force the bar light or dark regardless of the app's appearance. */\n readonly userInterfaceStyle = input<HeaderInterfaceStyle>();\n\n // --- large title (iOS) ---------------------------------------------------------------\n\n /** iOS large title, which collapses into the bar as content scrolls. */\n readonly largeTitle = input(undefined, { transform: optionalBoolean });\n readonly largeTitleColor = input<string | number>();\n readonly largeTitleBackgroundColor = input<string | number>();\n readonly largeTitleFontFamily = input<string>();\n readonly largeTitleFontSize = input(undefined, { transform: optionalNumber });\n readonly largeTitleFontWeight = input<string>();\n readonly largeTitleHideShadow = input(undefined, { transform: optionalBoolean });\n\n // --- the back button -----------------------------------------------------------------\n\n /**\n * What *this* screen's back button says. `RNSScreenStackHeaderConfig.mm` writes it onto the\n * previous screen's navigation item, falling back to that screen's title, which is why an\n * unset back title reads as the title of the screen you came from.\n */\n readonly backTitle = input<string>();\n readonly backTitleFontFamily = input<string>();\n readonly backTitleFontSize = input(undefined, { transform: optionalNumber });\n /** Show the back button's title at all. Native defaults this to true. */\n readonly backTitleVisible = input(undefined, { transform: optionalBoolean });\n /** iOS 14+: how aggressively the back title is shortened when space is tight. */\n readonly backButtonDisplayMode = input<BackButtonDisplayMode>();\n /** Put the back button in the custom view slot, so a `back` header item can replace it. */\n readonly backButtonInCustomView = input(undefined, { transform: optionalBoolean });\n /** Drop the long-press menu of the screens below. */\n readonly disableBackButtonMenu = input(undefined, { transform: optionalBoolean });\n /** Hide the back button without disabling the swipe-back gesture. */\n readonly hideBackButton = input(undefined, { transform: optionalBoolean });\n\n // --- insets (experimental in react-native-screens) -------------------------------------\n\n /**\n * Android: whether the bar takes the status bar's height as padding, so its title sits below\n * it rather than under it.\n *\n * Defaulted per platform because getting it wrong is invisible in a screenshot until you look\n * for it: react-native-screens leaves it false, Android 15 draws every app edge to edge, and\n * the result is a title behind the clock. iOS has a navigation controller doing this itself.\n */\n readonly consumeTopInset = input(nativePlatform() === 'android', {\n transform: optionalBoolean,\n });\n readonly consumeLeftInset = input(undefined, { transform: optionalBoolean });\n readonly consumeRightInset = input(undefined, { transform: optionalBoolean });\n readonly consumeBottomInset = input(undefined, { transform: optionalBoolean });\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Links into the app, with the stack they would have been reached through.
|
|
3
|
+
*
|
|
4
|
+
* A deep link to `/settings/notifications` would otherwise open that page alone, with no Back:
|
|
5
|
+
* the history holds one entry, and the stack one screen. An app that says which page a link
|
|
6
|
+
* belongs under gets that page first and the link pushed on top of it - real navigations, so
|
|
7
|
+
* each screen has its own route and Back pops the way it would have. The parent's own parent is
|
|
8
|
+
* asked in turn, so a link five screens deep opens on all five.
|
|
9
|
+
*
|
|
10
|
+
* Which page is the app's call, not a rule: the detail column of a split view, for one, must not
|
|
11
|
+
* have its empty state pushed beneath it.
|
|
12
|
+
*/
|
|
13
|
+
/** The url to open beneath `url`, or null to open it on its own. */
|
|
14
|
+
export type LinkParent = (url: string) => string | null;
|
|
15
|
+
/** What following a link needs of the router. */
|
|
16
|
+
export interface LinkRouter {
|
|
17
|
+
readonly url: string;
|
|
18
|
+
navigateByUrl(url: string): Promise<boolean>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The pages a link sits under, outermost first: its parent, that page's parent, and so on up, as
|
|
22
|
+
* far as `parentOf` goes. A cycle stops where it would repeat.
|
|
23
|
+
*/
|
|
24
|
+
export declare function linkAncestry(url: string, parentOf: LinkParent): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Open a link on top of the pages it belongs under: each one a navigation of its own, so each is a
|
|
27
|
+
* screen with its own route and Back retraces them. Pages already showing, from the page on screen
|
|
28
|
+
* down, are not opened again.
|
|
29
|
+
*/
|
|
30
|
+
export declare function followLink(router: LinkRouter, url: string, parentOf: LinkParent): Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Links into the app, with the stack they would have been reached through.
|
|
3
|
+
*
|
|
4
|
+
* A deep link to `/settings/notifications` would otherwise open that page alone, with no Back:
|
|
5
|
+
* the history holds one entry, and the stack one screen. An app that says which page a link
|
|
6
|
+
* belongs under gets that page first and the link pushed on top of it - real navigations, so
|
|
7
|
+
* each screen has its own route and Back pops the way it would have. The parent's own parent is
|
|
8
|
+
* asked in turn, so a link five screens deep opens on all five.
|
|
9
|
+
*
|
|
10
|
+
* Which page is the app's call, not a rule: the detail column of a split view, for one, must not
|
|
11
|
+
* have its empty state pushed beneath it.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The pages a link sits under, outermost first: its parent, that page's parent, and so on up, as
|
|
15
|
+
* far as `parentOf` goes. A cycle stops where it would repeat.
|
|
16
|
+
*/
|
|
17
|
+
export function linkAncestry(url, parentOf) {
|
|
18
|
+
const chain = [];
|
|
19
|
+
for (let parent = parentOf(url); parent && parent !== url; parent = parentOf(parent)) {
|
|
20
|
+
if (chain.includes(parent))
|
|
21
|
+
break;
|
|
22
|
+
chain.unshift(parent);
|
|
23
|
+
}
|
|
24
|
+
return chain;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Open a link on top of the pages it belongs under: each one a navigation of its own, so each is a
|
|
28
|
+
* screen with its own route and Back retraces them. Pages already showing, from the page on screen
|
|
29
|
+
* down, are not opened again.
|
|
30
|
+
*/
|
|
31
|
+
export async function followLink(router, url, parentOf) {
|
|
32
|
+
if (router.url === url)
|
|
33
|
+
return;
|
|
34
|
+
const chain = linkAncestry(url, parentOf);
|
|
35
|
+
const from = chain.indexOf(router.url);
|
|
36
|
+
for (const parent of chain.slice(from + 1))
|
|
37
|
+
await router.navigateByUrl(parent);
|
|
38
|
+
await router.navigateByUrl(url);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=native-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-links.js","sourceRoot":"","sources":["../src/native-links.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,QAAoB;IAC5D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAI,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,MAAM,KAAK,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACrF,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,MAAM;QAClC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAkB,EAClB,GAAW,EACX,QAAoB;IAEpB,IAAI,MAAM,CAAC,GAAG,KAAK,GAAG;QAAE,OAAO;IAC/B,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvC,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;QAAE,MAAM,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/E,MAAM,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC","sourcesContent":["/**\n * Links into the app, with the stack they would have been reached through.\n *\n * A deep link to `/settings/notifications` would otherwise open that page alone, with no Back:\n * the history holds one entry, and the stack one screen. An app that says which page a link\n * belongs under gets that page first and the link pushed on top of it - real navigations, so\n * each screen has its own route and Back pops the way it would have. The parent's own parent is\n * asked in turn, so a link five screens deep opens on all five.\n *\n * Which page is the app's call, not a rule: the detail column of a split view, for one, must not\n * have its empty state pushed beneath it.\n */\n\n/** The url to open beneath `url`, or null to open it on its own. */\nexport type LinkParent = (url: string) => string | null;\n\n/** What following a link needs of the router. */\nexport interface LinkRouter {\n readonly url: string;\n navigateByUrl(url: string): Promise<boolean>;\n}\n\n/**\n * The pages a link sits under, outermost first: its parent, that page's parent, and so on up, as\n * far as `parentOf` goes. A cycle stops where it would repeat.\n */\nexport function linkAncestry(url: string, parentOf: LinkParent): string[] {\n const chain: string[] = [];\n for (let parent = parentOf(url); parent && parent !== url; parent = parentOf(parent)) {\n if (chain.includes(parent)) break;\n chain.unshift(parent);\n }\n return chain;\n}\n\n/**\n * Open a link on top of the pages it belongs under: each one a navigation of its own, so each is a\n * screen with its own route and Back retraces them. Pages already showing, from the page on screen\n * down, are not opened again.\n */\nexport async function followLink(\n router: LinkRouter,\n url: string,\n parentOf: LinkParent,\n): Promise<void> {\n if (router.url === url) return;\n const chain = linkAncestry(url, parentOf);\n const from = chain.indexOf(router.url);\n for (const parent of chain.slice(from + 1)) await router.navigateByUrl(parent);\n await router.navigateByUrl(url);\n}\n"]}
|