@mmstack/router-core 19.2.0 → 19.2.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @mmstack/router-core
2
2
 
3
- Core utilities and Signal-based primitives for enhancing development with `@angular/router`. This library provides helpers for common routing tasks and reactive integration with router state.
3
+ Core utilities and Signal-based primitives for enhancing development with `@angular/router`. This library provides helpers for common routing tasks, reactive integration with router state, and intelligent module preloading.
4
4
 
5
5
  Part of the `@mmstack` ecosystem, designed to complement [@mmstack/primitives](https://www.npmjs.com/package/@mmstack/primitives).
6
6
 
@@ -77,3 +77,72 @@ export class HeaderComponent {
77
77
  protected readonly currentUrl = url();
78
78
  }
79
79
  ```
80
+
81
+ ## Preloading Utilities
82
+
83
+ ---
84
+
85
+ Enhance your application's performance by preloading Angular modules. This library provides a flexible directive and a smart preloading strategy to load modules just before they are needed.
86
+
87
+ ### PreloadStrategy
88
+
89
+ This is a custom Angular PreloadingStrategy that works in tandem with the mmstack `LinkDirective` (via an internal `PreloadService`) to intelligently preload lazy-loaded modules.
90
+
91
+ **Features**:
92
+
93
+ - Listens for preload requests triggered by the `LinkDirective`.
94
+ - Uses advanced path matching to identify the correct route to preload, even with route parameters and matrix parameters.
95
+ - Avoids preloading if the connection is slow (e.g., '2g' effective type) or if the user has data-saving enabled in their browser.
96
+ - Respects a `data: { preload: false }` flag in route configurations to explicitly disable preloading for specific routes.
97
+ - Prevents redundant preloading attempts for the same route path.
98
+
99
+ To enable this preloading strategy, you need to provide it in your application's main routing configuration.
100
+
101
+ ```typescript
102
+ import { PreloadStrategy } from '@mmstack/router-core';
103
+ import { ApplicationConfig } from '@angular/core';
104
+ import { provideRouter, withPreloading } from '@angular/router';
105
+ import { routes } from './app.routes';
106
+
107
+ export const appConfig: ApplicationConfig = {
108
+ providers: [
109
+ //...other providers
110
+ provideRouter(routes, withPreloading(PreloadStrategy)),
111
+ ],
112
+ };
113
+ ```
114
+
115
+ ### LinkDirective (mmLink)
116
+
117
+ The `LinkDirective` (used with the `mmLink` attribute) is an enhancement for Angular's standard `RouterLink` directive. It adds the capability to preload the JavaScript modules associated with the linked route, based on user interaction or visibility. Other than the added `preloadOn` input & `preloading` output it directly proxies `RouterLink`.
118
+
119
+ - `preloadOn`: `input<'hover' | 'visible' | null>()` [default: 'hover'] specifies when to preload, `null` disables preloading
120
+ - `preloading` - `output<void>()` fires when route is registered for preloading (before load)
121
+
122
+ To use it simply replace any exiting routerLinks that you would like to enable preloading on with the mmLink, you can keep all existing inputs the same. And add the mmstack `PreloadStrategy` in your configuration
123
+
124
+ ```typescript
125
+ import { LinkDirective } from '@mmstack/router-core';
126
+ import { RouterLink } from '@angular/router';
127
+
128
+ @Component({
129
+ selector: 'app-navigation',
130
+ standalone: true,
131
+ imports: [LinkDirective, RouterLink],
132
+ template: `
133
+ <nav>
134
+ <!-- preload on hover -->
135
+ <a [mmLink]="['/features']" preloadOn="hover">Features</a>
136
+ <!-- preload on visible -->
137
+ <a [mmLink]="['/pricing']" preloadOn="visible">Pricing</a>
138
+ <!-- no preload -->
139
+ <a [mmLink]="['/contact']" [preloadOn]="null">Contact</a>
140
+ <!-- preload on hover -->
141
+ <a [mmLink]="['/about']">About</a>
142
+ <!-- no preload, or just use [preloadOn]="null" -->
143
+ <a [routerLink]="['/terms']">Terms & Conditions</a>
144
+ </nav>
145
+ `,
146
+ })
147
+ export class NavigationComponent {}
148
+ ```
@@ -1,101 +1,33 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, input, booleanAttribute, untracked, isDevMode, ElementRef, DestroyRef, Directive, computed, isSignal } from '@angular/core';
2
+ import { Injectable, inject, input, booleanAttribute, output, computed, untracked, effect, Directive, isSignal } from '@angular/core';
3
3
  import * as i1 from '@angular/router';
4
- import { Router, PreloadingStrategy, NoPreloading, PreloadAllModules, RouterPreloader, RouterLink, RouterLinkWithHref, ActivatedRoute, EventType } from '@angular/router';
5
- import { EMPTY } from 'rxjs';
4
+ import { RouterLink, RouterLinkWithHref, Router, UrlTree, PRIMARY_OUTLET, ActivatedRoute, EventType } from '@angular/router';
5
+ import { elementVisibility, toWritable } from '@mmstack/primitives';
6
+ import { Subject, EMPTY, filter, take, switchMap, finalize } from 'rxjs';
6
7
  import { toSignal } from '@angular/core/rxjs-interop';
7
- import { toWritable } from '@mmstack/primitives';
8
- import { filter, map } from 'rxjs/operators';
8
+ import { filter as filter$1, map } from 'rxjs/operators';
9
9
 
10
- function flattenRoutes(routes) {
11
- return routes;
12
- }
13
-
14
- function hasSlowConnection() {
15
- if (globalThis.window &&
16
- 'navigator' in globalThis.window &&
17
- 'connection' in globalThis.window.navigator &&
18
- typeof globalThis.window.navigator.connection === 'object' &&
19
- !!globalThis.window.navigator.connection &&
20
- 'effectiveType' in globalThis.window.navigator.connection &&
21
- typeof globalThis.window.navigator.connection.effectiveType === 'string')
22
- return globalThis.window.navigator.connection.effectiveType.endsWith('2g');
23
- return false;
24
- }
25
- const HAS_SLOW_CONNECTION = hasSlowConnection();
26
- function noPreload(route) {
27
- return route.data && route.data['preload'] === false;
28
- }
29
- class PreloadLinkStrategy {
30
- routeMap = flattenRoutes(inject(Router).config);
31
- preload(route, _) {
32
- if (HAS_SLOW_CONNECTION || noPreload(route))
33
- return EMPTY;
34
- return EMPTY;
10
+ class PreloadService {
11
+ preloadOnDemand$ = new Subject();
12
+ preloadRequested$ = this.preloadOnDemand$.asObservable();
13
+ startPreload(routePath) {
14
+ this.preloadOnDemand$.next(routePath);
35
15
  }
36
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: PreloadLinkStrategy, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
37
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: PreloadLinkStrategy, providedIn: 'root' });
16
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: PreloadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
17
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: PreloadService, providedIn: 'root' });
38
18
  }
39
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: PreloadLinkStrategy, decorators: [{
19
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: PreloadService, decorators: [{
40
20
  type: Injectable,
41
- args: [{
42
- providedIn: 'root',
43
- }]
44
- }] });
45
- function observerSupported() {
46
- return typeof IntersectionObserver !== 'undefined';
47
- }
48
- function injectPreloader() {
49
- const strategy = inject(PreloadingStrategy, {
50
- optional: true,
51
- });
52
- if (!strategy ||
53
- strategy instanceof NoPreloading ||
54
- strategy instanceof PreloadAllModules)
55
- return null;
56
- return inject(RouterPreloader, {
57
- optional: true,
58
- });
59
- }
60
- class VisibleLinkHandler {
61
- map = new WeakMap();
62
- observer = observerSupported()
63
- ? new IntersectionObserver((entries) => {
64
- entries.forEach((entry) => {
65
- if (!entry.isIntersecting)
66
- return;
67
- this.map.get(entry.target)?.();
68
- this.observer?.unobserve(entry.target);
69
- });
70
- })
71
- : null;
72
- register(el, onVisible) {
73
- if (!this.observer)
74
- return () => {
75
- // noop
76
- };
77
- this.map.set(el, onVisible);
78
- this.observer.observe(el);
79
- return () => {
80
- this.map.delete(el);
81
- this.observer?.unobserve(el);
82
- };
83
- }
84
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: VisibleLinkHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
85
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: VisibleLinkHandler, providedIn: 'root' });
86
- }
87
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: VisibleLinkHandler, decorators: [{
88
- type: Injectable,
89
- args: [{
90
- providedIn: 'root',
91
- }]
21
+ args: [{ providedIn: 'root' }]
92
22
  }] });
23
+
93
24
  class LinkDirective {
94
25
  routerLink = inject(RouterLink, {
95
26
  self: true,
96
27
  optional: true,
97
28
  }) ?? inject(RouterLinkWithHref, { self: true, optional: true });
98
- preloader = injectPreloader();
29
+ svc = inject(PreloadService);
30
+ router = inject(Router);
99
31
  target = input();
100
32
  queryParams = input();
101
33
  fragment = input();
@@ -106,40 +38,56 @@ class LinkDirective {
106
38
  skipLocationChange = input(false, { transform: booleanAttribute });
107
39
  replaceUrl = input(false, { transform: booleanAttribute });
108
40
  mmLink = input.required();
109
- preloadOn = input(null);
41
+ preloadOn = input('hover');
42
+ preloading = output();
43
+ urlTree = computed(() => {
44
+ const link = this.mmLink();
45
+ const relativeTo = this.relativeTo();
46
+ const fragment = this.fragment();
47
+ const queryParams = this.queryParams();
48
+ const queryParamsHandling = this.queryParamsHandling();
49
+ const resolvedTree = this.routerLink?.urlTree;
50
+ if (resolvedTree)
51
+ return resolvedTree;
52
+ if (link instanceof UrlTree)
53
+ return link;
54
+ const arr = Array.isArray(link) ? link : [link];
55
+ return this.router.createUrlTree(arr, {
56
+ relativeTo,
57
+ queryParams,
58
+ fragment,
59
+ queryParamsHandling,
60
+ });
61
+ });
62
+ fullPath = computed(() => {
63
+ const urlTree = this.urlTree();
64
+ return this.router.serializeUrl(urlTree);
65
+ });
110
66
  onHover() {
111
67
  if (untracked(this.preloadOn) !== 'hover')
112
68
  return;
113
- if (!this.preloader) {
114
- if (isDevMode())
115
- console.error('Preloader not available, please configure a preloading strategy');
116
- return;
117
- }
118
- this.preloader?.preload().subscribe();
119
- }
120
- onVisible() {
121
- if (untracked(this.preloadOn) !== 'visible')
122
- return;
123
- if (!this.preloader) {
124
- if (isDevMode())
125
- console.error('Preloader not available, please configure a preloading strategy');
126
- return;
127
- }
69
+ this.requestPreload();
128
70
  }
129
71
  constructor() {
130
- const el = inject(ElementRef, {
131
- self: true,
72
+ const intersection = elementVisibility();
73
+ effect(() => {
74
+ if (this.preloadOn() !== 'visible')
75
+ return;
76
+ if (intersection.visible())
77
+ this.requestPreload();
132
78
  });
133
- const unsub = inject(VisibleLinkHandler).register(el.nativeElement, (() => {
134
- this.onVisible();
135
- }).bind(this));
136
- inject(DestroyRef).onDestroy(() => unsub());
79
+ }
80
+ requestPreload() {
81
+ if (!this.routerLink || !untracked(this.fullPath))
82
+ return;
83
+ this.svc.startPreload(untracked(this.fullPath));
84
+ this.preloading.emit();
137
85
  }
138
86
  onClick(button, ctrlKey, shiftKey, altKey, metaKey) {
139
87
  return this.routerLink?.onClick(button, ctrlKey, shiftKey, altKey, metaKey);
140
88
  }
141
89
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: LinkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
142
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.3", type: LinkDirective, isStandalone: true, selector: "[mmLink]", inputs: { target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, queryParams: { classPropertyName: "queryParams", publicName: "queryParams", isSignal: true, isRequired: false, transformFunction: null }, fragment: { classPropertyName: "fragment", publicName: "fragment", isSignal: true, isRequired: false, transformFunction: null }, queryParamsHandling: { classPropertyName: "queryParamsHandling", publicName: "queryParamsHandling", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, info: { classPropertyName: "info", publicName: "info", isSignal: true, isRequired: false, transformFunction: null }, relativeTo: { classPropertyName: "relativeTo", publicName: "relativeTo", isSignal: true, isRequired: false, transformFunction: null }, skipLocationChange: { classPropertyName: "skipLocationChange", publicName: "skipLocationChange", isSignal: true, isRequired: false, transformFunction: null }, replaceUrl: { classPropertyName: "replaceUrl", publicName: "replaceUrl", isSignal: true, isRequired: false, transformFunction: null }, mmLink: { classPropertyName: "mmLink", publicName: "mmLink", isSignal: true, isRequired: true, transformFunction: null }, preloadOn: { classPropertyName: "preloadOn", publicName: "preloadOn", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onHover()" } }, exportAs: ["mmLink"], hostDirectives: [{ directive: i1.RouterLink, inputs: ["routerLink", "mmLink", "target", "target", "queryParams", "queryParams", "fragment", "fragment", "queryParamsHandling", "queryParamsHandling", "state", "state", "relativeTo", "relativeTo", "skipLocationChange", "skipLocationChange", "replaceUrl", "replaceUrl"] }], ngImport: i0 });
90
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.3", type: LinkDirective, isStandalone: true, selector: "[mmLink]", inputs: { target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, queryParams: { classPropertyName: "queryParams", publicName: "queryParams", isSignal: true, isRequired: false, transformFunction: null }, fragment: { classPropertyName: "fragment", publicName: "fragment", isSignal: true, isRequired: false, transformFunction: null }, queryParamsHandling: { classPropertyName: "queryParamsHandling", publicName: "queryParamsHandling", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, info: { classPropertyName: "info", publicName: "info", isSignal: true, isRequired: false, transformFunction: null }, relativeTo: { classPropertyName: "relativeTo", publicName: "relativeTo", isSignal: true, isRequired: false, transformFunction: null }, skipLocationChange: { classPropertyName: "skipLocationChange", publicName: "skipLocationChange", isSignal: true, isRequired: false, transformFunction: null }, replaceUrl: { classPropertyName: "replaceUrl", publicName: "replaceUrl", isSignal: true, isRequired: false, transformFunction: null }, mmLink: { classPropertyName: "mmLink", publicName: "mmLink", isSignal: true, isRequired: true, transformFunction: null }, preloadOn: { classPropertyName: "preloadOn", publicName: "preloadOn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { preloading: "preloading" }, host: { listeners: { "mouseenter": "onHover()" } }, exportAs: ["mmLink"], hostDirectives: [{ directive: i1.RouterLink, inputs: ["routerLink", "mmLink", "target", "target", "queryParams", "queryParams", "fragment", "fragment", "queryParamsHandling", "queryParamsHandling", "state", "state", "relativeTo", "relativeTo", "skipLocationChange", "skipLocationChange", "replaceUrl", "replaceUrl"] }], ngImport: i0 });
143
91
  }
144
92
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: LinkDirective, decorators: [{
145
93
  type: Directive,
@@ -168,6 +116,228 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.3", ngImpor
168
116
  }]
169
117
  }], ctorParameters: () => [] });
170
118
 
119
+ function parsePathSegment(segmentString) {
120
+ const parts = segmentString.split(';');
121
+ const pathPart = parts[0];
122
+ const matrixParams = {};
123
+ for (let i = 1; i < parts.length; i++) {
124
+ const [key, value = 'true'] = parts[i].split('=');
125
+ if (key) {
126
+ matrixParams[key] = value;
127
+ }
128
+ }
129
+ return { pathPart, matrixParams };
130
+ }
131
+ function createBasePredicate(path) {
132
+ const partPredicates = path
133
+ .split('/')
134
+ .filter((part) => !!part.trim())
135
+ .map((configSegmentString) => {
136
+ const { pathPart: configPathPart, matrixParams: configMatrixParams } = parsePathSegment(configSegmentString);
137
+ let singlePathPartPredicate;
138
+ if (configPathPart.startsWith(':')) {
139
+ singlePathPartPredicate = () => true;
140
+ }
141
+ else {
142
+ singlePathPartPredicate = (linkSegmentPathPart) => linkSegmentPathPart === configPathPart;
143
+ }
144
+ const configSegmentHasMatrixParams = Object.keys(configMatrixParams).length > 0;
145
+ return (linkSegmentString) => {
146
+ const { pathPart: linkPathPart, matrixParams: linkMatrixParams } = parsePathSegment(linkSegmentString);
147
+ if (!singlePathPartPredicate(linkPathPart)) {
148
+ return false;
149
+ }
150
+ if (!configSegmentHasMatrixParams) {
151
+ return true;
152
+ }
153
+ return Object.entries(configMatrixParams).every(([key, value]) => linkMatrixParams.hasOwnProperty(key) &&
154
+ linkMatrixParams[key] === value);
155
+ };
156
+ });
157
+ return (path) => {
158
+ const linkPathOnly = path.split(/[?#]/).at(0) ?? '';
159
+ if (!linkPathOnly && partPredicates.length > 0)
160
+ return false;
161
+ if (!linkPathOnly && partPredicates.length === 0)
162
+ return true;
163
+ const parts = linkPathOnly.split('/').filter((part) => !!part.trim());
164
+ if (parts.length < partPredicates.length)
165
+ return false;
166
+ return parts.every((seg, idx) => {
167
+ const pred = partPredicates.at(idx);
168
+ if (!pred)
169
+ return true;
170
+ return pred(seg);
171
+ });
172
+ };
173
+ }
174
+ function singleSegmentMatches(configSegment, linkSegment) {
175
+ if (configSegment.pathPart.startsWith(':')) {
176
+ }
177
+ else if (configSegment.pathPart !== linkSegment.pathPart) {
178
+ return false;
179
+ }
180
+ const configMatrix = configSegment.matrixParams;
181
+ const linkMatrix = linkSegment.matrixParams;
182
+ for (const key in configMatrix) {
183
+ if (!linkMatrix.hasOwnProperty(key) ||
184
+ linkMatrix[key] !== configMatrix[key]) {
185
+ return false;
186
+ }
187
+ }
188
+ return true;
189
+ }
190
+ function matchSegmentsRecursive(configSegments, linkSegments, configIdx, linkIdx) {
191
+ if (configIdx === configSegments.length) {
192
+ return linkIdx === linkSegments.length;
193
+ }
194
+ if (linkIdx === linkSegments.length) {
195
+ for (let i = configIdx; i < configSegments.length; i++) {
196
+ if (configSegments[i].pathPart !== '**') {
197
+ return false;
198
+ }
199
+ }
200
+ return true;
201
+ }
202
+ const currentConfigSegment = configSegments[configIdx];
203
+ if (currentConfigSegment.pathPart === '**') {
204
+ if (matchSegmentsRecursive(configSegments, linkSegments, configIdx + 1, linkIdx)) {
205
+ return true;
206
+ }
207
+ if (linkIdx < linkSegments.length) {
208
+ if (matchSegmentsRecursive(configSegments, linkSegments, configIdx, linkIdx + 1)) {
209
+ return true;
210
+ }
211
+ }
212
+ return false;
213
+ }
214
+ else {
215
+ if (linkIdx < linkSegments.length &&
216
+ singleSegmentMatches(currentConfigSegment, linkSegments[linkIdx])) {
217
+ return matchSegmentsRecursive(configSegments, linkSegments, configIdx + 1, linkIdx + 1);
218
+ }
219
+ return false;
220
+ }
221
+ }
222
+ function createWildcardPredicate(path) {
223
+ const configSegments = path
224
+ .split('/')
225
+ .filter((p) => !!p.trim())
226
+ .map((segment) => parsePathSegment(segment));
227
+ return (linkPath) => {
228
+ const linkPathOnly = linkPath.split(/[?#]/).at(0) ?? '';
229
+ const linkSegments = linkPathOnly
230
+ .split('/')
231
+ .filter((p) => !!p.trim())
232
+ .map((segment) => parsePathSegment(segment));
233
+ return matchSegmentsRecursive(configSegments, linkSegments, 0, 0);
234
+ };
235
+ }
236
+ function createRoutePredicate(path) {
237
+ return path.includes('**')
238
+ ? createWildcardPredicate(path)
239
+ : createBasePredicate(path);
240
+ }
241
+
242
+ // The following functions are adapted from ngx-quicklink,
243
+ // (https://github.com/mgechev/ngx-quicklink)
244
+ // Copyright (c) Minko Gechev and contributors, licensed under the MIT License.
245
+ function isPrimaryRoute(route) {
246
+ return route.outlet === PRIMARY_OUTLET || !route.outlet;
247
+ }
248
+ const findPath = (config, route) => {
249
+ const configQueue = config.slice();
250
+ const parent = new Map();
251
+ const visited = new Set();
252
+ while (configQueue.length) {
253
+ const el = configQueue.shift();
254
+ if (!el) {
255
+ continue;
256
+ }
257
+ visited.add(el);
258
+ if (el === route) {
259
+ break;
260
+ }
261
+ (el.children || []).forEach((childRoute) => {
262
+ if (!visited.has(childRoute)) {
263
+ parent.set(childRoute, el);
264
+ configQueue.push(childRoute);
265
+ }
266
+ });
267
+ const lazyRoutes = el._loadedRoutes || [];
268
+ if (Array.isArray(lazyRoutes)) {
269
+ lazyRoutes.forEach((lazyRoute) => {
270
+ if (lazyRoute && !visited.has(lazyRoute)) {
271
+ parent.set(lazyRoute, el);
272
+ configQueue.push(lazyRoute);
273
+ }
274
+ });
275
+ }
276
+ }
277
+ let path = '';
278
+ let currentRoute = route;
279
+ while (currentRoute) {
280
+ const currentPath = currentRoute.path || '';
281
+ if (isPrimaryRoute(currentRoute)) {
282
+ path = `/${currentPath}${path}`;
283
+ }
284
+ else {
285
+ path = `/(${currentRoute.outlet}:${currentPath})${path}`;
286
+ }
287
+ currentRoute = parent.get(currentRoute);
288
+ }
289
+ let normalizedPath = path.replaceAll(/\/+/g, '/');
290
+ if (normalizedPath !== '/' && normalizedPath.endsWith('/')) {
291
+ normalizedPath = normalizedPath.slice(0, -1);
292
+ }
293
+ return normalizedPath;
294
+ };
295
+
296
+ function hasSlowConnection() {
297
+ if (globalThis.window &&
298
+ 'navigator' in globalThis.window &&
299
+ 'connection' in globalThis.window.navigator &&
300
+ typeof globalThis.window.navigator.connection === 'object' &&
301
+ !!globalThis.window.navigator.connection) {
302
+ const is2g = 'effectiveType' in globalThis.window.navigator.connection &&
303
+ typeof globalThis.window.navigator.connection.effectiveType ===
304
+ 'string' &&
305
+ globalThis.window.navigator.connection.effectiveType.endsWith('2g');
306
+ if (is2g)
307
+ return true;
308
+ if ('saveData' in globalThis.window.navigator.connection &&
309
+ typeof globalThis.window.navigator.connection.saveData === 'boolean' &&
310
+ globalThis.window.navigator.connection.saveData)
311
+ return true;
312
+ }
313
+ return false;
314
+ }
315
+ function noPreload(route) {
316
+ return route.data && route.data['preload'] === false;
317
+ }
318
+ class PreloadStrategy {
319
+ loading = new Set();
320
+ router = inject(Router);
321
+ svc = inject(PreloadService);
322
+ preload(route, load) {
323
+ if (noPreload(route) || hasSlowConnection())
324
+ return EMPTY;
325
+ const fp = findPath(this.router.config, route);
326
+ if (this.loading.has(fp))
327
+ return EMPTY;
328
+ const predicate = createRoutePredicate(fp);
329
+ return this.svc.preloadRequested$.pipe(filter((path) => path === fp || predicate(path)), take(1), switchMap(() => load()), finalize(() => this.loading.delete(fp)));
330
+ }
331
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: PreloadStrategy, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
332
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: PreloadStrategy, providedIn: 'root' });
333
+ }
334
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: PreloadStrategy, decorators: [{
335
+ type: Injectable,
336
+ args: [{
337
+ providedIn: 'root',
338
+ }]
339
+ }] });
340
+
171
341
  /**
172
342
  * Creates a WritableSignal that synchronizes with a specific URL query parameter,
173
343
  * enabling two-way binding between the signal's state and the URL.
@@ -318,7 +488,7 @@ function isNavigationEnd(e) {
318
488
  */
319
489
  function url() {
320
490
  const router = inject(Router);
321
- return toSignal(router.events.pipe(filter(isNavigationEnd), map((e) => e.urlAfterRedirects)), {
491
+ return toSignal(router.events.pipe(filter$1(isNavigationEnd), map((e) => e.urlAfterRedirects)), {
322
492
  initialValue: router.url,
323
493
  });
324
494
  }
@@ -327,5 +497,5 @@ function url() {
327
497
  * Generated bundle index. Do not edit.
328
498
  */
329
499
 
330
- export { LinkDirective, PreloadLinkStrategy, queryParam, url };
500
+ export { LinkDirective, PreloadStrategy, queryParam, url };
331
501
  //# sourceMappingURL=mmstack-router-core.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mmstack-router-core.mjs","sources":["../../../../../packages/router/core/src/lib/preloading/flat-routes.ts","../../../../../packages/router/core/src/lib/preloading/link.directive.ts","../../../../../packages/router/core/src/lib/query-param.ts","../../../../../packages/router/core/src/lib/url.ts","../../../../../packages/router/core/src/mmstack-router-core.ts"],"sourcesContent":["import { Route } from '@angular/router';\r\n\r\nexport function flattenRoutes(routes: Route[]): Route[] {\r\n return routes;\r\n}\r\n","import {\r\n booleanAttribute,\r\n DestroyRef,\r\n Directive,\r\n ElementRef,\r\n inject,\r\n Injectable,\r\n input,\r\n isDevMode,\r\n untracked,\r\n} from '@angular/core';\r\nimport {\r\n NoPreloading,\r\n PreloadAllModules,\r\n PreloadingStrategy,\r\n Route,\r\n Router,\r\n RouterLink,\r\n RouterLinkWithHref,\r\n RouterPreloader,\r\n type ActivatedRoute,\r\n type Params,\r\n type UrlTree,\r\n} from '@angular/router';\r\nimport { EMPTY, Observable } from 'rxjs';\r\nimport { flattenRoutes } from './flat-routes';\r\n\r\nexport function hasSlowConnection() {\r\n if (\r\n globalThis.window &&\r\n 'navigator' in globalThis.window &&\r\n 'connection' in globalThis.window.navigator &&\r\n typeof globalThis.window.navigator.connection === 'object' &&\r\n !!globalThis.window.navigator.connection &&\r\n 'effectiveType' in globalThis.window.navigator.connection &&\r\n typeof globalThis.window.navigator.connection.effectiveType === 'string'\r\n )\r\n return globalThis.window.navigator.connection.effectiveType.endsWith('2g');\r\n\r\n return false;\r\n}\r\n\r\nconst HAS_SLOW_CONNECTION = hasSlowConnection();\r\n\r\nfunction noPreload(route: Route) {\r\n return route.data && route.data['preload'] === false;\r\n}\r\n\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\nexport class PreloadLinkStrategy implements PreloadingStrategy {\r\n private readonly routeMap = flattenRoutes(inject(Router).config);\r\n preload(route: Route, _: () => Observable<any>): Observable<any> {\r\n if (HAS_SLOW_CONNECTION || noPreload(route)) return EMPTY;\r\n return EMPTY;\r\n }\r\n}\r\n\r\nfunction observerSupported() {\r\n return typeof IntersectionObserver !== 'undefined';\r\n}\r\n\r\nfunction injectPreloader(): RouterPreloader | null {\r\n const strategy = inject(PreloadingStrategy, {\r\n optional: true,\r\n });\r\n\r\n if (\r\n !strategy ||\r\n strategy instanceof NoPreloading ||\r\n strategy instanceof PreloadAllModules\r\n )\r\n return null;\r\n\r\n return inject(RouterPreloader, {\r\n optional: true,\r\n });\r\n}\r\n\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\nexport class VisibleLinkHandler {\r\n private readonly map = new WeakMap<Element, () => void>();\r\n private readonly observer: IntersectionObserver | null = observerSupported()\r\n ? new IntersectionObserver((entries) => {\r\n entries.forEach((entry) => {\r\n if (!entry.isIntersecting) return;\r\n this.map.get(entry.target)?.();\r\n this.observer?.unobserve(entry.target);\r\n });\r\n })\r\n : null;\r\n\r\n register(el: Element, onVisible: () => void) {\r\n if (!this.observer)\r\n return () => {\r\n // noop\r\n };\r\n\r\n this.map.set(el, onVisible);\r\n this.observer.observe(el);\r\n\r\n return () => {\r\n this.map.delete(el);\r\n this.observer?.unobserve(el);\r\n };\r\n }\r\n}\r\n\r\n@Directive({\r\n selector: '[mmLink]',\r\n exportAs: 'mmLink',\r\n host: {\r\n '(mouseenter)': 'onHover()',\r\n },\r\n hostDirectives: [\r\n {\r\n directive: RouterLink,\r\n inputs: [\r\n 'routerLink: mmLink',\r\n 'target',\r\n 'queryParams',\r\n 'fragment',\r\n 'queryParamsHandling',\r\n 'state',\r\n 'relativeTo',\r\n 'skipLocationChange',\r\n 'replaceUrl',\r\n ],\r\n },\r\n ],\r\n})\r\nexport class LinkDirective {\r\n private readonly routerLink =\r\n inject(RouterLink, {\r\n self: true,\r\n optional: true,\r\n }) ?? inject(RouterLinkWithHref, { self: true, optional: true });\r\n\r\n private readonly preloader = injectPreloader();\r\n readonly target = input<string>();\r\n readonly queryParams = input<Params>();\r\n readonly fragment = input<string>();\r\n readonly queryParamsHandling = input<'merge' | 'preserve' | ''>();\r\n readonly state = input<Record<string, any>>();\r\n readonly info = input<unknown>();\r\n readonly relativeTo = input<ActivatedRoute>();\r\n readonly skipLocationChange = input(false, { transform: booleanAttribute });\r\n readonly replaceUrl = input(false, { transform: booleanAttribute });\r\n readonly mmLink = input.required<string | any[] | UrlTree>();\r\n readonly preloadOn = input<null | 'hover' | 'visible'>(null);\r\n\r\n protected onHover() {\r\n if (untracked(this.preloadOn) !== 'hover') return;\r\n if (!this.preloader) {\r\n if (isDevMode())\r\n console.error(\r\n 'Preloader not available, please configure a preloading strategy',\r\n );\r\n return;\r\n }\r\n\r\n this.preloader?.preload().subscribe();\r\n }\r\n\r\n protected onVisible() {\r\n if (untracked(this.preloadOn) !== 'visible') return;\r\n if (!this.preloader) {\r\n if (isDevMode())\r\n console.error(\r\n 'Preloader not available, please configure a preloading strategy',\r\n );\r\n return;\r\n }\r\n }\r\n\r\n constructor() {\r\n const el = inject<ElementRef<HTMLElement>>(ElementRef, {\r\n self: true,\r\n });\r\n const unsub = inject(VisibleLinkHandler).register(\r\n el.nativeElement,\r\n (() => {\r\n this.onVisible();\r\n }).bind(this),\r\n );\r\n\r\n inject(DestroyRef).onDestroy(() => unsub());\r\n }\r\n\r\n onClick(\r\n button: number,\r\n ctrlKey: boolean,\r\n shiftKey: boolean,\r\n altKey: boolean,\r\n metaKey: boolean,\r\n ) {\r\n return this.routerLink?.onClick(button, ctrlKey, shiftKey, altKey, metaKey);\r\n }\r\n}\r\n","import {\r\n computed,\r\n inject,\r\n isSignal,\r\n untracked,\r\n type WritableSignal,\r\n} from '@angular/core';\r\nimport { toSignal } from '@angular/core/rxjs-interop';\r\nimport { ActivatedRoute, Router } from '@angular/router';\r\nimport { toWritable } from '@mmstack/primitives';\r\n\r\n/**\r\n * Creates a WritableSignal that synchronizes with a specific URL query parameter,\r\n * enabling two-way binding between the signal's state and the URL.\r\n *\r\n * Reading the signal provides the current value of the query parameter (or null if absent).\r\n * Setting the signal updates the URL query parameter using `Router.navigate`, triggering\r\n * navigation and causing the signal to update reactively if the navigation is successful.\r\n *\r\n * @param key The key of the query parameter to synchronize with.\r\n * Can be a static string (e.g., `'search'`) or a function/signal returning a string\r\n * for dynamic keys (e.g., `() => this.userId() + '_filter'` or `computed(() => this.category() + '_sort')`).\r\n * The signal will reactively update if the key returned by the function/signal changes.\r\n * @returns {WritableSignal<string | null>} A signal representing the query parameter's value.\r\n * - Reading returns the current value string, or `null` if the parameter is absent in the URL.\r\n * - Setting the signal to a string updates the query parameter in the URL (e.g., `signal.set('value')` results in `?key=value`).\r\n * - Setting the signal to `null` removes the query parameter from the URL (e.g., `signal.set(null)` results in `?otherParam=...`).\r\n * - Automatically reflects changes if the query parameters update due to external navigation.\r\n * @remarks\r\n * - Requires Angular's `ActivatedRoute` and `Router` to be available in the injection context.\r\n * - Uses `Router.navigate` with `queryParamsHandling: 'merge'` to preserve other existing query parameters during updates.\r\n * - 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.\r\n * - 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.\r\n *\r\n * @example\r\n * ```ts\r\n * import { Component, computed, effect, signal } from '@angular/core';\r\n * import { queryParam } from '@mmstack/router-core'; // Adjust import path as needed\r\n * // import { FormsModule } from '@angular/forms'; // If using ngModel\r\n *\r\n * @Component({\r\n * selector: 'app-product-list',\r\n * standalone: true,\r\n * // imports: [FormsModule], // If using ngModel\r\n * template: `\r\n * <div>\r\n * Sort By:\r\n * <select [value]=\"sortSignal() ?? ''\" (change)=\"sortSignal.set($any($event.target).value || null)\">\r\n * <option value=\"\">Default</option>\r\n * <option value=\"price_asc\">Price Asc</option>\r\n * <option value=\"price_desc\">Price Desc</option>\r\n * <option value=\"name\">Name</option>\r\n * </select>\r\n * <button (click)=\"sortSignal.set(null)\" [disabled]=\"!sortSignal()\">Clear Sort</button>\r\n * </div>\r\n * <div>\r\n * Page:\r\n * <input type=\"number\" min=\"1\" [value]=\"pageSignal() ?? '1'\" #p (input)=\"setPage(p.value)\"/>\r\n * </div>\r\n * * `\r\n * })\r\n * export class ProductListComponent {\r\n * // Two-way bind the 'sort' query parameter (?sort=...)\r\n * // Defaults to null if param is missing\r\n * sortSignal = queryParam('sort');\r\n *\r\n * // Example with a different type (needs serialization or separate logic)\r\n * // For simplicity, we treat page as string | null here\r\n * pageSignal = queryParam('page');\r\n *\r\n * constructor() {\r\n * effect(() => {\r\n * const currentSort = this.sortSignal();\r\n * const currentPage = this.pageSignal(); // Read as string | null\r\n * console.log('Sort/Page changed, reloading products for:', { sort: currentSort, page: currentPage });\r\n * // --- Fetch data based on currentSort and currentPage ---\r\n * });\r\n * }\r\n *\r\n * setPage(value: string): void {\r\n * const pageNum = parseInt(value, 10);\r\n * // Set to null if page is 1 (to remove param), otherwise set string value\r\n * this.pageSignal.set(isNaN(pageNum) || pageNum <= 1 ? null : pageNum.toString());\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport function queryParam(\r\n key: string | (() => string),\r\n): WritableSignal<string | null> {\r\n const route = inject(ActivatedRoute);\r\n const router = inject(Router);\r\n\r\n const keySignal =\r\n typeof key === 'string'\r\n ? computed(() => key)\r\n : isSignal(key)\r\n ? key\r\n : computed(key);\r\n\r\n const queryParamMap = toSignal(route.queryParamMap, {\r\n initialValue: route.snapshot.queryParamMap,\r\n });\r\n\r\n const queryParams = toSignal(route.queryParams, {\r\n initialValue: route.snapshot.queryParams,\r\n });\r\n\r\n const queryParam = computed(() => queryParamMap().get(keySignal()));\r\n\r\n const set = (newValue: string | null) => {\r\n const next = {\r\n ...untracked(queryParams),\r\n };\r\n const key = untracked(keySignal);\r\n\r\n if (newValue === null) {\r\n delete next[key];\r\n } else {\r\n next[key] = newValue;\r\n }\r\n\r\n router.navigate([], {\r\n relativeTo: route,\r\n queryParams: next,\r\n queryParamsHandling: 'merge',\r\n });\r\n };\r\n\r\n return toWritable(queryParam, set);\r\n}\r\n","import { inject, type Signal } from '@angular/core';\r\nimport { toSignal } from '@angular/core/rxjs-interop';\r\nimport {\r\n type Event,\r\n EventType,\r\n type NavigationEnd,\r\n Router,\r\n} from '@angular/router';\r\nimport { filter, map } from 'rxjs/operators';\r\n\r\n/**\r\n * Type guard to check if a Router Event is a NavigationEnd event.\r\n * @internal\r\n */\r\nfunction isNavigationEnd(e: Event): e is NavigationEnd {\r\n return 'type' in e && e.type === EventType.NavigationEnd;\r\n}\r\n\r\n/**\r\n * Creates a Signal that tracks the current router URL.\r\n *\r\n * The signal emits the URL string reflecting the router state *after* redirects\r\n * have completed for each successful navigation. It initializes with the router's\r\n * current URL state.\r\n *\r\n * @returns {Signal<string>} A Signal emitting the `urlAfterRedirects` upon successful navigation.\r\n *\r\n * @example\r\n * ```ts\r\n * import { Component, effect } from '@angular/core';\r\n * import { url } from '@mmstack/router-core'; // Adjust import path\r\n *\r\n * @Component({\r\n * selector: 'app-root',\r\n * template: `Current URL: {{ currentUrl() }}`\r\n * })\r\n * export class AppComponent {\r\n * currentUrl = url();\r\n *\r\n * constructor() {\r\n * effect(() => {\r\n * console.log('Navigation ended. New URL:', this.currentUrl());\r\n * // e.g., track page view with analytics\r\n * });\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport function url(): Signal<string> {\r\n const router = inject(Router);\r\n\r\n return toSignal(\r\n router.events.pipe(\r\n filter(isNavigationEnd),\r\n map((e) => e.urlAfterRedirects),\r\n ),\r\n {\r\n initialValue: router.url,\r\n },\r\n );\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;AAEM,SAAU,aAAa,CAAC,MAAe,EAAA;AAC3C,IAAA,OAAO,MAAM;AACf;;SCuBgB,iBAAiB,GAAA;IAC/B,IACE,UAAU,CAAC,MAAM;QACjB,WAAW,IAAI,UAAU,CAAC,MAAM;AAChC,QAAA,YAAY,IAAI,UAAU,CAAC,MAAM,CAAC,SAAS;QAC3C,OAAO,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,KAAK,QAAQ;AAC1D,QAAA,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU;AACxC,QAAA,eAAe,IAAI,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU;QACzD,OAAO,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,aAAa,KAAK,QAAQ;AAExE,QAAA,OAAO,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC;AAE5E,IAAA,OAAO,KAAK;AACd;AAEA,MAAM,mBAAmB,GAAG,iBAAiB,EAAE;AAE/C,SAAS,SAAS,CAAC,KAAY,EAAA;AAC7B,IAAA,OAAO,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,KAAK;AACtD;MAKa,mBAAmB,CAAA;IACb,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAChE,OAAO,CAAC,KAAY,EAAE,CAAwB,EAAA;AAC5C,QAAA,IAAI,mBAAmB,IAAI,SAAS,CAAC,KAAK,CAAC;AAAE,YAAA,OAAO,KAAK;AACzD,QAAA,OAAO,KAAK;;uGAJH,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAnB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,mBAAmB,cAFlB,MAAM,EAAA,CAAA;;2FAEP,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBAH/B,UAAU;AAAC,YAAA,IAAA,EAAA,CAAA;AACV,oBAAA,UAAU,EAAE,MAAM;AACnB,iBAAA;;AASD,SAAS,iBAAiB,GAAA;AACxB,IAAA,OAAO,OAAO,oBAAoB,KAAK,WAAW;AACpD;AAEA,SAAS,eAAe,GAAA;AACtB,IAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,kBAAkB,EAAE;AAC1C,QAAA,QAAQ,EAAE,IAAI;AACf,KAAA,CAAC;AAEF,IAAA,IACE,CAAC,QAAQ;AACT,QAAA,QAAQ,YAAY,YAAY;AAChC,QAAA,QAAQ,YAAY,iBAAiB;AAErC,QAAA,OAAO,IAAI;IAEb,OAAO,MAAM,CAAC,eAAe,EAAE;AAC7B,QAAA,QAAQ,EAAE,IAAI;AACf,KAAA,CAAC;AACJ;MAKa,kBAAkB,CAAA;AACZ,IAAA,GAAG,GAAG,IAAI,OAAO,EAAuB;IACxC,QAAQ,GAAgC,iBAAiB;AACxE,UAAE,IAAI,oBAAoB,CAAC,CAAC,OAAO,KAAI;AACnC,YAAA,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,KAAI;gBACxB,IAAI,CAAC,KAAK,CAAC,cAAc;oBAAE;gBAC3B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI;gBAC9B,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC;AACxC,aAAC,CAAC;AACJ,SAAC;UACD,IAAI;IAER,QAAQ,CAAC,EAAW,EAAE,SAAqB,EAAA;QACzC,IAAI,CAAC,IAAI,CAAC,QAAQ;AAChB,YAAA,OAAO,MAAK;;AAEZ,aAAC;QAEH,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC;AAC3B,QAAA,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;AAEzB,QAAA,OAAO,MAAK;AACV,YAAA,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;AACnB,YAAA,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC;AAC9B,SAAC;;uGAxBQ,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,kBAAkB,cAFjB,MAAM,EAAA,CAAA;;2FAEP,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAH9B,UAAU;AAAC,YAAA,IAAA,EAAA,CAAA;AACV,oBAAA,UAAU,EAAE,MAAM;AACnB,iBAAA;;MAoDY,aAAa,CAAA;AACP,IAAA,UAAU,GACzB,MAAM,CAAC,UAAU,EAAE;AACjB,QAAA,IAAI,EAAE,IAAI;AACV,QAAA,QAAQ,EAAE,IAAI;AACf,KAAA,CAAC,IAAI,MAAM,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAEjD,SAAS,GAAG,eAAe,EAAE;IACrC,MAAM,GAAG,KAAK,EAAU;IACxB,WAAW,GAAG,KAAK,EAAU;IAC7B,QAAQ,GAAG,KAAK,EAAU;IAC1B,mBAAmB,GAAG,KAAK,EAA6B;IACxD,KAAK,GAAG,KAAK,EAAuB;IACpC,IAAI,GAAG,KAAK,EAAW;IACvB,UAAU,GAAG,KAAK,EAAkB;IACpC,kBAAkB,GAAG,KAAK,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAClE,UAAU,GAAG,KAAK,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;AAC1D,IAAA,MAAM,GAAG,KAAK,CAAC,QAAQ,EAA4B;AACnD,IAAA,SAAS,GAAG,KAAK,CAA6B,IAAI,CAAC;IAElD,OAAO,GAAA;AACf,QAAA,IAAI,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,OAAO;YAAE;AAC3C,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;AACnB,YAAA,IAAI,SAAS,EAAE;AACb,gBAAA,OAAO,CAAC,KAAK,CACX,iEAAiE,CAClE;YACH;;QAGF,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,SAAS,EAAE;;IAG7B,SAAS,GAAA;AACjB,QAAA,IAAI,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,SAAS;YAAE;AAC7C,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;AACnB,YAAA,IAAI,SAAS,EAAE;AACb,gBAAA,OAAO,CAAC,KAAK,CACX,iEAAiE,CAClE;YACH;;;AAIJ,IAAA,WAAA,GAAA;AACE,QAAA,MAAM,EAAE,GAAG,MAAM,CAA0B,UAAU,EAAE;AACrD,YAAA,IAAI,EAAE,IAAI;AACX,SAAA,CAAC;AACF,QAAA,MAAM,KAAK,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAC,QAAQ,CAC/C,EAAE,CAAC,aAAa,EAChB,CAAC,MAAK;YACJ,IAAI,CAAC,SAAS,EAAE;AAClB,SAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CACd;AAED,QAAA,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,KAAK,EAAE,CAAC;;IAG7C,OAAO,CACL,MAAc,EACd,OAAgB,EAChB,QAAiB,EACjB,MAAe,EACf,OAAgB,EAAA;AAEhB,QAAA,OAAO,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC;;uGAjElE,aAAa,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAb,aAAa,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,mBAAA,EAAA,EAAA,iBAAA,EAAA,qBAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,kBAAA,EAAA,EAAA,iBAAA,EAAA,oBAAA,EAAA,UAAA,EAAA,oBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,YAAA,EAAA,WAAA,EAAA,EAAA,EAAA,QAAA,EAAA,CAAA,QAAA,CAAA,EAAA,cAAA,EAAA,CAAA,EAAA,SAAA,EAAA,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,CAAA,YAAA,EAAA,QAAA,EAAA,QAAA,EAAA,QAAA,EAAA,aAAA,EAAA,aAAA,EAAA,UAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,qBAAA,EAAA,OAAA,EAAA,OAAA,EAAA,YAAA,EAAA,YAAA,EAAA,oBAAA,EAAA,oBAAA,EAAA,YAAA,EAAA,YAAA,CAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAb,aAAa,EAAA,UAAA,EAAA,CAAA;kBAvBzB,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,UAAU;AACpB,oBAAA,QAAQ,EAAE,QAAQ;AAClB,oBAAA,IAAI,EAAE;AACJ,wBAAA,cAAc,EAAE,WAAW;AAC5B,qBAAA;AACD,oBAAA,cAAc,EAAE;AACd,wBAAA;AACE,4BAAA,SAAS,EAAE,UAAU;AACrB,4BAAA,MAAM,EAAE;gCACN,oBAAoB;gCACpB,QAAQ;gCACR,aAAa;gCACb,UAAU;gCACV,qBAAqB;gCACrB,OAAO;gCACP,YAAY;gCACZ,oBAAoB;gCACpB,YAAY;AACb,6BAAA;AACF,yBAAA;AACF,qBAAA;AACF,iBAAA;;;AC1HD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EG;AACG,SAAU,UAAU,CACxB,GAA4B,EAAA;AAE5B,IAAA,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC;AACpC,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AAE7B,IAAA,MAAM,SAAS,GACb,OAAO,GAAG,KAAK;AACb,UAAE,QAAQ,CAAC,MAAM,GAAG;AACpB,UAAE,QAAQ,CAAC,GAAG;AACZ,cAAE;AACF,cAAE,QAAQ,CAAC,GAAG,CAAC;AAErB,IAAA,MAAM,aAAa,GAAG,QAAQ,CAAC,KAAK,CAAC,aAAa,EAAE;AAClD,QAAA,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,aAAa;AAC3C,KAAA,CAAC;AAEF,IAAA,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE;AAC9C,QAAA,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,WAAW;AACzC,KAAA,CAAC;AAEF,IAAA,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,aAAa,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;AAEnE,IAAA,MAAM,GAAG,GAAG,CAAC,QAAuB,KAAI;AACtC,QAAA,MAAM,IAAI,GAAG;YACX,GAAG,SAAS,CAAC,WAAW,CAAC;SAC1B;AACD,QAAA,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC;AAEhC,QAAA,IAAI,QAAQ,KAAK,IAAI,EAAE;AACrB,YAAA,OAAO,IAAI,CAAC,GAAG,CAAC;;aACX;AACL,YAAA,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ;;AAGtB,QAAA,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE;AAClB,YAAA,UAAU,EAAE,KAAK;AACjB,YAAA,WAAW,EAAE,IAAI;AACjB,YAAA,mBAAmB,EAAE,OAAO;AAC7B,SAAA,CAAC;AACJ,KAAC;AAED,IAAA,OAAO,UAAU,CAAC,UAAU,EAAE,GAAG,CAAC;AACpC;;ACxHA;;;AAGG;AACH,SAAS,eAAe,CAAC,CAAQ,EAAA;IAC/B,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,aAAa;AAC1D;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BG;SACa,GAAG,GAAA;AACjB,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAE7B,OAAO,QAAQ,CACb,MAAM,CAAC,MAAM,CAAC,IAAI,CAChB,MAAM,CAAC,eAAe,CAAC,EACvB,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC,CAChC,EACD;QACE,YAAY,EAAE,MAAM,CAAC,GAAG;AACzB,KAAA,CACF;AACH;;AC5DA;;AAEG;;;;"}
1
+ {"version":3,"file":"mmstack-router-core.mjs","sources":["../../../../../packages/router/core/src/lib/preload.service.ts","../../../../../packages/router/core/src/lib/link.directive.ts","../../../../../packages/router/core/src/lib/util/create-route-predicate.ts","../../../../../packages/router/core/src/lib/util/find-path.ts","../../../../../packages/router/core/src/lib/preload.strategy.ts","../../../../../packages/router/core/src/lib/query-param.ts","../../../../../packages/router/core/src/lib/url.ts","../../../../../packages/router/core/src/mmstack-router-core.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { Subject } from 'rxjs';\n\n@Injectable({ providedIn: 'root' })\nexport class PreloadService {\n private readonly preloadOnDemand$ = new Subject<string>();\n readonly preloadRequested$ = this.preloadOnDemand$.asObservable();\n\n startPreload(routePath: string) {\n this.preloadOnDemand$.next(routePath);\n }\n}\n","import {\n booleanAttribute,\n computed,\n Directive,\n effect,\n inject,\n input,\n output,\n untracked,\n} from '@angular/core';\nimport {\n type ActivatedRoute,\n type Params,\n Router,\n RouterLink,\n RouterLinkWithHref,\n UrlTree,\n} from '@angular/router';\nimport { elementVisibility } from '@mmstack/primitives';\nimport { PreloadService } from './preload.service';\n\n@Directive({\n selector: '[mmLink]',\n exportAs: 'mmLink',\n host: {\n '(mouseenter)': 'onHover()',\n },\n hostDirectives: [\n {\n directive: RouterLink,\n inputs: [\n 'routerLink: mmLink',\n 'target',\n 'queryParams',\n 'fragment',\n 'queryParamsHandling',\n 'state',\n 'relativeTo',\n 'skipLocationChange',\n 'replaceUrl',\n ],\n },\n ],\n})\nexport class LinkDirective {\n private readonly routerLink =\n inject(RouterLink, {\n self: true,\n optional: true,\n }) ?? inject(RouterLinkWithHref, { self: true, optional: true });\n\n private readonly svc = inject(PreloadService);\n private readonly router = inject(Router);\n\n readonly target = input<string>();\n readonly queryParams = input<Params>();\n readonly fragment = input<string>();\n readonly queryParamsHandling = input<'merge' | 'preserve' | ''>();\n readonly state = input<Record<string, any>>();\n readonly info = input<unknown>();\n readonly relativeTo = input<ActivatedRoute>();\n readonly skipLocationChange = input(false, { transform: booleanAttribute });\n readonly replaceUrl = input(false, { transform: booleanAttribute });\n readonly mmLink = input.required<string | any[] | UrlTree>();\n readonly preloadOn = input<'hover' | 'visible' | null>('hover');\n\n readonly preloading = output<void>();\n\n private readonly urlTree = computed(() => {\n const link = this.mmLink();\n const relativeTo = this.relativeTo();\n const fragment = this.fragment();\n const queryParams = this.queryParams();\n const queryParamsHandling = this.queryParamsHandling();\n\n const resolvedTree = this.routerLink?.urlTree;\n if (resolvedTree) return resolvedTree;\n\n if (link instanceof UrlTree) return link;\n\n const arr = Array.isArray(link) ? link : [link];\n\n return this.router.createUrlTree(arr, {\n relativeTo,\n queryParams,\n fragment,\n queryParamsHandling,\n });\n });\n\n private readonly fullPath = computed(() => {\n const urlTree = this.urlTree();\n return this.router.serializeUrl(urlTree);\n });\n\n onHover() {\n if (untracked(this.preloadOn) !== 'hover') return;\n this.requestPreload();\n }\n\n constructor() {\n const intersection = elementVisibility();\n\n effect(() => {\n if (this.preloadOn() !== 'visible') return;\n if (intersection.visible()) this.requestPreload();\n });\n }\n\n private requestPreload() {\n if (!this.routerLink || !untracked(this.fullPath)) return;\n this.svc.startPreload(untracked(this.fullPath));\n this.preloading.emit();\n }\n\n onClick(\n button: number,\n ctrlKey: boolean,\n shiftKey: boolean,\n altKey: boolean,\n metaKey: boolean,\n ) {\n return this.routerLink?.onClick(button, ctrlKey, shiftKey, altKey, metaKey);\n }\n}\n","function parsePathSegment(segmentString: string): {\n pathPart: string;\n matrixParams: Record<string, string>;\n} {\n const parts = segmentString.split(';');\n const pathPart = parts[0];\n const matrixParams: Record<string, string> = {};\n for (let i = 1; i < parts.length; i++) {\n const [key, value = 'true'] = parts[i].split('=');\n if (key) {\n matrixParams[key] = value;\n }\n }\n return { pathPart, matrixParams };\n}\n\nfunction createBasePredicate(path: string): (path: string) => boolean {\n const partPredicates = path\n .split('/')\n .filter((part) => !!part.trim())\n .map((configSegmentString) => {\n const { pathPart: configPathPart, matrixParams: configMatrixParams } =\n parsePathSegment(configSegmentString);\n\n let singlePathPartPredicate: (linkSegmentPathPart: string) => boolean;\n if (configPathPart.startsWith(':')) {\n singlePathPartPredicate = () => true;\n } else {\n singlePathPartPredicate = (linkSegmentPathPart: string) =>\n linkSegmentPathPart === configPathPart;\n }\n\n const configSegmentHasMatrixParams =\n Object.keys(configMatrixParams).length > 0;\n\n return (linkSegmentString: string) => {\n const { pathPart: linkPathPart, matrixParams: linkMatrixParams } =\n parsePathSegment(linkSegmentString);\n\n if (!singlePathPartPredicate(linkPathPart)) {\n return false;\n }\n\n if (!configSegmentHasMatrixParams) {\n return true;\n }\n\n return Object.entries(configMatrixParams).every(\n ([key, value]) =>\n linkMatrixParams.hasOwnProperty(key) &&\n linkMatrixParams[key] === value,\n );\n };\n });\n\n return (path: string) => {\n const linkPathOnly = path.split(/[?#]/).at(0) ?? '';\n if (!linkPathOnly && partPredicates.length > 0) return false;\n if (!linkPathOnly && partPredicates.length === 0) return true;\n\n const parts = linkPathOnly.split('/').filter((part) => !!part.trim());\n if (parts.length < partPredicates.length) return false;\n\n return parts.every((seg, idx) => {\n const pred = partPredicates.at(idx);\n if (!pred) return true;\n return pred(seg);\n });\n };\n}\n\ntype ParsedSegment = {\n pathPart: string;\n matrixParams: Record<string, string>;\n};\n\nfunction singleSegmentMatches(\n configSegment: ParsedSegment,\n linkSegment: ParsedSegment,\n): boolean {\n if (configSegment.pathPart.startsWith(':')) {\n } else if (configSegment.pathPart !== linkSegment.pathPart) {\n return false;\n }\n\n const configMatrix = configSegment.matrixParams;\n const linkMatrix = linkSegment.matrixParams;\n for (const key in configMatrix) {\n if (\n !linkMatrix.hasOwnProperty(key) ||\n linkMatrix[key] !== configMatrix[key]\n ) {\n return false;\n }\n }\n return true;\n}\n\nfunction matchSegmentsRecursive(\n configSegments: ParsedSegment[],\n linkSegments: ParsedSegment[],\n configIdx: number,\n linkIdx: number,\n): boolean {\n if (configIdx === configSegments.length) {\n return linkIdx === linkSegments.length;\n }\n\n if (linkIdx === linkSegments.length) {\n for (let i = configIdx; i < configSegments.length; i++) {\n if (configSegments[i].pathPart !== '**') {\n return false;\n }\n }\n return true;\n }\n\n const currentConfigSegment = configSegments[configIdx];\n\n if (currentConfigSegment.pathPart === '**') {\n if (\n matchSegmentsRecursive(\n configSegments,\n linkSegments,\n configIdx + 1,\n linkIdx,\n )\n ) {\n return true;\n }\n\n if (linkIdx < linkSegments.length) {\n if (\n matchSegmentsRecursive(\n configSegments,\n linkSegments,\n configIdx,\n linkIdx + 1,\n )\n ) {\n return true;\n }\n }\n\n return false;\n } else {\n if (\n linkIdx < linkSegments.length &&\n singleSegmentMatches(currentConfigSegment, linkSegments[linkIdx])\n ) {\n return matchSegmentsRecursive(\n configSegments,\n linkSegments,\n configIdx + 1,\n linkIdx + 1,\n );\n }\n\n return false;\n }\n}\n\nfunction createWildcardPredicate(path: string): (linkPath: string) => boolean {\n const configSegments = path\n .split('/')\n .filter((p) => !!p.trim())\n .map((segment) => parsePathSegment(segment));\n\n return (linkPath: string): boolean => {\n const linkPathOnly = linkPath.split(/[?#]/).at(0) ?? '';\n const linkSegments = linkPathOnly\n .split('/')\n .filter((p) => !!p.trim())\n .map((segment) => parsePathSegment(segment));\n\n return matchSegmentsRecursive(configSegments, linkSegments, 0, 0);\n };\n}\n\nexport function createRoutePredicate(\n path: string,\n): (linkPath: string) => boolean {\n return path.includes('**')\n ? createWildcardPredicate(path)\n : createBasePredicate(path);\n}\n","// The following functions are adapted from ngx-quicklink,\n// (https://github.com/mgechev/ngx-quicklink)\n// Copyright (c) Minko Gechev and contributors, licensed under the MIT License.\n\nimport { PRIMARY_OUTLET, Route } from '@angular/router';\n\nfunction isPrimaryRoute(route: Route): boolean {\n return route.outlet === PRIMARY_OUTLET || !route.outlet;\n}\n\nexport const findPath = (config: Route[], route: Route): string => {\n const configQueue = config.slice();\n const parent = new Map<Route, Route>();\n const visited = new Set<Route>();\n\n while (configQueue.length) {\n const el = configQueue.shift();\n if (!el) {\n continue;\n }\n\n visited.add(el);\n\n if (el === route) {\n break;\n }\n\n (el.children || []).forEach((childRoute: Route) => {\n if (!visited.has(childRoute)) {\n parent.set(childRoute, el);\n configQueue.push(childRoute);\n }\n });\n\n const lazyRoutes = (el as any)._loadedRoutes || [];\n if (Array.isArray(lazyRoutes)) {\n lazyRoutes.forEach((lazyRoute: Route) => {\n if (lazyRoute && !visited.has(lazyRoute)) {\n parent.set(lazyRoute, el);\n configQueue.push(lazyRoute);\n }\n });\n }\n }\n\n let path = '';\n let currentRoute: Route | undefined = route;\n\n while (currentRoute) {\n const currentPath = currentRoute.path || '';\n if (isPrimaryRoute(currentRoute)) {\n path = `/${currentPath}${path}`;\n } else {\n path = `/(${currentRoute.outlet}:${currentPath})${path}`;\n }\n currentRoute = parent.get(currentRoute);\n }\n\n let normalizedPath = path.replaceAll(/\\/+/g, '/');\n\n if (normalizedPath !== '/' && normalizedPath.endsWith('/')) {\n normalizedPath = normalizedPath.slice(0, -1);\n }\n\n return normalizedPath;\n};\n","import { inject, Injectable } from '@angular/core';\nimport { PreloadingStrategy, Route, Router } from '@angular/router';\nimport { EMPTY, filter, finalize, Observable, switchMap, take } from 'rxjs';\nimport { PreloadService } from './preload.service';\nimport { createRoutePredicate, findPath } from './util';\n\nfunction hasSlowConnection() {\n if (\n globalThis.window &&\n 'navigator' in globalThis.window &&\n 'connection' in globalThis.window.navigator &&\n typeof globalThis.window.navigator.connection === 'object' &&\n !!globalThis.window.navigator.connection\n ) {\n const is2g =\n 'effectiveType' in globalThis.window.navigator.connection &&\n typeof globalThis.window.navigator.connection.effectiveType ===\n 'string' &&\n globalThis.window.navigator.connection.effectiveType.endsWith('2g');\n if (is2g) return true;\n if (\n 'saveData' in globalThis.window.navigator.connection &&\n typeof globalThis.window.navigator.connection.saveData === 'boolean' &&\n globalThis.window.navigator.connection.saveData\n )\n return true;\n }\n\n return false;\n}\n\nfunction noPreload(route: Route) {\n return route.data && route.data['preload'] === false;\n}\n\n@Injectable({\n providedIn: 'root',\n})\nexport class PreloadStrategy implements PreloadingStrategy {\n private readonly loading = new Set<string>();\n private readonly router = inject(Router);\n private readonly svc = inject(PreloadService);\n\n preload(route: Route, load: () => Observable<any>): Observable<any> {\n if (noPreload(route) || hasSlowConnection()) return EMPTY;\n\n const fp = findPath(this.router.config, route);\n\n if (this.loading.has(fp)) return EMPTY;\n\n const predicate = createRoutePredicate(fp);\n return this.svc.preloadRequested$.pipe(\n filter((path) => path === fp || predicate(path)),\n take(1),\n switchMap(() => load()),\n finalize(() => this.loading.delete(fp)),\n );\n }\n}\n","import {\r\n computed,\r\n inject,\r\n isSignal,\r\n untracked,\r\n type WritableSignal,\r\n} from '@angular/core';\r\nimport { toSignal } from '@angular/core/rxjs-interop';\r\nimport { ActivatedRoute, Router } from '@angular/router';\r\nimport { toWritable } from '@mmstack/primitives';\r\n\r\n/**\r\n * Creates a WritableSignal that synchronizes with a specific URL query parameter,\r\n * enabling two-way binding between the signal's state and the URL.\r\n *\r\n * Reading the signal provides the current value of the query parameter (or null if absent).\r\n * Setting the signal updates the URL query parameter using `Router.navigate`, triggering\r\n * navigation and causing the signal to update reactively if the navigation is successful.\r\n *\r\n * @param key The key of the query parameter to synchronize with.\r\n * Can be a static string (e.g., `'search'`) or a function/signal returning a string\r\n * for dynamic keys (e.g., `() => this.userId() + '_filter'` or `computed(() => this.category() + '_sort')`).\r\n * The signal will reactively update if the key returned by the function/signal changes.\r\n * @returns {WritableSignal<string | null>} A signal representing the query parameter's value.\r\n * - Reading returns the current value string, or `null` if the parameter is absent in the URL.\r\n * - Setting the signal to a string updates the query parameter in the URL (e.g., `signal.set('value')` results in `?key=value`).\r\n * - Setting the signal to `null` removes the query parameter from the URL (e.g., `signal.set(null)` results in `?otherParam=...`).\r\n * - Automatically reflects changes if the query parameters update due to external navigation.\r\n * @remarks\r\n * - Requires Angular's `ActivatedRoute` and `Router` to be available in the injection context.\r\n * - Uses `Router.navigate` with `queryParamsHandling: 'merge'` to preserve other existing query parameters during updates.\r\n * - 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.\r\n * - 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.\r\n *\r\n * @example\r\n * ```ts\r\n * import { Component, computed, effect, signal } from '@angular/core';\r\n * import { queryParam } from '@mmstack/router-core'; // Adjust import path as needed\r\n * // import { FormsModule } from '@angular/forms'; // If using ngModel\r\n *\r\n * @Component({\r\n * selector: 'app-product-list',\r\n * standalone: true,\r\n * // imports: [FormsModule], // If using ngModel\r\n * template: `\r\n * <div>\r\n * Sort By:\r\n * <select [value]=\"sortSignal() ?? ''\" (change)=\"sortSignal.set($any($event.target).value || null)\">\r\n * <option value=\"\">Default</option>\r\n * <option value=\"price_asc\">Price Asc</option>\r\n * <option value=\"price_desc\">Price Desc</option>\r\n * <option value=\"name\">Name</option>\r\n * </select>\r\n * <button (click)=\"sortSignal.set(null)\" [disabled]=\"!sortSignal()\">Clear Sort</button>\r\n * </div>\r\n * <div>\r\n * Page:\r\n * <input type=\"number\" min=\"1\" [value]=\"pageSignal() ?? '1'\" #p (input)=\"setPage(p.value)\"/>\r\n * </div>\r\n * * `\r\n * })\r\n * export class ProductListComponent {\r\n * // Two-way bind the 'sort' query parameter (?sort=...)\r\n * // Defaults to null if param is missing\r\n * sortSignal = queryParam('sort');\r\n *\r\n * // Example with a different type (needs serialization or separate logic)\r\n * // For simplicity, we treat page as string | null here\r\n * pageSignal = queryParam('page');\r\n *\r\n * constructor() {\r\n * effect(() => {\r\n * const currentSort = this.sortSignal();\r\n * const currentPage = this.pageSignal(); // Read as string | null\r\n * console.log('Sort/Page changed, reloading products for:', { sort: currentSort, page: currentPage });\r\n * // --- Fetch data based on currentSort and currentPage ---\r\n * });\r\n * }\r\n *\r\n * setPage(value: string): void {\r\n * const pageNum = parseInt(value, 10);\r\n * // Set to null if page is 1 (to remove param), otherwise set string value\r\n * this.pageSignal.set(isNaN(pageNum) || pageNum <= 1 ? null : pageNum.toString());\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport function queryParam(\r\n key: string | (() => string),\r\n): WritableSignal<string | null> {\r\n const route = inject(ActivatedRoute);\r\n const router = inject(Router);\r\n\r\n const keySignal =\r\n typeof key === 'string'\r\n ? computed(() => key)\r\n : isSignal(key)\r\n ? key\r\n : computed(key);\r\n\r\n const queryParamMap = toSignal(route.queryParamMap, {\r\n initialValue: route.snapshot.queryParamMap,\r\n });\r\n\r\n const queryParams = toSignal(route.queryParams, {\r\n initialValue: route.snapshot.queryParams,\r\n });\r\n\r\n const queryParam = computed(() => queryParamMap().get(keySignal()));\r\n\r\n const set = (newValue: string | null) => {\r\n const next = {\r\n ...untracked(queryParams),\r\n };\r\n const key = untracked(keySignal);\r\n\r\n if (newValue === null) {\r\n delete next[key];\r\n } else {\r\n next[key] = newValue;\r\n }\r\n\r\n router.navigate([], {\r\n relativeTo: route,\r\n queryParams: next,\r\n queryParamsHandling: 'merge',\r\n });\r\n };\r\n\r\n return toWritable(queryParam, set);\r\n}\r\n","import { inject, type Signal } from '@angular/core';\r\nimport { toSignal } from '@angular/core/rxjs-interop';\r\nimport {\r\n type Event,\r\n EventType,\r\n type NavigationEnd,\r\n Router,\r\n} from '@angular/router';\r\nimport { filter, map } from 'rxjs/operators';\r\n\r\n/**\r\n * Type guard to check if a Router Event is a NavigationEnd event.\r\n * @internal\r\n */\r\nfunction isNavigationEnd(e: Event): e is NavigationEnd {\r\n return 'type' in e && e.type === EventType.NavigationEnd;\r\n}\r\n\r\n/**\r\n * Creates a Signal that tracks the current router URL.\r\n *\r\n * The signal emits the URL string reflecting the router state *after* redirects\r\n * have completed for each successful navigation. It initializes with the router's\r\n * current URL state.\r\n *\r\n * @returns {Signal<string>} A Signal emitting the `urlAfterRedirects` upon successful navigation.\r\n *\r\n * @example\r\n * ```ts\r\n * import { Component, effect } from '@angular/core';\r\n * import { url } from '@mmstack/router-core'; // Adjust import path\r\n *\r\n * @Component({\r\n * selector: 'app-root',\r\n * template: `Current URL: {{ currentUrl() }}`\r\n * })\r\n * export class AppComponent {\r\n * currentUrl = url();\r\n *\r\n * constructor() {\r\n * effect(() => {\r\n * console.log('Navigation ended. New URL:', this.currentUrl());\r\n * // e.g., track page view with analytics\r\n * });\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport function url(): Signal<string> {\r\n const router = inject(Router);\r\n\r\n return toSignal(\r\n router.events.pipe(\r\n filter(isNavigationEnd),\r\n map((e) => e.urlAfterRedirects),\r\n ),\r\n {\r\n initialValue: router.url,\r\n },\r\n );\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["filter"],"mappings":"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"}
package/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- export * from './lib/preloading/public_api';
1
+ export * from './lib/link.directive';
2
+ export * from './lib/preload.strategy';
2
3
  export * from './lib/query-param';
3
4
  export * from './lib/url';
@@ -1,24 +1,10 @@
1
- import { PreloadingStrategy, Route, type ActivatedRoute, type Params, type UrlTree } from '@angular/router';
2
- import { Observable } from 'rxjs';
1
+ import { type ActivatedRoute, type Params, UrlTree } from '@angular/router';
3
2
  import * as i0 from "@angular/core";
4
3
  import * as i1 from "@angular/router";
5
- export declare function hasSlowConnection(): boolean;
6
- export declare class PreloadLinkStrategy implements PreloadingStrategy {
7
- private readonly routeMap;
8
- preload(route: Route, _: () => Observable<any>): Observable<any>;
9
- static ɵfac: i0.ɵɵFactoryDeclaration<PreloadLinkStrategy, never>;
10
- static ɵprov: i0.ɵɵInjectableDeclaration<PreloadLinkStrategy>;
11
- }
12
- export declare class VisibleLinkHandler {
13
- private readonly map;
14
- private readonly observer;
15
- register(el: Element, onVisible: () => void): () => void;
16
- static ɵfac: i0.ɵɵFactoryDeclaration<VisibleLinkHandler, never>;
17
- static ɵprov: i0.ɵɵInjectableDeclaration<VisibleLinkHandler>;
18
- }
19
4
  export declare class LinkDirective {
20
5
  private readonly routerLink;
21
- private readonly preloader;
6
+ private readonly svc;
7
+ private readonly router;
22
8
  readonly target: import("@angular/core").InputSignal<string | undefined>;
23
9
  readonly queryParams: import("@angular/core").InputSignal<Params | undefined>;
24
10
  readonly fragment: import("@angular/core").InputSignal<string | undefined>;
@@ -30,10 +16,13 @@ export declare class LinkDirective {
30
16
  readonly replaceUrl: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
31
17
  readonly mmLink: import("@angular/core").InputSignal<string | any[] | UrlTree>;
32
18
  readonly preloadOn: import("@angular/core").InputSignal<"hover" | "visible" | null>;
33
- protected onHover(): void;
34
- protected onVisible(): void;
19
+ readonly preloading: import("@angular/core").OutputEmitterRef<void>;
20
+ private readonly urlTree;
21
+ private readonly fullPath;
22
+ onHover(): void;
35
23
  constructor();
24
+ private requestPreload;
36
25
  onClick(button: number, ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean): boolean | undefined;
37
26
  static ɵfac: i0.ɵɵFactoryDeclaration<LinkDirective, never>;
38
- static ɵdir: i0.ɵɵ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": true; "isSignal": true; }; "preloadOn": { "alias": "preloadOn"; "required": false; "isSignal": true; }; }, {}, 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: {}; }]>;
27
+ static ɵdir: i0.ɵɵ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": true; "isSignal": true; }; "preloadOn": { "alias": "preloadOn"; "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: {}; }]>;
39
28
  }
@@ -0,0 +1,8 @@
1
+ import * as i0 from "@angular/core";
2
+ export declare class PreloadService {
3
+ private readonly preloadOnDemand$;
4
+ readonly preloadRequested$: import("rxjs").Observable<string>;
5
+ startPreload(routePath: string): void;
6
+ static ɵfac: i0.ɵɵFactoryDeclaration<PreloadService, never>;
7
+ static ɵprov: i0.ɵɵInjectableDeclaration<PreloadService>;
8
+ }
@@ -0,0 +1,11 @@
1
+ import { PreloadingStrategy, Route } from '@angular/router';
2
+ import { Observable } from 'rxjs';
3
+ import * as i0 from "@angular/core";
4
+ export declare class PreloadStrategy implements PreloadingStrategy {
5
+ private readonly loading;
6
+ private readonly router;
7
+ private readonly svc;
8
+ preload(route: Route, load: () => Observable<any>): Observable<any>;
9
+ static ɵfac: i0.ɵɵFactoryDeclaration<PreloadStrategy, never>;
10
+ static ɵprov: i0.ɵɵInjectableDeclaration<PreloadStrategy>;
11
+ }
@@ -0,0 +1 @@
1
+ export declare function createRoutePredicate(path: string): (linkPath: string) => boolean;
@@ -0,0 +1,2 @@
1
+ import { Route } from '@angular/router';
2
+ export declare const findPath: (config: Route[], route: Route) => string;
@@ -0,0 +1,2 @@
1
+ export * from './create-route-predicate';
2
+ export * from './find-path';
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "@mmstack/router-core",
3
- "version": "19.2.0",
3
+ "version": "19.2.1",
4
4
  "keywords": [
5
5
  "angular",
6
6
  "signals",
7
7
  "router",
8
8
  "url",
9
9
  "query",
10
- "params"
10
+ "params",
11
+ "preloading",
12
+ "preload",
13
+ "navigation"
11
14
  ],
12
15
  "license": "MIT",
13
16
  "repository": {
@@ -16,7 +19,7 @@
16
19
  },
17
20
  "homepage": "https://github.com/mihajm/mmstack/blob/master/packages/router/core",
18
21
  "dependencies": {
19
- "@mmstack/primitives": "^19.2.1",
22
+ "@mmstack/primitives": "^19.2.3",
20
23
  "tslib": "^2.3.0"
21
24
  },
22
25
  "peerDependencies": {
@@ -1,2 +0,0 @@
1
- import { Route } from '@angular/router';
2
- export declare function flattenRoutes(routes: Route[]): Route[];
@@ -1 +0,0 @@
1
- export { LinkDirective, PreloadLinkStrategy } from './link.directive';