@ng-native/router 0.6.0 → 0.7.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.
- package/dist/file-routes.js +18 -6
- package/dist/file-routes.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/native-back.d.ts +8 -0
- package/dist/native-back.js +8 -0
- package/dist/native-back.js.map +1 -1
- package/dist/native-navigation.d.ts +11 -1
- package/dist/native-navigation.js +66 -2
- package/dist/native-navigation.js.map +1 -1
- package/dist/native-router-link.js +8 -6
- package/dist/native-router-link.js.map +1 -1
- package/dist/native-stack-outlet.js +9 -2
- package/dist/native-stack-outlet.js.map +1 -1
- package/dist/native-stack-reuse-strategy.js +5 -4
- package/dist/native-stack-reuse-strategy.js.map +1 -1
- package/dist/native-toolbar.d.ts +39 -0
- package/dist/native-toolbar.js +108 -0
- package/dist/native-toolbar.js.map +1 -0
- package/dist/provide-native-router.js +2 -1
- package/dist/provide-native-router.js.map +1 -1
- package/dist/screens.js +23 -0
- package/dist/screens.js.map +1 -1
- package/package.json +3 -3
package/dist/file-routes.js
CHANGED
|
@@ -175,9 +175,10 @@ function urlOf(parts) {
|
|
|
175
175
|
.join('/');
|
|
176
176
|
}
|
|
177
177
|
/**
|
|
178
|
-
* The routes under `entry`, in the order Angular should try them: a static path first,
|
|
179
|
-
* group's layout, then a path with a parameter, then a
|
|
180
|
-
* layout of its own adds nothing to the URL or the
|
|
178
|
+
* The routes under `entry`, in the order Angular should try them: a static path first, a longer
|
|
179
|
+
* one before a shorter it starts with, then a group's layout, then a path with a parameter, then a
|
|
180
|
+
* catch-all, each by its path. A group with no layout of its own adds nothing to the URL or the
|
|
181
|
+
* tree, so its routes join its siblings'.
|
|
181
182
|
*/
|
|
182
183
|
function routesOf(entry, options) {
|
|
183
184
|
const ranked = [];
|
|
@@ -189,9 +190,20 @@ function routesOf(entry, options) {
|
|
|
189
190
|
else
|
|
190
191
|
ranked.push(rankedRoute(child, path, children, options));
|
|
191
192
|
}
|
|
192
|
-
return ranked.sort((a, b) => a.rank - b.rank ||
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
return ranked.sort((a, b) => a.rank - b.rank || byPath(a.path, b.path) || (a.key < b.key ? -1 : 1));
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Paths by their segments, a longer one before the path it starts with: `users/new` is tried
|
|
197
|
+
* before the `users` folder, whose `:id` would take `new`, as Analog orders them.
|
|
198
|
+
*/
|
|
199
|
+
function byPath(a, b) {
|
|
200
|
+
const left = a.split('/');
|
|
201
|
+
const right = b.split('/');
|
|
202
|
+
for (let i = 0; i < Math.min(left.length, right.length); i++) {
|
|
203
|
+
if (left[i] !== right[i])
|
|
204
|
+
return left[i] < right[i] ? -1 : 1;
|
|
205
|
+
}
|
|
206
|
+
return right.length - left.length;
|
|
195
207
|
}
|
|
196
208
|
/** The route for a file or folder name at `path`, with its routes under it. */
|
|
197
209
|
function rankedRoute(entry, path, children, options) {
|
package/dist/file-routes.js.map
CHANGED
|
@@ -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, then a\n * group's layout, then a path with a parameter, then a catch-all, each by its path. A group with no\n * layout of its own adds nothing to the URL or the 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) =>\n a.rank - b.rank ||\n (a.path < b.path ? -1 : a.path > b.path ? 1 : 0) ||\n (a.key < b.key ? -1 : 1),\n );\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 { 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"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export { NATIVE_HEADER_DEFAULTS, NATIVE_TAB_DEFAULTS, type HeaderDefaults, type
|
|
|
14
14
|
export { NativeHeader, type BackButtonDisplayMode, type BlurEffect, type HeaderDirection, type HeaderInterfaceStyle, } from './native-header.ts';
|
|
15
15
|
export { DEFAULT_HEADER_PALETTE, NATIVE_HEADER_PALETTE, type HeaderColors, } from './native-header-palette.ts';
|
|
16
16
|
export { NativeHeaderItem, type HeaderItemType } from './native-header-item.ts';
|
|
17
|
+
export { NativeToolbar, NativeToolbarItem, type ToolbarItemType } from './native-toolbar.ts';
|
|
17
18
|
export { NativeSearchBar, type SearchBarPlacement } from './native-search-bar.ts';
|
|
18
19
|
export { NativeStackOutlet } from './native-stack-outlet.ts';
|
|
19
20
|
export { NativeTab, type TabAppearance, type TabBarBlurEffect, type TabIcon, type TabIconProps, type TabItemAppearance, type TabStateAppearance, type TabSystemItem, } from './native-tab.ts';
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export { NATIVE_HEADER_DEFAULTS, NATIVE_TAB_DEFAULTS, } from "./native-bar-defau
|
|
|
14
14
|
export { NativeHeader, } from "./native-header.js";
|
|
15
15
|
export { DEFAULT_HEADER_PALETTE, NATIVE_HEADER_PALETTE, } from "./native-header-palette.js";
|
|
16
16
|
export { NativeHeaderItem } from "./native-header-item.js";
|
|
17
|
+
export { NativeToolbar, NativeToolbarItem } from "./native-toolbar.js";
|
|
17
18
|
export { NativeSearchBar } from "./native-search-bar.js";
|
|
18
19
|
export { NativeStackOutlet } from "./native-stack-outlet.js";
|
|
19
20
|
export { NativeTab, } from "./native-tab.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,gBAAgB,GAIjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAmB,MAAM,+BAA+B,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,mBAAmB,GAIpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,YAAY,GAKb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GAEtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAuB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,eAAe,EAA2B,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EACL,SAAS,GAQV,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAA2B,MAAM,4BAA4B,CAAC;AACzF,OAAO,EAAE,eAAe,EAAwB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACzF,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,eAAe,GAEhB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAON,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,aAAa,EAA2B,MAAM,cAAc,CAAC;AACtE,OAAO,EACL,UAAU,GAKX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,gBAAgB,GAEjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["/**\n * `@angular/router` on a native stack: the outlet that maps activated routes onto `RNSScreen`,\n * the `PlatformLocation` that stands in for the browser's history, and the reuse strategy that\n * keeps a popped screen's component alive while it animates out.\n *\n * Two of these are decorated, so the same caveat as the components barrel applies: a Node test\n * reaches the source file, not this.\n */\nexport { followLink, type LinkParent } from './native-links.ts';\nexport {\n NativeNavigation,\n type NativeNavigationOptions,\n type NavigationCommands,\n type PresentOptions,\n} from './native-navigation.ts';\nexport { NativePlatformLocation, type LinkSource } from './native-platform-location.ts';\nexport { NativeRouterLink } from './native-router-link.ts';\nexport {\n NATIVE_HEADER_DEFAULTS,\n NATIVE_TAB_DEFAULTS,\n type HeaderDefaults,\n type SchemeDefaults,\n type TabDefaults,\n} from './native-bar-defaults.ts';\nexport {\n NativeHeader,\n type BackButtonDisplayMode,\n type BlurEffect,\n type HeaderDirection,\n type HeaderInterfaceStyle,\n} from './native-header.ts';\nexport {\n DEFAULT_HEADER_PALETTE,\n NATIVE_HEADER_PALETTE,\n type HeaderColors,\n} from './native-header-palette.ts';\nexport { NativeHeaderItem, type HeaderItemType } from './native-header-item.ts';\nexport { NativeSearchBar, type SearchBarPlacement } from './native-search-bar.ts';\nexport { NativeStackOutlet } from './native-stack-outlet.ts';\nexport {\n NativeTab,\n type TabAppearance,\n type TabBarBlurEffect,\n type TabIcon,\n type TabIconProps,\n type TabItemAppearance,\n type TabStateAppearance,\n type TabSystemItem,\n} from './native-tab.ts';\nexport { NativeTabsOutlet } from './native-tabs-outlet.ts';\nexport { ScreenSafeAreaView, type ScreenSafeAreaEdge } from './screen-safe-area-view.ts';\nexport { TabSafeAreaView, type TabSafeAreaEdge } from './tab-safe-area-view.ts';\nexport { NativeStackReuseStrategy, reuseScreen } from './native-stack-reuse-strategy.ts';\nexport {\n provideNativeRouter,\n withHeaderDefaults,\n withLinkParent,\n withTabDefaults,\n type NativeRouterFeature,\n} from './provide-native-router.ts';\nexport {\n type GestureResponseDistance,\n type ReplaceAnimation,\n type ScreenPresentation,\n type StackAnimation,\n type StackPresentation,\n type SwipeDirection,\n} from './screen-presentation.ts';\nexport { ActivityState, type ActivityStateValue } from './screens.ts';\nexport {\n fileRoutes,\n type FileRoutesOptions,\n type PageContext,\n type PageFiles,\n type RouteMeta,\n} from './file-routes.ts';\nexport {\n MARKDOWN_PAGE,\n injectMarkdownPage,\n markdownPageFile,\n type MarkdownPageFile,\n} from './markdown-page.ts';\nexport { FullWindowOverlay } from './full-window-overlay.ts';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,gBAAgB,GAIjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAmB,MAAM,+BAA+B,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,mBAAmB,GAIpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,YAAY,GAKb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GAEtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAuB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAwB,MAAM,qBAAqB,CAAC;AAC7F,OAAO,EAAE,eAAe,EAA2B,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EACL,SAAS,GAQV,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAA2B,MAAM,4BAA4B,CAAC;AACzF,OAAO,EAAE,eAAe,EAAwB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACzF,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,eAAe,GAEhB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAON,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,aAAa,EAA2B,MAAM,cAAc,CAAC;AACtE,OAAO,EACL,UAAU,GAKX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,gBAAgB,GAEjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["/**\n * `@angular/router` on a native stack: the outlet that maps activated routes onto `RNSScreen`,\n * the `PlatformLocation` that stands in for the browser's history, and the reuse strategy that\n * keeps a popped screen's component alive while it animates out.\n *\n * Two of these are decorated, so the same caveat as the components barrel applies: a Node test\n * reaches the source file, not this.\n */\nexport { followLink, type LinkParent } from './native-links.ts';\nexport {\n NativeNavigation,\n type NativeNavigationOptions,\n type NavigationCommands,\n type PresentOptions,\n} from './native-navigation.ts';\nexport { NativePlatformLocation, type LinkSource } from './native-platform-location.ts';\nexport { NativeRouterLink } from './native-router-link.ts';\nexport {\n NATIVE_HEADER_DEFAULTS,\n NATIVE_TAB_DEFAULTS,\n type HeaderDefaults,\n type SchemeDefaults,\n type TabDefaults,\n} from './native-bar-defaults.ts';\nexport {\n NativeHeader,\n type BackButtonDisplayMode,\n type BlurEffect,\n type HeaderDirection,\n type HeaderInterfaceStyle,\n} from './native-header.ts';\nexport {\n DEFAULT_HEADER_PALETTE,\n NATIVE_HEADER_PALETTE,\n type HeaderColors,\n} from './native-header-palette.ts';\nexport { NativeHeaderItem, type HeaderItemType } from './native-header-item.ts';\nexport { NativeToolbar, NativeToolbarItem, type ToolbarItemType } from './native-toolbar.ts';\nexport { NativeSearchBar, type SearchBarPlacement } from './native-search-bar.ts';\nexport { NativeStackOutlet } from './native-stack-outlet.ts';\nexport {\n NativeTab,\n type TabAppearance,\n type TabBarBlurEffect,\n type TabIcon,\n type TabIconProps,\n type TabItemAppearance,\n type TabStateAppearance,\n type TabSystemItem,\n} from './native-tab.ts';\nexport { NativeTabsOutlet } from './native-tabs-outlet.ts';\nexport { ScreenSafeAreaView, type ScreenSafeAreaEdge } from './screen-safe-area-view.ts';\nexport { TabSafeAreaView, type TabSafeAreaEdge } from './tab-safe-area-view.ts';\nexport { NativeStackReuseStrategy, reuseScreen } from './native-stack-reuse-strategy.ts';\nexport {\n provideNativeRouter,\n withHeaderDefaults,\n withLinkParent,\n withTabDefaults,\n type NativeRouterFeature,\n} from './provide-native-router.ts';\nexport {\n type GestureResponseDistance,\n type ReplaceAnimation,\n type ScreenPresentation,\n type StackAnimation,\n type StackPresentation,\n type SwipeDirection,\n} from './screen-presentation.ts';\nexport { ActivityState, type ActivityStateValue } from './screens.ts';\nexport {\n fileRoutes,\n type FileRoutesOptions,\n type PageContext,\n type PageFiles,\n type RouteMeta,\n} from './file-routes.ts';\nexport {\n MARKDOWN_PAGE,\n injectMarkdownPage,\n markdownPageFile,\n type MarkdownPageFile,\n} from './markdown-page.ts';\nexport { FullWindowOverlay } from './full-window-overlay.ts';\n"]}
|
package/dist/native-back.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface PoppableStack {
|
|
|
8
8
|
popToRoot(): Promise<boolean> | null;
|
|
9
9
|
/** Pop to the screen showing `url`, or null when no screen below the top shows it. */
|
|
10
10
|
popTo(url: string): Promise<boolean> | null;
|
|
11
|
+
/** Take every screen under the top off at once, or false when it is showing only that one. */
|
|
12
|
+
dropUnderTop(): boolean;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* What a tab bar offers `NativeNavigation` for a push into one of its tabs from outside it. A tab
|
|
@@ -43,6 +45,12 @@ export declare class NativeBack {
|
|
|
43
45
|
addStack(stack: PoppableStack): () => void;
|
|
44
46
|
/** Pop the innermost stack in front that is deeper than one screen, to its first. */
|
|
45
47
|
popToRoot(): Promise<boolean>;
|
|
48
|
+
/**
|
|
49
|
+
* Take the screens under the top of the innermost stack in front that has any off it, leaving
|
|
50
|
+
* the top one: a reset to the url already showing, which the router skips. False when no stack
|
|
51
|
+
* in front had any.
|
|
52
|
+
*/
|
|
53
|
+
resetToTop(): boolean;
|
|
46
54
|
/** Pop the innermost stack in front that has a screen at `url` below its top, down to it. */
|
|
47
55
|
popTo(url: string): Promise<boolean>;
|
|
48
56
|
/**
|
package/dist/native-back.js
CHANGED
|
@@ -69,6 +69,14 @@ export class NativeBack {
|
|
|
69
69
|
}
|
|
70
70
|
return Promise.resolve(false);
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Take the screens under the top of the innermost stack in front that has any off it, leaving
|
|
74
|
+
* the top one: a reset to the url already showing, which the router skips. False when no stack
|
|
75
|
+
* in front had any.
|
|
76
|
+
*/
|
|
77
|
+
resetToTop() {
|
|
78
|
+
return [...this.stacks].reverse().some((stack) => stack.showing() && stack.dropUnderTop());
|
|
79
|
+
}
|
|
72
80
|
/** Pop the innermost stack in front that has a screen at `url` below its top, down to it. */
|
|
73
81
|
popTo(url) {
|
|
74
82
|
for (const stack of [...this.stacks].reverse()) {
|
package/dist/native-back.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-back.js","sourceRoot":"","sources":["../src/native-back.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;;
|
|
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 { Service, inject } from '@angular/core';\nimport type { ActivatedRoute, Router } from '@angular/router';\nimport { HardwareBack } from '@ng-native/device';\n\n/** What a stack offers `NativeNavigation` beyond a back: going down it several screens at once. */\nexport interface PoppableStack {\n /** Whether this stack is part of what the router shows now. */\n showing(): boolean;\n /** Pop to its first screen, or null when it is showing only that one. */\n popToRoot(): Promise<boolean> | null;\n /** Pop to the screen showing `url`, or null when no screen below the top shows it. */\n popTo(url: string): Promise<boolean> | null;\n /** Take every screen under the top off at once, or false when it is showing only that one. */\n dropUnderTop(): boolean;\n}\n\n/**\n * What a tab bar offers `NativeNavigation` for a push into one of its tabs from outside it. A tab\n * nobody has opened has no stack yet, and a page pushed into it would be the stack's first\n * screen, with nothing under it to go back to.\n */\nexport interface TabBar {\n /** The url of the tab `url` is a page inside, when that tab has not been opened. */\n unopenedTabOf(url: string): string | null;\n /** Whether `url` is a tab other than the one in front, or a page inside one. */\n behind(url: string): boolean;\n}\n\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 /** 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 to the url already showing, which the router skips. False when no stack\n * in front had any.\n */\n resetToTop(): boolean {\n return [...this.stacks].reverse().some((stack) => stack.showing() && stack.dropUnderTop());\n }\n\n /** Pop the innermost stack in front that has a screen at `url` below its top, down to it. */\n popTo(url: string): Promise<boolean> {\n for (const stack of [...this.stacks].reverse()) {\n if (!stack.showing()) continue;\n const popped = stack.popTo(url);\n if (popped) return popped;\n }\n return Promise.resolve(false);\n }\n\n /**\n * Offer an outlet's answer to a back. It returns whether it took the back, and false lets the\n * next one out try. Returns an unsubscribe.\n */\n handle(answer: BackAnswer): () => void {\n this.answers.push(answer);\n const stop = this.hardwareBack.handle(() => answer('button'));\n return () => {\n stop();\n const index = this.answers.indexOf(answer);\n if (index !== -1) this.answers.splice(index, 1);\n };\n }\n\n /** Go back as the button would, innermost outlet first. False when none had anywhere to go. */\n back(): boolean {\n return [...this.answers].reverse().some((answer) => answer('app'));\n }\n}\n\n/**\n * An outlet's answer to a back, told where it came from: Android's button, which is the platform's\n * own dismissal and a screen may refuse, or the app's `NativeNavigation.back()`, which it may not.\n */\nexport type BackAnswer = (from: 'button' | 'app') => boolean;\n\n/**\n * Whether a route is part of what the router shows now, rather than a subtree the reuse strategy\n * detached and is keeping for later: a tab behind, or a screen covered by one pushed over it.\n */\nexport function isShowing(router: Router, route: ActivatedRoute | null): boolean {\n if (!route) return false;\n const visit = (node: ActivatedRoute): boolean => node === route || node.children.some(visit);\n return visit(router.routerState.root);\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type NavigationExtras } from '@angular/router';
|
|
1
|
+
import { type Navigation, type NavigationExtras } from '@angular/router';
|
|
2
2
|
import type { ScreenPresentation, StackPresentation } from './screen-presentation.ts';
|
|
3
3
|
/** What `Router` takes: a url string, or the array form with segments and params. */
|
|
4
4
|
export type NavigationCommands = string | readonly unknown[];
|
|
@@ -23,6 +23,16 @@ export interface PresentOptions extends NativeNavigationOptions {
|
|
|
23
23
|
}
|
|
24
24
|
/** Read the intent off a navigation's state, if it carries one. */
|
|
25
25
|
export declare function intentOf(state: unknown): NativeIntent | null;
|
|
26
|
+
/** What a navigation asks of the stack: its own intent, or the one it was redirected from. */
|
|
27
|
+
export declare function navigationIntent(router: object, navigation: Pick<Navigation, 'id' | 'extras'> | null | undefined): NativeIntent | null;
|
|
28
|
+
/**
|
|
29
|
+
* Hand a navigation's intent on to the one its guard redirects it to. The router schedules that
|
|
30
|
+
* navigation as soon as it has reported the cancel, so it takes the next id.
|
|
31
|
+
*
|
|
32
|
+
* A reset to the url already showing, its own or the one a guard sends it to, is one the router
|
|
33
|
+
* skips: no outlet activates anything for it, so the stack is reset here, to the screen on top.
|
|
34
|
+
*/
|
|
35
|
+
export declare function carryIntentAcrossRedirects(): void;
|
|
26
36
|
export declare class NativeNavigation {
|
|
27
37
|
private readonly router;
|
|
28
38
|
private readonly outlets;
|
|
@@ -13,8 +13,8 @@
|
|
|
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 { inject } from '@angular/core';
|
|
17
|
-
import { NavigationCancel, NavigationEnd, NavigationError, Router, } from '@angular/router';
|
|
16
|
+
import { DestroyRef, inject } from '@angular/core';
|
|
17
|
+
import { NavigationCancel, NavigationCancellationCode, NavigationEnd, NavigationError, NavigationSkipped, NavigationSkippedCode, Router, } from '@angular/router';
|
|
18
18
|
import { NativeBack } from "./native-back.js";
|
|
19
19
|
import { leavingPresented, presentedCommands } from "./presented-route.js";
|
|
20
20
|
/**
|
|
@@ -31,6 +31,70 @@ export function intentOf(state) {
|
|
|
31
31
|
const intent = state[NATIVE_INTENT];
|
|
32
32
|
return intent && typeof intent === 'object' ? intent : null;
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* The intent of a navigation a guard redirected, for the navigation the redirect starts.
|
|
36
|
+
*
|
|
37
|
+
* The router starts a new navigation for a guard's `UrlTree`, keeping `replaceUrl` but not
|
|
38
|
+
* `state`, so a reset to a page that sends a signed-out user to sign in would push the sign-in
|
|
39
|
+
* page over the stack it was meant to clear. Held for that one navigation by its id, which no
|
|
40
|
+
* later navigation shares.
|
|
41
|
+
*/
|
|
42
|
+
const redirected = new WeakMap();
|
|
43
|
+
/** What a navigation asks of the stack: its own intent, or the one it was redirected from. */
|
|
44
|
+
export function navigationIntent(router, navigation) {
|
|
45
|
+
if (!navigation)
|
|
46
|
+
return null;
|
|
47
|
+
const own = intentOf(navigation.extras.state);
|
|
48
|
+
if (own)
|
|
49
|
+
return own;
|
|
50
|
+
const carried = redirected.get(router);
|
|
51
|
+
return carried?.id === navigation.id ? carried.intent : null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Hand a navigation's intent on to the one its guard redirects it to. The router schedules that
|
|
55
|
+
* navigation as soon as it has reported the cancel, so it takes the next id.
|
|
56
|
+
*
|
|
57
|
+
* A reset to the url already showing, its own or the one a guard sends it to, is one the router
|
|
58
|
+
* skips: no outlet activates anything for it, so the stack is reset here, to the screen on top.
|
|
59
|
+
*/
|
|
60
|
+
export function carryIntentAcrossRedirects() {
|
|
61
|
+
const router = inject(Router);
|
|
62
|
+
const outlets = inject(NativeBack);
|
|
63
|
+
const events = router.events.subscribe((event) => {
|
|
64
|
+
if (event instanceof NavigationCancel && event.code === NavigationCancellationCode.Redirect) {
|
|
65
|
+
carryPast(router, event.id);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (event instanceof NavigationSkipped &&
|
|
69
|
+
event.code === NavigationSkippedCode.IgnoredSameUrlNavigation &&
|
|
70
|
+
intentAt(router, event.id)?.stack === 'reset') {
|
|
71
|
+
outlets.resetToTop();
|
|
72
|
+
}
|
|
73
|
+
if (ended(event) && redirected.get(router)?.id === event.id)
|
|
74
|
+
redirected.delete(router);
|
|
75
|
+
});
|
|
76
|
+
inject(DestroyRef).onDestroy(() => events.unsubscribe());
|
|
77
|
+
}
|
|
78
|
+
/** The intent of the navigation in progress, when it is the one with `id`. */
|
|
79
|
+
function intentAt(router, id) {
|
|
80
|
+
const navigation = router.currentNavigation();
|
|
81
|
+
return navigation?.id === id ? navigationIntent(router, navigation) : null;
|
|
82
|
+
}
|
|
83
|
+
/** Hold the intent of the navigation a guard redirected for the one the redirect starts. */
|
|
84
|
+
function carryPast(router, id) {
|
|
85
|
+
const intent = intentAt(router, id);
|
|
86
|
+
if (intent)
|
|
87
|
+
redirected.set(router, { id: id + 1, intent });
|
|
88
|
+
else
|
|
89
|
+
redirected.delete(router);
|
|
90
|
+
}
|
|
91
|
+
/** Whether the event is the last a navigation reports. */
|
|
92
|
+
function ended(event) {
|
|
93
|
+
return (event instanceof NavigationEnd ||
|
|
94
|
+
event instanceof NavigationCancel ||
|
|
95
|
+
event instanceof NavigationError ||
|
|
96
|
+
event instanceof NavigationSkipped);
|
|
97
|
+
}
|
|
34
98
|
export class NativeNavigation {
|
|
35
99
|
router = inject(Router);
|
|
36
100
|
outlets = inject(NativeBack);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-navigation.js","sourceRoot":"","sources":["../src/native-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The typed facade over `Router` for the intents a URL cannot express (ADR 0003).\n *\n * Angular's `Router` stays the single source of truth: each of these is a line over\n * `router.navigate`, setting `replaceUrl` and a presentation on `NavigationExtras.state`. Deep\n * links, guards and resolvers keep working because it is still the Router, and because state\n * rides in history a back restores the presentation the screen was opened with.\n *\n * A navigation service that owns the stack was rejected: it either re-implements guards,\n * resolvers and deep linking, or it drifts from the URL, and once the stack is not derived from\n * router state the two can disagree.\n *\n * No decorators, so tests can import it at runtime and so it can be provided with `useClass`,\n * exactly as `NativePlatformLocation` and `NativeStackReuseStrategy` are.\n */\nimport { inject } from '@angular/core';\nimport {\n NavigationCancel,\n NavigationEnd,\n NavigationError,\n Router,\n type NavigationExtras,\n} from '@angular/router';\nimport { NativeBack } from './native-back.ts';\nimport type { ScreenPresentation, StackPresentation } from './screen-presentation.ts';\nimport { leavingPresented, presentedCommands } from './presented-route.ts';\n\n/** What `Router` takes: a url string, or the array form with segments and params. */\nexport type NavigationCommands = string | readonly unknown[];\n\n/**\n * Where the intent rides. `NavigationExtras.state` is the app's own object, so ours sits under\n * one reserved key rather than spread through it.\n */\nexport const NATIVE_INTENT = 'ɵnativeStack';\n\n/** What a navigation asks the outlet to do with the stack, over and above rendering the route. */\nexport interface NativeIntent {\n /** `replace` drops the screen it supersedes; `reset` empties the stack first. */\n readonly stack?: 'push' | 'replace' | 'reset';\n readonly presentation?: ScreenPresentation;\n}\n\nexport interface NativeNavigationOptions extends NavigationExtras {\n /** How the screen arrives: presentation, animation, gesture and sheet props. */\n presentation?: ScreenPresentation;\n}\n\nexport interface PresentOptions extends NativeNavigationOptions {\n /** Shorthand for `presentation.stackPresentation`. Defaults to `modal`. */\n as?: StackPresentation;\n}\n\n/** A url string up to its query or fragment. */\nconst pathOf = (url: string): string => url.split(/[?#]/, 1)[0]!;\n\n/** Read the intent off a navigation's state, if it carries one. */\nexport function intentOf(state: unknown): NativeIntent | null {\n if (!state || typeof state !== 'object') return null;\n const intent = (state as Record<string, unknown>)[NATIVE_INTENT];\n return intent && typeof intent === 'object' ? (intent as NativeIntent) : null;\n}\n\nexport class NativeNavigation {\n private readonly router = inject(Router);\n private readonly outlets = inject(NativeBack);\n\n /**\n * Push a new screen onto the stack. The plain case, and what `nativeRouterLink` does.\n *\n * From a presented screen the new one is presented the same way, over it, since a pushed\n * screen would slide in under it; `presentation` says otherwise, `stackAnimation` and all.\n */\n push(commands: NavigationCommands, options: NativeNavigationOptions = {}): Promise<boolean> {\n return this.go(commands, options, { stack: 'push' });\n }\n\n /**\n * Swap the current screen for another. The history entry is replaced too, so a back from the\n * new screen goes where a back from the old one would have.\n */\n replace(commands: NavigationCommands, options: NavigationExtras = {}): Promise<boolean> {\n return this.go(commands, { ...options, replaceUrl: true }, { stack: 'replace' });\n }\n\n /**\n * Present a screen over the stack rather than pushing into it: a modal, or a sheet.\n *\n * `present('/filters', { as: 'formSheet', presentation: { sheetAllowedDetents: [0.5, 1] } })`.\n *\n * A presented screen is put on screen outside the stack's navigation controller, so it has no\n * native header and no automatic safe-area insets: it needs its own way out, and its own\n * `safe-area-view`. A sheet can also be dragged away, but `fullScreenModal` cannot, and with\n * `gestureEnabled: false` neither can.\n */\n present(commands: NavigationCommands, options: PresentOptions = {}): Promise<boolean> {\n const { as = 'modal', presentation, ...extras } = options;\n const intent: NativeIntent = {\n stack: 'push',\n presentation: { stackPresentation: as, ...presentation },\n };\n // Asked at once, and only waited for when the page is another tab's: finding its route may\n // load a lazy one.\n const over = this.overTheTabInFront(commands, extras);\n if (!over) return this.go(commands, extras, intent);\n return over.then((found) =>\n this.go(found?.commands ?? commands, found?.extras ?? extras, intent),\n );\n }\n\n /**\n * Replace the whole stack with one screen, the way signing out or finishing onboarding does.\n * Every screen below is destroyed, so there is nothing to go back to.\n */\n reset(commands: NavigationCommands, options: NavigationExtras = {}): Promise<boolean> {\n return this.go(commands, { ...options, replaceUrl: true }, { stack: 'reset' });\n }\n\n /**\n * Go back exactly as the Android back button does: pop the stack in front, or at the root of a\n * tab's stack, go to the first tab. Not one entry back through history, which after a trip to\n * another tab is that tab. At the root of the app there is nowhere to go, and this does nothing.\n */\n back(): void {\n // Optional call: a test that stands a plain object in for the router has no such signal.\n if (!this.router.currentNavigation?.()) {\n this.outlets.back();\n return;\n }\n // Asked while a navigation is still putting its screen up: a page that leaves as it appears,\n // from an effect in its constructor. No stack has that screen to pop yet, so the back waits\n // for the navigation, and goes nowhere if the navigation does not arrive.\n if (this.backWaiting) return;\n this.backWaiting = this.router.events.subscribe((event) => {\n const ended = event instanceof NavigationEnd;\n if (!ended && !(event instanceof NavigationCancel) && !(event instanceof NavigationError)) {\n return;\n }\n this.backWaiting?.unsubscribe();\n this.backWaiting = null;\n if (ended) this.outlets.back();\n });\n }\n\n /** The wait of a back asked for mid-navigation, so two asked for in one are one. */\n private backWaiting: { unsubscribe(): void } | null = null;\n\n /**\n * Pop straight back to the screen at `commands`, taking every screen above it off the stack at\n * once - `popToViewController`. The screen is the one already there, with its state, not a new\n * one. Resolves false, doing nothing, when no screen below the top of a stack in front is\n * showing that url; a push is the way to a screen that is not there.\n */\n popTo(commands: NavigationCommands): Promise<boolean> {\n const tree =\n typeof commands === 'string'\n ? this.router.parseUrl(commands)\n : this.router.createUrlTree([...commands]);\n return this.outlets.popTo(this.router.serializeUrl(tree));\n }\n\n /**\n * Pop the stack in front to its first screen - `popToRootViewController`. Inside a tab, that is\n * the tab's first screen; the screens below keep their state. Resolves false at the root already.\n */\n popToRoot(): Promise<boolean> {\n return this.outlets.popToRoot();\n }\n\n private go(\n commands: NavigationCommands,\n options: NativeNavigationOptions,\n intent: NativeIntent,\n ): Promise<boolean> {\n const { presentation, state, ...extras } = options;\n const full: NativeIntent = presentation\n ? { ...intent, presentation: { ...intent.presentation, ...presentation } }\n : intent;\n\n const navigate = () => {\n const list = typeof commands === 'string' ? [pathOf(commands)] : [...commands];\n const all = {\n ...extras,\n ...(typeof commands === 'string' ? this.queryOf(commands, extras) : {}),\n state: { ...(state as object), [NATIVE_INTENT]: full },\n };\n const leaving = leavingPresented(this.router, list, all);\n return leaving ? this.router.navigateByUrl(leaving, all) : this.router.navigate(list, all);\n };\n // A screen stacked on the first one waits for it. Navigating while the router's first\n // navigation is still running - a lazily loaded root waiting on its import - cancels that\n // navigation, and the app opens on this screen with nothing beneath it. A reset replaces the\n // stack anyway, so it goes at once.\n const start = () => this.underTabRoot(commands, extras, full, navigate);\n return intent.stack !== 'reset' && this.firstNavigationRunning()\n ? this.firstNavigation().then(start)\n : start();\n }\n\n /**\n * A push into a tab nobody has opened goes to the tab first, so its stack has the tab's own\n * first screen under the page, and a back from the page lands there: what a tab bar with a\n * navigation controller per tab does. Without it the page is the stack's only screen, and the\n * same push leaves a different stack behind depending on whether the tab was ever tapped. A\n * presented screen goes where it is asked, and a replace or a reset has nothing to keep.\n */\n private underTabRoot(\n commands: NavigationCommands,\n extras: NavigationExtras,\n intent: NativeIntent,\n navigate: () => Promise<boolean>,\n ): Promise<boolean> {\n if (intent.stack !== 'push' || intent.presentation) return navigate();\n const tab = this.outlets.unopenedTabOf(this.pathFor(commands, extras));\n if (!tab) return navigate();\n return this.router.navigateByUrl(tab).then((arrived) => (arrived ? navigate() : false));\n }\n\n /** The path the commands lead to, without its query and fragment. */\n private pathFor(commands: NavigationCommands, extras: NavigationExtras): string {\n const url =\n typeof commands === 'string'\n ? commands\n : this.router.serializeUrl(this.router.createUrlTree([...commands], extras));\n return pathOf(url);\n }\n\n /**\n * A page of a tab that is not in front, presented over the one that is: its url would select\n * its own tab and show it there, so it goes to the root's presented outlet instead, where the\n * app's own stack shows it over everything. See `presented-route.ts`. Null for a page of the\n * tab in front, or of no tab, which is presented where its url puts it.\n */\n private overTheTabInFront(\n commands: NavigationCommands,\n extras: NavigationExtras,\n ): Promise<{ commands: unknown[]; extras: NavigationExtras } | null> | null {\n if (!this.outlets.presents) return null;\n const path = this.pathFor(commands, extras);\n if (!this.outlets.inTabBehind(path)) return null;\n const query = typeof commands === 'string' ? this.queryOf(commands, extras) : {};\n return presentedCommands(this.router, path).then(\n (presented) =>\n presented && { commands: presented, extras: { ...extras, ...query, relativeTo: null } },\n );\n }\n\n /**\n * The query and fragment written into a url string, as the extras `navigate` takes them: it\n * reads a string command as one path segment, and would encode a `?` into it. Extras given\n * alongside win over the ones in the string.\n */\n private queryOf(url: string, extras: NavigationExtras): NavigationExtras {\n const cut = url.search(/[?#]/);\n if (cut < 0) return {};\n const { queryParams, fragment } = this.router.parseUrl(url.slice(cut));\n return {\n queryParams: { ...queryParams, ...extras.queryParams },\n fragment: extras.fragment ?? fragment ?? undefined,\n };\n }\n\n private firstNavigationRunning(): boolean {\n const router = this.router as Partial<Router>;\n return router.navigated === false && !!router.currentNavigation?.();\n }\n\n private firstNavigation(): Promise<void> {\n return new Promise((resolve) => {\n const events = this.router.events.subscribe((event) => {\n if (\n event instanceof NavigationEnd ||\n event instanceof NavigationCancel ||\n event instanceof NavigationError\n ) {\n events.unsubscribe();\n resolve();\n }\n });\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"native-navigation.js","sourceRoot":"","sources":["../src/native-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The typed facade over `Router` for the intents a URL cannot express (ADR 0003).\n *\n * Angular's `Router` stays the single source of truth: each of these is a line over\n * `router.navigate`, setting `replaceUrl` and a presentation on `NavigationExtras.state`. Deep\n * links, guards and resolvers keep working because it is still the Router, and because state\n * rides in history a back restores the presentation the screen was opened with.\n *\n * A navigation service that owns the stack was rejected: it either re-implements guards,\n * resolvers and deep linking, or it drifts from the URL, and once the stack is not derived from\n * router state the two can disagree.\n *\n * No decorators, so tests can import it at runtime and so it can be provided with `useClass`,\n * exactly as `NativePlatformLocation` and `NativeStackReuseStrategy` are.\n */\nimport { DestroyRef, inject } from '@angular/core';\nimport {\n NavigationCancel,\n NavigationCancellationCode,\n NavigationEnd,\n NavigationError,\n NavigationSkipped,\n NavigationSkippedCode,\n Router,\n type Navigation,\n type NavigationExtras,\n} from '@angular/router';\nimport { NativeBack } from './native-back.ts';\nimport type { ScreenPresentation, StackPresentation } from './screen-presentation.ts';\nimport { leavingPresented, presentedCommands } from './presented-route.ts';\n\n/** What `Router` takes: a url string, or the array form with segments and params. */\nexport type NavigationCommands = string | readonly unknown[];\n\n/**\n * Where the intent rides. `NavigationExtras.state` is the app's own object, so ours sits under\n * one reserved key rather than spread through it.\n */\nexport const NATIVE_INTENT = 'ɵnativeStack';\n\n/** What a navigation asks the outlet to do with the stack, over and above rendering the route. */\nexport interface NativeIntent {\n /** `replace` drops the screen it supersedes; `reset` empties the stack first. */\n readonly stack?: 'push' | 'replace' | 'reset';\n readonly presentation?: ScreenPresentation;\n}\n\nexport interface NativeNavigationOptions extends NavigationExtras {\n /** How the screen arrives: presentation, animation, gesture and sheet props. */\n presentation?: ScreenPresentation;\n}\n\nexport interface PresentOptions extends NativeNavigationOptions {\n /** Shorthand for `presentation.stackPresentation`. Defaults to `modal`. */\n as?: StackPresentation;\n}\n\n/** A url string up to its query or fragment. */\nconst pathOf = (url: string): string => url.split(/[?#]/, 1)[0]!;\n\n/** Read the intent off a navigation's state, if it carries one. */\nexport function intentOf(state: unknown): NativeIntent | null {\n if (!state || typeof state !== 'object') return null;\n const intent = (state as Record<string, unknown>)[NATIVE_INTENT];\n return intent && typeof intent === 'object' ? (intent as NativeIntent) : null;\n}\n\n/**\n * The intent of a navigation a guard redirected, for the navigation the redirect starts.\n *\n * The router starts a new navigation for a guard's `UrlTree`, keeping `replaceUrl` but not\n * `state`, so a reset to a page that sends a signed-out user to sign in would push the sign-in\n * page over the stack it was meant to clear. Held for that one navigation by its id, which no\n * later navigation shares.\n */\nconst redirected = new WeakMap<object, { readonly id: number; readonly intent: NativeIntent }>();\n\n/** What a navigation asks of the stack: its own intent, or the one it was redirected from. */\nexport function navigationIntent(\n router: object,\n navigation: Pick<Navigation, 'id' | 'extras'> | null | undefined,\n): NativeIntent | null {\n if (!navigation) return null;\n const own = intentOf(navigation.extras.state);\n if (own) return own;\n const carried = redirected.get(router);\n return carried?.id === navigation.id ? carried.intent : null;\n}\n\n/**\n * Hand a navigation's intent on to the one its guard redirects it to. The router schedules that\n * navigation as soon as it has reported the cancel, so it takes the next id.\n *\n * A reset to the url already showing, its own or the one a guard sends it to, is one the router\n * skips: no outlet activates anything for it, so the stack is reset here, to the screen on top.\n */\nexport function carryIntentAcrossRedirects(): void {\n const router = inject(Router);\n const outlets = inject(NativeBack);\n const events = router.events.subscribe((event) => {\n if (event instanceof NavigationCancel && event.code === NavigationCancellationCode.Redirect) {\n carryPast(router, event.id);\n return;\n }\n if (\n event instanceof NavigationSkipped &&\n event.code === NavigationSkippedCode.IgnoredSameUrlNavigation &&\n intentAt(router, event.id)?.stack === 'reset'\n ) {\n outlets.resetToTop();\n }\n if (ended(event) && redirected.get(router)?.id === event.id) redirected.delete(router);\n });\n inject(DestroyRef).onDestroy(() => events.unsubscribe());\n}\n\n/** The intent of the navigation in progress, when it is the one with `id`. */\nfunction intentAt(router: Router, id: number): NativeIntent | null {\n const navigation = router.currentNavigation();\n return navigation?.id === id ? navigationIntent(router, navigation) : null;\n}\n\n/** Hold the intent of the navigation a guard redirected for the one the redirect starts. */\nfunction carryPast(router: Router, id: number): void {\n const intent = intentAt(router, id);\n if (intent) redirected.set(router, { id: id + 1, intent });\n else redirected.delete(router);\n}\n\n/** Whether the event is the last a navigation reports. */\nfunction ended(event: unknown): event is { readonly id: number } {\n return (\n event instanceof NavigationEnd ||\n event instanceof NavigationCancel ||\n event instanceof NavigationError ||\n event instanceof NavigationSkipped\n );\n}\n\nexport class NativeNavigation {\n private readonly router = inject(Router);\n private readonly outlets = inject(NativeBack);\n\n /**\n * Push a new screen onto the stack. The plain case, and what `nativeRouterLink` does.\n *\n * From a presented screen the new one is presented the same way, over it, since a pushed\n * screen would slide in under it; `presentation` says otherwise, `stackAnimation` and all.\n */\n push(commands: NavigationCommands, options: NativeNavigationOptions = {}): Promise<boolean> {\n return this.go(commands, options, { stack: 'push' });\n }\n\n /**\n * Swap the current screen for another. The history entry is replaced too, so a back from the\n * new screen goes where a back from the old one would have.\n */\n replace(commands: NavigationCommands, options: NavigationExtras = {}): Promise<boolean> {\n return this.go(commands, { ...options, replaceUrl: true }, { stack: 'replace' });\n }\n\n /**\n * Present a screen over the stack rather than pushing into it: a modal, or a sheet.\n *\n * `present('/filters', { as: 'formSheet', presentation: { sheetAllowedDetents: [0.5, 1] } })`.\n *\n * A presented screen is put on screen outside the stack's navigation controller, so it has no\n * native header and no automatic safe-area insets: it needs its own way out, and its own\n * `safe-area-view`. A sheet can also be dragged away, but `fullScreenModal` cannot, and with\n * `gestureEnabled: false` neither can.\n */\n present(commands: NavigationCommands, options: PresentOptions = {}): Promise<boolean> {\n const { as = 'modal', presentation, ...extras } = options;\n const intent: NativeIntent = {\n stack: 'push',\n presentation: { stackPresentation: as, ...presentation },\n };\n // Asked at once, and only waited for when the page is another tab's: finding its route may\n // load a lazy one.\n const over = this.overTheTabInFront(commands, extras);\n if (!over) return this.go(commands, extras, intent);\n return over.then((found) =>\n this.go(found?.commands ?? commands, found?.extras ?? extras, intent),\n );\n }\n\n /**\n * Replace the whole stack with one screen, the way signing out or finishing onboarding does.\n * Every screen below is destroyed, so there is nothing to go back to.\n */\n reset(commands: NavigationCommands, options: NavigationExtras = {}): Promise<boolean> {\n return this.go(commands, { ...options, replaceUrl: true }, { stack: 'reset' });\n }\n\n /**\n * Go back exactly as the Android back button does: pop the stack in front, or at the root of a\n * tab's stack, go to the first tab. Not one entry back through history, which after a trip to\n * another tab is that tab. At the root of the app there is nowhere to go, and this does nothing.\n */\n back(): void {\n // Optional call: a test that stands a plain object in for the router has no such signal.\n if (!this.router.currentNavigation?.()) {\n this.outlets.back();\n return;\n }\n // Asked while a navigation is still putting its screen up: a page that leaves as it appears,\n // from an effect in its constructor. No stack has that screen to pop yet, so the back waits\n // for the navigation, and goes nowhere if the navigation does not arrive.\n if (this.backWaiting) return;\n this.backWaiting = this.router.events.subscribe((event) => {\n const ended = event instanceof NavigationEnd;\n if (!ended && !(event instanceof NavigationCancel) && !(event instanceof NavigationError)) {\n return;\n }\n this.backWaiting?.unsubscribe();\n this.backWaiting = null;\n if (ended) this.outlets.back();\n });\n }\n\n /** The wait of a back asked for mid-navigation, so two asked for in one are one. */\n private backWaiting: { unsubscribe(): void } | null = null;\n\n /**\n * Pop straight back to the screen at `commands`, taking every screen above it off the stack at\n * once - `popToViewController`. The screen is the one already there, with its state, not a new\n * one. Resolves false, doing nothing, when no screen below the top of a stack in front is\n * showing that url; a push is the way to a screen that is not there.\n */\n popTo(commands: NavigationCommands): Promise<boolean> {\n const tree =\n typeof commands === 'string'\n ? this.router.parseUrl(commands)\n : this.router.createUrlTree([...commands]);\n return this.outlets.popTo(this.router.serializeUrl(tree));\n }\n\n /**\n * Pop the stack in front to its first screen - `popToRootViewController`. Inside a tab, that is\n * the tab's first screen; the screens below keep their state. Resolves false at the root already.\n */\n popToRoot(): Promise<boolean> {\n return this.outlets.popToRoot();\n }\n\n private go(\n commands: NavigationCommands,\n options: NativeNavigationOptions,\n intent: NativeIntent,\n ): Promise<boolean> {\n const { presentation, state, ...extras } = options;\n const full: NativeIntent = presentation\n ? { ...intent, presentation: { ...intent.presentation, ...presentation } }\n : intent;\n\n const navigate = () => {\n const list = typeof commands === 'string' ? [pathOf(commands)] : [...commands];\n const all = {\n ...extras,\n ...(typeof commands === 'string' ? this.queryOf(commands, extras) : {}),\n state: { ...(state as object), [NATIVE_INTENT]: full },\n };\n const leaving = leavingPresented(this.router, list, all);\n return leaving ? this.router.navigateByUrl(leaving, all) : this.router.navigate(list, all);\n };\n // A screen stacked on the first one waits for it. Navigating while the router's first\n // navigation is still running - a lazily loaded root waiting on its import - cancels that\n // navigation, and the app opens on this screen with nothing beneath it. A reset replaces the\n // stack anyway, so it goes at once.\n const start = () => this.underTabRoot(commands, extras, full, navigate);\n return intent.stack !== 'reset' && this.firstNavigationRunning()\n ? this.firstNavigation().then(start)\n : start();\n }\n\n /**\n * A push into a tab nobody has opened goes to the tab first, so its stack has the tab's own\n * first screen under the page, and a back from the page lands there: what a tab bar with a\n * navigation controller per tab does. Without it the page is the stack's only screen, and the\n * same push leaves a different stack behind depending on whether the tab was ever tapped. A\n * presented screen goes where it is asked, and a replace or a reset has nothing to keep.\n */\n private underTabRoot(\n commands: NavigationCommands,\n extras: NavigationExtras,\n intent: NativeIntent,\n navigate: () => Promise<boolean>,\n ): Promise<boolean> {\n if (intent.stack !== 'push' || intent.presentation) return navigate();\n const tab = this.outlets.unopenedTabOf(this.pathFor(commands, extras));\n if (!tab) return navigate();\n return this.router.navigateByUrl(tab).then((arrived) => (arrived ? navigate() : false));\n }\n\n /** The path the commands lead to, without its query and fragment. */\n private pathFor(commands: NavigationCommands, extras: NavigationExtras): string {\n const url =\n typeof commands === 'string'\n ? commands\n : this.router.serializeUrl(this.router.createUrlTree([...commands], extras));\n return pathOf(url);\n }\n\n /**\n * A page of a tab that is not in front, presented over the one that is: its url would select\n * its own tab and show it there, so it goes to the root's presented outlet instead, where the\n * app's own stack shows it over everything. See `presented-route.ts`. Null for a page of the\n * tab in front, or of no tab, which is presented where its url puts it.\n */\n private overTheTabInFront(\n commands: NavigationCommands,\n extras: NavigationExtras,\n ): Promise<{ commands: unknown[]; extras: NavigationExtras } | null> | null {\n if (!this.outlets.presents) return null;\n const path = this.pathFor(commands, extras);\n if (!this.outlets.inTabBehind(path)) return null;\n const query = typeof commands === 'string' ? this.queryOf(commands, extras) : {};\n return presentedCommands(this.router, path).then(\n (presented) =>\n presented && { commands: presented, extras: { ...extras, ...query, relativeTo: null } },\n );\n }\n\n /**\n * The query and fragment written into a url string, as the extras `navigate` takes them: it\n * reads a string command as one path segment, and would encode a `?` into it. Extras given\n * alongside win over the ones in the string.\n */\n private queryOf(url: string, extras: NavigationExtras): NavigationExtras {\n const cut = url.search(/[?#]/);\n if (cut < 0) return {};\n const { queryParams, fragment } = this.router.parseUrl(url.slice(cut));\n return {\n queryParams: { ...queryParams, ...extras.queryParams },\n fragment: extras.fragment ?? fragment ?? undefined,\n };\n }\n\n private firstNavigationRunning(): boolean {\n const router = this.router as Partial<Router>;\n return router.navigated === false && !!router.currentNavigation?.();\n }\n\n private firstNavigation(): Promise<void> {\n return new Promise((resolve) => {\n const events = this.router.events.subscribe((event) => {\n if (\n event instanceof NavigationEnd ||\n event instanceof NavigationCancel ||\n event instanceof NavigationError\n ) {\n events.unsubscribe();\n resolve();\n }\n });\n });\n }\n}\n"]}
|
|
@@ -30,12 +30,14 @@ export class NativeRouterLink {
|
|
|
30
30
|
replaceUrl: this.replace(),
|
|
31
31
|
...own,
|
|
32
32
|
};
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
// The stack reads the intent, as `NativeNavigation` sets it. `replaceUrl` only swaps the
|
|
34
|
+
// history entry: without `replace` the replaced screen stays mounted underneath, and a swipe
|
|
35
|
+
// back lands on a page the history no longer has. So the intent follows `replaceUrl` as the
|
|
36
|
+
// navigation has it, `[extras]` included, not the `replace` input alone. Without `push` a
|
|
37
|
+
// link to a url already on the stack goes back to that screen rather than putting a new one
|
|
38
|
+
// over it.
|
|
39
|
+
const stack = extras.replaceUrl ? 'replace' : 'push';
|
|
40
|
+
extras.state = { ...own?.state, [NATIVE_INTENT]: { stack } };
|
|
39
41
|
void this.router.navigate(Array.isArray(target) ? [...target] : [target], extras);
|
|
40
42
|
}
|
|
41
43
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: NativeRouterLink, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-router-link.js","sourceRoot":"","sources":["../src/native-router-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,EAAyB,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;;AAMvD,MAAM,OAAO,gBAAgB;IAC3B,kEAAkE;IACzD,gBAAgB,GAAG,KAAK,CAAC,QAAQ;yFAA+B,CAAC;IACjE,MAAM,GAAG,KAAK;0FAAoB,CAAC;IAE5C,gEAAgE;IACvD,OAAO,GAAG,KAAK,CAAC,KAAK,+EAAI,SAAS,EAAE,gBAAgB,GAAG,CAAC;IAEhD,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACxB,KAAK,GAAG,MAAM,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1D,QAAQ;QAChB,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAqB;YAC/B,UAAU,EAAE,IAAI,CAAC,KAAK;YACtB,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE;YAC1B,GAAG,GAAG;SACP,CAAC;QACF,4FAA4F;QAC5F,
|
|
1
|
+
{"version":3,"file":"native-router-link.js","sourceRoot":"","sources":["../src/native-router-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,EAAyB,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;;AAMvD,MAAM,OAAO,gBAAgB;IAC3B,kEAAkE;IACzD,gBAAgB,GAAG,KAAK,CAAC,QAAQ;yFAA+B,CAAC;IACjE,MAAM,GAAG,KAAK;0FAAoB,CAAC;IAE5C,gEAAgE;IACvD,OAAO,GAAG,KAAK,CAAC,KAAK,+EAAI,SAAS,EAAE,gBAAgB,GAAG,CAAC;IAEhD,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACxB,KAAK,GAAG,MAAM,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1D,QAAQ;QAChB,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAqB;YAC/B,UAAU,EAAE,IAAI,CAAC,KAAK;YACtB,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE;YAC1B,GAAG,GAAG;SACP,CAAC;QACF,yFAAyF;QACzF,6FAA6F;QAC7F,4FAA4F;QAC5F,0FAA0F;QAC1F,4FAA4F;QAC5F,WAAW;QACX,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;QACrD,MAAM,CAAC,KAAK,GAAG,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC;QAE7D,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IACpF,CAAC;uGA7BU,gBAAgB;2FAAhB,gBAAgB;;2FAAhB,gBAAgB;kBAJ5B,SAAS;mBAAC;oBACT,QAAQ,EAAE,oBAAoB;oBAC9B,IAAI,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE;iBAClC","sourcesContent":["/**\n * `routerLink` for native.\n *\n * Angular's `RouterLink` host-binds `href` and listens for a DOM click, neither of which exists\n * here. This binds to a `<pressable>`'s `press` output instead.\n *\n * Because Angular resolves outputs across every directive on an element, the host listener below\n * binds to `Pressable`'s `press` output without importing it, so the router package does not\n * depend on the components package.\n */\nimport { Directive, booleanAttribute, inject, input } from '@angular/core';\nimport { ActivatedRoute, Router, type NavigationExtras } from '@angular/router';\nimport { NATIVE_INTENT } from './native-navigation.ts';\n\n@Directive({\n selector: '[nativeRouterLink]',\n host: { '(press)': 'navigate()' },\n})\nexport class NativeRouterLink {\n /** A url string, or the command array `Router.navigate` takes. */\n readonly nativeRouterLink = input.required<string | readonly unknown[]>();\n readonly extras = input<NavigationExtras>();\n\n /** Replace the current screen rather than pushing a new one. */\n readonly replace = input(false, { transform: booleanAttribute });\n\n private readonly router = inject(Router);\n private readonly route = inject(ActivatedRoute, { optional: true });\n\n protected navigate(): void {\n const target = this.nativeRouterLink();\n const own = this.extras();\n const extras: NavigationExtras = {\n relativeTo: this.route,\n replaceUrl: this.replace(),\n ...own,\n };\n // The stack reads the intent, as `NativeNavigation` sets it. `replaceUrl` only swaps the\n // history entry: without `replace` the replaced screen stays mounted underneath, and a swipe\n // back lands on a page the history no longer has. So the intent follows `replaceUrl` as the\n // navigation has it, `[extras]` included, not the `replace` input alone. Without `push` a\n // link to a url already on the stack goes back to that screen rather than putting a new one\n // over it.\n const stack = extras.replaceUrl ? 'replace' : 'push';\n extras.state = { ...own?.state, [NATIVE_INTENT]: { stack } };\n\n void this.router.navigate(Array.isArray(target) ? [...target] : [target], extras);\n }\n}\n"]}
|