@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.
@@ -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
- * @param factory A function that returns a `CreateBreadcrumbOptions` object.
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
- * breadcrumb: createBreadcrumb(() => ({
153
- * label: 'Home',
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
- * label: () => userStore.user().name ?? 'Loading...
163
- * };
164
- * })
164
+ * label: () => userStore.user().name ?? 'Loading...',
165
+ * };
166
+ * }),
165
167
  * },
166
- * }
168
+ * },
167
169
  * ];
168
170
  * ```
169
171
  */
170
- declare function createBreadcrumb(factory: () => CreateBreadcrumbOptions): ResolveFn<void>;
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<Record<string, any> | undefined>;
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 fn
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(fn: () => string | (() => string), awaitValue?: boolean): ResolveFn<string>;
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 };