@mmstack/router-core 21.0.2 → 21.0.4
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/README.md +352 -206
- package/fesm2022/mmstack-router-core.mjs +692 -414
- package/fesm2022/mmstack-router-core.mjs.map +1 -1
- package/package.json +5 -3
- package/types/mmstack-router-core.d.ts +218 -34
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
import * as i1 from '@angular/router';
|
|
2
|
+
import { ActivatedRouteSnapshot, ResolveFn, Params, ActivatedRoute, UrlTree, IsActiveMatchOptions, PreloadingStrategy, Route, Router } from '@angular/router';
|
|
1
3
|
import * as _angular_core from '@angular/core';
|
|
2
4
|
import { Signal, InjectionToken, Provider, WritableSignal } from '@angular/core';
|
|
3
|
-
import * as i1 from '@angular/router';
|
|
4
|
-
import { ActivatedRouteSnapshot, ResolveFn, UrlTree, ActivatedRoute, Params, PreloadingStrategy, Route } from '@angular/router';
|
|
5
5
|
import { Observable } from 'rxjs';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
type ResolvedLeafRoute = {
|
|
11
|
+
route: ActivatedRouteSnapshot;
|
|
12
|
+
segment: {
|
|
13
|
+
path: string;
|
|
14
|
+
resolved: string;
|
|
15
|
+
};
|
|
16
|
+
path: string;
|
|
17
|
+
link: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
7
20
|
/**
|
|
8
21
|
* Represents a single breadcrumb item within the navigation path.
|
|
9
22
|
* All dynamic properties are represented as Angular Signals to enable reactivity.
|
|
@@ -32,19 +45,6 @@ type Breadcrumb = {
|
|
|
32
45
|
link: Signal<string>;
|
|
33
46
|
};
|
|
34
47
|
|
|
35
|
-
/**
|
|
36
|
-
* @internal
|
|
37
|
-
*/
|
|
38
|
-
type ResolvedLeafRoute = {
|
|
39
|
-
route: ActivatedRouteSnapshot;
|
|
40
|
-
segment: {
|
|
41
|
-
path: string;
|
|
42
|
-
resolved: string;
|
|
43
|
-
};
|
|
44
|
-
path: string;
|
|
45
|
-
link: string;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
48
|
/**
|
|
49
49
|
* A function that returns a custom label generation function.
|
|
50
50
|
* The outer function is called in a root injection context
|
|
@@ -136,10 +136,11 @@ type CreateBreadcrumbOptions = {
|
|
|
136
136
|
/**
|
|
137
137
|
* Creates and registers a breadcrumb for a specific route.
|
|
138
138
|
* This function is designed to be used as an Angular Route `ResolveFn`.
|
|
139
|
-
* It handles the registration of the breadcrumb with the `BreadcrumbStore`
|
|
140
|
-
* and ensures automatic deregistration when the route is destroyed.
|
|
141
139
|
*
|
|
142
|
-
*
|
|
140
|
+
* Accepts a static label (`string`), a static options object, or a factory returning either —
|
|
141
|
+
* use a factory when you need `inject()` for dynamic data.
|
|
142
|
+
*
|
|
143
|
+
* @param factoryOrValue A static label, a static `CreateBreadcrumbOptions`, or a factory returning either.
|
|
143
144
|
* @see CreateBreadcrumbOptions
|
|
144
145
|
*
|
|
145
146
|
* @example
|
|
@@ -149,25 +150,26 @@ type CreateBreadcrumbOptions = {
|
|
|
149
150
|
* path: 'home',
|
|
150
151
|
* component: HomeComponent,
|
|
151
152
|
* resolve: {
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* });
|
|
153
|
+
* // shorthand for { label: 'Home' }
|
|
154
|
+
* breadcrumb: createBreadcrumb('Home'),
|
|
155
155
|
* },
|
|
156
|
+
* },
|
|
157
|
+
* {
|
|
156
158
|
* path: 'users/:userId',
|
|
157
159
|
* component: UserProfileComponent,
|
|
158
160
|
* resolve: {
|
|
159
161
|
* breadcrumb: createBreadcrumb(() => {
|
|
160
162
|
* const userStore = inject(UserStore);
|
|
161
163
|
* return {
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
164
|
+
* label: () => userStore.user().name ?? 'Loading...',
|
|
165
|
+
* };
|
|
166
|
+
* }),
|
|
165
167
|
* },
|
|
166
|
-
* }
|
|
168
|
+
* },
|
|
167
169
|
* ];
|
|
168
170
|
* ```
|
|
169
171
|
*/
|
|
170
|
-
declare function createBreadcrumb(
|
|
172
|
+
declare function createBreadcrumb(factoryOrValue: (() => CreateBreadcrumbOptions | string) | string | CreateBreadcrumbOptions): ResolveFn<void>;
|
|
171
173
|
|
|
172
174
|
/**
|
|
173
175
|
* Injects and provides access to a reactive list of breadcrumbs.
|
|
@@ -199,6 +201,38 @@ declare function createBreadcrumb(factory: () => CreateBreadcrumbOptions): Resol
|
|
|
199
201
|
*/
|
|
200
202
|
declare function injectBreadcrumbs(): Signal<Breadcrumb[]>;
|
|
201
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Returns an imperative function that triggers preloading for an arbitrary link, using
|
|
206
|
+
* the same path resolution and {@link PreloadStrategy} pipeline as the {@link Link}
|
|
207
|
+
* (`mmLink`) directive.
|
|
208
|
+
*
|
|
209
|
+
* Use this when the `Link` directive isn't a fit — for example, preloading a route from
|
|
210
|
+
* an effect when a user opens a menu, hovers a non-link element, or reacts to a signal
|
|
211
|
+
* change — and you don't want to render an `<a [mmLink]>` just to request the preload.
|
|
212
|
+
*
|
|
213
|
+
* Requires {@link PreloadStrategy} to be wired up via `provideRouter(routes, withPreloading(PreloadStrategy))`,
|
|
214
|
+
* just like the directive.
|
|
215
|
+
*
|
|
216
|
+
* @returns A function accepting the same link descriptor shape as `mmLink` (`string`,
|
|
217
|
+
* commands array, `UrlTree`, or `null`). Passing `null` or an unresolvable link is a no-op.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```typescript
|
|
221
|
+
* @Component({ ... })
|
|
222
|
+
* export class CommandPaletteComponent {
|
|
223
|
+
* private readonly triggerPreload = injectTriggerPreload();
|
|
224
|
+
*
|
|
225
|
+
* protected readonly highlighted = signal<string | null>(null);
|
|
226
|
+
*
|
|
227
|
+
* constructor() {
|
|
228
|
+
* effect(() => {
|
|
229
|
+
* const target = this.highlighted();
|
|
230
|
+
* if (target) this.triggerPreload(target);
|
|
231
|
+
* });
|
|
232
|
+
* }
|
|
233
|
+
* }
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
202
236
|
declare function injectTriggerPreload(): (link: string | any[] | UrlTree | null, relativeTo?: ActivatedRoute, queryParams?: Params, fragment?: string, queryParamsHandling?: "merge" | "preserve" | "") => void;
|
|
203
237
|
/**
|
|
204
238
|
* Configuration for the `mmLink` directive.
|
|
@@ -225,7 +259,7 @@ declare class Link {
|
|
|
225
259
|
readonly queryParams: _angular_core.InputSignal<Params | undefined>;
|
|
226
260
|
readonly fragment: _angular_core.InputSignal<string | undefined>;
|
|
227
261
|
readonly queryParamsHandling: _angular_core.InputSignal<"" | "merge" | "preserve" | undefined>;
|
|
228
|
-
readonly state: _angular_core.InputSignal<
|
|
262
|
+
readonly state: _angular_core.InputSignal<object | undefined>;
|
|
229
263
|
readonly info: _angular_core.InputSignal<unknown>;
|
|
230
264
|
readonly relativeTo: _angular_core.InputSignal<ActivatedRoute | undefined>;
|
|
231
265
|
readonly skipLocationChange: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
@@ -247,6 +281,151 @@ declare class Link {
|
|
|
247
281
|
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<Link, "[mmLink]", ["mmLink"], { "target": { "alias": "target"; "required": false; "isSignal": true; }; "queryParams": { "alias": "queryParams"; "required": false; "isSignal": true; }; "fragment": { "alias": "fragment"; "required": false; "isSignal": true; }; "queryParamsHandling": { "alias": "queryParamsHandling"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "info": { "alias": "info"; "required": false; "isSignal": true; }; "relativeTo": { "alias": "relativeTo"; "required": false; "isSignal": true; }; "skipLocationChange": { "alias": "skipLocationChange"; "required": false; "isSignal": true; }; "replaceUrl": { "alias": "replaceUrl"; "required": false; "isSignal": true; }; "mmLink": { "alias": "mmLink"; "required": false; "isSignal": true; }; "preloadOn": { "alias": "preloadOn"; "required": false; "isSignal": true; }; "useMouseDown": { "alias": "useMouseDown"; "required": false; "isSignal": true; }; "beforeNavigate": { "alias": "beforeNavigate"; "required": false; "isSignal": true; }; }, { "preloading": "preloading"; }, never, never, true, [{ directive: typeof i1.RouterLink; inputs: { "routerLink": "mmLink"; "target": "target"; "queryParams": "queryParams"; "fragment": "fragment"; "queryParamsHandling": "queryParamsHandling"; "state": "state"; "relativeTo": "relativeTo"; "skipLocationChange": "skipLocationChange"; "replaceUrl": "replaceUrl"; }; outputs: {}; }]>;
|
|
248
282
|
}
|
|
249
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Description of a single navigation item.
|
|
286
|
+
*
|
|
287
|
+
* Reactive fields accept either a static value or a zero-arg function — the function
|
|
288
|
+
* form is wrapped in a `computed` so reading signals inside it produces a reactive value.
|
|
289
|
+
*
|
|
290
|
+
* @typeParam TMeta Arbitrary consumer-defined metadata round-tripped to {@link NavItem.meta}.
|
|
291
|
+
*
|
|
292
|
+
* @see createNavItems
|
|
293
|
+
* @see NavItem
|
|
294
|
+
*/
|
|
295
|
+
type CreateNavItem<TMeta = Record<string, unknown>> = {
|
|
296
|
+
/** Visible text. */
|
|
297
|
+
label: string | (() => string);
|
|
298
|
+
/**
|
|
299
|
+
* The navigation target. Resolved relative to the route the resolver is attached to —
|
|
300
|
+
* the same convention as Angular's `routerLink`:
|
|
301
|
+
*
|
|
302
|
+
* - `'a'` / `'a/b'` / `['a', 'b']` — relative segments, resolve to `${mount}/a` etc.
|
|
303
|
+
* - `'/foo'` / `['/foo', 'bar']` — absolute, leading-slash escape hatch.
|
|
304
|
+
* - A pre-built `UrlTree` is passed through unchanged.
|
|
305
|
+
*
|
|
306
|
+
* Omit (or return `null`) for a pure grouping header — `active` will then fall through
|
|
307
|
+
* to the children-active check.
|
|
308
|
+
*/
|
|
309
|
+
link?: string | any[] | (() => string | any[] | null) | null;
|
|
310
|
+
/** Accessible label. Defaults to `label`. */
|
|
311
|
+
ariaLabel?: string | (() => string);
|
|
312
|
+
/** When true, the item is rendered but non-interactive. Cascades to descendants. */
|
|
313
|
+
disabled?: boolean | (() => boolean);
|
|
314
|
+
/** When true, the item (and its subtree) is filtered out of the consumer-facing array. */
|
|
315
|
+
hidden?: boolean | (() => boolean);
|
|
316
|
+
/** Arbitrary metadata round-tripped on the resulting `NavItem`. */
|
|
317
|
+
meta?: TMeta | (() => TMeta);
|
|
318
|
+
/**
|
|
319
|
+
* Override the match options used to compute `active`. Merged on top of the global default
|
|
320
|
+
* from `provideNavConfig`, which is in turn merged on top of `@angular/router`'s
|
|
321
|
+
* `subsetMatchOptions` defaults.
|
|
322
|
+
*
|
|
323
|
+
* Setting this also implicitly disables the default child-active OR — see
|
|
324
|
+
* {@link matchesWhenChildActive} to opt back in.
|
|
325
|
+
*/
|
|
326
|
+
activeMatch?: Partial<IsActiveMatchOptions>;
|
|
327
|
+
/**
|
|
328
|
+
* Controls whether `active` ORs with descendant `active`.
|
|
329
|
+
*
|
|
330
|
+
* Default: `true` when `activeMatch` is omitted, `false` when `activeMatch` is set.
|
|
331
|
+
* Explicit values override the default.
|
|
332
|
+
*/
|
|
333
|
+
matchesWhenChildActive?: boolean;
|
|
334
|
+
/** Optional stable id. Defaults to the serialized link when present. */
|
|
335
|
+
id?: string | (() => string);
|
|
336
|
+
/** Nested items. */
|
|
337
|
+
children?: CreateNavItem<TMeta>[];
|
|
338
|
+
};
|
|
339
|
+
/**
|
|
340
|
+
* A navigation item exposed by `injectNavItems`. All fields are signals so consumers can
|
|
341
|
+
* read reactively without caring whether the source was static or signal-derived.
|
|
342
|
+
*
|
|
343
|
+
* @typeParam TMeta Same as the corresponding {@link CreateNavItem}.
|
|
344
|
+
*/
|
|
345
|
+
type NavItem<TMeta = Record<string, unknown>> = {
|
|
346
|
+
id: Signal<string>;
|
|
347
|
+
label: Signal<string>;
|
|
348
|
+
ariaLabel: Signal<string>;
|
|
349
|
+
/** Serialized URL, or `null` for pure grouping items with no link. */
|
|
350
|
+
link: Signal<string | null>;
|
|
351
|
+
active: Signal<boolean>;
|
|
352
|
+
disabled: Signal<boolean>;
|
|
353
|
+
meta: Signal<TMeta>;
|
|
354
|
+
/** Children that survive the hidden filter. */
|
|
355
|
+
children: Signal<NavItem<TMeta>[]>;
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Global configuration for the nav system.
|
|
360
|
+
* @see provideNavConfig
|
|
361
|
+
*/
|
|
362
|
+
type NavConfig = {
|
|
363
|
+
/**
|
|
364
|
+
* Default match options used when computing `NavItem.active`. Per-item `activeMatch`
|
|
365
|
+
* (and the router's built-in `subsetMatchOptions`) are still merged on top.
|
|
366
|
+
*/
|
|
367
|
+
activeMatch?: Partial<IsActiveMatchOptions>;
|
|
368
|
+
};
|
|
369
|
+
/**
|
|
370
|
+
* Provides global configuration for the nav system.
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```typescript
|
|
374
|
+
* provideNavConfig({
|
|
375
|
+
* activeMatch: { queryParams: 'ignored' },
|
|
376
|
+
* }),
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
declare function provideNavConfig(config?: NavConfig): Provider;
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Registers a set of nav items for the activating route under the given scope.
|
|
383
|
+
* Mirrors `createBreadcrumb` / `createTitle` — designed to be used in a route's
|
|
384
|
+
* `resolve` map.
|
|
385
|
+
*
|
|
386
|
+
* Multiple scopes can be registered on a single route by giving each its own `name`
|
|
387
|
+
* (and a unique key in the `resolve` map):
|
|
388
|
+
*
|
|
389
|
+
* ```typescript
|
|
390
|
+
* resolve: {
|
|
391
|
+
* mainNav: createNavItems([...], { name: 'main' }),
|
|
392
|
+
* sideNav: createNavItems([...], { name: 'side' }),
|
|
393
|
+
* }
|
|
394
|
+
* ```
|
|
395
|
+
*
|
|
396
|
+
* Scope override semantics: when multiple routes in the active chain register items
|
|
397
|
+
* under the same scope, the deepest active registration wins. Navigating away restores
|
|
398
|
+
* the shallower registration.
|
|
399
|
+
*/
|
|
400
|
+
declare function createNavItems<TMeta = Record<string, unknown>>(itemsOrFactory: CreateNavItem<TMeta>[] | (() => CreateNavItem<TMeta>[]), options?: {
|
|
401
|
+
name?: string;
|
|
402
|
+
}): ResolveFn<void>;
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Returns a reactive list of nav items for the requested scope.
|
|
406
|
+
*
|
|
407
|
+
* The returned signal reflects the nearest active ancestor route that registered items
|
|
408
|
+
* for `name` via `createNavItems`. Hidden items are filtered out.
|
|
409
|
+
*
|
|
410
|
+
* @typeParam TMeta The shape of `NavItem.meta` for the consuming code. Untyped at the
|
|
411
|
+
* registration site — this is a consumer-side assertion.
|
|
412
|
+
*
|
|
413
|
+
* @example
|
|
414
|
+
* ```typescript
|
|
415
|
+
* @Component({
|
|
416
|
+
* template: `
|
|
417
|
+
* @for (item of items(); track item) {
|
|
418
|
+
* <a [href]="item.link()" [class.active]="item.active()">{{ item.label() }}</a>
|
|
419
|
+
* }
|
|
420
|
+
* `,
|
|
421
|
+
* })
|
|
422
|
+
* export class TopBar {
|
|
423
|
+
* protected readonly items = injectNavItems();
|
|
424
|
+
* }
|
|
425
|
+
* ```
|
|
426
|
+
*/
|
|
427
|
+
declare function injectNavItems<TMeta = Record<string, unknown>>(name?: string): Signal<NavItem<TMeta>[]>;
|
|
428
|
+
|
|
250
429
|
declare class PreloadStrategy implements PreloadingStrategy {
|
|
251
430
|
private readonly loading;
|
|
252
431
|
private readonly router;
|
|
@@ -287,7 +466,6 @@ declare class PreloadStrategy implements PreloadingStrategy {
|
|
|
287
466
|
*
|
|
288
467
|
* @Component({
|
|
289
468
|
* selector: 'app-product-list',
|
|
290
|
-
* standalone: true,
|
|
291
469
|
* // imports: [FormsModule], // If using ngModel
|
|
292
470
|
* template: `
|
|
293
471
|
* <div>
|
|
@@ -340,6 +518,12 @@ declare function queryParam(key: string | (() => string), route?: ActivatedRoute
|
|
|
340
518
|
* @see {createTitle}
|
|
341
519
|
*/
|
|
342
520
|
type TitleConfig = {
|
|
521
|
+
/**
|
|
522
|
+
* The base title to fallback to, by default Title.getTitle() is called on instantiation and that is used as a fallback,
|
|
523
|
+
* which in most cases should resolve to what is in index.html, unless you specifically call Title.setTitle() before any routes,
|
|
524
|
+
* are initialized.
|
|
525
|
+
*/
|
|
526
|
+
initialTitle?: string;
|
|
343
527
|
/**
|
|
344
528
|
* The title to be used when no title is set.
|
|
345
529
|
* If not provided it defaults to an empty string
|
|
@@ -361,13 +545,13 @@ declare function provideTitleConfig(config?: TitleConfig): Provider;
|
|
|
361
545
|
*
|
|
362
546
|
* Creates a title resolver function that can be used in Angular's router.
|
|
363
547
|
*
|
|
364
|
-
* @param
|
|
365
|
-
* A function that returns a string or a Signal<string> representing the title.
|
|
548
|
+
* @param factoryOrValue
|
|
549
|
+
* A function that returns a string or a Signal<string> representing the title or just the string directly.
|
|
366
550
|
* @param awaitValue
|
|
367
551
|
* If `true`, the resolver will wait until the title signal has a value before resolving.
|
|
368
552
|
* Defaults to `false`.
|
|
369
553
|
*/
|
|
370
|
-
declare function createTitle(
|
|
554
|
+
declare function createTitle(factoryOrValue: (() => string | (() => string)) | string, awaitValue?: boolean): ResolveFn<string>;
|
|
371
555
|
|
|
372
556
|
/**
|
|
373
557
|
* Creates a Signal that tracks the current router URL.
|
|
@@ -399,7 +583,7 @@ declare function createTitle(fn: () => string | (() => string), awaitValue?: boo
|
|
|
399
583
|
* }
|
|
400
584
|
* ```
|
|
401
585
|
*/
|
|
402
|
-
declare function url(): Signal<string>;
|
|
586
|
+
declare function url(router?: Router): Signal<string>;
|
|
403
587
|
|
|
404
|
-
export { Link, PreloadStrategy, createBreadcrumb, createTitle, injectBreadcrumbs, injectTriggerPreload, provideBreadcrumbConfig, provideMMLinkDefaultConfig, provideTitleConfig, queryParam, url };
|
|
405
|
-
export type { Breadcrumb, TitleConfig };
|
|
588
|
+
export { Link, PreloadStrategy, createBreadcrumb, createNavItems, createTitle, injectBreadcrumbs, injectNavItems, injectTriggerPreload, provideBreadcrumbConfig, provideMMLinkDefaultConfig, provideNavConfig, provideTitleConfig, queryParam, url };
|
|
589
|
+
export type { Breadcrumb, BreadcrumbConfig, CreateNavItem, NavConfig, NavItem, ResolvedLeafRoute, TitleConfig };
|