@ng-native/router 0.18.2 → 0.19.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.
@@ -13,7 +13,7 @@
13
13
  * `fileRoutes` and one routed by `@ng-native/analog` without being renamed. Nothing here imports
14
14
  * Analog: the routes are made here, as plain `@angular/router` routes.
15
15
  */
16
- import { EnvironmentInjector, inject, runInInjectionContext } from '@angular/core';
16
+ import { createEnvironmentInjector, EnvironmentInjector, inject, runInInjectionContext, } from '@angular/core';
17
17
  import { UrlSegment, } from '@angular/router';
18
18
  import { MARKDOWN_PAGE, markdownPageFile } from "./markdown-page.js";
19
19
  /** What a page's file sets about its route by being where it is, so `routeMeta` does not. */
@@ -255,8 +255,14 @@ function pageRoute(page, at, children, options) {
255
255
  if (at.path === '' && !children)
256
256
  at = { ...at, pathMatch: 'full' };
257
257
  if (!page.lazy) {
258
- const { canMatch, ...own } = ownRoute(page, page.load(), children, options);
259
- return { ...at, ...(canMatch ? { canMatch } : {}), children: [own] };
258
+ // The page's providers go up with its guards, which Angular runs in a route's own providers.
259
+ const { canMatch, providers, ...own } = ownRoute(page, page.load(), children, options);
260
+ return {
261
+ ...at,
262
+ ...(canMatch ? { canMatch } : {}),
263
+ ...(providers ? { providers } : {}),
264
+ children: [own],
265
+ };
260
266
  }
261
267
  const load = loaderOf(page);
262
268
  const loadChildren = async () => {
@@ -286,13 +292,23 @@ function loaderOf(page) {
286
292
  }
287
293
  /** Runs a loaded page's `canMatch` guards in turn, until one says other than `true`. */
288
294
  async function passes(meta, injector, args) {
289
- const guards = meta?.canMatch ?? [];
290
- for (const guard of guards) {
291
- const result = await firstOf(runInInjectionContext(injector, () => guard(...args)));
292
- if (result !== true)
293
- return result;
295
+ const { canMatch: guards = [], providers } = (meta ?? {});
296
+ if (!guards.length)
297
+ return true;
298
+ // In the page's providers, as Angular runs an eager page's guards.
299
+ const scoped = providers ? createEnvironmentInjector(providers, injector) : injector;
300
+ try {
301
+ for (const guard of guards) {
302
+ const result = await firstOf(runInInjectionContext(scoped, () => guard(...args)));
303
+ if (result !== true)
304
+ return result;
305
+ }
306
+ return true;
307
+ }
308
+ finally {
309
+ if (scoped !== injector)
310
+ scoped.destroy();
294
311
  }
295
- return true;
296
312
  }
297
313
  /** A guard's result, from an Observable's first value or a promise as from a value. */
298
314
  function firstOf(result) {
@@ -1 +1 @@
1
- {"version":3,"file":"file-routes.js","sourceRoot":"","sources":["../src/file-routes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * File-based routing: each page is a file in `src/app/pages`, and its path there is its URL.\n *\n * ```ts\n * const pages = require.context('./pages', true, /\\.page\\.ts$/, 'lazy');\n *\n * mount(rootTag, App, fabric, { providers: [provideNativeRouter(fileRoutes(pages))] });\n * ```\n *\n * The file names are Analog's (`index.page.ts`, `[id].page.ts`, `[...slug].page.ts`, `(group)`\n * folders, `products.page.ts` beside `products/` as a layout) and so is a page module's shape (a\n * default export and an optional `routeMeta`), so a page moves between an app routed by\n * `fileRoutes` and one routed by `@ng-native/analog` without being renamed. Nothing here imports\n * Analog: the routes are made here, as plain `@angular/router` routes.\n */\nimport { EnvironmentInjector, inject, runInInjectionContext, type Type } from '@angular/core';\nimport {\n UrlSegment,\n type CanMatchFn,\n type GuardResult,\n type Route,\n type Routes,\n type UrlMatcher,\n} from '@angular/router';\nimport { MARKDOWN_PAGE, markdownPageFile } from './markdown-page.ts';\n\n/**\n * What Metro's `require.context(directory, true, pattern, mode)` returns: the files it found, each\n * keyed by its path from that directory (`./users/[id].page.ts`), and a call that loads one, which\n * returns a promise in `'lazy'` mode and the module itself in `'sync'` mode.\n */\nexport interface PageContext {\n keys(): string[];\n (key: string): unknown;\n}\n\n/**\n * The files of an `import.meta.glob`, as Vite and Vitest make it: each file's path, with a call\n * that loads it (`import.meta.glob('./pages/**\\/*.page.ts')`) or the module itself\n * (`{ eager: true }`).\n */\nexport type PageFiles = Readonly<Record<string, unknown>>;\n\nexport interface FileRoutesOptions {\n /**\n * The component a `.md` page is drawn with. It reads its file with `injectMarkdownPage()` and\n * draws `tokens` with `<markdown>` from `@ng-native/components/markdown`. Required when the\n * pages include a `.md` file.\n */\n markdownPage?: Type<unknown>;\n}\n\n/** What a page's file sets about its route by being where it is, so `routeMeta` does not. */\nconst SET_BY_THE_FILE = [\n 'path',\n 'matcher',\n 'component',\n 'loadComponent',\n 'children',\n 'loadChildren',\n 'canLoad',\n 'outlet',\n] as const;\n\n/**\n * A page's `export const routeMeta`, as Analog has it: the page's route config, less what the\n * file's place sets. `meta`, the page's HTML meta tags in Analog, is accepted and left out, since\n * there is no document head on a device. A page that only redirects has `redirectTo` and no\n * component; its `pathMatch` is `'full'` unless it says otherwise.\n */\nexport type RouteMeta =\n | (Omit<Route, (typeof SET_BY_THE_FILE)[number] | 'redirectTo' | 'pathMatch' | 'canMatch'> & {\n canMatch?: CanMatchFn[];\n meta?: unknown;\n redirectTo?: never;\n })\n | { redirectTo: NonNullable<Route['redirectTo']>; pathMatch?: Route['pathMatch'] };\n\n/** A page module: its component as the default export, and its `routeMeta`. */\ninterface PageModule {\n default?: unknown;\n routeMeta?: RouteMeta;\n}\n\n/** One part of a URL a file or folder name makes. */\ntype Part =\n | { kind: 'static'; path: string }\n | { kind: 'group' }\n | { kind: 'param'; name: string }\n | { kind: 'rest'; name: string };\n\n/** A file found, by the key it was listed under and its path from the pages folder. */\ninterface Page {\n readonly key: string;\n /** Its path from the pages folder: `users/[id].page.ts`. */\n readonly file: string;\n readonly markdown: boolean;\n /** The module, or a promise of it. Called on navigation for a lazy page, at once otherwise. */\n readonly load: () => unknown;\n readonly lazy: boolean;\n}\n\n/** A file or folder name in the pages folder, with the file it names and what is under it. */\ninterface Entry {\n readonly parts: Part[];\n readonly pages: Page[];\n readonly children: Map<string, Entry>;\n}\n\n/** A route at one level, with what it sorts by. */\ninterface Ranked {\n readonly rank: number;\n readonly path: string;\n readonly key: string;\n readonly route: Route;\n}\n\n/**\n * The routes for an app's pages, for `provideNativeRouter`.\n *\n * `pages` is the `require.context` of the pages folder, or, in a Vitest test, which has no\n * `require.context`, the files of an `import.meta.glob` such as\n * `import.meta.glob('./pages/**\\/*.page.ts')`. A page from a `require.context`, or from a glob\n * that loads each file, is loaded the first time it is navigated to; a page from an eager glob is\n * routed as it is.\n *\n * | File | Route |\n * | --------------------- | ---------------------------------------------------------- |\n * | `index.page.ts` | `''`, the folder's own page |\n * | `about.page.ts` | `about` |\n * | `[id].page.ts` | `:id` |\n * | `[...slug].page.ts` | all under its folder, the rest of the URL as `slug` |\n * | `(auth)/` | no segment of its own |\n * | `blog.[slug].page.ts` | `blog/:slug`, without a layout |\n * | `products.page.ts` | a layout around `products/`, with an outlet for its pages |\n *\n * At each level a static path comes first, then a group's layout, then a parameter, then a\n * catch-all. Two files at the same URL, a name with brackets that are not one of the above, and a\n * `.md` page with no `options.markdownPage` each throw here, naming the file. A page with no\n * default export throws here when it is eager, and on the navigation that loads it when it is\n * lazy.\n */\nexport function fileRoutes(\n pages: PageContext | PageFiles,\n options: FileRoutesOptions = {},\n): Routes {\n const found = typeof pages === 'function' ? fromContext(pages) : fromGlob(pages);\n const root: Entry = { parts: [], pages: [], children: new Map() };\n for (const [path, page] of found) {\n if (page.markdown && !options.markdownPage) {\n throw new Error(\n `${page.key} is a Markdown page, and fileRoutes has no component to draw it with. Pass ` +\n 'one: fileRoutes(pages, { markdownPage }). See injectMarkdownPage.',\n );\n }\n const names = path.split('/');\n let entry = root;\n for (const name of names) {\n let next = entry.children.get(name);\n if (!next) {\n next = { parts: partsOf(name, page.key), pages: [], children: new Map() };\n entry.children.set(name, next);\n }\n entry = next;\n }\n entry.pages.push(page);\n }\n checkPaths(root, [], new Map());\n return routesOf(root, options).map((ranked) => ranked.route);\n}\n\n/** The context's files, each by its path from the pages folder without its extension. */\nfunction fromContext(pages: PageContext): [string, Page][] {\n return pages.keys().map((key) => {\n const path = key.replace(/\\\\/g, '/').replace(/^\\.?\\//, '');\n return named(key, path, () => pages(key), true);\n });\n}\n\n/**\n * The glob's files, each by its path from the pages folder: what follows the first `pages/` in\n * its key (`/src/app/pages/about.page.ts`, `./pages/about.page.ts`), or the key itself when it\n * has none, for a glob made inside the pages folder.\n */\nfunction fromGlob(pages: PageFiles): [string, Page][] {\n return Object.keys(pages).map((key) => {\n const slashed = key.replace(/\\\\/g, '/');\n const path = /^(?:.*?\\/)?pages\\/(.*)$/.exec(slashed)?.[1] ?? slashed.replace(/^\\.?\\//, '');\n const value = pages[key];\n return typeof value === 'function'\n ? named(key, path, value as () => unknown, true)\n : named(key, path, () => value, false);\n });\n}\n\nfunction named(key: string, path: string, load: () => unknown, lazy: boolean): [string, Page] {\n const markdown = path.endsWith('.md');\n const stem = path.replace(/\\.page\\.[cm]?[jt]s$/, '').replace(/\\.md$/, '');\n if (stem === path) {\n throw new Error(\n `${key} is not a page. A page's file name ends in .page.ts, or .md for a Markdown page: ` +\n 'narrow the require.context or import.meta.glob to those.',\n );\n }\n return [stem, { key, file: path, markdown, load, lazy }];\n}\n\n/**\n * The URL parts a file or folder name makes. A dot splits a name into segments, as in Analog\n * (`blog.[slug]` is `blog/:slug`), except inside brackets (`[...slug]`).\n */\nfunction partsOf(name: string, key: string): Part[] {\n return name.split(/\\.(?![^[]*\\])/).map((piece): Part => {\n if (piece === 'index') return { kind: 'static', path: '' };\n if (/^\\([^()[\\]./]+\\)$/.test(piece)) return { kind: 'group' };\n const rest = /^\\[\\[\\.\\.\\.([\\w$-]+)\\]\\]$/.exec(piece) ?? /^\\[\\.\\.\\.([\\w$-]+)\\]$/.exec(piece);\n if (rest) return { kind: 'rest', name: rest[1]! };\n const param = /^\\[([\\w$-]+)\\]$/.exec(piece);\n if (param) return { kind: 'param', name: param[1]! };\n if (piece && !/[[\\]():]/.test(piece)) return { kind: 'static', path: piece };\n throw new Error(\n `${key}: \"${piece}\" is not a page name fileRoutes reads. A name is a segment (about), a ` +\n 'parameter ([id]), a catch-all ([...slug]) or a group ((auth)), and index is the ' +\n \"folder's own page.\",\n );\n });\n}\n\n/**\n * Throws for a catch-all with anything after it, and for two pages at one URL: two files for one\n * name (`about.page.ts` and `about.md`), or two pages whose paths differ only in groups or in\n * parameter names. A layout is not a page at its URL; its folder's `index.page.ts` is.\n */\nfunction checkPaths(entry: Entry, above: Part[], seen: Map<string, string>): void {\n for (const child of entry.children.values()) {\n const parts = [...above, ...child.parts];\n const rest = parts.findIndex((part) => part.kind === 'rest');\n const key = child.pages[0]?.key ?? firstKey(child);\n if (rest !== -1 && (rest < parts.length - 1 || child.children.size > 0)) {\n throw new Error(\n `${key}: a catch-all ([...slug]) takes the rest of the URL, so it is the last part of a ` +\n 'path, with no folder of pages under it.',\n );\n }\n if (child.pages.length > 1) {\n throw new Error(\n `${child.pages[0]!.key} and ${child.pages[1]!.key} are the same page. Keep one of them.`,\n );\n }\n if (child.pages.length === 1 && child.children.size === 0) {\n const url = urlOf(parts);\n const other = seen.get(url);\n if (other) {\n throw new Error(\n `${other} and ${key} are both the page at /${url}. Keep one of them, or move one.`,\n );\n }\n seen.set(url, key);\n }\n checkPaths(child, parts, seen);\n }\n}\n\nfunction firstKey(entry: Entry): string {\n for (const child of entry.children.values()) return child.pages[0]?.key ?? firstKey(child);\n return '';\n}\n\n/** A page's URL as a pattern, with every parameter alike, for telling two pages at one URL. */\nfunction urlOf(parts: Part[]): string {\n return parts\n .flatMap((part) => {\n if (part.kind === 'param') return [':'];\n if (part.kind === 'rest') return ['**'];\n return part.kind === 'static' && part.path ? [part.path] : [];\n })\n .join('/');\n}\n\n/**\n * The routes under `entry`, in the order Angular should try them: a static path first, a longer\n * one before a shorter it starts with, then a group's layout, then a path with a parameter, then a\n * catch-all, each by its path. A group with no layout of its own adds nothing to the URL or the\n * tree, so its routes join its siblings'.\n */\nfunction routesOf(entry: Entry, options: FileRoutesOptions): Ranked[] {\n const ranked: Ranked[] = [];\n for (const child of entry.children.values()) {\n const children = routesOf(child, options);\n const path = pathOf(child.parts);\n if (!child.pages[0] && !path) ranked.push(...children);\n else ranked.push(rankedRoute(child, path, children, options));\n }\n return ranked.sort(\n (a, b) => a.rank - b.rank || byPath(a.path, b.path) || (a.key < b.key ? -1 : 1),\n );\n}\n\n/**\n * Paths by their segments, a longer one before the path it starts with: `users/new` is tried\n * before the `users` folder, whose `:id` would take `new`, as Analog orders them.\n */\nfunction byPath(a: string, b: string): number {\n const left = a.split('/');\n const right = b.split('/');\n for (let i = 0; i < Math.min(left.length, right.length); i++) {\n if (left[i] !== right[i]) return left[i]! < right[i]! ? -1 : 1;\n }\n return right.length - left.length;\n}\n\n/** The route for a file or folder name at `path`, with its routes under it. */\nfunction rankedRoute(\n entry: Entry,\n path: string,\n children: Ranked[],\n options: FileRoutesOptions,\n): Ranked {\n const page = entry.pages[0];\n const rest = entry.parts.find((part) => part.kind === 'rest');\n const nested = children.length ? children.map((child) => child.route) : undefined;\n let route: Route = page\n ? pageRoute(page, rest ? { matcher: restOf(rest.name) } : { path }, nested, options)\n : { path, children: nested };\n if (rest && path) route = { path, children: [route] };\n const rank = rest ? 3 : path.includes(':') ? 2 : !path && nested ? 1 : 0;\n return { rank, path, key: page?.key ?? firstKey(entry), route };\n}\n\n/** The path a name's parts make, without its groups, its index or its catch-all. */\nfunction pathOf(parts: Part[]): string {\n return parts\n .flatMap((part) => (part.kind === 'static' || part.kind === 'param' ? [segmentOf(part)] : []))\n .filter(Boolean)\n .join('/');\n}\n\nfunction segmentOf(part: Part): string {\n if (part.kind === 'param') return `:${part.name}`;\n return part.kind === 'static' ? part.path : '';\n}\n\n/**\n * A catch-all's matcher: it takes every segment left, none included, as Analog's `**` does, and\n * hands them over as one parameter, `docs/intro` for `/docs/intro` under the catch-all's folder.\n */\nfunction restOf(name: string): UrlMatcher {\n return (segments) => ({\n consumed: segments,\n posParams: { [name]: new UrlSegment(segments.map((segment) => segment.path).join('/'), {}) },\n });\n}\n\n/**\n * A page's route, at `at`: one with no component that takes the page's place in the URL, and the\n * page's own route under it at `''`, as Analog makes it, so the page's `routeMeta` can wait for a\n * lazy page to load. The page's `children`, when it is a layout, go under its own route.\n *\n * `canMatch` is on the outer route, not the page's own: Angular takes a route whose children match\n * none of an empty rest of the URL as matched, so a page refused there would leave an empty screen\n * where the next route that matches should be. An index page's outer route matches only the whole\n * URL, so its guards, and its load when it is lazy, run for no other page's URL: a guard that\n * redirects would otherwise run again for the URL it sent the router to.\n */\nfunction pageRoute(\n page: Page,\n at: Pick<Route, 'path' | 'matcher' | 'pathMatch'>,\n children: Routes | undefined,\n options: FileRoutesOptions,\n): Route {\n if (at.path === '' && !children) at = { ...at, pathMatch: 'full' };\n if (!page.lazy) {\n const { canMatch, ...own } = ownRoute(page, page.load(), children, options);\n return { ...at, ...(canMatch ? { canMatch } : {}), children: [own] };\n }\n const load = loaderOf(page);\n const loadChildren = async () => {\n const { canMatch: _matchedAbove, ...own } = ownRoute(page, await load(), children, options);\n return [own];\n };\n if (page.markdown) return { ...at, loadChildren };\n const canMatch: CanMatchFn = (...args) => {\n const injector = inject(EnvironmentInjector);\n return load().then((module) => passes((module as PageModule)?.routeMeta, injector, args));\n };\n return { ...at, canMatch: [canMatch], loadChildren };\n}\n\n/**\n * Loads a lazy page once for its `canMatch` and its routes both. A load that fails is forgotten,\n * so the next navigation tries again, as Angular does with a `loadChildren` that fails.\n */\nfunction loaderOf(page: Page): () => Promise<unknown> {\n let loading: Promise<unknown> | undefined;\n return () =>\n (loading ??= Promise.resolve()\n .then(page.load)\n .catch((error: unknown) => {\n loading = undefined;\n throw error;\n }));\n}\n\n/** Runs a loaded page's `canMatch` guards in turn, until one says other than `true`. */\nasync function passes(\n meta: RouteMeta | undefined,\n injector: EnvironmentInjector,\n args: Parameters<CanMatchFn>,\n): Promise<GuardResult> {\n const guards = (meta as { canMatch?: CanMatchFn[] } | undefined)?.canMatch ?? [];\n for (const guard of guards) {\n const result = await firstOf(runInInjectionContext(injector, () => guard(...args)));\n if (result !== true) return result as GuardResult;\n }\n return true;\n}\n\n/** A guard's result, from an Observable's first value or a promise as from a value. */\nfunction firstOf(result: unknown): Promise<unknown> {\n const observable = result as { subscribe?: unknown } | null;\n if (typeof observable?.subscribe !== 'function') return Promise.resolve(result);\n const source = observable as { subscribe(observer: object): { unsubscribe(): void } };\n return new Promise((resolve, reject) => {\n let done = false;\n let subscription: { unsubscribe(): void } | undefined;\n subscription = source.subscribe({\n next: (value: unknown) => {\n if (done) return;\n done = true;\n resolve(value);\n subscription?.unsubscribe();\n },\n error: (error: unknown) => {\n if (done) return;\n done = true;\n reject(error);\n },\n complete: () => {\n if (done) return;\n done = true;\n resolve(false);\n },\n });\n if (done) subscription.unsubscribe();\n });\n}\n\nfunction ownRoute(\n page: Page,\n loaded: unknown,\n children: Routes | undefined,\n options: FileRoutesOptions,\n): Route {\n const module = page.markdown ? markdownModule(page, loaded, options) : (loaded as PageModule);\n const meta = (module?.routeMeta ?? {}) as Record<string, unknown>;\n const set = SET_BY_THE_FILE.find((name) => name in meta);\n if (set) {\n throw new Error(\n `${page.key} sets routeMeta.${set}, which a page's file sets by where it is. Leave it out.`,\n );\n }\n const config = { ...meta } as Route;\n delete (config as { meta?: unknown }).meta;\n if (config.redirectTo !== undefined) {\n if (children) {\n throw new Error(\n `${page.key} redirects, and a page that redirects is not a layout: its folder's pages ` +\n 'would never show. Redirect from a page with no folder beside it.',\n );\n }\n return { path: '', pathMatch: 'full', ...config };\n }\n if (typeof module?.default !== 'function') {\n throw new Error(\n `${page.key} has no default export. A page exports its component as the default: ` +\n '`export default class AboutPage {}`.',\n );\n }\n return {\n path: '',\n component: module.default as Type<unknown>,\n runGuardsAndResolvers: 'paramsOrQueryParamsChange',\n ...config,\n ...(children ? { children } : {}),\n };\n}\n\n/** A `.md` page as a page module: `markdownPage`, titled by the file's front matter. */\nfunction markdownModule(page: Page, loaded: unknown, options: FileRoutesOptions): PageModule {\n const file = markdownPageFile(page.file, loaded);\n const title = file.attributes['title'];\n return {\n default: options.markdownPage,\n routeMeta: {\n ...(typeof title === 'string' ? { title } : {}),\n data: { [MARKDOWN_PAGE]: file },\n },\n };\n}\n"]}
1
+ {"version":3,"file":"file-routes.js","sourceRoot":"","sources":["../src/file-routes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * File-based routing: each page is a file in `src/app/pages`, and its path there is its URL.\n *\n * ```ts\n * const pages = require.context('./pages', true, /\\.page\\.ts$/, 'lazy');\n *\n * mount(rootTag, App, fabric, { providers: [provideNativeRouter(fileRoutes(pages))] });\n * ```\n *\n * The file names are Analog's (`index.page.ts`, `[id].page.ts`, `[...slug].page.ts`, `(group)`\n * folders, `products.page.ts` beside `products/` as a layout) and so is a page module's shape (a\n * default export and an optional `routeMeta`), so a page moves between an app routed by\n * `fileRoutes` and one routed by `@ng-native/analog` without being renamed. Nothing here imports\n * Analog: the routes are made here, as plain `@angular/router` routes.\n */\nimport {\n createEnvironmentInjector,\n EnvironmentInjector,\n inject,\n runInInjectionContext,\n type Type,\n} from '@angular/core';\nimport {\n UrlSegment,\n type CanMatchFn,\n type GuardResult,\n type Route,\n type Routes,\n type UrlMatcher,\n} from '@angular/router';\nimport { MARKDOWN_PAGE, markdownPageFile } from './markdown-page.ts';\n\n/**\n * What Metro's `require.context(directory, true, pattern, mode)` returns: the files it found, each\n * keyed by its path from that directory (`./users/[id].page.ts`), and a call that loads one, which\n * returns a promise in `'lazy'` mode and the module itself in `'sync'` mode.\n */\nexport interface PageContext {\n keys(): string[];\n (key: string): unknown;\n}\n\n/**\n * The files of an `import.meta.glob`, as Vite and Vitest make it: each file's path, with a call\n * that loads it (`import.meta.glob('./pages/**\\/*.page.ts')`) or the module itself\n * (`{ eager: true }`).\n */\nexport type PageFiles = Readonly<Record<string, unknown>>;\n\nexport interface FileRoutesOptions {\n /**\n * The component a `.md` page is drawn with. It reads its file with `injectMarkdownPage()` and\n * draws `tokens` with `<markdown>` from `@ng-native/components/markdown`. Required when the\n * pages include a `.md` file.\n */\n markdownPage?: Type<unknown>;\n}\n\n/** What a page's file sets about its route by being where it is, so `routeMeta` does not. */\nconst SET_BY_THE_FILE = [\n 'path',\n 'matcher',\n 'component',\n 'loadComponent',\n 'children',\n 'loadChildren',\n 'canLoad',\n 'outlet',\n] as const;\n\n/**\n * A page's `export const routeMeta`, as Analog has it: the page's route config, less what the\n * file's place sets. `meta`, the page's HTML meta tags in Analog, is accepted and left out, since\n * there is no document head on a device. A page that only redirects has `redirectTo` and no\n * component; its `pathMatch` is `'full'` unless it says otherwise.\n */\nexport type RouteMeta =\n | (Omit<Route, (typeof SET_BY_THE_FILE)[number] | 'redirectTo' | 'pathMatch' | 'canMatch'> & {\n canMatch?: CanMatchFn[];\n meta?: unknown;\n redirectTo?: never;\n })\n | { redirectTo: NonNullable<Route['redirectTo']>; pathMatch?: Route['pathMatch'] };\n\n/** A page module: its component as the default export, and its `routeMeta`. */\ninterface PageModule {\n default?: unknown;\n routeMeta?: RouteMeta;\n}\n\n/** One part of a URL a file or folder name makes. */\ntype Part =\n | { kind: 'static'; path: string }\n | { kind: 'group' }\n | { kind: 'param'; name: string }\n | { kind: 'rest'; name: string };\n\n/** A file found, by the key it was listed under and its path from the pages folder. */\ninterface Page {\n readonly key: string;\n /** Its path from the pages folder: `users/[id].page.ts`. */\n readonly file: string;\n readonly markdown: boolean;\n /** The module, or a promise of it. Called on navigation for a lazy page, at once otherwise. */\n readonly load: () => unknown;\n readonly lazy: boolean;\n}\n\n/** A file or folder name in the pages folder, with the file it names and what is under it. */\ninterface Entry {\n readonly parts: Part[];\n readonly pages: Page[];\n readonly children: Map<string, Entry>;\n}\n\n/** A route at one level, with what it sorts by. */\ninterface Ranked {\n readonly rank: number;\n readonly path: string;\n readonly key: string;\n readonly route: Route;\n}\n\n/**\n * The routes for an app's pages, for `provideNativeRouter`.\n *\n * `pages` is the `require.context` of the pages folder, or, in a Vitest test, which has no\n * `require.context`, the files of an `import.meta.glob` such as\n * `import.meta.glob('./pages/**\\/*.page.ts')`. A page from a `require.context`, or from a glob\n * that loads each file, is loaded the first time it is navigated to; a page from an eager glob is\n * routed as it is.\n *\n * | File | Route |\n * | --------------------- | ---------------------------------------------------------- |\n * | `index.page.ts` | `''`, the folder's own page |\n * | `about.page.ts` | `about` |\n * | `[id].page.ts` | `:id` |\n * | `[...slug].page.ts` | all under its folder, the rest of the URL as `slug` |\n * | `(auth)/` | no segment of its own |\n * | `blog.[slug].page.ts` | `blog/:slug`, without a layout |\n * | `products.page.ts` | a layout around `products/`, with an outlet for its pages |\n *\n * At each level a static path comes first, then a group's layout, then a parameter, then a\n * catch-all. Two files at the same URL, a name with brackets that are not one of the above, and a\n * `.md` page with no `options.markdownPage` each throw here, naming the file. A page with no\n * default export throws here when it is eager, and on the navigation that loads it when it is\n * lazy.\n */\nexport function fileRoutes(\n pages: PageContext | PageFiles,\n options: FileRoutesOptions = {},\n): Routes {\n const found = typeof pages === 'function' ? fromContext(pages) : fromGlob(pages);\n const root: Entry = { parts: [], pages: [], children: new Map() };\n for (const [path, page] of found) {\n if (page.markdown && !options.markdownPage) {\n throw new Error(\n `${page.key} is a Markdown page, and fileRoutes has no component to draw it with. Pass ` +\n 'one: fileRoutes(pages, { markdownPage }). See injectMarkdownPage.',\n );\n }\n const names = path.split('/');\n let entry = root;\n for (const name of names) {\n let next = entry.children.get(name);\n if (!next) {\n next = { parts: partsOf(name, page.key), pages: [], children: new Map() };\n entry.children.set(name, next);\n }\n entry = next;\n }\n entry.pages.push(page);\n }\n checkPaths(root, [], new Map());\n return routesOf(root, options).map((ranked) => ranked.route);\n}\n\n/** The context's files, each by its path from the pages folder without its extension. */\nfunction fromContext(pages: PageContext): [string, Page][] {\n return pages.keys().map((key) => {\n const path = key.replace(/\\\\/g, '/').replace(/^\\.?\\//, '');\n return named(key, path, () => pages(key), true);\n });\n}\n\n/**\n * The glob's files, each by its path from the pages folder: what follows the first `pages/` in\n * its key (`/src/app/pages/about.page.ts`, `./pages/about.page.ts`), or the key itself when it\n * has none, for a glob made inside the pages folder.\n */\nfunction fromGlob(pages: PageFiles): [string, Page][] {\n return Object.keys(pages).map((key) => {\n const slashed = key.replace(/\\\\/g, '/');\n const path = /^(?:.*?\\/)?pages\\/(.*)$/.exec(slashed)?.[1] ?? slashed.replace(/^\\.?\\//, '');\n const value = pages[key];\n return typeof value === 'function'\n ? named(key, path, value as () => unknown, true)\n : named(key, path, () => value, false);\n });\n}\n\nfunction named(key: string, path: string, load: () => unknown, lazy: boolean): [string, Page] {\n const markdown = path.endsWith('.md');\n const stem = path.replace(/\\.page\\.[cm]?[jt]s$/, '').replace(/\\.md$/, '');\n if (stem === path) {\n throw new Error(\n `${key} is not a page. A page's file name ends in .page.ts, or .md for a Markdown page: ` +\n 'narrow the require.context or import.meta.glob to those.',\n );\n }\n return [stem, { key, file: path, markdown, load, lazy }];\n}\n\n/**\n * The URL parts a file or folder name makes. A dot splits a name into segments, as in Analog\n * (`blog.[slug]` is `blog/:slug`), except inside brackets (`[...slug]`).\n */\nfunction partsOf(name: string, key: string): Part[] {\n return name.split(/\\.(?![^[]*\\])/).map((piece): Part => {\n if (piece === 'index') return { kind: 'static', path: '' };\n if (/^\\([^()[\\]./]+\\)$/.test(piece)) return { kind: 'group' };\n const rest = /^\\[\\[\\.\\.\\.([\\w$-]+)\\]\\]$/.exec(piece) ?? /^\\[\\.\\.\\.([\\w$-]+)\\]$/.exec(piece);\n if (rest) return { kind: 'rest', name: rest[1]! };\n const param = /^\\[([\\w$-]+)\\]$/.exec(piece);\n if (param) return { kind: 'param', name: param[1]! };\n if (piece && !/[[\\]():]/.test(piece)) return { kind: 'static', path: piece };\n throw new Error(\n `${key}: \"${piece}\" is not a page name fileRoutes reads. A name is a segment (about), a ` +\n 'parameter ([id]), a catch-all ([...slug]) or a group ((auth)), and index is the ' +\n \"folder's own page.\",\n );\n });\n}\n\n/**\n * Throws for a catch-all with anything after it, and for two pages at one URL: two files for one\n * name (`about.page.ts` and `about.md`), or two pages whose paths differ only in groups or in\n * parameter names. A layout is not a page at its URL; its folder's `index.page.ts` is.\n */\nfunction checkPaths(entry: Entry, above: Part[], seen: Map<string, string>): void {\n for (const child of entry.children.values()) {\n const parts = [...above, ...child.parts];\n const rest = parts.findIndex((part) => part.kind === 'rest');\n const key = child.pages[0]?.key ?? firstKey(child);\n if (rest !== -1 && (rest < parts.length - 1 || child.children.size > 0)) {\n throw new Error(\n `${key}: a catch-all ([...slug]) takes the rest of the URL, so it is the last part of a ` +\n 'path, with no folder of pages under it.',\n );\n }\n if (child.pages.length > 1) {\n throw new Error(\n `${child.pages[0]!.key} and ${child.pages[1]!.key} are the same page. Keep one of them.`,\n );\n }\n if (child.pages.length === 1 && child.children.size === 0) {\n const url = urlOf(parts);\n const other = seen.get(url);\n if (other) {\n throw new Error(\n `${other} and ${key} are both the page at /${url}. Keep one of them, or move one.`,\n );\n }\n seen.set(url, key);\n }\n checkPaths(child, parts, seen);\n }\n}\n\nfunction firstKey(entry: Entry): string {\n for (const child of entry.children.values()) return child.pages[0]?.key ?? firstKey(child);\n return '';\n}\n\n/** A page's URL as a pattern, with every parameter alike, for telling two pages at one URL. */\nfunction urlOf(parts: Part[]): string {\n return parts\n .flatMap((part) => {\n if (part.kind === 'param') return [':'];\n if (part.kind === 'rest') return ['**'];\n return part.kind === 'static' && part.path ? [part.path] : [];\n })\n .join('/');\n}\n\n/**\n * The routes under `entry`, in the order Angular should try them: a static path first, a longer\n * one before a shorter it starts with, then a group's layout, then a path with a parameter, then a\n * catch-all, each by its path. A group with no layout of its own adds nothing to the URL or the\n * tree, so its routes join its siblings'.\n */\nfunction routesOf(entry: Entry, options: FileRoutesOptions): Ranked[] {\n const ranked: Ranked[] = [];\n for (const child of entry.children.values()) {\n const children = routesOf(child, options);\n const path = pathOf(child.parts);\n if (!child.pages[0] && !path) ranked.push(...children);\n else ranked.push(rankedRoute(child, path, children, options));\n }\n return ranked.sort(\n (a, b) => a.rank - b.rank || byPath(a.path, b.path) || (a.key < b.key ? -1 : 1),\n );\n}\n\n/**\n * Paths by their segments, a longer one before the path it starts with: `users/new` is tried\n * before the `users` folder, whose `:id` would take `new`, as Analog orders them.\n */\nfunction byPath(a: string, b: string): number {\n const left = a.split('/');\n const right = b.split('/');\n for (let i = 0; i < Math.min(left.length, right.length); i++) {\n if (left[i] !== right[i]) return left[i]! < right[i]! ? -1 : 1;\n }\n return right.length - left.length;\n}\n\n/** The route for a file or folder name at `path`, with its routes under it. */\nfunction rankedRoute(\n entry: Entry,\n path: string,\n children: Ranked[],\n options: FileRoutesOptions,\n): Ranked {\n const page = entry.pages[0];\n const rest = entry.parts.find((part) => part.kind === 'rest');\n const nested = children.length ? children.map((child) => child.route) : undefined;\n let route: Route = page\n ? pageRoute(page, rest ? { matcher: restOf(rest.name) } : { path }, nested, options)\n : { path, children: nested };\n if (rest && path) route = { path, children: [route] };\n const rank = rest ? 3 : path.includes(':') ? 2 : !path && nested ? 1 : 0;\n return { rank, path, key: page?.key ?? firstKey(entry), route };\n}\n\n/** The path a name's parts make, without its groups, its index or its catch-all. */\nfunction pathOf(parts: Part[]): string {\n return parts\n .flatMap((part) => (part.kind === 'static' || part.kind === 'param' ? [segmentOf(part)] : []))\n .filter(Boolean)\n .join('/');\n}\n\nfunction segmentOf(part: Part): string {\n if (part.kind === 'param') return `:${part.name}`;\n return part.kind === 'static' ? part.path : '';\n}\n\n/**\n * A catch-all's matcher: it takes every segment left, none included, as Analog's `**` does, and\n * hands them over as one parameter, `docs/intro` for `/docs/intro` under the catch-all's folder.\n */\nfunction restOf(name: string): UrlMatcher {\n return (segments) => ({\n consumed: segments,\n posParams: { [name]: new UrlSegment(segments.map((segment) => segment.path).join('/'), {}) },\n });\n}\n\n/**\n * A page's route, at `at`: one with no component that takes the page's place in the URL, and the\n * page's own route under it at `''`, as Analog makes it, so the page's `routeMeta` can wait for a\n * lazy page to load. The page's `children`, when it is a layout, go under its own route.\n *\n * `canMatch` is on the outer route, not the page's own: Angular takes a route whose children match\n * none of an empty rest of the URL as matched, so a page refused there would leave an empty screen\n * where the next route that matches should be. An index page's outer route matches only the whole\n * URL, so its guards, and its load when it is lazy, run for no other page's URL: a guard that\n * redirects would otherwise run again for the URL it sent the router to.\n */\nfunction pageRoute(\n page: Page,\n at: Pick<Route, 'path' | 'matcher' | 'pathMatch'>,\n children: Routes | undefined,\n options: FileRoutesOptions,\n): Route {\n if (at.path === '' && !children) at = { ...at, pathMatch: 'full' };\n if (!page.lazy) {\n // The page's providers go up with its guards, which Angular runs in a route's own providers.\n const { canMatch, providers, ...own } = ownRoute(page, page.load(), children, options);\n return {\n ...at,\n ...(canMatch ? { canMatch } : {}),\n ...(providers ? { providers } : {}),\n children: [own],\n };\n }\n const load = loaderOf(page);\n const loadChildren = async () => {\n const { canMatch: _matchedAbove, ...own } = ownRoute(page, await load(), children, options);\n return [own];\n };\n if (page.markdown) return { ...at, loadChildren };\n const canMatch: CanMatchFn = (...args) => {\n const injector = inject(EnvironmentInjector);\n return load().then((module) => passes((module as PageModule)?.routeMeta, injector, args));\n };\n return { ...at, canMatch: [canMatch], loadChildren };\n}\n\n/**\n * Loads a lazy page once for its `canMatch` and its routes both. A load that fails is forgotten,\n * so the next navigation tries again, as Angular does with a `loadChildren` that fails.\n */\nfunction loaderOf(page: Page): () => Promise<unknown> {\n let loading: Promise<unknown> | undefined;\n return () =>\n (loading ??= Promise.resolve()\n .then(page.load)\n .catch((error: unknown) => {\n loading = undefined;\n throw error;\n }));\n}\n\n/** Runs a loaded page's `canMatch` guards in turn, until one says other than `true`. */\nasync function passes(\n meta: RouteMeta | undefined,\n injector: EnvironmentInjector,\n args: Parameters<CanMatchFn>,\n): Promise<GuardResult> {\n const { canMatch: guards = [], providers } = (meta ?? {}) as Route;\n if (!guards.length) return true;\n // In the page's providers, as Angular runs an eager page's guards.\n const scoped = providers ? createEnvironmentInjector(providers, injector) : injector;\n try {\n for (const guard of guards as CanMatchFn[]) {\n const result = await firstOf(runInInjectionContext(scoped, () => guard(...args)));\n if (result !== true) return result as GuardResult;\n }\n return true;\n } finally {\n if (scoped !== injector) scoped.destroy();\n }\n}\n\n/** A guard's result, from an Observable's first value or a promise as from a value. */\nfunction firstOf(result: unknown): Promise<unknown> {\n const observable = result as { subscribe?: unknown } | null;\n if (typeof observable?.subscribe !== 'function') return Promise.resolve(result);\n const source = observable as { subscribe(observer: object): { unsubscribe(): void } };\n return new Promise((resolve, reject) => {\n let done = false;\n let subscription: { unsubscribe(): void } | undefined;\n subscription = source.subscribe({\n next: (value: unknown) => {\n if (done) return;\n done = true;\n resolve(value);\n subscription?.unsubscribe();\n },\n error: (error: unknown) => {\n if (done) return;\n done = true;\n reject(error);\n },\n complete: () => {\n if (done) return;\n done = true;\n resolve(false);\n },\n });\n if (done) subscription.unsubscribe();\n });\n}\n\nfunction ownRoute(\n page: Page,\n loaded: unknown,\n children: Routes | undefined,\n options: FileRoutesOptions,\n): Route {\n const module = page.markdown ? markdownModule(page, loaded, options) : (loaded as PageModule);\n const meta = (module?.routeMeta ?? {}) as Record<string, unknown>;\n const set = SET_BY_THE_FILE.find((name) => name in meta);\n if (set) {\n throw new Error(\n `${page.key} sets routeMeta.${set}, which a page's file sets by where it is. Leave it out.`,\n );\n }\n const config = { ...meta } as Route;\n delete (config as { meta?: unknown }).meta;\n if (config.redirectTo !== undefined) {\n if (children) {\n throw new Error(\n `${page.key} redirects, and a page that redirects is not a layout: its folder's pages ` +\n 'would never show. Redirect from a page with no folder beside it.',\n );\n }\n return { path: '', pathMatch: 'full', ...config };\n }\n if (typeof module?.default !== 'function') {\n throw new Error(\n `${page.key} has no default export. A page exports its component as the default: ` +\n '`export default class AboutPage {}`.',\n );\n }\n return {\n path: '',\n component: module.default as Type<unknown>,\n runGuardsAndResolvers: 'paramsOrQueryParamsChange',\n ...config,\n ...(children ? { children } : {}),\n };\n}\n\n/** A `.md` page as a page module: `markdownPage`, titled by the file's front matter. */\nfunction markdownModule(page: Page, loaded: unknown, options: FileRoutesOptions): PageModule {\n const file = markdownPageFile(page.file, loaded);\n const title = file.attributes['title'];\n return {\n default: options.markdownPage,\n routeMeta: {\n ...(typeof title === 'string' ? { title } : {}),\n data: { [MARKDOWN_PAGE]: file },\n },\n };\n}\n"]}
@@ -10,6 +10,10 @@ export interface PoppableStack {
10
10
  popTo(url: string): Promise<boolean> | null;
11
11
  /** Take every screen under the top off at once, or false when it is showing only that one. */
12
12
  dropUnderTop(): boolean;
13
+ /** The route of the screen on top, or null when it has none in front. */
14
+ top(): ActivatedRoute | null;
15
+ /** The route of the first screen this stack presents `fullScreenModal`, or null for none. */
16
+ fullScreen(): ActivatedRoute | null;
13
17
  }
14
18
  /**
15
19
  * What a tab bar offers `NativeNavigation` for a push into one of its tabs from outside it. A tab
@@ -51,6 +55,10 @@ export declare class NativeBack {
51
55
  presents: boolean;
52
56
  /** Offer a stack for popping several screens at once. Returns an unsubscribe. */
53
57
  addStack(stack: PoppableStack): () => void;
58
+ /** The routes of the screens the stacks present `fullScreenModal`. */
59
+ fullScreens(): ActivatedRoute[];
60
+ /** Whether `route` is the screen on top of a stack. */
61
+ onTopOfStack(route: ActivatedRoute): boolean;
54
62
  /** Pop the innermost stack in front that is deeper than one screen, to its first. */
55
63
  popToRoot(): Promise<boolean>;
56
64
  /**
@@ -68,6 +68,14 @@ export class NativeBack {
68
68
  this.stacks.splice(index, 1);
69
69
  };
70
70
  }
71
+ /** The routes of the screens the stacks present `fullScreenModal`. */
72
+ fullScreens() {
73
+ return this.stacks.flatMap((stack) => stack.fullScreen() ?? []);
74
+ }
75
+ /** Whether `route` is the screen on top of a stack. */
76
+ onTopOfStack(route) {
77
+ return this.stacks.some((stack) => stack.top() === route);
78
+ }
71
79
  /** Pop the innermost stack in front that is deeper than one screen, to its first. */
72
80
  popToRoot() {
73
81
  for (const stack of [...this.stacks].reverse()) {
@@ -1 +1 @@
1
- {"version":3,"file":"native-back.js","sourceRoot":"","sources":["../src/native-back.ts"],"names":[],"mappings":"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","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"]}
1
+ {"version":3,"file":"native-back.js","sourceRoot":"","sources":["../src/native-back.ts"],"names":[],"mappings":"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","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 /** The route of the screen on top, or null when it has none in front. */\n top(): ActivatedRoute | null;\n /** The route of the first screen this stack presents `fullScreenModal`, or null for none. */\n fullScreen(): ActivatedRoute | null;\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 /** The routes of the screens the stacks present `fullScreenModal`. */\n fullScreens(): ActivatedRoute[] {\n return this.stacks.flatMap((stack) => stack.fullScreen() ?? []);\n }\n\n /** Whether `route` is the screen on top of a stack. */\n onTopOfStack(route: ActivatedRoute): boolean {\n return this.stacks.some((stack) => stack.top() === route);\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"]}
@@ -105,7 +105,12 @@ export declare class NativeHeader {
105
105
  readonly backTitleFontSize: import("@angular/core").InputSignalWithTransform<number | undefined, unknown>;
106
106
  /** Show the back button's title at all. Native defaults this to true. */
107
107
  readonly backTitleVisible: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
108
- /** iOS 14+: how aggressively the back title is shortened when space is tight. */
108
+ /**
109
+ * iOS 14+: how aggressively the back title is shortened when space is tight. When iOS draws the
110
+ * button as the chevron alone (`minimal` without a back title font or `disableBackButtonMenu`, or
111
+ * `[backTitleVisible]="false"`), VoiceOver reads it as "Back" rather than the back title: UIKit
112
+ * labels it itself.
113
+ */
109
114
  readonly backButtonDisplayMode: import("@angular/core").InputSignal<BackButtonDisplayMode | undefined>;
110
115
  /** Put the back button in the custom view slot, so a `back` header item can replace it. */
111
116
  readonly backButtonInCustomView: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
@@ -185,7 +185,12 @@ export class NativeHeader {
185
185
  backTitleFontSize = input(undefined, { ...(ngDevMode ? { debugName: "backTitleFontSize" } : /* istanbul ignore next */ {}), transform: optionalNumber });
186
186
  /** Show the back button's title at all. Native defaults this to true. */
187
187
  backTitleVisible = input(undefined, { ...(ngDevMode ? { debugName: "backTitleVisible" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
188
- /** iOS 14+: how aggressively the back title is shortened when space is tight. */
188
+ /**
189
+ * iOS 14+: how aggressively the back title is shortened when space is tight. When iOS draws the
190
+ * button as the chevron alone (`minimal` without a back title font or `disableBackButtonMenu`, or
191
+ * `[backTitleVisible]="false"`), VoiceOver reads it as "Back" rather than the back title: UIKit
192
+ * labels it itself.
193
+ */
189
194
  backButtonDisplayMode = input(/* @ts-ignore */
190
195
  ...(ngDevMode ? [undefined, { debugName: "backButtonDisplayMode" }] : /* istanbul ignore next */ []));
191
196
  /** Put the back button in the custom view slot, so a `back` header item can replace it. */
@@ -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, 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"]}
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 /**\n * iOS 14+: how aggressively the back title is shortened when space is tight. When iOS draws the\n * button as the chevron alone (`minimal` without a back title font or `disableBackButtonMenu`, or\n * `[backTitleVisible]=\"false\"`), VoiceOver reads it as \"Back\" rather than the back title: UIKit\n * labels it itself.\n */\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"]}
@@ -36,6 +36,7 @@ export declare function navigationIntent(router: object, navigation: Pick<Naviga
36
36
  export declare function carryIntentAcrossRedirects(): void;
37
37
  export declare class NativeNavigation {
38
38
  private readonly router;
39
+ private readonly injector;
39
40
  private readonly outlets;
40
41
  /**
41
42
  * Push a new screen onto the stack. The plain case, and what `nativeRouterLink` does.
@@ -61,6 +62,9 @@ export declare class NativeNavigation {
61
62
  * native header and no automatic safe-area insets: it needs its own way out, and its own
62
63
  * `safe-area-view`. A sheet can also be dragged away, but `fullScreenModal` cannot, and with
63
64
  * `gestureEnabled: false` neither can.
65
+ *
66
+ * On iOS, from a screen a tab's stack presents `fullScreenModal`, a screen for the app's own
67
+ * stack cannot show, and this resolves false: see `refuse-screens-under-full-screen.ts`.
64
68
  */
65
69
  present(commands: NavigationCommands, options?: PresentOptions): Promise<boolean>;
66
70
  /**
@@ -13,7 +13,7 @@
13
13
  * No decorators, so tests can import it at runtime and so it can be provided with `useClass`,
14
14
  * exactly as `NativePlatformLocation` and `NativeStackReuseStrategy` are.
15
15
  */
16
- import { DestroyRef, inject } from '@angular/core';
16
+ import { DestroyRef, EnvironmentInjector, inject } from '@angular/core';
17
17
  import { NavigationCancel, NavigationCancellationCode, NavigationEnd, NavigationError, NavigationSkipped, NavigationSkippedCode, PRIMARY_OUTLET, Router, } from '@angular/router';
18
18
  import { NativeBack } from "./native-back.js";
19
19
  import { PRESENTED, leavingPresented, presentedCommands } from "./presented-route.js";
@@ -125,6 +125,8 @@ function ended(event) {
125
125
  }
126
126
  export class NativeNavigation {
127
127
  router = inject(Router);
128
+ // Absent only where the service is made outside an app, as a unit test makes it.
129
+ injector = inject(EnvironmentInjector, { optional: true }) ?? undefined;
128
130
  outlets = inject(NativeBack);
129
131
  /**
130
132
  * Push a new screen onto the stack. The plain case, and what `nativeRouterLink` does.
@@ -154,6 +156,9 @@ export class NativeNavigation {
154
156
  * native header and no automatic safe-area insets: it needs its own way out, and its own
155
157
  * `safe-area-view`. A sheet can also be dragged away, but `fullScreenModal` cannot, and with
156
158
  * `gestureEnabled: false` neither can.
159
+ *
160
+ * On iOS, from a screen a tab's stack presents `fullScreenModal`, a screen for the app's own
161
+ * stack cannot show, and this resolves false: see `refuse-screens-under-full-screen.ts`.
157
162
  */
158
163
  present(commands, options = {}) {
159
164
  if (this.firstNavigationRunning()) {
@@ -323,7 +328,7 @@ export class NativeNavigation {
323
328
  if (!this.outlets.inTabBehind(path) && !this.outlets.coveredTabAt(path))
324
329
  return null;
325
330
  const query = typeof commands === 'string' ? this.queryOf(commands, extras) : {};
326
- return presentedCommands(this.router, path).then((presented) => presented && {
331
+ return presentedCommands(this.router, path, this.injector).then((presented) => presented && {
327
332
  commands: presented,
328
333
  extras: { ...extras, ...query, relativeTo: null },
329
334
  written: { path, outlet: PRESENTED },