@ng-native/router 0.17.0 → 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
@@ -47,8 +47,11 @@ export declare class NativeNavigation {
47
47
  /**
48
48
  * Swap the current screen for another. The history entry is replaced too, so a back from the
49
49
  * new screen goes where a back from the old one would have.
50
+ *
51
+ * A `stackAnimation` in `presentation` is the one the new screen arrives with. Without one, with
52
+ * `default`, or with a `replaceAnimation`, the swap animates as the screen leaving was opened.
50
53
  */
51
- replace(commands: NavigationCommands, options?: NavigationExtras): Promise<boolean>;
54
+ replace(commands: NavigationCommands, options?: NativeNavigationOptions): Promise<boolean>;
52
55
  /**
53
56
  * Present a screen over the stack rather than pushing into it: a modal, or a sheet.
54
57
  *
@@ -63,8 +66,13 @@ export declare class NativeNavigation {
63
66
  /**
64
67
  * Replace the whole stack with one screen, the way signing out or finishing onboarding does.
65
68
  * Every screen below is destroyed, so there is nothing to go back to.
69
+ *
70
+ * A `stackAnimation` in `presentation` is the one a new screen arrives with:
71
+ * `reset('/sign-in', { presentation: { stackAnimation: 'none' } })` lands with no animation.
72
+ * Without one, with `default`, or with a `replaceAnimation`, the reset animates as the screen
73
+ * leaving was opened, and so does a reset to a screen still on the stack, which is kept.
66
74
  */
67
- reset(commands: NavigationCommands, options?: NavigationExtras): Promise<boolean>;
75
+ reset(commands: NavigationCommands, options?: NativeNavigationOptions): Promise<boolean>;
68
76
  /**
69
77
  * Go back exactly as the Android back button does: pop the stack in front, or at the root of a
70
78
  * tab's stack, go to the first tab. Not one entry back through history, which after a trip to
@@ -14,9 +14,10 @@
14
14
  * exactly as `NativePlatformLocation` and `NativeStackReuseStrategy` are.
15
15
  */
16
16
  import { DestroyRef, inject } from '@angular/core';
17
- import { NavigationCancel, NavigationCancellationCode, NavigationEnd, NavigationError, NavigationSkipped, NavigationSkippedCode, Router, } from '@angular/router';
17
+ import { NavigationCancel, NavigationCancellationCode, NavigationEnd, NavigationError, NavigationSkipped, NavigationSkippedCode, PRIMARY_OUTLET, Router, } from '@angular/router';
18
18
  import { NativeBack } from "./native-back.js";
19
- import { leavingPresented, presentedCommands } from "./presented-route.js";
19
+ import { PRESENTED, leavingPresented, presentedCommands } from "./presented-route.js";
20
+ import { hasCommandKeyParameters, urlCommands, withWrittenParameters } from "./url-commands.js";
20
21
  /**
21
22
  * Where the intent rides. `NavigationExtras.state` is the app's own object, so ours sits under
22
23
  * one reserved key rather than spread through it.
@@ -28,6 +29,19 @@ const asksMore = (commands, extras) => (typeof commands === 'string' && /[?#]/.t
28
29
  extras.fragment != null;
29
30
  /** A url string up to its query or fragment. */
30
31
  const pathOf = (url) => url.split(/[?#]/, 1)[0];
32
+ /**
33
+ * A replace's or a reset's presentation, made to push its screen in with the `stackAnimation` it
34
+ * names unless it names a `replaceAnimation` too. `RNSScreenStack` otherwise runs it as a pop of
35
+ * the screen leaving, which animates as that screen says and never reads the one arriving.
36
+ * `default` names no animation of its own, so it is left a pop, as no `stackAnimation` is.
37
+ */
38
+ function arrivingAs(intent, presentation) {
39
+ const replacing = intent.stack === 'replace' || intent.stack === 'reset';
40
+ const named = presentation.stackAnimation !== undefined && presentation.stackAnimation !== 'default';
41
+ if (!replacing || !named || presentation.replaceAnimation)
42
+ return presentation;
43
+ return { ...presentation, replaceAnimation: 'push' };
44
+ }
31
45
  /** Read the intent off a navigation's state, if it carries one. */
32
46
  export function intentOf(state) {
33
47
  if (!state || typeof state !== 'object')
@@ -124,6 +138,9 @@ export class NativeNavigation {
124
138
  /**
125
139
  * Swap the current screen for another. The history entry is replaced too, so a back from the
126
140
  * new screen goes where a back from the old one would have.
141
+ *
142
+ * A `stackAnimation` in `presentation` is the one the new screen arrives with. Without one, with
143
+ * `default`, or with a `replaceAnimation`, the swap animates as the screen leaving was opened.
127
144
  */
128
145
  replace(commands, options = {}) {
129
146
  return this.go(commands, { ...options, replaceUrl: true }, { stack: 'replace' });
@@ -152,11 +169,18 @@ export class NativeNavigation {
152
169
  const over = this.overTheTabInFront(commands, extras);
153
170
  if (!over)
154
171
  return this.go(commands, extras, intent);
155
- return over.then((found) => this.go(found?.commands ?? commands, found?.extras ?? extras, intent));
172
+ return over.then((found) => found
173
+ ? this.go(found.commands, found.extras, intent, found.written)
174
+ : this.go(commands, extras, intent));
156
175
  }
157
176
  /**
158
177
  * Replace the whole stack with one screen, the way signing out or finishing onboarding does.
159
178
  * Every screen below is destroyed, so there is nothing to go back to.
179
+ *
180
+ * A `stackAnimation` in `presentation` is the one a new screen arrives with:
181
+ * `reset('/sign-in', { presentation: { stackAnimation: 'none' } })` lands with no animation.
182
+ * Without one, with `default`, or with a `replaceAnimation`, the reset animates as the screen
183
+ * leaving was opened, and so does a reset to a screen still on the stack, which is kept.
160
184
  */
161
185
  reset(commands, options = {}) {
162
186
  return this.go(commands, { ...options, replaceUrl: true }, { stack: 'reset' });
@@ -210,19 +234,27 @@ export class NativeNavigation {
210
234
  popToRoot() {
211
235
  return this.outlets.popToRoot();
212
236
  }
213
- go(commands, options, intent) {
237
+ go(commands, options, intent, written = typeof commands === 'string'
238
+ ? { path: pathOf(commands), outlet: PRIMARY_OUTLET }
239
+ : null) {
214
240
  const { presentation, state, ...extras } = options;
215
241
  const full = presentation
216
- ? { ...intent, presentation: { ...intent.presentation, ...presentation } }
242
+ ? { ...intent, presentation: arrivingAs(intent, { ...intent.presentation, ...presentation }) }
217
243
  : intent;
218
244
  const navigate = () => {
219
- const list = typeof commands === 'string' ? [pathOf(commands)] : [...commands];
245
+ const list = typeof commands === 'string' ? urlCommands(this.router, pathOf(commands)) : [...commands];
220
246
  const all = {
221
247
  ...extras,
222
248
  ...(typeof commands === 'string' ? this.queryOf(commands, extras) : {}),
223
249
  state: { ...state, [NATIVE_INTENT]: full },
224
250
  };
225
251
  const leaving = leavingPresented(this.router, list, all);
252
+ // A parameter the commands could not carry goes back on the tree they make, which is what
253
+ // `navigate` would go to; `navigateByUrl` then takes it as it is.
254
+ if (written && hasCommandKeyParameters(this.router, written.path)) {
255
+ const tree = leaving ?? this.router.createUrlTree(list, all);
256
+ return this.router.navigateByUrl(withWrittenParameters(this.router, tree, written.path, written.outlet), all);
257
+ }
226
258
  return leaving ? this.router.navigateByUrl(leaving, all) : this.router.navigate(list, all);
227
259
  };
228
260
  // A screen stacked on the first one waits for it. Navigating while the router's first
@@ -272,8 +304,8 @@ export class NativeNavigation {
272
304
  pathFor(commands, extras) {
273
305
  if (typeof commands === 'string' && commands.startsWith('/'))
274
306
  return pathOf(commands);
275
- // Resolved as `go` navigates them: a relative string against `relativeTo`, as a command.
276
- const list = typeof commands === 'string' ? [pathOf(commands)] : [...commands];
307
+ // Resolved as `go` navigates them: a relative string against `relativeTo`.
308
+ const list = typeof commands === 'string' ? urlCommands(this.router, pathOf(commands)) : [...commands];
277
309
  return pathOf(this.router.serializeUrl(this.router.createUrlTree(list, extras)));
278
310
  }
279
311
  /**
@@ -291,7 +323,11 @@ export class NativeNavigation {
291
323
  if (!this.outlets.inTabBehind(path) && !this.outlets.coveredTabAt(path))
292
324
  return null;
293
325
  const query = typeof commands === 'string' ? this.queryOf(commands, extras) : {};
294
- return presentedCommands(this.router, path).then((presented) => presented && { commands: presented, extras: { ...extras, ...query, relativeTo: null } });
326
+ return presentedCommands(this.router, path).then((presented) => presented && {
327
+ commands: presented,
328
+ extras: { ...extras, ...query, relativeTo: null },
329
+ written: { path, outlet: PRESENTED },
330
+ });
295
331
  }
296
332
  /**
297
333
  * The query and fragment written into a url string, as the extras `navigate` takes them: it
@@ -1 +1 @@
1
- {"version":3,"file":"native-navigation.js","sourceRoot":"","sources":["../src/native-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The typed facade over `Router` for the intents a URL cannot express (ADR 0003).\n *\n * Angular's `Router` stays the single source of truth: each of these is a line over\n * `router.navigate`, setting `replaceUrl` and a presentation on `NavigationExtras.state`. Deep\n * links, guards and resolvers keep working because it is still the Router, and because state\n * rides in history a back restores the presentation the screen was opened with.\n *\n * A navigation service that owns the stack was rejected: it either re-implements guards,\n * resolvers and deep linking, or it drifts from the URL, and once the stack is not derived from\n * router state the two can disagree.\n *\n * No decorators, so tests can import it at runtime and so it can be provided with `useClass`,\n * exactly as `NativePlatformLocation` and `NativeStackReuseStrategy` are.\n */\nimport { DestroyRef, inject } from '@angular/core';\nimport {\n NavigationCancel,\n NavigationCancellationCode,\n NavigationEnd,\n NavigationError,\n NavigationSkipped,\n NavigationSkippedCode,\n Router,\n type Navigation,\n type NavigationExtras,\n} from '@angular/router';\nimport { NativeBack } from './native-back.ts';\nimport type { ScreenPresentation, StackPresentation } from './screen-presentation.ts';\nimport { leavingPresented, presentedCommands } from './presented-route.ts';\n\n/** What `Router` takes: a url string, or the array form with segments and params. */\nexport type NavigationCommands = string | readonly unknown[];\n\n/**\n * Where the intent rides. `NavigationExtras.state` is the app's own object, so ours sits under\n * one reserved key rather than spread through it.\n */\nexport const NATIVE_INTENT = 'ɵnativeStack';\n\n/** What a navigation asks the outlet to do with the stack, over and above rendering the route. */\nexport interface NativeIntent {\n /** `replace` drops the screen it supersedes; `reset` empties the stack first. */\n readonly stack?: 'push' | 'replace' | 'reset';\n readonly presentation?: ScreenPresentation;\n}\n\nexport interface NativeNavigationOptions extends NavigationExtras {\n /** How the screen arrives: presentation, animation, gesture and sheet props. */\n presentation?: ScreenPresentation;\n}\n\nexport interface PresentOptions extends NativeNavigationOptions {\n /** Shorthand for `presentation.stackPresentation`. Defaults to `modal`. */\n as?: StackPresentation;\n}\n\n/** Whether commands ask for a query or a fragment, written into a url string or as extras. */\nconst asksMore = (commands: NavigationCommands, extras: NavigationExtras): boolean =>\n (typeof commands === 'string' && /[?#]/.test(commands)) ||\n extras.queryParams != null ||\n extras.fragment != null;\n\n/** A url string up to its query or fragment. */\nconst pathOf = (url: string): string => url.split(/[?#]/, 1)[0]!;\n\n/** Read the intent off a navigation's state, if it carries one. */\nexport function intentOf(state: unknown): NativeIntent | null {\n if (!state || typeof state !== 'object') return null;\n const intent = (state as Record<string, unknown>)[NATIVE_INTENT];\n return intent && typeof intent === 'object' ? (intent as NativeIntent) : null;\n}\n\n/**\n * The intent of a navigation a guard redirected, for the navigation the redirect starts.\n *\n * The router starts a new navigation for a guard's `UrlTree`, keeping `replaceUrl` but not\n * `state`, so a reset to a page that sends a signed-out user to sign in would push the sign-in\n * page over the stack it was meant to clear. Held for that one navigation by its id, which no\n * later navigation shares.\n */\nconst redirected = new WeakMap<object, { readonly id: number; readonly intent: NativeIntent }>();\n\n/** What a navigation asks of the stack: its own intent, or the one it was redirected from. */\nexport function navigationIntent(\n router: object,\n navigation: Pick<Navigation, 'id' | 'extras'> | null | undefined,\n): NativeIntent | null {\n if (!navigation) return null;\n const own = intentOf(navigation.extras.state);\n if (own) return own;\n const carried = redirected.get(router);\n return carried?.id === navigation.id ? carried.intent : null;\n}\n\n/**\n * Hand a navigation's intent on to the one its guard redirects it to. The router schedules that\n * navigation as soon as it has reported the cancel, so it takes the next id.\n *\n * A reset that lands on the screen already on top, its own url or the one a redirect sends it to,\n * is one no outlet activates anything for: the router skips the url already showing, and reuses\n * the screen for another url of it. So the stack is reset here, to the screen on top.\n */\nexport function carryIntentAcrossRedirects(): void {\n const router = inject(Router);\n const outlets = inject(NativeBack);\n const events = router.events.subscribe((event) => {\n if (event instanceof NavigationCancel && event.code === NavigationCancellationCode.Redirect) {\n carryPast(router, event.id);\n return;\n }\n if (keptTop(event, outlets) && intentAt(router, event.id)?.stack === 'reset') {\n outlets.resetToTop();\n }\n if (ended(event) && redirected.get(router)?.id === event.id) redirected.delete(router);\n });\n inject(DestroyRef).onDestroy(() => events.unsubscribe());\n}\n\n/**\n * Whether the navigation `event` ends left the screen on top where it was: one the router skipped\n * for the url already showing, or one no stack put a screen on top for, such as a url with another\n * query, or one a redirect sends to the page on top.\n */\nfunction keptTop(event: unknown, outlets: NativeBack): event is { readonly id: number } {\n if (event instanceof NavigationSkipped) {\n return event.code === NavigationSkippedCode.IgnoredSameUrlNavigation;\n }\n return event instanceof NavigationEnd && !outlets.stackedIn(event.id);\n}\n\n/** The intent of the navigation in progress, when it is the one with `id`. */\nfunction intentAt(router: Router, id: number): NativeIntent | null {\n const navigation = router.currentNavigation();\n return navigation?.id === id ? navigationIntent(router, navigation) : null;\n}\n\n/** Hold the intent of the navigation a guard redirected for the one the redirect starts. */\nfunction carryPast(router: Router, id: number): void {\n const intent = intentAt(router, id);\n if (intent) redirected.set(router, { id: id + 1, intent });\n else redirected.delete(router);\n}\n\n/** Whether the event is the last a navigation reports. */\nfunction ended(event: unknown): event is { readonly id: number } {\n return (\n event instanceof NavigationEnd ||\n event instanceof NavigationCancel ||\n event instanceof NavigationError ||\n event instanceof NavigationSkipped\n );\n}\n\nexport class NativeNavigation {\n private readonly router = inject(Router);\n private readonly outlets = inject(NativeBack);\n\n /**\n * Push a new screen onto the stack. The plain case, and what `nativeRouterLink` does.\n *\n * From a presented screen the new one is presented the same way, over it, since a pushed\n * screen would slide in under it; `presentation` says otherwise, `stackAnimation` and all.\n */\n push(commands: NavigationCommands, options: NativeNavigationOptions = {}): Promise<boolean> {\n return this.go(commands, options, { stack: 'push' });\n }\n\n /**\n * Swap the current screen for another. The history entry is replaced too, so a back from the\n * new screen goes where a back from the old one would have.\n */\n replace(commands: NavigationCommands, options: NavigationExtras = {}): Promise<boolean> {\n return this.go(commands, { ...options, replaceUrl: true }, { stack: 'replace' });\n }\n\n /**\n * Present a screen over the stack rather than pushing into it: a modal, or a sheet.\n *\n * `present('/filters', { as: 'formSheet', presentation: { sheetAllowedDetents: [0.5, 1] } })`.\n *\n * A presented screen is put on screen outside the stack's navigation controller, so it has no\n * native header and no automatic safe-area insets: it needs its own way out, and its own\n * `safe-area-view`. A sheet can also be dragged away, but `fullScreenModal` cannot, and with\n * `gestureEnabled: false` neither can.\n */\n present(commands: NavigationCommands, options: PresentOptions = {}): Promise<boolean> {\n if (this.firstNavigationRunning()) {\n return this.firstNavigation().then(() => this.present(commands, options));\n }\n const { as = 'modal', presentation, ...extras } = options;\n const intent: NativeIntent = {\n stack: 'push',\n presentation: { stackPresentation: as, ...presentation },\n };\n // Asked at once, and only waited for when the page is another tab's: finding its route may\n // load a lazy one.\n const over = this.overTheTabInFront(commands, extras);\n if (!over) return this.go(commands, extras, intent);\n return over.then((found) =>\n this.go(found?.commands ?? commands, found?.extras ?? extras, intent),\n );\n }\n\n /**\n * Replace the whole stack with one screen, the way signing out or finishing onboarding does.\n * Every screen below is destroyed, so there is nothing to go back to.\n */\n reset(commands: NavigationCommands, options: NavigationExtras = {}): Promise<boolean> {\n return this.go(commands, { ...options, replaceUrl: true }, { stack: 'reset' });\n }\n\n /**\n * Go back exactly as the Android back button does: pop the stack in front, or at the root of a\n * tab's stack, go to the first tab. Not one entry back through history, which after a trip to\n * another tab is that tab. At the root of the app there is nowhere to go, and this does nothing.\n */\n back(): void {\n // Optional call: a test that stands a plain object in for the router has no such signal.\n if (!this.router.currentNavigation?.()) {\n this.outlets.back();\n return;\n }\n // Asked while a navigation is still putting its screen up: a page that leaves as it appears,\n // from an effect in its constructor. No stack has that screen to pop yet, so the back waits\n // for the navigation, and goes nowhere if the navigation does not arrive.\n if (this.backWaiting) return;\n this.backWaiting = this.router.events.subscribe((event) => {\n const ended = event instanceof NavigationEnd;\n if (!ended && !(event instanceof NavigationCancel) && !(event instanceof NavigationError)) {\n return;\n }\n this.backWaiting?.unsubscribe();\n this.backWaiting = null;\n if (ended) this.outlets.back();\n });\n }\n\n /** The wait of a back asked for mid-navigation, so two asked for in one are one. */\n private backWaiting: { unsubscribe(): void } | null = null;\n\n /**\n * Pop straight back to the screen at `commands`, taking every screen above it off the stack at\n * once - `popToViewController`. The screen is the one already there, with its state, not a new\n * one; where that url is also the one showing, the screen on top is the one kept. Resolves\n * false, doing nothing, when no screen below the top of a stack in front is showing that url; a\n * push is the way to a screen that is not there.\n */\n popTo(commands: NavigationCommands): Promise<boolean> {\n const tree =\n typeof commands === 'string'\n ? this.router.parseUrl(commands)\n : this.router.createUrlTree([...commands]);\n return this.outlets.popTo(this.router.serializeUrl(tree));\n }\n\n /**\n * Pop the stack in front to its first screen - `popToRootViewController`. Inside a tab, that is\n * the tab's first screen; the screens below keep their state. Resolves false at the root already.\n */\n popToRoot(): Promise<boolean> {\n return this.outlets.popToRoot();\n }\n\n private go(\n commands: NavigationCommands,\n options: NativeNavigationOptions,\n intent: NativeIntent,\n ): Promise<boolean> {\n const { presentation, state, ...extras } = options;\n const full: NativeIntent = presentation\n ? { ...intent, presentation: { ...intent.presentation, ...presentation } }\n : intent;\n\n const navigate = () => {\n const list = typeof commands === 'string' ? [pathOf(commands)] : [...commands];\n const all = {\n ...extras,\n ...(typeof commands === 'string' ? this.queryOf(commands, extras) : {}),\n state: { ...(state as object), [NATIVE_INTENT]: full },\n };\n const leaving = leavingPresented(this.router, list, all);\n return leaving ? this.router.navigateByUrl(leaving, all) : this.router.navigate(list, all);\n };\n // A screen stacked on the first one waits for it. Navigating while the router's first\n // navigation is still running - a lazily loaded root waiting on its import - cancels that\n // navigation, and the app opens on this screen with nothing beneath it. A reset replaces the\n // stack anyway, so it goes at once.\n const start = () => this.underTabRoot(commands, extras, full, navigate);\n return intent.stack !== 'reset' && this.firstNavigationRunning()\n ? this.firstNavigation().then(start)\n : start();\n }\n\n /**\n * A push to a page of a tab bar that a screen over it covers, pushed on the app's own stack or\n * presented, goes back to the bar first: to its tab, where that was left, and then pushes the\n * page there unless the tab is already showing it. One tab bar, as a native app has. Pushed as\n * it was asked, the page would build a second bar over the screen.\n *\n * A push into a tab nobody has opened goes to the tab first, so its stack has the tab's own\n * first screen under the page, and a back from the page lands there: what a tab bar with a\n * navigation controller per tab does. Without it the page is the stack's only screen, and the\n * same push leaves a different stack behind depending on whether the tab was ever tapped. A\n * presented screen goes where it is asked, and a replace or a reset has nothing to keep.\n */\n private underTabRoot(\n commands: NavigationCommands,\n extras: NavigationExtras,\n intent: NativeIntent,\n navigate: () => Promise<boolean>,\n ): Promise<boolean> {\n if (intent.stack !== 'push' || intent.presentation) return navigate();\n const path = this.pathFor(commands, extras);\n const left = this.outlets.coveredTabAt(path);\n if (left) {\n // The tab is open once the bar is back, so the page goes straight onto it, unless it is the\n // page the tab was left on and nothing more, a query or a fragment, is asked of it.\n const asked = pathOf(left) !== path || asksMore(commands, extras);\n // A guard that redirects elsewhere still resolves true, so the tab must be in front now.\n return this.router.navigateByUrl(left).then((arrived) => {\n const there =\n arrived && !this.outlets.coveredTabAt(path) && !this.outlets.inTabBehind(path);\n return there && asked ? navigate() : there;\n });\n }\n const tab = this.outlets.unopenedTabOf(path);\n if (!tab) return navigate();\n return this.router\n .navigateByUrl(tab)\n .then((arrived) => (arrived && !this.outlets.unopenedTabOf(path) ? navigate() : false));\n }\n\n /** The path the commands lead to, without its query and fragment. */\n private pathFor(commands: NavigationCommands, extras: NavigationExtras): string {\n if (typeof commands === 'string' && commands.startsWith('/')) return pathOf(commands);\n // Resolved as `go` navigates them: a relative string against `relativeTo`, as a command.\n const list = typeof commands === 'string' ? [pathOf(commands)] : [...commands];\n return pathOf(this.router.serializeUrl(this.router.createUrlTree(list, extras)));\n }\n\n /**\n * A page of a tab that is not in front, presented over the one that is, or over a screen that\n * covers the bar: its url would select\n * its own tab and show it there, so it goes to the root's presented outlet instead, where the\n * app's own stack shows it over everything. See `presented-route.ts`. Null for a page of the\n * tab in front, or of no tab, which is presented where its url puts it.\n */\n private overTheTabInFront(\n commands: NavigationCommands,\n extras: NavigationExtras,\n ): Promise<{ commands: unknown[]; extras: NavigationExtras } | null> | null {\n if (!this.outlets.presents) return null;\n const path = this.pathFor(commands, extras);\n // A tab in front of a bar a screen covers is behind that screen just the same.\n if (!this.outlets.inTabBehind(path) && !this.outlets.coveredTabAt(path)) return null;\n const query = typeof commands === 'string' ? this.queryOf(commands, extras) : {};\n return presentedCommands(this.router, path).then(\n (presented) =>\n presented && { commands: presented, extras: { ...extras, ...query, relativeTo: null } },\n );\n }\n\n /**\n * The query and fragment written into a url string, as the extras `navigate` takes them: it\n * reads a string command as one path segment, and would encode a `?` into it. Extras given\n * alongside win over the ones in the string.\n */\n private queryOf(url: string, extras: NavigationExtras): NavigationExtras {\n const cut = url.search(/[?#]/);\n if (cut < 0) return {};\n const { queryParams, fragment } = this.router.parseUrl(url.slice(cut));\n return {\n queryParams: { ...queryParams, ...extras.queryParams },\n fragment: extras.fragment ?? fragment ?? undefined,\n };\n }\n\n private firstNavigationRunning(): boolean {\n const router = this.router as Partial<Router>;\n return router.navigated === false && !!router.currentNavigation?.();\n }\n\n private firstNavigation(): Promise<void> {\n return new Promise((resolve) => {\n const events = this.router.events.subscribe((event) => {\n if (ended(event) && !carriesOn(event)) {\n events.unsubscribe();\n resolve();\n }\n });\n });\n }\n}\n\n/**\n * Whether a navigation ended only for another to take its place: a guard's redirect does, and so\n * does a newer navigation. Waiting for a navigation to end goes on past one of these, or what\n * starts next supersedes the redirect and opens with nothing beneath it, past the guard.\n */\nexport function carriesOn(event: unknown): boolean {\n return (\n event instanceof NavigationCancel &&\n (event.code === NavigationCancellationCode.Redirect ||\n event.code === NavigationCancellationCode.SupersededByNewNavigation)\n );\n}\n"]}
1
+ {"version":3,"file":"native-navigation.js","sourceRoot":"","sources":["../src/native-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The typed facade over `Router` for the intents a URL cannot express (ADR 0003).\n *\n * Angular's `Router` stays the single source of truth: each of these is a line over\n * `router.navigate`, setting `replaceUrl` and a presentation on `NavigationExtras.state`. Deep\n * links, guards and resolvers keep working because it is still the Router, and because state\n * rides in history a back restores the presentation the screen was opened with.\n *\n * A navigation service that owns the stack was rejected: it either re-implements guards,\n * resolvers and deep linking, or it drifts from the URL, and once the stack is not derived from\n * router state the two can disagree.\n *\n * No decorators, so tests can import it at runtime and so it can be provided with `useClass`,\n * exactly as `NativePlatformLocation` and `NativeStackReuseStrategy` are.\n */\nimport { DestroyRef, inject } from '@angular/core';\nimport {\n NavigationCancel,\n NavigationCancellationCode,\n NavigationEnd,\n NavigationError,\n NavigationSkipped,\n NavigationSkippedCode,\n PRIMARY_OUTLET,\n Router,\n type Navigation,\n type NavigationExtras,\n} from '@angular/router';\nimport { NativeBack } from './native-back.ts';\nimport type { ScreenPresentation, StackPresentation } from './screen-presentation.ts';\nimport { PRESENTED, leavingPresented, presentedCommands } from './presented-route.ts';\nimport { hasCommandKeyParameters, urlCommands, withWrittenParameters } from './url-commands.ts';\n\n/** What `Router` takes: a url string, or the array form with segments and params. */\nexport type NavigationCommands = string | readonly unknown[];\n\n/**\n * Where the intent rides. `NavigationExtras.state` is the app's own object, so ours sits under\n * one reserved key rather than spread through it.\n */\nexport const NATIVE_INTENT = 'ɵnativeStack';\n\n/** What a navigation asks the outlet to do with the stack, over and above rendering the route. */\nexport interface NativeIntent {\n /** `replace` drops the screen it supersedes; `reset` empties the stack first. */\n readonly stack?: 'push' | 'replace' | 'reset';\n readonly presentation?: ScreenPresentation;\n}\n\nexport interface NativeNavigationOptions extends NavigationExtras {\n /** How the screen arrives: presentation, animation, gesture and sheet props. */\n presentation?: ScreenPresentation;\n}\n\nexport interface PresentOptions extends NativeNavigationOptions {\n /** Shorthand for `presentation.stackPresentation`. Defaults to `modal`. */\n as?: StackPresentation;\n}\n\n/** Whether commands ask for a query or a fragment, written into a url string or as extras. */\nconst asksMore = (commands: NavigationCommands, extras: NavigationExtras): boolean =>\n (typeof commands === 'string' && /[?#]/.test(commands)) ||\n extras.queryParams != null ||\n extras.fragment != null;\n\n/** A url string's path, and the outlet whose segments it writes. */\ninterface WrittenPath {\n readonly path: string;\n readonly outlet: string;\n}\n\n/** A url string up to its query or fragment. */\nconst pathOf = (url: string): string => url.split(/[?#]/, 1)[0]!;\n\n/**\n * A replace's or a reset's presentation, made to push its screen in with the `stackAnimation` it\n * names unless it names a `replaceAnimation` too. `RNSScreenStack` otherwise runs it as a pop of\n * the screen leaving, which animates as that screen says and never reads the one arriving.\n * `default` names no animation of its own, so it is left a pop, as no `stackAnimation` is.\n */\nfunction arrivingAs(intent: NativeIntent, presentation: ScreenPresentation): ScreenPresentation {\n const replacing = intent.stack === 'replace' || intent.stack === 'reset';\n const named =\n presentation.stackAnimation !== undefined && presentation.stackAnimation !== 'default';\n if (!replacing || !named || presentation.replaceAnimation) return presentation;\n return { ...presentation, replaceAnimation: 'push' };\n}\n\n/** Read the intent off a navigation's state, if it carries one. */\nexport function intentOf(state: unknown): NativeIntent | null {\n if (!state || typeof state !== 'object') return null;\n const intent = (state as Record<string, unknown>)[NATIVE_INTENT];\n return intent && typeof intent === 'object' ? (intent as NativeIntent) : null;\n}\n\n/**\n * The intent of a navigation a guard redirected, for the navigation the redirect starts.\n *\n * The router starts a new navigation for a guard's `UrlTree`, keeping `replaceUrl` but not\n * `state`, so a reset to a page that sends a signed-out user to sign in would push the sign-in\n * page over the stack it was meant to clear. Held for that one navigation by its id, which no\n * later navigation shares.\n */\nconst redirected = new WeakMap<object, { readonly id: number; readonly intent: NativeIntent }>();\n\n/** What a navigation asks of the stack: its own intent, or the one it was redirected from. */\nexport function navigationIntent(\n router: object,\n navigation: Pick<Navigation, 'id' | 'extras'> | null | undefined,\n): NativeIntent | null {\n if (!navigation) return null;\n const own = intentOf(navigation.extras.state);\n if (own) return own;\n const carried = redirected.get(router);\n return carried?.id === navigation.id ? carried.intent : null;\n}\n\n/**\n * Hand a navigation's intent on to the one its guard redirects it to. The router schedules that\n * navigation as soon as it has reported the cancel, so it takes the next id.\n *\n * A reset that lands on the screen already on top, its own url or the one a redirect sends it to,\n * is one no outlet activates anything for: the router skips the url already showing, and reuses\n * the screen for another url of it. So the stack is reset here, to the screen on top.\n */\nexport function carryIntentAcrossRedirects(): void {\n const router = inject(Router);\n const outlets = inject(NativeBack);\n const events = router.events.subscribe((event) => {\n if (event instanceof NavigationCancel && event.code === NavigationCancellationCode.Redirect) {\n carryPast(router, event.id);\n return;\n }\n if (keptTop(event, outlets) && intentAt(router, event.id)?.stack === 'reset') {\n outlets.resetToTop();\n }\n if (ended(event) && redirected.get(router)?.id === event.id) redirected.delete(router);\n });\n inject(DestroyRef).onDestroy(() => events.unsubscribe());\n}\n\n/**\n * Whether the navigation `event` ends left the screen on top where it was: one the router skipped\n * for the url already showing, or one no stack put a screen on top for, such as a url with another\n * query, or one a redirect sends to the page on top.\n */\nfunction keptTop(event: unknown, outlets: NativeBack): event is { readonly id: number } {\n if (event instanceof NavigationSkipped) {\n return event.code === NavigationSkippedCode.IgnoredSameUrlNavigation;\n }\n return event instanceof NavigationEnd && !outlets.stackedIn(event.id);\n}\n\n/** The intent of the navigation in progress, when it is the one with `id`. */\nfunction intentAt(router: Router, id: number): NativeIntent | null {\n const navigation = router.currentNavigation();\n return navigation?.id === id ? navigationIntent(router, navigation) : null;\n}\n\n/** Hold the intent of the navigation a guard redirected for the one the redirect starts. */\nfunction carryPast(router: Router, id: number): void {\n const intent = intentAt(router, id);\n if (intent) redirected.set(router, { id: id + 1, intent });\n else redirected.delete(router);\n}\n\n/** Whether the event is the last a navigation reports. */\nfunction ended(event: unknown): event is { readonly id: number } {\n return (\n event instanceof NavigationEnd ||\n event instanceof NavigationCancel ||\n event instanceof NavigationError ||\n event instanceof NavigationSkipped\n );\n}\n\nexport class NativeNavigation {\n private readonly router = inject(Router);\n private readonly outlets = inject(NativeBack);\n\n /**\n * Push a new screen onto the stack. The plain case, and what `nativeRouterLink` does.\n *\n * From a presented screen the new one is presented the same way, over it, since a pushed\n * screen would slide in under it; `presentation` says otherwise, `stackAnimation` and all.\n */\n push(commands: NavigationCommands, options: NativeNavigationOptions = {}): Promise<boolean> {\n return this.go(commands, options, { stack: 'push' });\n }\n\n /**\n * Swap the current screen for another. The history entry is replaced too, so a back from the\n * new screen goes where a back from the old one would have.\n *\n * A `stackAnimation` in `presentation` is the one the new screen arrives with. Without one, with\n * `default`, or with a `replaceAnimation`, the swap animates as the screen leaving was opened.\n */\n replace(commands: NavigationCommands, options: NativeNavigationOptions = {}): Promise<boolean> {\n return this.go(commands, { ...options, replaceUrl: true }, { stack: 'replace' });\n }\n\n /**\n * Present a screen over the stack rather than pushing into it: a modal, or a sheet.\n *\n * `present('/filters', { as: 'formSheet', presentation: { sheetAllowedDetents: [0.5, 1] } })`.\n *\n * A presented screen is put on screen outside the stack's navigation controller, so it has no\n * native header and no automatic safe-area insets: it needs its own way out, and its own\n * `safe-area-view`. A sheet can also be dragged away, but `fullScreenModal` cannot, and with\n * `gestureEnabled: false` neither can.\n */\n present(commands: NavigationCommands, options: PresentOptions = {}): Promise<boolean> {\n if (this.firstNavigationRunning()) {\n return this.firstNavigation().then(() => this.present(commands, options));\n }\n const { as = 'modal', presentation, ...extras } = options;\n const intent: NativeIntent = {\n stack: 'push',\n presentation: { stackPresentation: as, ...presentation },\n };\n // Asked at once, and only waited for when the page is another tab's: finding its route may\n // load a lazy one.\n const over = this.overTheTabInFront(commands, extras);\n if (!over) return this.go(commands, extras, intent);\n return over.then((found) =>\n found\n ? this.go(found.commands, found.extras, intent, found.written)\n : this.go(commands, extras, intent),\n );\n }\n\n /**\n * Replace the whole stack with one screen, the way signing out or finishing onboarding does.\n * Every screen below is destroyed, so there is nothing to go back to.\n *\n * A `stackAnimation` in `presentation` is the one a new screen arrives with:\n * `reset('/sign-in', { presentation: { stackAnimation: 'none' } })` lands with no animation.\n * Without one, with `default`, or with a `replaceAnimation`, the reset animates as the screen\n * leaving was opened, and so does a reset to a screen still on the stack, which is kept.\n */\n reset(commands: NavigationCommands, options: NativeNavigationOptions = {}): Promise<boolean> {\n return this.go(commands, { ...options, replaceUrl: true }, { stack: 'reset' });\n }\n\n /**\n * Go back exactly as the Android back button does: pop the stack in front, or at the root of a\n * tab's stack, go to the first tab. Not one entry back through history, which after a trip to\n * another tab is that tab. At the root of the app there is nowhere to go, and this does nothing.\n */\n back(): void {\n // Optional call: a test that stands a plain object in for the router has no such signal.\n if (!this.router.currentNavigation?.()) {\n this.outlets.back();\n return;\n }\n // Asked while a navigation is still putting its screen up: a page that leaves as it appears,\n // from an effect in its constructor. No stack has that screen to pop yet, so the back waits\n // for the navigation, and goes nowhere if the navigation does not arrive.\n if (this.backWaiting) return;\n this.backWaiting = this.router.events.subscribe((event) => {\n const ended = event instanceof NavigationEnd;\n if (!ended && !(event instanceof NavigationCancel) && !(event instanceof NavigationError)) {\n return;\n }\n this.backWaiting?.unsubscribe();\n this.backWaiting = null;\n if (ended) this.outlets.back();\n });\n }\n\n /** The wait of a back asked for mid-navigation, so two asked for in one are one. */\n private backWaiting: { unsubscribe(): void } | null = null;\n\n /**\n * Pop straight back to the screen at `commands`, taking every screen above it off the stack at\n * once - `popToViewController`. The screen is the one already there, with its state, not a new\n * one; where that url is also the one showing, the screen on top is the one kept. Resolves\n * false, doing nothing, when no screen below the top of a stack in front is showing that url; a\n * push is the way to a screen that is not there.\n */\n popTo(commands: NavigationCommands): Promise<boolean> {\n const tree =\n typeof commands === 'string'\n ? this.router.parseUrl(commands)\n : this.router.createUrlTree([...commands]);\n return this.outlets.popTo(this.router.serializeUrl(tree));\n }\n\n /**\n * Pop the stack in front to its first screen - `popToRootViewController`. Inside a tab, that is\n * the tab's first screen; the screens below keep their state. Resolves false at the root already.\n */\n popToRoot(): Promise<boolean> {\n return this.outlets.popToRoot();\n }\n\n private go(\n commands: NavigationCommands,\n options: NativeNavigationOptions,\n intent: NativeIntent,\n written: WrittenPath | null = typeof commands === 'string'\n ? { path: pathOf(commands), outlet: PRIMARY_OUTLET }\n : null,\n ): Promise<boolean> {\n const { presentation, state, ...extras } = options;\n const full: NativeIntent = presentation\n ? { ...intent, presentation: arrivingAs(intent, { ...intent.presentation, ...presentation }) }\n : intent;\n\n const navigate = () => {\n const list =\n typeof commands === 'string' ? urlCommands(this.router, pathOf(commands)) : [...commands];\n const all = {\n ...extras,\n ...(typeof commands === 'string' ? this.queryOf(commands, extras) : {}),\n state: { ...(state as object), [NATIVE_INTENT]: full },\n };\n const leaving = leavingPresented(this.router, list, all);\n // A parameter the commands could not carry goes back on the tree they make, which is what\n // `navigate` would go to; `navigateByUrl` then takes it as it is.\n if (written && hasCommandKeyParameters(this.router, written.path)) {\n const tree = leaving ?? this.router.createUrlTree(list, all);\n return this.router.navigateByUrl(\n withWrittenParameters(this.router, tree, written.path, written.outlet),\n all,\n );\n }\n return leaving ? this.router.navigateByUrl(leaving, all) : this.router.navigate(list, all);\n };\n // A screen stacked on the first one waits for it. Navigating while the router's first\n // navigation is still running - a lazily loaded root waiting on its import - cancels that\n // navigation, and the app opens on this screen with nothing beneath it. A reset replaces the\n // stack anyway, so it goes at once.\n const start = () => this.underTabRoot(commands, extras, full, navigate);\n return intent.stack !== 'reset' && this.firstNavigationRunning()\n ? this.firstNavigation().then(start)\n : start();\n }\n\n /**\n * A push to a page of a tab bar that a screen over it covers, pushed on the app's own stack or\n * presented, goes back to the bar first: to its tab, where that was left, and then pushes the\n * page there unless the tab is already showing it. One tab bar, as a native app has. Pushed as\n * it was asked, the page would build a second bar over the screen.\n *\n * A push into a tab nobody has opened goes to the tab first, so its stack has the tab's own\n * first screen under the page, and a back from the page lands there: what a tab bar with a\n * navigation controller per tab does. Without it the page is the stack's only screen, and the\n * same push leaves a different stack behind depending on whether the tab was ever tapped. A\n * presented screen goes where it is asked, and a replace or a reset has nothing to keep.\n */\n private underTabRoot(\n commands: NavigationCommands,\n extras: NavigationExtras,\n intent: NativeIntent,\n navigate: () => Promise<boolean>,\n ): Promise<boolean> {\n if (intent.stack !== 'push' || intent.presentation) return navigate();\n const path = this.pathFor(commands, extras);\n const left = this.outlets.coveredTabAt(path);\n if (left) {\n // The tab is open once the bar is back, so the page goes straight onto it, unless it is the\n // page the tab was left on and nothing more, a query or a fragment, is asked of it.\n const asked = pathOf(left) !== path || asksMore(commands, extras);\n // A guard that redirects elsewhere still resolves true, so the tab must be in front now.\n return this.router.navigateByUrl(left).then((arrived) => {\n const there =\n arrived && !this.outlets.coveredTabAt(path) && !this.outlets.inTabBehind(path);\n return there && asked ? navigate() : there;\n });\n }\n const tab = this.outlets.unopenedTabOf(path);\n if (!tab) return navigate();\n return this.router\n .navigateByUrl(tab)\n .then((arrived) => (arrived && !this.outlets.unopenedTabOf(path) ? navigate() : false));\n }\n\n /** The path the commands lead to, without its query and fragment. */\n private pathFor(commands: NavigationCommands, extras: NavigationExtras): string {\n if (typeof commands === 'string' && commands.startsWith('/')) return pathOf(commands);\n // Resolved as `go` navigates them: a relative string against `relativeTo`.\n const list =\n typeof commands === 'string' ? urlCommands(this.router, pathOf(commands)) : [...commands];\n return pathOf(this.router.serializeUrl(this.router.createUrlTree(list, extras)));\n }\n\n /**\n * A page of a tab that is not in front, presented over the one that is, or over a screen that\n * covers the bar: its url would select\n * its own tab and show it there, so it goes to the root's presented outlet instead, where the\n * app's own stack shows it over everything. See `presented-route.ts`. Null for a page of the\n * tab in front, or of no tab, which is presented where its url puts it.\n */\n private overTheTabInFront(\n commands: NavigationCommands,\n extras: NavigationExtras,\n ): Promise<{\n commands: unknown[];\n extras: NavigationExtras;\n written: WrittenPath;\n } | null> | null {\n if (!this.outlets.presents) return null;\n const path = this.pathFor(commands, extras);\n // A tab in front of a bar a screen covers is behind that screen just the same.\n if (!this.outlets.inTabBehind(path) && !this.outlets.coveredTabAt(path)) return null;\n const query = typeof commands === 'string' ? this.queryOf(commands, extras) : {};\n return presentedCommands(this.router, path).then(\n (presented) =>\n presented && {\n commands: presented,\n extras: { ...extras, ...query, relativeTo: null },\n written: { path, outlet: PRESENTED },\n },\n );\n }\n\n /**\n * The query and fragment written into a url string, as the extras `navigate` takes them: it\n * reads a string command as one path segment, and would encode a `?` into it. Extras given\n * alongside win over the ones in the string.\n */\n private queryOf(url: string, extras: NavigationExtras): NavigationExtras {\n const cut = url.search(/[?#]/);\n if (cut < 0) return {};\n const { queryParams, fragment } = this.router.parseUrl(url.slice(cut));\n return {\n queryParams: { ...queryParams, ...extras.queryParams },\n fragment: extras.fragment ?? fragment ?? undefined,\n };\n }\n\n private firstNavigationRunning(): boolean {\n const router = this.router as Partial<Router>;\n return router.navigated === false && !!router.currentNavigation?.();\n }\n\n private firstNavigation(): Promise<void> {\n return new Promise((resolve) => {\n const events = this.router.events.subscribe((event) => {\n if (ended(event) && !carriesOn(event)) {\n events.unsubscribe();\n resolve();\n }\n });\n });\n }\n}\n\n/**\n * Whether a navigation ended only for another to take its place: a guard's redirect does, and so\n * does a newer navigation. Waiting for a navigation to end goes on past one of these, or what\n * starts next supersedes the redirect and opens with nothing beneath it, past the guard.\n */\nexport function carriesOn(event: unknown): boolean {\n return (\n event instanceof NavigationCancel &&\n (event.code === NavigationCancellationCode.Redirect ||\n event.code === NavigationCancellationCode.SupersededByNewNavigation)\n );\n}\n"]}
@@ -22,9 +22,21 @@ export interface LinkSource {
22
22
  /** Links that arrive while the app is running. Returns an unsubscribe. */
23
23
  subscribe(listener: (url: string) => void): () => void;
24
24
  }
25
+ interface HistoryEntry {
26
+ url: string;
27
+ state: unknown;
28
+ }
29
+ /** A move through history by `historyGo`, and the entry it reached as it was then. */
30
+ export interface HistoryMove {
31
+ readonly from: number;
32
+ readonly to: number;
33
+ readonly entry: HistoryEntry;
34
+ }
25
35
  export declare class NativePlatformLocation implements PlatformLocation {
26
36
  private readonly entries;
27
37
  private cursor;
38
+ /** The last move by `historyGo`, until history changes some other way. */
39
+ private moved;
28
40
  private readonly popStateListeners;
29
41
  private readonly hashChangeListeners;
30
42
  /**
@@ -41,11 +53,40 @@ export declare class NativePlatformLocation implements PlatformLocation {
41
53
  * stack outlet, which has to know whether going back reaches the screen a native pop revealed.
42
54
  */
43
55
  ɵurlAt(offset: number): string | null;
56
+ /**
57
+ * The navigation ids the router wrote into the entries, the ones ahead included. For the reuse
58
+ * strategy, which forgets a screen's entries once history no longer holds them.
59
+ */
60
+ ɵnavigationIds(): Set<number>;
44
61
  /** The history up to the page showing, oldest first. For parking it across a reload. */
45
62
  ɵpages(): readonly {
46
63
  readonly url: string;
47
64
  readonly state: unknown;
48
65
  }[];
66
+ /**
67
+ * Go back `steps` entries without a popstate, for a navigation already on the page there. The
68
+ * entries ahead stay as forward ones, as after a back.
69
+ */
70
+ ɵrewind(steps: number): void;
71
+ /** The last move by `historyGo`, while it is still the last change to the history. */
72
+ ɵlastMove(): HistoryMove | null;
73
+ /**
74
+ * Undo `move`, with no popstate: the entry it reached as it was before the router wrote over it,
75
+ * and the entry it left showing again. For a back whose navigation failed. Nothing when history
76
+ * has moved on since.
77
+ */
78
+ ɵundo(move: HistoryMove): void;
79
+ /**
80
+ * Take out the `count` entries just behind the one showing, with no popstate. For a navigation
81
+ * that went back to a screen further down and on to a page under it: the screens taken off on
82
+ * the way leave no entries between the two.
83
+ */
84
+ ɵdropBehind(count: number): void;
85
+ /**
86
+ * Take out the entry showing, with no popstate: the one before it shows again, and none is left
87
+ * ahead. For a navigation that wrote its entry and then failed.
88
+ */
89
+ ɵdiscard(): void;
49
90
  private get current();
50
91
  getBaseHrefFromDOM(): string;
51
92
  getState(): unknown;
@@ -66,3 +107,4 @@ export declare class NativePlatformLocation implements PlatformLocation {
66
107
  historyGo(relativePosition?: number): void;
67
108
  private emitPopState;
68
109
  }
110
+ export {};
@@ -11,6 +11,8 @@ function parse(url) {
11
11
  export class NativePlatformLocation {
12
12
  entries = [{ url: '/', state: null }];
13
13
  cursor = 0;
14
+ /** The last move by `historyGo`, until history changes some other way. */
15
+ moved = null;
14
16
  popStateListeners = new Set();
15
17
  hashChangeListeners = new Set();
16
18
  /**
@@ -39,10 +41,72 @@ export class NativePlatformLocation {
39
41
  ɵurlAt(offset) {
40
42
  return this.entries[this.cursor + offset]?.url ?? null;
41
43
  }
44
+ /**
45
+ * The navigation ids the router wrote into the entries, the ones ahead included. For the reuse
46
+ * strategy, which forgets a screen's entries once history no longer holds them.
47
+ */
48
+ ɵnavigationIds() {
49
+ const ids = new Set();
50
+ for (const { state } of this.entries) {
51
+ const id = state?.navigationId;
52
+ if (typeof id === 'number')
53
+ ids.add(id);
54
+ }
55
+ return ids;
56
+ }
42
57
  /** The history up to the page showing, oldest first. For parking it across a reload. */
43
58
  ɵpages() {
44
59
  return this.entries.slice(0, this.cursor + 1);
45
60
  }
61
+ /**
62
+ * Go back `steps` entries without a popstate, for a navigation already on the page there. The
63
+ * entries ahead stay as forward ones, as after a back.
64
+ */
65
+ ɵrewind(steps) {
66
+ if (steps < 1 || steps > this.cursor)
67
+ return;
68
+ this.cursor -= steps;
69
+ this.moved = null;
70
+ }
71
+ /** The last move by `historyGo`, while it is still the last change to the history. */
72
+ ɵlastMove() {
73
+ return this.moved;
74
+ }
75
+ /**
76
+ * Undo `move`, with no popstate: the entry it reached as it was before the router wrote over it,
77
+ * and the entry it left showing again. For a back whose navigation failed. Nothing when history
78
+ * has moved on since.
79
+ */
80
+ ɵundo(move) {
81
+ if (this.moved !== move || this.cursor !== move.to)
82
+ return;
83
+ this.entries[move.to] = move.entry;
84
+ this.cursor = move.from;
85
+ this.moved = null;
86
+ }
87
+ /**
88
+ * Take out the `count` entries just behind the one showing, with no popstate. For a navigation
89
+ * that went back to a screen further down and on to a page under it: the screens taken off on
90
+ * the way leave no entries between the two.
91
+ */
92
+ ɵdropBehind(count) {
93
+ if (count < 1 || count > this.cursor)
94
+ return;
95
+ this.entries.splice(this.cursor - count, count);
96
+ this.cursor -= count;
97
+ this.moved = null;
98
+ }
99
+ /**
100
+ * Take out the entry showing, with no popstate: the one before it shows again, and none is left
101
+ * ahead. For a navigation that wrote its entry and then failed.
102
+ */
103
+ ɵdiscard() {
104
+ if (this.cursor === 0)
105
+ return;
106
+ this.entries.splice(this.cursor, 1);
107
+ this.cursor--;
108
+ this.moved = null;
109
+ }
46
110
  get current() {
47
111
  return this.entries[this.cursor];
48
112
  }
@@ -88,6 +152,7 @@ export class NativePlatformLocation {
88
152
  this.entries.length = this.cursor + 1;
89
153
  this.entries.push({ url, state });
90
154
  this.cursor++;
155
+ this.moved = null;
91
156
  }
92
157
  replaceState(state, _title, url) {
93
158
  this.entries[this.cursor] = { url, state };
@@ -103,6 +168,7 @@ export class NativePlatformLocation {
103
168
  // Out of range is a no-op, matching the browser rather than throwing.
104
169
  if (target < 0 || target >= this.entries.length)
105
170
  return;
171
+ this.moved = { from: this.cursor, to: target, entry: this.entries[target] };
106
172
  this.cursor = target;
107
173
  this.emitPopState();
108
174
  }
@@ -1 +1 @@
1
- {"version":3,"file":"native-platform-location.js","sourceRoot":"","sources":["../src/native-platform-location.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `PlatformLocation` over an in-memory history.\n *\n * Angular's `Location` reads and writes the URL through this. On the web the browser owns the\n * history stack; here nothing does, so we keep it ourselves and the URL tree stays the source of\n * truth in both directions: `router.navigate()` pushes an entry, and a swipe-back reaches the\n * outlet as the screen's `dismissed` event, which usually calls `historyGo(-1)` here and emits\n * `popstate` so the router follows. Not always: see the stack outlet's `popBy` for when the entry\n * behind is another tab.\n *\n * No decorators, so tests can import it at runtime and so it can be provided with `useClass`.\n */\nimport type { LocationChangeListener, PlatformLocation } from '@angular/common';\n/**\n * What the history needs of a deep-link source, which `DeepLinks` from\n * `@ng-native/device` satisfies. Structural rather than the class itself: the router has no\n * use for the rest of it, and a test has no use for a real one.\n */\nexport interface LinkSource {\n /** The path the app was launched with, or null for a normal launch. */\n initialUrl(): string | null;\n /** Links that arrive while the app is running. Returns an unsubscribe. */\n subscribe(listener: (url: string) => void): () => void;\n}\n\ninterface HistoryEntry {\n url: string;\n state: unknown;\n}\n\n/** Split a url into its path, `?search` and `#hash` parts. */\nfunction parse(url: string): { pathname: string; search: string; hash: string } {\n const hashAt = url.indexOf('#');\n const hash = hashAt === -1 ? '' : url.slice(hashAt);\n const withoutHash = hashAt === -1 ? url : url.slice(0, hashAt);\n\n const searchAt = withoutHash.indexOf('?');\n const search = searchAt === -1 ? '' : withoutHash.slice(searchAt);\n const pathname = searchAt === -1 ? withoutHash : withoutHash.slice(0, searchAt);\n\n return { pathname: pathname || '/', search, hash };\n}\n\nexport class NativePlatformLocation implements PlatformLocation {\n private readonly entries: HistoryEntry[] = [{ url: '/', state: null }];\n private cursor = 0;\n\n private readonly popStateListeners = new Set<LocationChangeListener>();\n private readonly hashChangeListeners = new Set<LocationChangeListener>();\n\n /**\n * A deep link is history, not a navigation of its own: the launch url *is* the first entry.\n * One that arrives later is pushed and announced as a popstate when `links` hands it here;\n * `provideNativeRouter` hands those to `NativeNavigation` instead, so a link is pushed over\n * the screen showing. `links` is null in a test that has no interest in either.\n */\n constructor(links: LinkSource | null = null) {\n const initial = links?.initialUrl();\n if (initial) this.entries[0] = { url: initial, state: null };\n links?.subscribe((url) => this.open(url));\n }\n\n /** Follow a link that arrived while the app was running. */\n open(url: string): void {\n if (url === this.current.url) return;\n this.pushState(null, '', url);\n this.emitPopState();\n }\n\n /**\n * The url of the entry `offset` away from the current one, or null past either end. For the\n * stack outlet, which has to know whether going back reaches the screen a native pop revealed.\n */\n ɵurlAt(offset: number): string | null {\n return this.entries[this.cursor + offset]?.url ?? null;\n }\n\n /** The history up to the page showing, oldest first. For parking it across a reload. */\n ɵpages(): readonly { readonly url: string; readonly state: unknown }[] {\n return this.entries.slice(0, this.cursor + 1);\n }\n\n private get current(): HistoryEntry {\n return this.entries[this.cursor]!;\n }\n\n // --- PlatformLocation ---------------------------------------------------\n\n getBaseHrefFromDOM(): string {\n return '/';\n }\n\n getState(): unknown {\n return this.current.state;\n }\n\n onPopState(fn: LocationChangeListener): VoidFunction {\n this.popStateListeners.add(fn);\n return () => this.popStateListeners.delete(fn);\n }\n\n onHashChange(fn: LocationChangeListener): VoidFunction {\n this.hashChangeListeners.add(fn);\n return () => this.hashChangeListeners.delete(fn);\n }\n\n get href(): string {\n return this.current.url;\n }\n\n /** There is no origin on a device. Angular only ever compares these, never resolves them. */\n get protocol(): string {\n return 'app:';\n }\n\n get hostname(): string {\n return '';\n }\n\n get port(): string {\n return '';\n }\n\n get pathname(): string {\n return parse(this.current.url).pathname;\n }\n\n get search(): string {\n return parse(this.current.url).search;\n }\n\n get hash(): string {\n return parse(this.current.url).hash;\n }\n\n pushState(state: unknown, _title: string, url: string): void {\n // A push after going back discards the forward entries, as a browser does.\n this.entries.length = this.cursor + 1;\n this.entries.push({ url, state });\n this.cursor++;\n }\n\n replaceState(state: unknown, _title: string, url: string): void {\n this.entries[this.cursor] = { url, state };\n }\n\n forward(): void {\n this.historyGo(1);\n }\n\n back(): void {\n this.historyGo(-1);\n }\n\n historyGo(relativePosition = 0): void {\n const target = this.cursor + relativePosition;\n // Out of range is a no-op, matching the browser rather than throwing.\n if (target < 0 || target >= this.entries.length) return;\n this.cursor = target;\n this.emitPopState();\n }\n\n private emitPopState(): void {\n const event = { type: 'popstate', state: this.current.state };\n for (const listener of [...this.popStateListeners]) listener(event);\n }\n}\n"]}
1
+ {"version":3,"file":"native-platform-location.js","sourceRoot":"","sources":["../src/native-platform-location.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `PlatformLocation` over an in-memory history.\n *\n * Angular's `Location` reads and writes the URL through this. On the web the browser owns the\n * history stack; here nothing does, so we keep it ourselves and the URL tree stays the source of\n * truth in both directions: `router.navigate()` pushes an entry, and a swipe-back reaches the\n * outlet as the screen's `dismissed` event, which usually calls `historyGo(-1)` here and emits\n * `popstate` so the router follows. Not always: see the stack outlet's `popBy` for when the entry\n * behind is another tab.\n *\n * No decorators, so tests can import it at runtime and so it can be provided with `useClass`.\n */\nimport type { LocationChangeListener, PlatformLocation } from '@angular/common';\n/**\n * What the history needs of a deep-link source, which `DeepLinks` from\n * `@ng-native/device` satisfies. Structural rather than the class itself: the router has no\n * use for the rest of it, and a test has no use for a real one.\n */\nexport interface LinkSource {\n /** The path the app was launched with, or null for a normal launch. */\n initialUrl(): string | null;\n /** Links that arrive while the app is running. Returns an unsubscribe. */\n subscribe(listener: (url: string) => void): () => void;\n}\n\ninterface HistoryEntry {\n url: string;\n state: unknown;\n}\n\n/** A move through history by `historyGo`, and the entry it reached as it was then. */\nexport interface HistoryMove {\n readonly from: number;\n readonly to: number;\n readonly entry: HistoryEntry;\n}\n\n/** Split a url into its path, `?search` and `#hash` parts. */\nfunction parse(url: string): { pathname: string; search: string; hash: string } {\n const hashAt = url.indexOf('#');\n const hash = hashAt === -1 ? '' : url.slice(hashAt);\n const withoutHash = hashAt === -1 ? url : url.slice(0, hashAt);\n\n const searchAt = withoutHash.indexOf('?');\n const search = searchAt === -1 ? '' : withoutHash.slice(searchAt);\n const pathname = searchAt === -1 ? withoutHash : withoutHash.slice(0, searchAt);\n\n return { pathname: pathname || '/', search, hash };\n}\n\nexport class NativePlatformLocation implements PlatformLocation {\n private readonly entries: HistoryEntry[] = [{ url: '/', state: null }];\n private cursor = 0;\n /** The last move by `historyGo`, until history changes some other way. */\n private moved: HistoryMove | null = null;\n\n private readonly popStateListeners = new Set<LocationChangeListener>();\n private readonly hashChangeListeners = new Set<LocationChangeListener>();\n\n /**\n * A deep link is history, not a navigation of its own: the launch url *is* the first entry.\n * One that arrives later is pushed and announced as a popstate when `links` hands it here;\n * `provideNativeRouter` hands those to `NativeNavigation` instead, so a link is pushed over\n * the screen showing. `links` is null in a test that has no interest in either.\n */\n constructor(links: LinkSource | null = null) {\n const initial = links?.initialUrl();\n if (initial) this.entries[0] = { url: initial, state: null };\n links?.subscribe((url) => this.open(url));\n }\n\n /** Follow a link that arrived while the app was running. */\n open(url: string): void {\n if (url === this.current.url) return;\n this.pushState(null, '', url);\n this.emitPopState();\n }\n\n /**\n * The url of the entry `offset` away from the current one, or null past either end. For the\n * stack outlet, which has to know whether going back reaches the screen a native pop revealed.\n */\n ɵurlAt(offset: number): string | null {\n return this.entries[this.cursor + offset]?.url ?? null;\n }\n\n /**\n * The navigation ids the router wrote into the entries, the ones ahead included. For the reuse\n * strategy, which forgets a screen's entries once history no longer holds them.\n */\n ɵnavigationIds(): Set<number> {\n const ids = new Set<number>();\n for (const { state } of this.entries) {\n const id = (state as { navigationId?: unknown } | null)?.navigationId;\n if (typeof id === 'number') ids.add(id);\n }\n return ids;\n }\n\n /** The history up to the page showing, oldest first. For parking it across a reload. */\n ɵpages(): readonly { readonly url: string; readonly state: unknown }[] {\n return this.entries.slice(0, this.cursor + 1);\n }\n\n /**\n * Go back `steps` entries without a popstate, for a navigation already on the page there. The\n * entries ahead stay as forward ones, as after a back.\n */\n ɵrewind(steps: number): void {\n if (steps < 1 || steps > this.cursor) return;\n this.cursor -= steps;\n this.moved = null;\n }\n\n /** The last move by `historyGo`, while it is still the last change to the history. */\n ɵlastMove(): HistoryMove | null {\n return this.moved;\n }\n\n /**\n * Undo `move`, with no popstate: the entry it reached as it was before the router wrote over it,\n * and the entry it left showing again. For a back whose navigation failed. Nothing when history\n * has moved on since.\n */\n ɵundo(move: HistoryMove): void {\n if (this.moved !== move || this.cursor !== move.to) return;\n this.entries[move.to] = move.entry;\n this.cursor = move.from;\n this.moved = null;\n }\n\n /**\n * Take out the `count` entries just behind the one showing, with no popstate. For a navigation\n * that went back to a screen further down and on to a page under it: the screens taken off on\n * the way leave no entries between the two.\n */\n ɵdropBehind(count: number): void {\n if (count < 1 || count > this.cursor) return;\n this.entries.splice(this.cursor - count, count);\n this.cursor -= count;\n this.moved = null;\n }\n\n /**\n * Take out the entry showing, with no popstate: the one before it shows again, and none is left\n * ahead. For a navigation that wrote its entry and then failed.\n */\n ɵdiscard(): void {\n if (this.cursor === 0) return;\n this.entries.splice(this.cursor, 1);\n this.cursor--;\n this.moved = null;\n }\n\n private get current(): HistoryEntry {\n return this.entries[this.cursor]!;\n }\n\n // --- PlatformLocation ---------------------------------------------------\n\n getBaseHrefFromDOM(): string {\n return '/';\n }\n\n getState(): unknown {\n return this.current.state;\n }\n\n onPopState(fn: LocationChangeListener): VoidFunction {\n this.popStateListeners.add(fn);\n return () => this.popStateListeners.delete(fn);\n }\n\n onHashChange(fn: LocationChangeListener): VoidFunction {\n this.hashChangeListeners.add(fn);\n return () => this.hashChangeListeners.delete(fn);\n }\n\n get href(): string {\n return this.current.url;\n }\n\n /** There is no origin on a device. Angular only ever compares these, never resolves them. */\n get protocol(): string {\n return 'app:';\n }\n\n get hostname(): string {\n return '';\n }\n\n get port(): string {\n return '';\n }\n\n get pathname(): string {\n return parse(this.current.url).pathname;\n }\n\n get search(): string {\n return parse(this.current.url).search;\n }\n\n get hash(): string {\n return parse(this.current.url).hash;\n }\n\n pushState(state: unknown, _title: string, url: string): void {\n // A push after going back discards the forward entries, as a browser does.\n this.entries.length = this.cursor + 1;\n this.entries.push({ url, state });\n this.cursor++;\n this.moved = null;\n }\n\n replaceState(state: unknown, _title: string, url: string): void {\n this.entries[this.cursor] = { url, state };\n }\n\n forward(): void {\n this.historyGo(1);\n }\n\n back(): void {\n this.historyGo(-1);\n }\n\n historyGo(relativePosition = 0): void {\n const target = this.cursor + relativePosition;\n // Out of range is a no-op, matching the browser rather than throwing.\n if (target < 0 || target >= this.entries.length) return;\n this.moved = { from: this.cursor, to: target, entry: this.entries[target]! };\n this.cursor = target;\n this.emitPopState();\n }\n\n private emitPopState(): void {\n const event = { type: 'popstate', state: this.current.state };\n for (const listener of [...this.popStateListeners]) listener(event);\n }\n}\n"]}