@ng-native/router 0.17.1 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/discard-failed-entries.d.ts +11 -0
  2. package/dist/discard-failed-entries.js +48 -0
  3. package/dist/discard-failed-entries.js.map +1 -0
  4. package/dist/in-tab.d.ts +9 -1
  5. package/dist/in-tab.js +2 -0
  6. package/dist/in-tab.js.map +1 -1
  7. package/dist/native-back.d.ts +21 -1
  8. package/dist/native-back.js +94 -3
  9. package/dist/native-back.js.map +1 -1
  10. package/dist/native-header.d.ts +8 -1
  11. package/dist/native-header.js +18 -4
  12. package/dist/native-header.js.map +1 -1
  13. package/dist/native-navigation.d.ts +10 -2
  14. package/dist/native-navigation.js +45 -9
  15. package/dist/native-navigation.js.map +1 -1
  16. package/dist/native-platform-location.d.ts +42 -0
  17. package/dist/native-platform-location.js +66 -0
  18. package/dist/native-platform-location.js.map +1 -1
  19. package/dist/native-stack-outlet.d.ts +83 -0
  20. package/dist/native-stack-outlet.js +245 -19
  21. package/dist/native-stack-outlet.js.map +1 -1
  22. package/dist/native-stack-reuse-strategy.d.ts +34 -0
  23. package/dist/native-stack-reuse-strategy.js +119 -7
  24. package/dist/native-stack-reuse-strategy.js.map +1 -1
  25. package/dist/native-tab.d.ts +8 -6
  26. package/dist/native-tab.js +23 -2
  27. package/dist/native-tab.js.map +1 -1
  28. package/dist/native-tabs-outlet.d.ts +9 -3
  29. package/dist/native-tabs-outlet.js +21 -4
  30. package/dist/native-tabs-outlet.js.map +1 -1
  31. package/dist/popping-to.d.ts +8 -0
  32. package/dist/popping-to.js +2 -0
  33. package/dist/popping-to.js.map +1 -0
  34. package/dist/presented-route.js +12 -10
  35. package/dist/presented-route.js.map +1 -1
  36. package/dist/provide-native-router.js +4 -0
  37. package/dist/provide-native-router.js.map +1 -1
  38. package/dist/screen-header.d.ts +5 -3
  39. package/dist/screen-header.js +5 -3
  40. package/dist/screen-header.js.map +1 -1
  41. package/dist/screen-presentation.d.ts +5 -0
  42. package/dist/screen-presentation.js.map +1 -1
  43. package/dist/screen-safe-area-view.d.ts +6 -1
  44. package/dist/screen-safe-area-view.js +12 -4
  45. package/dist/screen-safe-area-view.js.map +1 -1
  46. package/dist/screens-version.d.ts +12 -0
  47. package/dist/screens-version.js +20 -0
  48. package/dist/screens-version.js.map +1 -0
  49. package/dist/screens.js +3 -0
  50. package/dist/screens.js.map +1 -1
  51. package/dist/url-commands.d.ts +24 -0
  52. package/dist/url-commands.js +102 -0
  53. package/dist/url-commands.js.map +1 -0
  54. package/package.json +3 -3
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Take out the history entry a navigation wrote before it failed or was refused.
3
+ *
4
+ * The router writes a navigation's url as it activates it, or as soon as it is recognised with
5
+ * `urlUpdateStrategy: 'eager'`, and puts its own url back over that entry when the navigation then
6
+ * fails. That leaves two entries for the page showing, and a Back from it goes to the same page.
7
+ *
8
+ * A back through history that fails is undone instead: the router writes its url over the entry
9
+ * the back reached, so that entry is put back as it was and the one left shows again.
10
+ */
11
+ export declare function discardFailedEntries(): void;
@@ -0,0 +1,48 @@
1
+ import { PlatformLocation } from '@angular/common';
2
+ import { DestroyRef, inject } from '@angular/core';
3
+ import { NavigationCancel, NavigationCancellationCode, NavigationError, NavigationStart, Router, } from '@angular/router';
4
+ import { NativePlatformLocation } from "./native-platform-location.js";
5
+ /** Whether the router puts its url back for `event`, as it does for a navigation that failed. */
6
+ const rolledBack = (event) => event instanceof NavigationError ||
7
+ (event instanceof NavigationCancel &&
8
+ event.code !== NavigationCancellationCode.Redirect &&
9
+ event.code !== NavigationCancellationCode.SupersededByNewNavigation);
10
+ /**
11
+ * Take out the history entry a navigation wrote before it failed or was refused.
12
+ *
13
+ * The router writes a navigation's url as it activates it, or as soon as it is recognised with
14
+ * `urlUpdateStrategy: 'eager'`, and puts its own url back over that entry when the navigation then
15
+ * fails. That leaves two entries for the page showing, and a Back from it goes to the same page.
16
+ *
17
+ * A back through history that fails is undone instead: the router writes its url over the entry
18
+ * the back reached, so that entry is put back as it was and the one left shows again.
19
+ */
20
+ export function discardFailedEntries() {
21
+ const history = inject(PlatformLocation);
22
+ if (!(history instanceof NativePlatformLocation))
23
+ return;
24
+ // As the navigation in flight found it. One ends before the next starts. A guard's redirect
25
+ // keeps it: with 'eager' the redirected navigation replaces the entry the first one wrote.
26
+ let depth = 0;
27
+ let redirected = false;
28
+ let back = null;
29
+ const events = inject(Router).events.subscribe((event) => {
30
+ if (event instanceof NavigationStart) {
31
+ if (!redirected)
32
+ depth = history.ɵpages().length;
33
+ redirected = false;
34
+ back = event.navigationTrigger === 'popstate' ? history.ɵlastMove() : null;
35
+ }
36
+ else if (rolledBack(event) && back) {
37
+ history.ɵundo(back);
38
+ }
39
+ else if (event instanceof NavigationCancel &&
40
+ event.code === NavigationCancellationCode.Redirect) {
41
+ redirected = true;
42
+ }
43
+ else if (rolledBack(event) && history.ɵpages().length === depth + 1)
44
+ history.ɵdiscard();
45
+ });
46
+ inject(DestroyRef).onDestroy(() => events.unsubscribe());
47
+ }
48
+ //# sourceMappingURL=discard-failed-entries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discard-failed-entries.js","sourceRoot":"","sources":["../src/discard-failed-entries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,eAAe,EACf,eAAe,EACf,MAAM,GAEP,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,sBAAsB,EAAoB,MAAM,+BAA+B,CAAC;AAEzF,iGAAiG;AACjG,MAAM,UAAU,GAAG,CAAC,KAAY,EAA+C,EAAE,CAC/E,KAAK,YAAY,eAAe;IAChC,CAAC,KAAK,YAAY,gBAAgB;QAChC,KAAK,CAAC,IAAI,KAAK,0BAA0B,CAAC,QAAQ;QAClD,KAAK,CAAC,IAAI,KAAK,0BAA0B,CAAC,yBAAyB,CAAC,CAAC;AAEzE;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACzC,IAAI,CAAC,CAAC,OAAO,YAAY,sBAAsB,CAAC;QAAE,OAAO;IACzD,4FAA4F;IAC5F,2FAA2F;IAC3F,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,IAAI,GAAuB,IAAI,CAAC;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;QACvD,IAAI,KAAK,YAAY,eAAe,EAAE,CAAC;YACrC,IAAI,CAAC,UAAU;gBAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC;YACjD,UAAU,GAAG,KAAK,CAAC;YACnB,IAAI,GAAG,KAAK,CAAC,iBAAiB,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7E,CAAC;aAAM,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;YACrC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;aAAM,IACL,KAAK,YAAY,gBAAgB;YACjC,KAAK,CAAC,IAAI,KAAK,0BAA0B,CAAC,QAAQ,EAClD,CAAC;YACD,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;aAAM,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,MAAM,KAAK,KAAK,GAAG,CAAC;YAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC5F,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;AAC3D,CAAC","sourcesContent":["import { PlatformLocation } from '@angular/common';\nimport { DestroyRef, inject } from '@angular/core';\nimport {\n NavigationCancel,\n NavigationCancellationCode,\n NavigationError,\n NavigationStart,\n Router,\n type Event,\n} from '@angular/router';\nimport { NativePlatformLocation, type HistoryMove } from './native-platform-location.ts';\n\n/** Whether the router puts its url back for `event`, as it does for a navigation that failed. */\nconst rolledBack = (event: Event): event is NavigationError | NavigationCancel =>\n event instanceof NavigationError ||\n (event instanceof NavigationCancel &&\n event.code !== NavigationCancellationCode.Redirect &&\n event.code !== NavigationCancellationCode.SupersededByNewNavigation);\n\n/**\n * Take out the history entry a navigation wrote before it failed or was refused.\n *\n * The router writes a navigation's url as it activates it, or as soon as it is recognised with\n * `urlUpdateStrategy: 'eager'`, and puts its own url back over that entry when the navigation then\n * fails. That leaves two entries for the page showing, and a Back from it goes to the same page.\n *\n * A back through history that fails is undone instead: the router writes its url over the entry\n * the back reached, so that entry is put back as it was and the one left shows again.\n */\nexport function discardFailedEntries(): void {\n const history = inject(PlatformLocation);\n if (!(history instanceof NativePlatformLocation)) return;\n // As the navigation in flight found it. One ends before the next starts. A guard's redirect\n // keeps it: with 'eager' the redirected navigation replaces the entry the first one wrote.\n let depth = 0;\n let redirected = false;\n let back: HistoryMove | null = null;\n const events = inject(Router).events.subscribe((event) => {\n if (event instanceof NavigationStart) {\n if (!redirected) depth = history.ɵpages().length;\n redirected = false;\n back = event.navigationTrigger === 'popstate' ? history.ɵlastMove() : null;\n } else if (rolledBack(event) && back) {\n history.ɵundo(back);\n } else if (\n event instanceof NavigationCancel &&\n event.code === NavigationCancellationCode.Redirect\n ) {\n redirected = true;\n } else if (rolledBack(event) && history.ɵpages().length === depth + 1) history.ɵdiscard();\n });\n inject(DestroyRef).onDestroy(() => events.unsubscribe());\n}\n"]}
package/dist/in-tab.d.ts CHANGED
@@ -7,8 +7,10 @@
7
7
  * beneath. On Android react-native-screens draws it inside the tab's own stack, a modal as a push
8
8
  * and a form sheet as a bottom sheet, so the bar stays in reach; it is dismissed there too, so a
9
9
  * tab comes back on the same page on both platforms.
10
+ *
11
+ * A page can also ask for the tab bar to be hidden while it shows: see `NativeHeader.tabBarHidden`.
10
12
  */
11
- import { InjectionToken } from '@angular/core';
13
+ import { InjectionToken, type Signal } from '@angular/core';
12
14
  export interface InTab {
13
15
  /**
14
16
  * Runs `leave` each time another tab of the bar comes in front of this one. `leave` answers the
@@ -16,5 +18,11 @@ export interface InTab {
16
18
  * nothing. Answers the unsubscribe.
17
19
  */
18
20
  onLeave(leave: () => string | null): () => void;
21
+ /**
22
+ * Hides the bar while `hidden` reads true, unless the outlet's own `[barHidden]` says otherwise.
23
+ * `hidden` reads false while its page is behind, in this tab or because another tab is in front.
24
+ * Answers the withdrawal.
25
+ */
26
+ hideBar(hidden: Signal<boolean>): () => void;
19
27
  }
20
28
  export declare const IN_TAB: InjectionToken<InTab>;
package/dist/in-tab.js CHANGED
@@ -7,6 +7,8 @@
7
7
  * beneath. On Android react-native-screens draws it inside the tab's own stack, a modal as a push
8
8
  * and a form sheet as a bottom sheet, so the bar stays in reach; it is dismissed there too, so a
9
9
  * tab comes back on the same page on both platforms.
10
+ *
11
+ * A page can also ask for the tab bar to be hidden while it shows: see `NativeHeader.tabBarHidden`.
10
12
  */
11
13
  import { InjectionToken } from '@angular/core';
12
14
  export const IN_TAB = new InjectionToken('ng-native.router.in-tab');
@@ -1 +1 @@
1
- {"version":3,"file":"in-tab.js","sourceRoot":"","sources":["../src/in-tab.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAW/C,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,cAAc,CAAQ,yBAAyB,CAAC,CAAC","sourcesContent":["/**\n * The tab a page is in, for a stack inside it to hear when another tab comes in front.\n *\n * On iOS a sheet or a modal presented in a tab's own stack is shown over the whole window, the tab\n * bar included, so the tab cannot keep it up while it is behind: it would cover the tab in front.\n * The tab's stacks dismiss what they presented instead, and the tab is remembered at the page\n * beneath. On Android react-native-screens draws it inside the tab's own stack, a modal as a push\n * and a form sheet as a bottom sheet, so the bar stays in reach; it is dismissed there too, so a\n * tab comes back on the same page on both platforms.\n */\nimport { InjectionToken } from '@angular/core';\n\nexport interface InTab {\n /**\n * Runs `leave` each time another tab of the bar comes in front of this one. `leave` answers the\n * url the tab is left on once it has dismissed what it presented, or null when it dismissed\n * nothing. Answers the unsubscribe.\n */\n onLeave(leave: () => string | null): () => void;\n}\n\nexport const IN_TAB = new InjectionToken<InTab>('ng-native.router.in-tab');\n"]}
1
+ {"version":3,"file":"in-tab.js","sourceRoot":"","sources":["../src/in-tab.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,cAAc,EAAe,MAAM,eAAe,CAAC;AAiB5D,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,cAAc,CAAQ,yBAAyB,CAAC,CAAC","sourcesContent":["/**\n * The tab a page is in, for a stack inside it to hear when another tab comes in front.\n *\n * On iOS a sheet or a modal presented in a tab's own stack is shown over the whole window, the tab\n * bar included, so the tab cannot keep it up while it is behind: it would cover the tab in front.\n * The tab's stacks dismiss what they presented instead, and the tab is remembered at the page\n * beneath. On Android react-native-screens draws it inside the tab's own stack, a modal as a push\n * and a form sheet as a bottom sheet, so the bar stays in reach; it is dismissed there too, so a\n * tab comes back on the same page on both platforms.\n *\n * A page can also ask for the tab bar to be hidden while it shows: see `NativeHeader.tabBarHidden`.\n */\nimport { InjectionToken, type Signal } from '@angular/core';\n\nexport interface InTab {\n /**\n * Runs `leave` each time another tab of the bar comes in front of this one. `leave` answers the\n * url the tab is left on once it has dismissed what it presented, or null when it dismissed\n * nothing. Answers the unsubscribe.\n */\n onLeave(leave: () => string | null): () => void;\n /**\n * Hides the bar while `hidden` reads true, unless the outlet's own `[barHidden]` says otherwise.\n * `hidden` reads false while its page is behind, in this tab or because another tab is in front.\n * Answers the withdrawal.\n */\n hideBar(hidden: Signal<boolean>): () => void;\n}\n\nexport const IN_TAB = new InjectionToken<InTab>('ng-native.router.in-tab');\n"]}
@@ -1,4 +1,4 @@
1
- import type { ActivatedRoute, Router } from '@angular/router';
1
+ import { Router, type ActivatedRoute, type ActivatedRouteSnapshot } from '@angular/router';
2
2
  import * as i0 from "@angular/core";
3
3
  /** What a stack offers `NativeNavigation` beyond a back: going down it several screens at once. */
4
4
  export interface PoppableStack {
@@ -88,5 +88,25 @@ export type BackAnswer = (from: 'button' | 'app') => boolean;
88
88
  /**
89
89
  * Whether a route is part of what the router shows now, rather than a subtree the reuse strategy
90
90
  * detached and is keeping for later: a tab behind, or a screen covered by one pushed over it.
91
+ *
92
+ * Read from the snapshots: the root route is the same object in every state, and after a failed
93
+ * navigation its children are still the ones that navigation would have shown.
91
94
  */
92
95
  export declare function isShowing(router: Router, route: ActivatedRoute | null): boolean;
96
+ /**
97
+ * Put back the snapshots of the routes showing when a navigation fails, for `isShowing`.
98
+ *
99
+ * The router moves each route it keeps, such as the parent of a page that throws, on to the
100
+ * navigation's snapshot as it activates it, and on a failure puts back only its own state. That
101
+ * route would then be missing from the snapshots, and its outlet would ignore a Back.
102
+ */
103
+ export declare function restoreFailedSnapshots(): void;
104
+ /**
105
+ * Put back the snapshot a failed navigation moved `route` on from, and send what that move changed
106
+ * back through the route's observables, as the router sends a change: a page reading its query
107
+ * params, or bound to them as inputs, sees the values it had again.
108
+ *
109
+ * The router's observables for a route are the subjects it sends each change through, though it
110
+ * types them as plain observables. One that cannot be sent to is left as it is.
111
+ */
112
+ export declare function restoreSnapshot(route: ActivatedRoute, snapshot: ActivatedRouteSnapshot): void;
@@ -11,7 +11,8 @@
11
11
  * screen pushed over the whole tab bar, is still subscribed, so each answer first checks that
12
12
  * its outlet is showing what the router is showing.
13
13
  */
14
- import { Service, inject } from '@angular/core';
14
+ import { DestroyRef, Service, inject } from '@angular/core';
15
+ import { ChildrenOutletContexts, NavigationError, NavigationStart, Router, } from '@angular/router';
15
16
  import { HardwareBack } from '@ng-native/device';
16
17
  import * as i0 from "@angular/core";
17
18
  export class NativeBack {
@@ -138,11 +139,101 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
138
139
  /**
139
140
  * Whether a route is part of what the router shows now, rather than a subtree the reuse strategy
140
141
  * detached and is keeping for later: a tab behind, or a screen covered by one pushed over it.
142
+ *
143
+ * Read from the snapshots: the root route is the same object in every state, and after a failed
144
+ * navigation its children are still the ones that navigation would have shown.
141
145
  */
142
146
  export function isShowing(router, route) {
143
147
  if (!route)
144
148
  return false;
145
- const visit = (node) => node === route || node.children.some(visit);
146
- return visit(router.routerState.root);
149
+ const showing = route.snapshot;
150
+ const visit = (node) => node === showing || node.children.some(visit);
151
+ return visit(router.routerState.snapshot.root);
152
+ }
153
+ /**
154
+ * Put back the snapshots of the routes showing when a navigation fails, for `isShowing`.
155
+ *
156
+ * The router moves each route it keeps, such as the parent of a page that throws, on to the
157
+ * navigation's snapshot as it activates it, and on a failure puts back only its own state. That
158
+ * route would then be missing from the snapshots, and its outlet would ignore a Back.
159
+ */
160
+ export function restoreFailedSnapshots() {
161
+ const router = inject(Router);
162
+ const contexts = inject(ChildrenOutletContexts);
163
+ let showing = new Map();
164
+ const events = router.events.subscribe((event) => {
165
+ if (event instanceof NavigationStart)
166
+ showing = showingRoutes(router, contexts);
167
+ else if (event instanceof NavigationError) {
168
+ for (const [route, snapshot] of showing)
169
+ restoreSnapshot(route, snapshot);
170
+ }
171
+ });
172
+ inject(DestroyRef).onDestroy(() => events.unsubscribe());
173
+ }
174
+ /**
175
+ * Put back the snapshot a failed navigation moved `route` on from, and send what that move changed
176
+ * back through the route's observables, as the router sends a change: a page reading its query
177
+ * params, or bound to them as inputs, sees the values it had again.
178
+ *
179
+ * The router's observables for a route are the subjects it sends each change through, though it
180
+ * types them as plain observables. One that cannot be sent to is left as it is.
181
+ */
182
+ export function restoreSnapshot(route, snapshot) {
183
+ const failed = route.snapshot;
184
+ route.snapshot = snapshot;
185
+ if (failed === snapshot)
186
+ return;
187
+ const resend = (observable, from, to, same) => {
188
+ if (!same(from, to))
189
+ observable.next?.(to);
190
+ };
191
+ resend(route.queryParams, failed.queryParams, snapshot.queryParams, sameRecord);
192
+ resend(route.fragment, failed.fragment, snapshot.fragment, Object.is);
193
+ resend(route.params, failed.params, snapshot.params, sameRecord);
194
+ resend(route.url, failed.url, snapshot.url, sameSegments);
195
+ resend(route.data, failed.data, snapshot.data, sameRecord);
196
+ }
197
+ /** Whether two records hold the same values, an array of values compared item by item. */
198
+ function sameRecord(a, b) {
199
+ const keys = Object.keys(a);
200
+ if (keys.length !== Object.keys(b).length)
201
+ return false;
202
+ return keys.every((key) => {
203
+ const [x, y] = [a[key], b[key]];
204
+ if (Array.isArray(x) && Array.isArray(y)) {
205
+ return x.length === y.length && x.every((item, index) => item === y[index]);
206
+ }
207
+ return key in b && x === y;
208
+ });
209
+ }
210
+ function sameSegments(a, b) {
211
+ return (a.length === b.length &&
212
+ a.every((segment, index) => segment.path === b[index].path && sameRecord(segment.parameters, b[index].parameters)));
213
+ }
214
+ /**
215
+ * Each route the router shows now, with its snapshot, found through the outlets showing them.
216
+ *
217
+ * A route with no component, a `loadChildren` wrapper or a group, has no outlet of its own: the
218
+ * router activates its children in its parent's outlets, so they are looked up there.
219
+ */
220
+ function showingRoutes(router, contexts) {
221
+ const state = router.routerState;
222
+ const found = new Map([[state.root, state.snapshot.root]]);
223
+ const visit = (node, inside) => {
224
+ for (const child of node.children) {
225
+ if (!child.component) {
226
+ visit(child, inside);
227
+ continue;
228
+ }
229
+ const context = inside.getContext(child.outlet);
230
+ if (!context?.route)
231
+ continue;
232
+ found.set(context.route, child);
233
+ visit(child, context.children);
234
+ }
235
+ };
236
+ visit(state.snapshot.root, contexts);
237
+ return found;
147
238
  }
148
239
  //# sourceMappingURL=native-back.js.map
@@ -1 +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;;AAiCjD,MAAM,OAAO,UAAU;IACJ,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IACrD,kDAAkD;IACjC,OAAO,GAAiB,EAAE,CAAC;IAC5C,gEAAgE;IAC/C,MAAM,GAAoB,EAAE,CAAC;IAE9C,oBAAoB;IACH,OAAO,GAAa,EAAE,CAAC;IAExC,kFAAkF;IAClF,SAAS,CAAC,GAAW;QACnB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,GAAG,EAAE;YACV,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACxC,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAClD,CAAC,CAAC;IACJ,CAAC;IAED,iFAAiF;IACjF,aAAa,CAAC,GAAW;QACvB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,IAAI;gBAAE,OAAO,IAAI,CAAC;QACxB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,yFAAyF;IACzF,YAAY,CAAC,GAAW;QACtB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/B,MAAM,EAAE,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAI,EAAE;gBAAE,OAAO,EAAE,CAAC;QACpB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mEAAmE;IACnE,WAAW,CAAC,GAAW;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,CAAC;IAED;;;OAGG;IACH,QAAQ,GAAG,KAAK,CAAC;IAEjB,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;;;;OAIG;IACH,UAAU;QACR,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,qEAAqE;IAC7D,WAAW,CAAqB;IAExC,8FAA8F;IAC9F,OAAO,CAAC,UAA8B;QACpC,IAAI,UAAU,KAAK,SAAS;YAAE,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;IAC9D,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,UAAkB;QAC1B,OAAO,IAAI,CAAC,WAAW,KAAK,UAAU,CAAC;IACzC,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,MAAkB;QACvB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC9D,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,CAAC,KAAK,CAAC,CAAC,CAAC;IACrE,CAAC;uGAvHU,UAAU;wGAAV,UAAU;;2FAAV,UAAU;kBADtB,OAAO;;AAiIR;;;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 /** Take every screen under the top off at once, or false when it is showing only that one. */\n dropUnderTop(): boolean;\n}\n\n/**\n * What a tab bar offers `NativeNavigation` for a push into one of its tabs from outside it. A tab\n * nobody has opened has no stack yet, and a page pushed into it would be the stack's first\n * screen, with nothing under it to go back to.\n */\nexport interface TabBar {\n /** The url of the tab `url` is a page inside, when that tab has not been opened. */\n unopenedTabOf(url: string): string | null;\n /** Whether `url` is a tab other than the one in front, or a page inside one. */\n behind(url: string): boolean;\n /**\n * Where the tab `url` is in was left, when a screen of the stack the bar is on covers the bar:\n * the url that brings the bar back in front at that tab. Null when the bar is in front, or\n * `url` is in none of its tabs.\n */\n coveredAt(url: string): string | 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: BackAnswer[] = [];\n /** Oldest first, and so outermost first, as the answers are. */\n private readonly stacks: PoppableStack[] = [];\n\n /** Oldest first. */\n private readonly tabBars: TabBar[] = [];\n\n /** Offer a tab bar's unopened tabs to a push into one. Returns an unsubscribe. */\n addTabBar(bar: TabBar): () => void {\n this.tabBars.push(bar);\n return () => {\n const index = this.tabBars.indexOf(bar);\n if (index !== -1) this.tabBars.splice(index, 1);\n };\n }\n\n /** The url of the unopened tab `url` is a page inside, or null: see `TabBar`. */\n unopenedTabOf(url: string): string | null {\n for (const bar of this.tabBars) {\n const root = bar.unopenedTabOf(url);\n if (root) return root;\n }\n return null;\n }\n\n /** Where a covered bar's tab holding `url` was left, or null: see `TabBar.coveredAt`. */\n coveredTabAt(url: string): string | null {\n for (const bar of this.tabBars) {\n const at = bar.coveredAt(url);\n if (at) return at;\n }\n return null;\n }\n\n /** Whether `url` belongs to a tab that is not the one in front. */\n inTabBehind(url: string): boolean {\n return this.tabBars.some((bar) => bar.behind(url));\n }\n\n /**\n * Whether the app's own stack takes a page presented over whatever is showing. Set by the root\n * stack outlet for as long as there is one: see `presented-route.ts`.\n */\n presents = false;\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 /**\n * Take the screens under the top of the innermost stack in front that has any off it, leaving\n * the top one: a reset that lands on the screen already on top, which no stack activates\n * anything for. False when no stack in front had any.\n */\n resetToTop(): boolean {\n return [...this.stacks].reverse().some((stack) => stack.showing() && stack.dropUnderTop());\n }\n\n /** The id of the last navigation a stack put a screen on top for. */\n private lastStacked: number | undefined;\n\n /** A stack put a screen on top, a new one or one it kept, for the navigation with that id. */\n stacked(navigation: number | undefined): void {\n if (navigation !== undefined) this.lastStacked = navigation;\n }\n\n /**\n * Whether a stack put a screen on top for the navigation with that id. One that put none left\n * the screen on top where it was, as a navigation to it with another query does.\n */\n stackedIn(navigation: number): boolean {\n return this.lastStacked === navigation;\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 took the back, and false lets the\n * next one out try. Returns an unsubscribe.\n */\n handle(answer: BackAnswer): () => void {\n this.answers.push(answer);\n const stop = this.hardwareBack.handle(() => answer('button'));\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('app'));\n }\n}\n\n/**\n * An outlet's answer to a back, told where it came from: Android's button, which is the platform's\n * own dismissal and a screen may refuse, or the app's `NativeNavigation.back()`, which it may not.\n */\nexport type BackAnswer = (from: 'button' | 'app') => boolean;\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"]}
1
+ {"version":3,"file":"native-back.js","sourceRoot":"","sources":["../src/native-back.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,eAAe,EACf,MAAM,GAIP,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;;AAiCjD,MAAM,OAAO,UAAU;IACJ,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IACrD,kDAAkD;IACjC,OAAO,GAAiB,EAAE,CAAC;IAC5C,gEAAgE;IAC/C,MAAM,GAAoB,EAAE,CAAC;IAE9C,oBAAoB;IACH,OAAO,GAAa,EAAE,CAAC;IAExC,kFAAkF;IAClF,SAAS,CAAC,GAAW;QACnB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,GAAG,EAAE;YACV,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACxC,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAClD,CAAC,CAAC;IACJ,CAAC;IAED,iFAAiF;IACjF,aAAa,CAAC,GAAW;QACvB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,IAAI;gBAAE,OAAO,IAAI,CAAC;QACxB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,yFAAyF;IACzF,YAAY,CAAC,GAAW;QACtB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/B,MAAM,EAAE,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAI,EAAE;gBAAE,OAAO,EAAE,CAAC;QACpB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mEAAmE;IACnE,WAAW,CAAC,GAAW;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,CAAC;IAED;;;OAGG;IACH,QAAQ,GAAG,KAAK,CAAC;IAEjB,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;;;;OAIG;IACH,UAAU;QACR,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,qEAAqE;IAC7D,WAAW,CAAqB;IAExC,8FAA8F;IAC9F,OAAO,CAAC,UAA8B;QACpC,IAAI,UAAU,KAAK,SAAS;YAAE,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;IAC9D,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,UAAkB;QAC1B,OAAO,IAAI,CAAC,WAAW,KAAK,UAAU,CAAC;IACzC,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,MAAkB;QACvB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC9D,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,CAAC,KAAK,CAAC,CAAC,CAAC;IACrE,CAAC;uGAvHU,UAAU;wGAAV,UAAU;;2FAAV,UAAU;kBADtB,OAAO;;AAiIR;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,KAA4B;IACpE,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC;IAC/B,MAAM,KAAK,GAAG,CAAC,IAA4B,EAAW,EAAE,CACtD,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,OAAO,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAChD,IAAI,OAAO,GAAG,IAAI,GAAG,EAA0C,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;QAC/C,IAAI,KAAK,YAAY,eAAe;YAAE,OAAO,GAAG,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;aAC3E,IAAI,KAAK,YAAY,eAAe,EAAE,CAAC;YAC1C,KAAK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,OAAO;gBAAE,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,KAAqB,EAAE,QAAgC;IACrF,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC;IAC9B,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC1B,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO;IAChC,MAAM,MAAM,GAAG,CAAI,UAAkB,EAAE,IAAO,EAAE,EAAK,EAAE,IAA6B,EAAE,EAAE;QACtF,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAAG,UAA4C,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IAChF,CAAC,CAAC;IACF,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAChF,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACtE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACjE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAC1D,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AAC7D,CAAC;AAED,0FAA0F;AAC1F,SAAS,UAAU,CAAC,CAAoC,EAAE,CAAoC;IAC5F,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACxB,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAChC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9E,CAAC;QACD,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY,CAAC,CAAwB,EAAE,CAAwB;IACtE,OAAO,CACL,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QACrB,CAAC,CAAC,KAAK,CACL,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CACjB,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,CAAE,CAAC,IAAI,IAAI,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAE,CAAC,UAAU,CAAC,CAC1F,CACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,aAAa,CACpB,MAAc,EACd,QAAgC;IAEhC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC;IACjC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,CAAC,IAA4B,EAAE,MAA8B,EAAE,EAAE;QAC7E,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;gBACrB,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gBACrB,SAAS;YACX,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,CAAC,OAAO,EAAE,KAAK;gBAAE,SAAS;YAC9B,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAChC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,CAAC;IACF,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC;AACf,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 { DestroyRef, Service, inject } from '@angular/core';\nimport {\n ChildrenOutletContexts,\n NavigationError,\n NavigationStart,\n Router,\n type ActivatedRoute,\n type ActivatedRouteSnapshot,\n type UrlSegment,\n} 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 /** Take every screen under the top off at once, or false when it is showing only that one. */\n dropUnderTop(): boolean;\n}\n\n/**\n * What a tab bar offers `NativeNavigation` for a push into one of its tabs from outside it. A tab\n * nobody has opened has no stack yet, and a page pushed into it would be the stack's first\n * screen, with nothing under it to go back to.\n */\nexport interface TabBar {\n /** The url of the tab `url` is a page inside, when that tab has not been opened. */\n unopenedTabOf(url: string): string | null;\n /** Whether `url` is a tab other than the one in front, or a page inside one. */\n behind(url: string): boolean;\n /**\n * Where the tab `url` is in was left, when a screen of the stack the bar is on covers the bar:\n * the url that brings the bar back in front at that tab. Null when the bar is in front, or\n * `url` is in none of its tabs.\n */\n coveredAt(url: string): string | 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: BackAnswer[] = [];\n /** Oldest first, and so outermost first, as the answers are. */\n private readonly stacks: PoppableStack[] = [];\n\n /** Oldest first. */\n private readonly tabBars: TabBar[] = [];\n\n /** Offer a tab bar's unopened tabs to a push into one. Returns an unsubscribe. */\n addTabBar(bar: TabBar): () => void {\n this.tabBars.push(bar);\n return () => {\n const index = this.tabBars.indexOf(bar);\n if (index !== -1) this.tabBars.splice(index, 1);\n };\n }\n\n /** The url of the unopened tab `url` is a page inside, or null: see `TabBar`. */\n unopenedTabOf(url: string): string | null {\n for (const bar of this.tabBars) {\n const root = bar.unopenedTabOf(url);\n if (root) return root;\n }\n return null;\n }\n\n /** Where a covered bar's tab holding `url` was left, or null: see `TabBar.coveredAt`. */\n coveredTabAt(url: string): string | null {\n for (const bar of this.tabBars) {\n const at = bar.coveredAt(url);\n if (at) return at;\n }\n return null;\n }\n\n /** Whether `url` belongs to a tab that is not the one in front. */\n inTabBehind(url: string): boolean {\n return this.tabBars.some((bar) => bar.behind(url));\n }\n\n /**\n * Whether the app's own stack takes a page presented over whatever is showing. Set by the root\n * stack outlet for as long as there is one: see `presented-route.ts`.\n */\n presents = false;\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 /**\n * Take the screens under the top of the innermost stack in front that has any off it, leaving\n * the top one: a reset that lands on the screen already on top, which no stack activates\n * anything for. False when no stack in front had any.\n */\n resetToTop(): boolean {\n return [...this.stacks].reverse().some((stack) => stack.showing() && stack.dropUnderTop());\n }\n\n /** The id of the last navigation a stack put a screen on top for. */\n private lastStacked: number | undefined;\n\n /** A stack put a screen on top, a new one or one it kept, for the navigation with that id. */\n stacked(navigation: number | undefined): void {\n if (navigation !== undefined) this.lastStacked = navigation;\n }\n\n /**\n * Whether a stack put a screen on top for the navigation with that id. One that put none left\n * the screen on top where it was, as a navigation to it with another query does.\n */\n stackedIn(navigation: number): boolean {\n return this.lastStacked === navigation;\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 took the back, and false lets the\n * next one out try. Returns an unsubscribe.\n */\n handle(answer: BackAnswer): () => void {\n this.answers.push(answer);\n const stop = this.hardwareBack.handle(() => answer('button'));\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('app'));\n }\n}\n\n/**\n * An outlet's answer to a back, told where it came from: Android's button, which is the platform's\n * own dismissal and a screen may refuse, or the app's `NativeNavigation.back()`, which it may not.\n */\nexport type BackAnswer = (from: 'button' | 'app') => boolean;\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 *\n * Read from the snapshots: the root route is the same object in every state, and after a failed\n * navigation its children are still the ones that navigation would have shown.\n */\nexport function isShowing(router: Router, route: ActivatedRoute | null): boolean {\n if (!route) return false;\n const showing = route.snapshot;\n const visit = (node: ActivatedRouteSnapshot): boolean =>\n node === showing || node.children.some(visit);\n return visit(router.routerState.snapshot.root);\n}\n\n/**\n * Put back the snapshots of the routes showing when a navigation fails, for `isShowing`.\n *\n * The router moves each route it keeps, such as the parent of a page that throws, on to the\n * navigation's snapshot as it activates it, and on a failure puts back only its own state. That\n * route would then be missing from the snapshots, and its outlet would ignore a Back.\n */\nexport function restoreFailedSnapshots(): void {\n const router = inject(Router);\n const contexts = inject(ChildrenOutletContexts);\n let showing = new Map<ActivatedRoute, ActivatedRouteSnapshot>();\n const events = router.events.subscribe((event) => {\n if (event instanceof NavigationStart) showing = showingRoutes(router, contexts);\n else if (event instanceof NavigationError) {\n for (const [route, snapshot] of showing) restoreSnapshot(route, snapshot);\n }\n });\n inject(DestroyRef).onDestroy(() => events.unsubscribe());\n}\n\n/**\n * Put back the snapshot a failed navigation moved `route` on from, and send what that move changed\n * back through the route's observables, as the router sends a change: a page reading its query\n * params, or bound to them as inputs, sees the values it had again.\n *\n * The router's observables for a route are the subjects it sends each change through, though it\n * types them as plain observables. One that cannot be sent to is left as it is.\n */\nexport function restoreSnapshot(route: ActivatedRoute, snapshot: ActivatedRouteSnapshot): void {\n const failed = route.snapshot;\n route.snapshot = snapshot;\n if (failed === snapshot) return;\n const resend = <T>(observable: object, from: T, to: T, same: (a: T, b: T) => boolean) => {\n if (!same(from, to)) (observable as { next?: (value: T) => void }).next?.(to);\n };\n resend(route.queryParams, failed.queryParams, snapshot.queryParams, sameRecord);\n resend(route.fragment, failed.fragment, snapshot.fragment, Object.is);\n resend(route.params, failed.params, snapshot.params, sameRecord);\n resend(route.url, failed.url, snapshot.url, sameSegments);\n resend(route.data, failed.data, snapshot.data, sameRecord);\n}\n\n/** Whether two records hold the same values, an array of values compared item by item. */\nfunction sameRecord(a: Readonly<Record<string, unknown>>, b: Readonly<Record<string, unknown>>) {\n const keys = Object.keys(a);\n if (keys.length !== Object.keys(b).length) return false;\n return keys.every((key) => {\n const [x, y] = [a[key], b[key]];\n if (Array.isArray(x) && Array.isArray(y)) {\n return x.length === y.length && x.every((item, index) => item === y[index]);\n }\n return key in b && x === y;\n });\n}\n\nfunction sameSegments(a: readonly UrlSegment[], b: readonly UrlSegment[]): boolean {\n return (\n a.length === b.length &&\n a.every(\n (segment, index) =>\n segment.path === b[index]!.path && sameRecord(segment.parameters, b[index]!.parameters),\n )\n );\n}\n\n/**\n * Each route the router shows now, with its snapshot, found through the outlets showing them.\n *\n * A route with no component, a `loadChildren` wrapper or a group, has no outlet of its own: the\n * router activates its children in its parent's outlets, so they are looked up there.\n */\nfunction showingRoutes(\n router: Router,\n contexts: ChildrenOutletContexts,\n): Map<ActivatedRoute, ActivatedRouteSnapshot> {\n const state = router.routerState;\n const found = new Map([[state.root, state.snapshot.root]]);\n const visit = (node: ActivatedRouteSnapshot, inside: ChildrenOutletContexts) => {\n for (const child of node.children) {\n if (!child.component) {\n visit(child, inside);\n continue;\n }\n const context = inside.getContext(child.outlet);\n if (!context?.route) continue;\n found.set(context.route, child);\n visit(child, context.children);\n }\n };\n visit(state.snapshot.root, contexts);\n return found;\n}\n"]}
@@ -70,6 +70,13 @@ export declare class NativeHeader {
70
70
  readonly blurEffect: import("@angular/core").InputSignal<BlurEffect | undefined>;
71
71
  /** Hide the bar entirely, keeping the screen's gestures and safe areas. */
72
72
  readonly hidden: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
73
+ /**
74
+ * Hide the tab bar while this page is the one in front of its tab, as UIKit's
75
+ * `hidesBottomBarWhenPushed` does: on a detail page pushed in a tab's stack, say. The bar goes
76
+ * as the page is pushed and comes back as it is popped, without UIKit's slide. Outside a tab it
77
+ * does nothing, and `<native-tabs-outlet [barHidden]>` overrides it when bound.
78
+ */
79
+ readonly tabBarHidden: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
73
80
  /** Drop the hairline under the bar. */
74
81
  readonly hideShadow: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
75
82
  /** Let content scroll under the bar rather than starting below it. */
@@ -119,5 +126,5 @@ export declare class NativeHeader {
119
126
  readonly consumeRightInset: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
120
127
  readonly consumeBottomInset: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
121
128
  static ɵfac: i0.ɵɵFactoryDeclaration<NativeHeader, never>;
122
- 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; }; "liquidGlass": { "alias": "liquidGlass"; "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>;
129
+ 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; }; "liquidGlass": { "alias": "liquidGlass"; "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; }; "tabBarHidden": { "alias": "tabBarHidden"; "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>;
123
130
  }
@@ -26,7 +26,8 @@
26
26
  * than views, and they are a later opt-in rather than the primary API.
27
27
  */
28
28
  import { Component, DestroyRef, ElementRef, computed, effect, inject, input } from '@angular/core';
29
- import { ColorScheme, OS_VERSION } from '@ng-native/device';
29
+ import { ColorScheme, OS_VERSION, SCREEN_IN_FRONT } from '@ng-native/device';
30
+ import { IN_TAB } from "./in-tab.js";
30
31
  import { NATIVE_HEADER_DEFAULTS } from "./native-bar-defaults.js";
31
32
  import { NATIVE_HEADER_PALETTE } from "./native-header-palette.js";
32
33
  import { ownHost } from "./own-host.js";
@@ -43,11 +44,17 @@ export class NativeHeader {
43
44
  above = inject(SCREEN_HEADER, { optional: true });
44
45
  constructor() {
45
46
  ownHost(inject(ElementRef).nativeElement, this.constructor);
47
+ const destroyRef = inject(DestroyRef);
48
+ const tab = inject(IN_TAB, { optional: true });
49
+ if (tab) {
50
+ const inFront = inject(SCREEN_IN_FRONT);
51
+ destroyRef.onDestroy(tab.hideBar(computed(() => inFront() && !!this.tabBarHidden())));
52
+ }
46
53
  const above = this.above;
47
54
  if (!above)
48
55
  return;
49
56
  effect(() => above.set(!this.hidden() && !this.isTranslucent()));
50
- inject(DestroyRef).onDestroy(() => above.set(false));
57
+ destroyRef.onDestroy(() => above.set(false));
51
58
  }
52
59
  /**
53
60
  * The palette for the scheme the app is wearing, for whichever colours were not bound.
@@ -133,6 +140,13 @@ export class NativeHeader {
133
140
  ...(ngDevMode ? [undefined, { debugName: "blurEffect" }] : /* istanbul ignore next */ []));
134
141
  /** Hide the bar entirely, keeping the screen's gestures and safe areas. */
135
142
  hidden = input(undefined, { ...(ngDevMode ? { debugName: "hidden" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
143
+ /**
144
+ * Hide the tab bar while this page is the one in front of its tab, as UIKit's
145
+ * `hidesBottomBarWhenPushed` does: on a detail page pushed in a tab's stack, say. The bar goes
146
+ * as the page is pushed and comes back as it is popped, without UIKit's slide. Outside a tab it
147
+ * does nothing, and `<native-tabs-outlet [barHidden]>` overrides it when bound.
148
+ */
149
+ tabBarHidden = input(undefined, { ...(ngDevMode ? { debugName: "tabBarHidden" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
136
150
  /** Drop the hairline under the bar. */
137
151
  hideShadow = input(undefined, { ...(ngDevMode ? { debugName: "hideShadow" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
138
152
  /** Let content scroll under the bar rather than starting below it. */
@@ -194,7 +208,7 @@ export class NativeHeader {
194
208
  consumeRightInset = input(undefined, { ...(ngDevMode ? { debugName: "consumeRightInset" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
195
209
  consumeBottomInset = input(undefined, { ...(ngDevMode ? { debugName: "consumeBottomInset" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
196
210
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
197
- 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 }, liquidGlass: { classPropertyName: "liquidGlass", publicName: "liquidGlass", 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 ?? isLiquidGlass()", "translucent": "isTranslucent()", "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 });
211
+ 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 }, liquidGlass: { classPropertyName: "liquidGlass", publicName: "liquidGlass", 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 }, tabBarHidden: { classPropertyName: "tabBarHidden", publicName: "tabBarHidden", 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 ?? isLiquidGlass()", "translucent": "isTranslucent()", "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 });
198
212
  }
199
213
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeHeader, decorators: [{
200
214
  type: Component,
@@ -237,5 +251,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
237
251
  '[consumeBottomInset]': 'consumeBottomInset()',
238
252
  },
239
253
  }]
240
- }], 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 }] }], liquidGlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "liquidGlass", 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 }] }] } });
254
+ }], 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 }] }], liquidGlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "liquidGlass", 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 }] }], tabBarHidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabBarHidden", 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 }] }] } });
241
255
  //# sourceMappingURL=native-header.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"native-header.js","sourceRoot":"","sources":["../src/native-header.ts"],"names":[],"mappings":"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","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, DestroyRef, ElementRef, computed, effect, inject, input } from '@angular/core';\nimport { ColorScheme, OS_VERSION } 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 { SCREEN_HEADER } from './screen-header.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 ?? isLiquidGlass()',\n '[translucent]': 'isTranslucent()',\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 /** Told whether this is a bar the page starts below. See `screen-header.ts`. */\n private readonly above = inject(SCREEN_HEADER, { optional: true });\n\n constructor() {\n ownHost(inject(ElementRef).nativeElement as EngineNode, this.constructor);\n const above = this.above;\n if (!above) return;\n effect(() => above.set(!this.hidden() && !this.isTranslucent()));\n inject(DestroyRef).onDestroy(() => above.set(false));\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 /** iOS 26 and later, where the system's own bar is clear and floats over the content. */\n private readonly glass = nativePlatform() === 'ios' && (inject(OS_VERSION) ?? 0) >= 26;\n\n /**\n * Whether the bar is iOS 26's Liquid Glass one, which `liquidGlass` asks for here or through\n * `withHeaderDefaults`: clear, over the content, with no line under it, where the system blurs\n * what scrolls beneath and floats the buttons. Only on iOS 26 and later, and only while nothing\n * gave the bar a background of its own.\n */\n protected readonly isLiquidGlass = computed(() =>\n this.glass &&\n (this.liquidGlass() ?? this.defaults().liquidGlass) &&\n this.backgroundColor() === undefined &&\n this.defaults().backgroundColor === undefined\n ? true\n : undefined,\n );\n\n protected readonly resolvedBackgroundColor = computed(\n () =>\n this.backgroundColor() ??\n this.defaults().backgroundColor ??\n (this.isLiquidGlass() ? 'transparent' : 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 /** Whether the page runs under the bar rather than starting below it. */\n protected readonly isTranslucent = computed(\n () =>\n this.translucent() ??\n this.defaults().translucent ??\n this.iosLargeTitle() ??\n this.isLiquidGlass(),\n );\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 /**\n * On iOS 26 and later, the Liquid Glass navigation bar for this header, where it has no\n * background of its own: clear and unlined, with the content scrolling under it. `false` keeps\n * the neutral bar on a page of an app whose `withHeaderDefaults` asks for the glass one. The\n * bar is over the page, so its scroll view takes `contentInsetAdjustmentBehavior=\"automatic\"`.\n */\n readonly liquidGlass = input(undefined, { transform: optionalBoolean });\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"]}
1
+ {"version":3,"file":"native-header.js","sourceRoot":"","sources":["../src/native-header.ts"],"names":[],"mappings":"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","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, DestroyRef, ElementRef, computed, effect, inject, input } from '@angular/core';\nimport { ColorScheme, OS_VERSION, SCREEN_IN_FRONT } from '@ng-native/device';\nimport { IN_TAB } from './in-tab.ts';\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 { SCREEN_HEADER } from './screen-header.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 ?? isLiquidGlass()',\n '[translucent]': 'isTranslucent()',\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 /** Told whether this is a bar the page starts below. See `screen-header.ts`. */\n private readonly above = inject(SCREEN_HEADER, { optional: true });\n\n constructor() {\n ownHost(inject(ElementRef).nativeElement as EngineNode, this.constructor);\n const destroyRef = inject(DestroyRef);\n const tab = inject(IN_TAB, { optional: true });\n if (tab) {\n const inFront = inject(SCREEN_IN_FRONT);\n destroyRef.onDestroy(tab.hideBar(computed(() => inFront() && !!this.tabBarHidden())));\n }\n const above = this.above;\n if (!above) return;\n effect(() => above.set(!this.hidden() && !this.isTranslucent()));\n destroyRef.onDestroy(() => above.set(false));\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 /** iOS 26 and later, where the system's own bar is clear and floats over the content. */\n private readonly glass = nativePlatform() === 'ios' && (inject(OS_VERSION) ?? 0) >= 26;\n\n /**\n * Whether the bar is iOS 26's Liquid Glass one, which `liquidGlass` asks for here or through\n * `withHeaderDefaults`: clear, over the content, with no line under it, where the system blurs\n * what scrolls beneath and floats the buttons. Only on iOS 26 and later, and only while nothing\n * gave the bar a background of its own.\n */\n protected readonly isLiquidGlass = computed(() =>\n this.glass &&\n (this.liquidGlass() ?? this.defaults().liquidGlass) &&\n this.backgroundColor() === undefined &&\n this.defaults().backgroundColor === undefined\n ? true\n : undefined,\n );\n\n protected readonly resolvedBackgroundColor = computed(\n () =>\n this.backgroundColor() ??\n this.defaults().backgroundColor ??\n (this.isLiquidGlass() ? 'transparent' : 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 /** Whether the page runs under the bar rather than starting below it. */\n protected readonly isTranslucent = computed(\n () =>\n this.translucent() ??\n this.defaults().translucent ??\n this.iosLargeTitle() ??\n this.isLiquidGlass(),\n );\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 /**\n * On iOS 26 and later, the Liquid Glass navigation bar for this header, where it has no\n * background of its own: clear and unlined, with the content scrolling under it. `false` keeps\n * the neutral bar on a page of an app whose `withHeaderDefaults` asks for the glass one. The\n * bar is over the page, so its scroll view takes `contentInsetAdjustmentBehavior=\"automatic\"`.\n */\n readonly liquidGlass = input(undefined, { transform: optionalBoolean });\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 /**\n * Hide the tab bar while this page is the one in front of its tab, as UIKit's\n * `hidesBottomBarWhenPushed` does: on a detail page pushed in a tab's stack, say. The bar goes\n * as the page is pushed and comes back as it is popped, without UIKit's slide. Outside a tab it\n * does nothing, and `<native-tabs-outlet [barHidden]>` overrides it when bound.\n */\n readonly tabBarHidden = 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"]}