@mmstack/router-core 19.3.12 → 20.0.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/fesm2022/mmstack-router-core.mjs +22 -21
- package/fesm2022/mmstack-router-core.mjs.map +1 -1
- package/index.d.ts +404 -6
- package/package.json +5 -4
- package/lib/breadcrumb/breadcrumb.config.d.ts +0 -73
- package/lib/breadcrumb/breadcrumb.resolver.d.ts +0 -58
- package/lib/breadcrumb/breadcrumb.store.d.ts +0 -44
- package/lib/breadcrumb/breadcrumb.type.d.ts +0 -57
- package/lib/breadcrumb/public_api.d.ts +0 -4
- package/lib/link.directive.d.ts +0 -51
- package/lib/preloading/index.d.ts +0 -2
- package/lib/preloading/preload.service.d.ts +0 -8
- package/lib/preloading/preload.strategy.d.ts +0 -11
- package/lib/preloading/public_api.d.ts +0 -1
- package/lib/query-param.d.ts +0 -78
- package/lib/title/public_api.d.ts +0 -2
- package/lib/title/title.config.d.ts +0 -31
- package/lib/title/title.store.d.ts +0 -23
- package/lib/url.d.ts +0 -32
- package/lib/util/create-route-predicate.d.ts +0 -1
- package/lib/util/find-path.d.ts +0 -2
- package/lib/util/index.d.ts +0 -4
- package/lib/util/leaf.store.d.ts +0 -21
- package/lib/util/snapshot-path.d.ts +0 -2
package/index.d.ts
CHANGED
|
@@ -1,6 +1,404 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { InjectionToken, Signal, 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
|
+
import { Observable } from 'rxjs';
|
|
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
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A function that returns a custom label generation function.
|
|
22
|
+
* The outer function is called in a root injection context
|
|
23
|
+
* The returned function takes a `ResolvedLeafRoute` and produces a string label for the breadcrumb.
|
|
24
|
+
* As the inner function is wrapped in a computed, changes to signals called within it will update the breadcrumb label reactively.
|
|
25
|
+
*/
|
|
26
|
+
type GenerateBreadcrumbFn = () => (leaf: ResolvedLeafRoute) => string;
|
|
27
|
+
/**
|
|
28
|
+
* Configuration options for the breadcrumb system.
|
|
29
|
+
* Use `provideBreadcrumbConfig` to supply these options to your application.
|
|
30
|
+
*/
|
|
31
|
+
type BreadcrumbConfig = {
|
|
32
|
+
/**
|
|
33
|
+
* Defines how breadcrumb labels are generated.
|
|
34
|
+
* - If set to `'manual'`, breadcrumbs will only be displayed if manually registered
|
|
35
|
+
* via `createBreadcrumb`. Automatic generation based on routes is disabled.
|
|
36
|
+
* - Alternatively provide a custom label generation function
|
|
37
|
+
* If left undefined, the system will automatically generate labels based on the route's title, data, or path.
|
|
38
|
+
* @see GenerateBreadcrumbFn
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // For custom label generation:
|
|
42
|
+
* // const myCustomLabelGenerator = () => (leaf: ResolvedLeafRoute) => {
|
|
43
|
+
* // return leaf.route.data?.['customTitle'] || leaf.route.routeConfig?.path || 'Default';
|
|
44
|
+
* // };
|
|
45
|
+
* //
|
|
46
|
+
* // config: { generation: myCustomLabelGenerator }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
generation?: 'manual' | GenerateBreadcrumbFn;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Provides configuration for the breadcrumb system.
|
|
53
|
+
* @param config - A partial `BreadcrumbConfig` object with the desired settings. *
|
|
54
|
+
* @see BreadcrumbConfig
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* // In your app.module.ts or a standalone component's providers:
|
|
58
|
+
* // import { provideBreadcrumbConfig } from './breadcrumb.config'; // Adjust path
|
|
59
|
+
* // import { ResolvedLeafRoute } from './breadcrumb.type'; // Adjust path
|
|
60
|
+
*
|
|
61
|
+
* // const customLabelStrategy: GenerateBreadcrumbFn = () => {
|
|
62
|
+
* // return (leaf: ResolvedLeafRoute): string => {
|
|
63
|
+
* // // Example: Prioritize a 'navTitle' data property
|
|
64
|
+
* // if (leaf.route.data?.['navTitle']) {
|
|
65
|
+
* // return leaf.route.data['navTitle'];
|
|
66
|
+
* // }
|
|
67
|
+
* // // Fallback to a default mechanism
|
|
68
|
+
* // return leaf.route.title || leaf.segment.resolved || 'Unnamed';
|
|
69
|
+
* // };
|
|
70
|
+
* // };
|
|
71
|
+
*
|
|
72
|
+
* export const appConfig = [
|
|
73
|
+
* // ...rest
|
|
74
|
+
* provideBreadcrumbConfig({
|
|
75
|
+
* generation: customLabelStrategy, // or 'manual' to disable auto-generation
|
|
76
|
+
* }),
|
|
77
|
+
* ]
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
declare function provideBreadcrumbConfig(config: Partial<BreadcrumbConfig>): {
|
|
81
|
+
provide: InjectionToken<BreadcrumbConfig>;
|
|
82
|
+
useValue: {
|
|
83
|
+
generation?: ("manual" | GenerateBreadcrumbFn) | undefined;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Options for defining a breadcrumb.
|
|
89
|
+
*
|
|
90
|
+
*/
|
|
91
|
+
type CreateBreadcrumbOptions = {
|
|
92
|
+
/**
|
|
93
|
+
* The visible text for the breadcrumb.
|
|
94
|
+
* Can be a static string or a function for dynamic labels.
|
|
95
|
+
*/
|
|
96
|
+
label: string | (() => string);
|
|
97
|
+
/**
|
|
98
|
+
* An accessible label for the breadcrumb item.
|
|
99
|
+
* Defaults to the value of `label` if not provided.
|
|
100
|
+
* Can be a static string or a function returning a string for dynamic ARIA labels.
|
|
101
|
+
*/
|
|
102
|
+
ariaLabel?: string | (() => string);
|
|
103
|
+
/**
|
|
104
|
+
* If `true`, the route resolver will wait until the `label` signal has a value before `resolving`
|
|
105
|
+
*/
|
|
106
|
+
awaitValue?: boolean;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Creates and registers a breadcrumb for a specific route.
|
|
110
|
+
* This function is designed to be used as an Angular Route `ResolveFn`.
|
|
111
|
+
* It handles the registration of the breadcrumb with the `BreadcrumbStore`
|
|
112
|
+
* and ensures automatic deregistration when the route is destroyed.
|
|
113
|
+
*
|
|
114
|
+
* @param factory A function that returns a `CreateBreadcrumbOptions` object.
|
|
115
|
+
* @see CreateBreadcrumbOptions
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* export const appRoutes: Routes = [
|
|
120
|
+
* {
|
|
121
|
+
* path: 'home',
|
|
122
|
+
* component: HomeComponent,
|
|
123
|
+
* resolve: {
|
|
124
|
+
* breadcrumb: createBreadcrumb(() => ({
|
|
125
|
+
* label: 'Home',
|
|
126
|
+
* });
|
|
127
|
+
* },
|
|
128
|
+
* path: 'users/:userId',
|
|
129
|
+
* component: UserProfileComponent,
|
|
130
|
+
* resolve: {
|
|
131
|
+
* breadcrumb: createBreadcrumb(() => {
|
|
132
|
+
* const userStore = inject(UserStore);
|
|
133
|
+
* return {
|
|
134
|
+
* label: () => userStore.user().name ?? 'Loading...
|
|
135
|
+
* };
|
|
136
|
+
* })
|
|
137
|
+
* },
|
|
138
|
+
* }
|
|
139
|
+
* ];
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
declare function createBreadcrumb(factory: () => CreateBreadcrumbOptions): ResolveFn<void>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Represents a single breadcrumb item within the navigation path.
|
|
146
|
+
* All dynamic properties are represented as Angular Signals to enable reactivity.
|
|
147
|
+
*/
|
|
148
|
+
type Breadcrumb = {
|
|
149
|
+
/**
|
|
150
|
+
* A unique identifier for the breadcrumb item. Generally the unresolved path for example `/posts/:id`.
|
|
151
|
+
* Useful for `@for` tracking in templates.
|
|
152
|
+
*/
|
|
153
|
+
id: string;
|
|
154
|
+
/**
|
|
155
|
+
* The visible text for the breadcrumb item.
|
|
156
|
+
* Updated reactively as the url/link based on
|
|
157
|
+
* either a provided definition, or the current route.
|
|
158
|
+
*/
|
|
159
|
+
label: Signal<string>;
|
|
160
|
+
/**
|
|
161
|
+
* An accessible label for the breadcrumb item.
|
|
162
|
+
* Defaults to the same value as `label` if not provided.
|
|
163
|
+
*/
|
|
164
|
+
ariaLabel: Signal<string>;
|
|
165
|
+
/**
|
|
166
|
+
* The URL link for the breadcrumb item.
|
|
167
|
+
* Updates as the route changes.
|
|
168
|
+
*/
|
|
169
|
+
link: Signal<string>;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Injects and provides access to a reactive list of breadcrumbs.
|
|
174
|
+
*
|
|
175
|
+
* The breadcrumbs are ordered and reflect the current active navigation path.
|
|
176
|
+
* @see Breadcrumb
|
|
177
|
+
* @returns `Signal<Breadcrumb[]>`
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* @Component({
|
|
182
|
+
* selector: 'app-breadcrumbs',
|
|
183
|
+
* template: `
|
|
184
|
+
* <nav aria-label="breadcrumb">
|
|
185
|
+
* <ol>
|
|
186
|
+
* @for (crumb of breadcrumbs(); track crumb.id) {
|
|
187
|
+
* <li>
|
|
188
|
+
* <a [href]="crumb.link()" [attr.aria-label]="crumb.ariaLabel()">{{ crumb.label() }}</a>
|
|
189
|
+
* </li>
|
|
190
|
+
* }
|
|
191
|
+
* </ol>
|
|
192
|
+
* </nav>
|
|
193
|
+
* `
|
|
194
|
+
* })
|
|
195
|
+
* export class MyBreadcrumbsComponent {
|
|
196
|
+
* breadcrumbs = injectBreadcrumbs();
|
|
197
|
+
* }
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
declare function injectBreadcrumbs(): Signal<Breadcrumb[]>;
|
|
201
|
+
|
|
202
|
+
declare function injectTriggerPreload(): (link: string | any[] | UrlTree | null, relativeTo?: ActivatedRoute, queryParams?: Params, fragment?: string, queryParamsHandling?: "merge" | "preserve" | "") => void;
|
|
203
|
+
/**
|
|
204
|
+
* Configuration for the `mmLink` directive.
|
|
205
|
+
*/
|
|
206
|
+
type MMLinkConfig = {
|
|
207
|
+
/**
|
|
208
|
+
* The default preload behavior for links.
|
|
209
|
+
* Can be 'hover', 'visible', or null (no preloading).
|
|
210
|
+
* @default 'hover'
|
|
211
|
+
*/
|
|
212
|
+
preloadOn: 'hover' | 'visible' | null;
|
|
213
|
+
/**
|
|
214
|
+
* Whether to use mouse down events for preloading.
|
|
215
|
+
* @default false
|
|
216
|
+
*/
|
|
217
|
+
useMouseDown: boolean;
|
|
218
|
+
};
|
|
219
|
+
declare function provideMMLinkDefaultConfig(config: Partial<MMLinkConfig>): Provider;
|
|
220
|
+
declare class LinkDirective {
|
|
221
|
+
private readonly routerLink;
|
|
222
|
+
private readonly svc;
|
|
223
|
+
private readonly router;
|
|
224
|
+
readonly target: _angular_core.InputSignal<string | undefined>;
|
|
225
|
+
readonly queryParams: _angular_core.InputSignal<Params | undefined>;
|
|
226
|
+
readonly fragment: _angular_core.InputSignal<string | undefined>;
|
|
227
|
+
readonly queryParamsHandling: _angular_core.InputSignal<"" | "merge" | "preserve" | undefined>;
|
|
228
|
+
readonly state: _angular_core.InputSignal<Record<string, any> | undefined>;
|
|
229
|
+
readonly info: _angular_core.InputSignal<unknown>;
|
|
230
|
+
readonly relativeTo: _angular_core.InputSignal<ActivatedRoute | undefined>;
|
|
231
|
+
readonly skipLocationChange: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
232
|
+
readonly replaceUrl: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
233
|
+
readonly mmLink: _angular_core.InputSignal<string | any[] | UrlTree | null>;
|
|
234
|
+
readonly preloadOn: _angular_core.InputSignal<"hover" | "visible" | null>;
|
|
235
|
+
readonly useMouseDown: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
236
|
+
readonly preloading: _angular_core.OutputEmitterRef<void>;
|
|
237
|
+
private readonly urlTree;
|
|
238
|
+
private readonly fullPath;
|
|
239
|
+
onHover(): void;
|
|
240
|
+
onMouseDown(button: number, ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean): boolean | undefined;
|
|
241
|
+
onClick(button: number, ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean): boolean | undefined;
|
|
242
|
+
constructor();
|
|
243
|
+
private requestPreload;
|
|
244
|
+
private trigger;
|
|
245
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<LinkDirective, never>;
|
|
246
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<LinkDirective, "[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; }; }, { "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: {}; }]>;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
declare class PreloadStrategy implements PreloadingStrategy {
|
|
250
|
+
private readonly loading;
|
|
251
|
+
private readonly router;
|
|
252
|
+
private readonly svc;
|
|
253
|
+
preload(route: Route, load: () => Observable<any>): Observable<any>;
|
|
254
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<PreloadStrategy, never>;
|
|
255
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<PreloadStrategy>;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Creates a WritableSignal that synchronizes with a specific URL query parameter,
|
|
260
|
+
* enabling two-way binding between the signal's state and the URL.
|
|
261
|
+
*
|
|
262
|
+
* Reading the signal provides the current value of the query parameter (or null if absent).
|
|
263
|
+
* Setting the signal updates the URL query parameter using `Router.navigate`, triggering
|
|
264
|
+
* navigation and causing the signal to update reactively if the navigation is successful.
|
|
265
|
+
*
|
|
266
|
+
* @param key The key of the query parameter to synchronize with.
|
|
267
|
+
* Can be a static string (e.g., `'search'`) or a function/signal returning a string
|
|
268
|
+
* for dynamic keys (e.g., `() => this.userId() + '_filter'` or `computed(() => this.category() + '_sort')`).
|
|
269
|
+
* The signal will reactively update if the key returned by the function/signal changes.
|
|
270
|
+
* @returns {WritableSignal<string | null>} A signal representing the query parameter's value.
|
|
271
|
+
* - Reading returns the current value string, or `null` if the parameter is absent in the URL.
|
|
272
|
+
* - Setting the signal to a string updates the query parameter in the URL (e.g., `signal.set('value')` results in `?key=value`).
|
|
273
|
+
* - Setting the signal to `null` removes the query parameter from the URL (e.g., `signal.set(null)` results in `?otherParam=...`).
|
|
274
|
+
* - Automatically reflects changes if the query parameters update due to external navigation.
|
|
275
|
+
* @remarks
|
|
276
|
+
* - Requires Angular's `ActivatedRoute` and `Router` to be available in the injection context.
|
|
277
|
+
* - Uses `Router.navigate` with `queryParamsHandling: 'merge'` to preserve other existing query parameters during updates.
|
|
278
|
+
* - Handles dynamic keys reactively. If the result of the `key` function/signal changes, the signal will start reflecting the value of the *new* query parameter key.
|
|
279
|
+
* - During Server-Side Rendering (SSR), it reads the initial value from the route snapshot. Write operations (`set`) might have limited or no effect on the server depending on the platform configuration.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```ts
|
|
283
|
+
* import { Component, computed, effect, signal } from '@angular/core';
|
|
284
|
+
* import { queryParam } from '@mmstack/router-core'; // Adjust import path as needed
|
|
285
|
+
* // import { FormsModule } from '@angular/forms'; // If using ngModel
|
|
286
|
+
*
|
|
287
|
+
* @Component({
|
|
288
|
+
* selector: 'app-product-list',
|
|
289
|
+
* standalone: true,
|
|
290
|
+
* // imports: [FormsModule], // If using ngModel
|
|
291
|
+
* template: `
|
|
292
|
+
* <div>
|
|
293
|
+
* Sort By:
|
|
294
|
+
* <select [value]="sortSignal() ?? ''" (change)="sortSignal.set($any($event.target).value || null)">
|
|
295
|
+
* <option value="">Default</option>
|
|
296
|
+
* <option value="price_asc">Price Asc</option>
|
|
297
|
+
* <option value="price_desc">Price Desc</option>
|
|
298
|
+
* <option value="name">Name</option>
|
|
299
|
+
* </select>
|
|
300
|
+
* <button (click)="sortSignal.set(null)" [disabled]="!sortSignal()">Clear Sort</button>
|
|
301
|
+
* </div>
|
|
302
|
+
* <div>
|
|
303
|
+
* Page:
|
|
304
|
+
* <input type="number" min="1" [value]="pageSignal() ?? '1'" #p (input)="setPage(p.value)"/>
|
|
305
|
+
* </div>
|
|
306
|
+
* * `
|
|
307
|
+
* })
|
|
308
|
+
* export class ProductListComponent {
|
|
309
|
+
* // Two-way bind the 'sort' query parameter (?sort=...)
|
|
310
|
+
* // Defaults to null if param is missing
|
|
311
|
+
* sortSignal = queryParam('sort');
|
|
312
|
+
*
|
|
313
|
+
* // Example with a different type (needs serialization or separate logic)
|
|
314
|
+
* // For simplicity, we treat page as string | null here
|
|
315
|
+
* pageSignal = queryParam('page');
|
|
316
|
+
*
|
|
317
|
+
* constructor() {
|
|
318
|
+
* effect(() => {
|
|
319
|
+
* const currentSort = this.sortSignal();
|
|
320
|
+
* const currentPage = this.pageSignal(); // Read as string | null
|
|
321
|
+
* console.log('Sort/Page changed, reloading products for:', { sort: currentSort, page: currentPage });
|
|
322
|
+
* // --- Fetch data based on currentSort and currentPage ---
|
|
323
|
+
* });
|
|
324
|
+
* }
|
|
325
|
+
*
|
|
326
|
+
* setPage(value: string): void {
|
|
327
|
+
* const pageNum = parseInt(value, 10);
|
|
328
|
+
* // Set to null if page is 1 (to remove param), otherwise set string value
|
|
329
|
+
* this.pageSignal.set(isNaN(pageNum) || pageNum <= 1 ? null : pageNum.toString());
|
|
330
|
+
* }
|
|
331
|
+
* }
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
declare function queryParam(key: string | (() => string)): WritableSignal<string | null>;
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Title configuration interface.
|
|
338
|
+
* Defines how createTitle should behave
|
|
339
|
+
* @see {createTitle}
|
|
340
|
+
*/
|
|
341
|
+
type TitleConfig = {
|
|
342
|
+
/**
|
|
343
|
+
* The title to be used when no title is set.
|
|
344
|
+
* If not provided it defaults to an empty string
|
|
345
|
+
* @default ''
|
|
346
|
+
*/
|
|
347
|
+
prefix?: string | ((title: string) => string);
|
|
348
|
+
/**
|
|
349
|
+
* if false, the title will change to the url, otherwise default to true as that is standard behavior
|
|
350
|
+
* @default true
|
|
351
|
+
*/
|
|
352
|
+
keepLastKnownTitle?: boolean;
|
|
353
|
+
};
|
|
354
|
+
/**
|
|
355
|
+
* used to provide the title configuration, will not be applied unless a `createTitle` resolver is used
|
|
356
|
+
*/
|
|
357
|
+
declare function provideTitleConfig(config?: TitleConfig): Provider;
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
*
|
|
361
|
+
* Creates a title resolver function that can be used in Angular's router.
|
|
362
|
+
*
|
|
363
|
+
* @param fn
|
|
364
|
+
* A function that returns a string or a Signal<string> representing the title.
|
|
365
|
+
* @param awaitValue
|
|
366
|
+
* If `true`, the resolver will wait until the title signal has a value before resolving.
|
|
367
|
+
* Defaults to `false`.
|
|
368
|
+
*/
|
|
369
|
+
declare function createTitle(fn: () => string | (() => string), awaitValue?: boolean): ResolveFn<string>;
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Creates a Signal that tracks the current router URL.
|
|
373
|
+
*
|
|
374
|
+
* The signal emits the URL string reflecting the router state *after* redirects
|
|
375
|
+
* have completed for each successful navigation. It initializes with the router's
|
|
376
|
+
* current URL state.
|
|
377
|
+
*
|
|
378
|
+
* @returns {Signal<string>} A Signal emitting the `urlAfterRedirects` upon successful navigation.
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```ts
|
|
382
|
+
* import { Component, effect } from '@angular/core';
|
|
383
|
+
* import { url } from '@mmstack/router-core'; // Adjust import path
|
|
384
|
+
*
|
|
385
|
+
* @Component({
|
|
386
|
+
* selector: 'app-root',
|
|
387
|
+
* template: `Current URL: {{ currentUrl() }}`
|
|
388
|
+
* })
|
|
389
|
+
* export class AppComponent {
|
|
390
|
+
* currentUrl = url();
|
|
391
|
+
*
|
|
392
|
+
* constructor() {
|
|
393
|
+
* effect(() => {
|
|
394
|
+
* console.log('Navigation ended. New URL:', this.currentUrl());
|
|
395
|
+
* // e.g., track page view with analytics
|
|
396
|
+
* });
|
|
397
|
+
* }
|
|
398
|
+
* }
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
declare function url(): Signal<string>;
|
|
402
|
+
|
|
403
|
+
export { LinkDirective, PreloadStrategy, createBreadcrumb, createTitle, injectBreadcrumbs, injectTriggerPreload, provideBreadcrumbConfig, provideMMLinkDefaultConfig, provideTitleConfig, queryParam, url };
|
|
404
|
+
export type { Breadcrumb, TitleConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmstack/router-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "20.0.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"angular",
|
|
6
6
|
"signals",
|
|
@@ -20,12 +20,13 @@
|
|
|
20
20
|
},
|
|
21
21
|
"homepage": "https://github.com/mihajm/mmstack/blob/master/packages/router/core",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@mmstack/primitives": "^
|
|
23
|
+
"@mmstack/primitives": "^20.0.0",
|
|
24
24
|
"tslib": "^2.3.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"@angular/core": "
|
|
28
|
-
"@angular/router": "
|
|
27
|
+
"@angular/core": "~20.0.3",
|
|
28
|
+
"@angular/router": "~20.0.3",
|
|
29
|
+
"@angular/platform-browser": "~20.0.3",
|
|
29
30
|
"rxjs": "~7.8.2"
|
|
30
31
|
},
|
|
31
32
|
"sideEffects": false,
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { InjectionToken } from '@angular/core';
|
|
2
|
-
import { ResolvedLeafRoute } from '../util';
|
|
3
|
-
/**
|
|
4
|
-
* A function that returns a custom label generation function.
|
|
5
|
-
* The outer function is called in a root injection context
|
|
6
|
-
* The returned function takes a `ResolvedLeafRoute` and produces a string label for the breadcrumb.
|
|
7
|
-
* As the inner function is wrapped in a computed, changes to signals called within it will update the breadcrumb label reactively.
|
|
8
|
-
*/
|
|
9
|
-
type GenerateBreadcrumbFn = () => (leaf: ResolvedLeafRoute) => string;
|
|
10
|
-
/**
|
|
11
|
-
* Configuration options for the breadcrumb system.
|
|
12
|
-
* Use `provideBreadcrumbConfig` to supply these options to your application.
|
|
13
|
-
*/
|
|
14
|
-
export type BreadcrumbConfig = {
|
|
15
|
-
/**
|
|
16
|
-
* Defines how breadcrumb labels are generated.
|
|
17
|
-
* - If set to `'manual'`, breadcrumbs will only be displayed if manually registered
|
|
18
|
-
* via `createBreadcrumb`. Automatic generation based on routes is disabled.
|
|
19
|
-
* - Alternatively provide a custom label generation function
|
|
20
|
-
* If left undefined, the system will automatically generate labels based on the route's title, data, or path.
|
|
21
|
-
* @see GenerateBreadcrumbFn
|
|
22
|
-
* @example
|
|
23
|
-
* ```typescript
|
|
24
|
-
* // For custom label generation:
|
|
25
|
-
* // const myCustomLabelGenerator = () => (leaf: ResolvedLeafRoute) => {
|
|
26
|
-
* // return leaf.route.data?.['customTitle'] || leaf.route.routeConfig?.path || 'Default';
|
|
27
|
-
* // };
|
|
28
|
-
* //
|
|
29
|
-
* // config: { generation: myCustomLabelGenerator }
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
generation?: 'manual' | GenerateBreadcrumbFn;
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* Provides configuration for the breadcrumb system.
|
|
36
|
-
* @param config - A partial `BreadcrumbConfig` object with the desired settings. *
|
|
37
|
-
* @see BreadcrumbConfig
|
|
38
|
-
* @example
|
|
39
|
-
* ```typescript
|
|
40
|
-
* // In your app.module.ts or a standalone component's providers:
|
|
41
|
-
* // import { provideBreadcrumbConfig } from './breadcrumb.config'; // Adjust path
|
|
42
|
-
* // import { ResolvedLeafRoute } from './breadcrumb.type'; // Adjust path
|
|
43
|
-
*
|
|
44
|
-
* // const customLabelStrategy: GenerateBreadcrumbFn = () => {
|
|
45
|
-
* // return (leaf: ResolvedLeafRoute): string => {
|
|
46
|
-
* // // Example: Prioritize a 'navTitle' data property
|
|
47
|
-
* // if (leaf.route.data?.['navTitle']) {
|
|
48
|
-
* // return leaf.route.data['navTitle'];
|
|
49
|
-
* // }
|
|
50
|
-
* // // Fallback to a default mechanism
|
|
51
|
-
* // return leaf.route.title || leaf.segment.resolved || 'Unnamed';
|
|
52
|
-
* // };
|
|
53
|
-
* // };
|
|
54
|
-
*
|
|
55
|
-
* export const appConfig = [
|
|
56
|
-
* // ...rest
|
|
57
|
-
* provideBreadcrumbConfig({
|
|
58
|
-
* generation: customLabelStrategy, // or 'manual' to disable auto-generation
|
|
59
|
-
* }),
|
|
60
|
-
* ]
|
|
61
|
-
* ```
|
|
62
|
-
*/
|
|
63
|
-
export declare function provideBreadcrumbConfig(config: Partial<BreadcrumbConfig>): {
|
|
64
|
-
provide: InjectionToken<BreadcrumbConfig>;
|
|
65
|
-
useValue: {
|
|
66
|
-
generation?: ("manual" | GenerateBreadcrumbFn) | undefined;
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
|
-
/**
|
|
70
|
-
* @internal
|
|
71
|
-
*/
|
|
72
|
-
export declare function injectBreadcrumbConfig(): BreadcrumbConfig;
|
|
73
|
-
export {};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { type ResolveFn } from '@angular/router';
|
|
2
|
-
/**
|
|
3
|
-
* Options for defining a breadcrumb.
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
type CreateBreadcrumbOptions = {
|
|
7
|
-
/**
|
|
8
|
-
* The visible text for the breadcrumb.
|
|
9
|
-
* Can be a static string or a function for dynamic labels.
|
|
10
|
-
*/
|
|
11
|
-
label: string | (() => string);
|
|
12
|
-
/**
|
|
13
|
-
* An accessible label for the breadcrumb item.
|
|
14
|
-
* Defaults to the value of `label` if not provided.
|
|
15
|
-
* Can be a static string or a function returning a string for dynamic ARIA labels.
|
|
16
|
-
*/
|
|
17
|
-
ariaLabel?: string | (() => string);
|
|
18
|
-
/**
|
|
19
|
-
* If `true`, the route resolver will wait until the `label` signal has a value before `resolving`
|
|
20
|
-
*/
|
|
21
|
-
awaitValue?: boolean;
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Creates and registers a breadcrumb for a specific route.
|
|
25
|
-
* This function is designed to be used as an Angular Route `ResolveFn`.
|
|
26
|
-
* It handles the registration of the breadcrumb with the `BreadcrumbStore`
|
|
27
|
-
* and ensures automatic deregistration when the route is destroyed.
|
|
28
|
-
*
|
|
29
|
-
* @param factory A function that returns a `CreateBreadcrumbOptions` object.
|
|
30
|
-
* @see CreateBreadcrumbOptions
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```typescript
|
|
34
|
-
* export const appRoutes: Routes = [
|
|
35
|
-
* {
|
|
36
|
-
* path: 'home',
|
|
37
|
-
* component: HomeComponent,
|
|
38
|
-
* resolve: {
|
|
39
|
-
* breadcrumb: createBreadcrumb(() => ({
|
|
40
|
-
* label: 'Home',
|
|
41
|
-
* });
|
|
42
|
-
* },
|
|
43
|
-
* path: 'users/:userId',
|
|
44
|
-
* component: UserProfileComponent,
|
|
45
|
-
* resolve: {
|
|
46
|
-
* breadcrumb: createBreadcrumb(() => {
|
|
47
|
-
* const userStore = inject(UserStore);
|
|
48
|
-
* return {
|
|
49
|
-
* label: () => userStore.user().name ?? 'Loading...
|
|
50
|
-
* };
|
|
51
|
-
* })
|
|
52
|
-
* },
|
|
53
|
-
* }
|
|
54
|
-
* ];
|
|
55
|
-
* ```
|
|
56
|
-
*/
|
|
57
|
-
export declare function createBreadcrumb(factory: () => CreateBreadcrumbOptions): ResolveFn<void>;
|
|
58
|
-
export {};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Signal } from '@angular/core';
|
|
2
|
-
import { Breadcrumb, InternalBreadcrumb } from './breadcrumb.type';
|
|
3
|
-
import * as i0 from "@angular/core";
|
|
4
|
-
export declare class BreadcrumbStore {
|
|
5
|
-
private readonly map;
|
|
6
|
-
private readonly isManual;
|
|
7
|
-
private readonly autoGenerateLabelFn;
|
|
8
|
-
private readonly leafRoutes;
|
|
9
|
-
private readonly all;
|
|
10
|
-
private readonly crumbs;
|
|
11
|
-
readonly unwrapped: Signal<Breadcrumb[]>;
|
|
12
|
-
register(breadcrumb: InternalBreadcrumb): void;
|
|
13
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<BreadcrumbStore, never>;
|
|
14
|
-
static ɵprov: i0.ɵɵInjectableDeclaration<BreadcrumbStore>;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Injects and provides access to a reactive list of breadcrumbs.
|
|
18
|
-
*
|
|
19
|
-
* The breadcrumbs are ordered and reflect the current active navigation path.
|
|
20
|
-
* @see Breadcrumb
|
|
21
|
-
* @returns `Signal<Breadcrumb[]>`
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```typescript
|
|
25
|
-
* @Component({
|
|
26
|
-
* selector: 'app-breadcrumbs',
|
|
27
|
-
* template: `
|
|
28
|
-
* <nav aria-label="breadcrumb">
|
|
29
|
-
* <ol>
|
|
30
|
-
* @for (crumb of breadcrumbs(); track crumb.id) {
|
|
31
|
-
* <li>
|
|
32
|
-
* <a [href]="crumb.link()" [attr.aria-label]="crumb.ariaLabel()">{{ crumb.label() }}</a>
|
|
33
|
-
* </li>
|
|
34
|
-
* }
|
|
35
|
-
* </ol>
|
|
36
|
-
* </nav>
|
|
37
|
-
* `
|
|
38
|
-
* })
|
|
39
|
-
* export class MyBreadcrumbsComponent {
|
|
40
|
-
* breadcrumbs = injectBreadcrumbs();
|
|
41
|
-
* }
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
export declare function injectBreadcrumbs(): Signal<Breadcrumb[]>;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { type Signal } from '@angular/core';
|
|
2
|
-
/**
|
|
3
|
-
* Represents a single breadcrumb item within the navigation path.
|
|
4
|
-
* All dynamic properties are represented as Angular Signals to enable reactivity.
|
|
5
|
-
*/
|
|
6
|
-
export type Breadcrumb = {
|
|
7
|
-
/**
|
|
8
|
-
* A unique identifier for the breadcrumb item. Generally the unresolved path for example `/posts/:id`.
|
|
9
|
-
* Useful for `@for` tracking in templates.
|
|
10
|
-
*/
|
|
11
|
-
id: string;
|
|
12
|
-
/**
|
|
13
|
-
* The visible text for the breadcrumb item.
|
|
14
|
-
* Updated reactively as the url/link based on
|
|
15
|
-
* either a provided definition, or the current route.
|
|
16
|
-
*/
|
|
17
|
-
label: Signal<string>;
|
|
18
|
-
/**
|
|
19
|
-
* An accessible label for the breadcrumb item.
|
|
20
|
-
* Defaults to the same value as `label` if not provided.
|
|
21
|
-
*/
|
|
22
|
-
ariaLabel: Signal<string>;
|
|
23
|
-
/**
|
|
24
|
-
* The URL link for the breadcrumb item.
|
|
25
|
-
* Updates as the route changes.
|
|
26
|
-
*/
|
|
27
|
-
link: Signal<string>;
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* @internal
|
|
31
|
-
*/
|
|
32
|
-
declare const INTERNAL_BREADCRUMB_SYMBOL: unique symbol;
|
|
33
|
-
/**
|
|
34
|
-
* @internal
|
|
35
|
-
*/
|
|
36
|
-
export type InternalBreadcrumb = Breadcrumb & {
|
|
37
|
-
[INTERNAL_BREADCRUMB_SYMBOL]: {
|
|
38
|
-
active: Signal<boolean>;
|
|
39
|
-
registered: boolean;
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* @internal
|
|
44
|
-
*/
|
|
45
|
-
export declare function getBreadcrumbInternals(breadcrumb: InternalBreadcrumb): {
|
|
46
|
-
active: Signal<boolean>;
|
|
47
|
-
registered: boolean;
|
|
48
|
-
};
|
|
49
|
-
/**
|
|
50
|
-
* @internal
|
|
51
|
-
*/
|
|
52
|
-
export declare function createInternalBreadcrumb(bc: Breadcrumb, active: Signal<boolean>, registered?: boolean): InternalBreadcrumb;
|
|
53
|
-
/**
|
|
54
|
-
* @internal
|
|
55
|
-
*/
|
|
56
|
-
export declare function isInternalBreadcrumb(breadcrumb: Breadcrumb | InternalBreadcrumb): breadcrumb is InternalBreadcrumb;
|
|
57
|
-
export {};
|