@real-router/angular 0.0.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.
@@ -0,0 +1,575 @@
1
+ import * as i0 from '@angular/core';
2
+ import { signal, inject, DestroyRef, InjectionToken, makeEnvironmentProviders, input, TemplateRef, Directive, contentChildren, computed, Component, output, effect, ElementRef } from '@angular/core';
3
+ import { getNavigator, UNKNOWN_ROUTE } from '@real-router/core';
4
+ import { createRouteSource, createRouteNodeSource, createTransitionSource, createActiveRouteSource, createErrorSource } from '@real-router/sources';
5
+ import { getPluginApi } from '@real-router/core/api';
6
+ import { getRouteUtils, startsWithSegment } from '@real-router/route-utils';
7
+ import { NgTemplateOutlet } from '@angular/common';
8
+
9
+ /** Must be called within an injection context (constructor, field initializer, runInInjectionContext). */
10
+ function sourceToSignal(source) {
11
+ const sig = signal(source.getSnapshot(), ...(ngDevMode ? [{ debugName: "sig" }] : /* istanbul ignore next */ []));
12
+ const destroyRef = inject(DestroyRef);
13
+ const unsubscribe = source.subscribe(() => {
14
+ sig.set(source.getSnapshot());
15
+ });
16
+ destroyRef.onDestroy(() => {
17
+ unsubscribe();
18
+ source.destroy();
19
+ });
20
+ return sig.asReadonly();
21
+ }
22
+
23
+ const ROUTER = new InjectionToken("ROUTER");
24
+ const NAVIGATOR = new InjectionToken("NAVIGATOR");
25
+ const ROUTE = new InjectionToken("ROUTE");
26
+ function provideRealRouter(router) {
27
+ const navigator = getNavigator(router);
28
+ return makeEnvironmentProviders([
29
+ { provide: ROUTER, useValue: router },
30
+ { provide: NAVIGATOR, useValue: navigator },
31
+ {
32
+ provide: ROUTE,
33
+ useFactory: () => ({
34
+ routeState: sourceToSignal(createRouteSource(router)),
35
+ navigator,
36
+ }),
37
+ },
38
+ ]);
39
+ }
40
+
41
+ function injectOrThrow(token, fnName) {
42
+ const value = inject(token, { optional: true });
43
+ if (!value) {
44
+ throw new Error(`${fnName} must be used within a provideRealRouter context`);
45
+ }
46
+ return value;
47
+ }
48
+
49
+ function injectRouter() {
50
+ return injectOrThrow(ROUTER, "injectRouter");
51
+ }
52
+
53
+ function injectNavigator() {
54
+ return injectOrThrow(NAVIGATOR, "injectNavigator");
55
+ }
56
+
57
+ function injectRoute() {
58
+ return injectOrThrow(ROUTE, "injectRoute");
59
+ }
60
+
61
+ function injectRouteNode(nodeName) {
62
+ const router = injectRouter();
63
+ const navigator = getNavigator(router);
64
+ const source = createRouteNodeSource(router, nodeName);
65
+ const routeState = sourceToSignal(source);
66
+ return { routeState, navigator };
67
+ }
68
+
69
+ function injectRouteUtils() {
70
+ const router = injectRouter();
71
+ return getRouteUtils(getPluginApi(router).getTree());
72
+ }
73
+
74
+ function injectRouterTransition() {
75
+ const router = injectRouter();
76
+ const source = createTransitionSource(router);
77
+ return sourceToSignal(source);
78
+ }
79
+
80
+ function injectIsActiveRoute(routeName, params, options) {
81
+ const router = injectRouter();
82
+ const source = createActiveRouteSource(router, routeName, params, {
83
+ strict: options?.strict ?? false,
84
+ ignoreQueryParams: options?.ignoreQueryParams ?? true,
85
+ });
86
+ return sourceToSignal(source);
87
+ }
88
+
89
+ class RouteMatch {
90
+ routeMatch = input.required(...(ngDevMode ? [{ debugName: "routeMatch" }] : /* istanbul ignore next */ []));
91
+ templateRef = inject(TemplateRef);
92
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RouteMatch, deps: [], target: i0.ɵɵFactoryTarget.Directive });
93
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.8", type: RouteMatch, isStandalone: true, selector: "ng-template[routeMatch]", inputs: { routeMatch: { classPropertyName: "routeMatch", publicName: "routeMatch", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
94
+ }
95
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RouteMatch, decorators: [{
96
+ type: Directive,
97
+ args: [{ selector: "ng-template[routeMatch]" }]
98
+ }], propDecorators: { routeMatch: [{ type: i0.Input, args: [{ isSignal: true, alias: "routeMatch", required: true }] }] } });
99
+
100
+ class RouteNotFound {
101
+ templateRef = inject(TemplateRef);
102
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RouteNotFound, deps: [], target: i0.ɵɵFactoryTarget.Directive });
103
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.8", type: RouteNotFound, isStandalone: true, selector: "ng-template[routeNotFound]", ngImport: i0 });
104
+ }
105
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RouteNotFound, decorators: [{
106
+ type: Directive,
107
+ args: [{ selector: "ng-template[routeNotFound]" }]
108
+ }] });
109
+
110
+ const EMPTY_SNAPSHOT = Object.freeze({
111
+ route: undefined,
112
+ previousRoute: undefined,
113
+ });
114
+ class RouteView {
115
+ nodeName = input("", { ...(ngDevMode ? { debugName: "nodeName" } : /* istanbul ignore next */ {}), alias: "routeNode" });
116
+ matches = contentChildren(RouteMatch, { ...(ngDevMode ? { debugName: "matches" } : /* istanbul ignore next */ {}), descendants: true });
117
+ notFounds = contentChildren(RouteNotFound, { ...(ngDevMode ? { debugName: "notFounds" } : /* istanbul ignore next */ {}), descendants: true });
118
+ activeTemplate = computed(() => {
119
+ const snapshot = this.routeState();
120
+ const route = snapshot.route;
121
+ if (!route) {
122
+ return null;
123
+ }
124
+ const routeName = route.name;
125
+ const entries = this.matchEntries();
126
+ for (const { match, fullSegmentName } of entries) {
127
+ if (startsWithSegment(routeName, fullSegmentName)) {
128
+ return match.templateRef;
129
+ }
130
+ }
131
+ if (routeName === UNKNOWN_ROUTE) {
132
+ const last = this.notFounds().at(-1);
133
+ if (last) {
134
+ return last.templateRef;
135
+ }
136
+ }
137
+ return null;
138
+ }, ...(ngDevMode ? [{ debugName: "activeTemplate" }] : /* istanbul ignore next */ []));
139
+ matchEntries = computed(() => {
140
+ const nodeName = this.nodeName();
141
+ return this.matches().map((match) => {
142
+ const segment = match.routeMatch();
143
+ return {
144
+ match,
145
+ fullSegmentName: nodeName ? `${nodeName}.${segment}` : segment,
146
+ };
147
+ });
148
+ }, ...(ngDevMode ? [{ debugName: "matchEntries" }] : /* istanbul ignore next */ []));
149
+ router = injectRouter();
150
+ destroyRef = inject(DestroyRef);
151
+ routeState = signal(EMPTY_SNAPSHOT, ...(ngDevMode ? [{ debugName: "routeState" }] : /* istanbul ignore next */ []));
152
+ ngOnInit() {
153
+ const source = createRouteNodeSource(this.router, this.nodeName());
154
+ this.routeState.set(source.getSnapshot());
155
+ const unsub = source.subscribe(() => {
156
+ this.routeState.set(source.getSnapshot());
157
+ });
158
+ this.destroyRef.onDestroy(() => {
159
+ unsub();
160
+ source.destroy();
161
+ });
162
+ }
163
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RouteView, deps: [], target: i0.ɵɵFactoryTarget.Component });
164
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: RouteView, isStandalone: true, selector: "route-view", inputs: { nodeName: { classPropertyName: "nodeName", publicName: "routeNode", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "matches", predicate: RouteMatch, descendants: true, isSignal: true }, { propertyName: "notFounds", predicate: RouteNotFound, descendants: true, isSignal: true }], ngImport: i0, template: `
165
+ @if (activeTemplate()) {
166
+ <ng-container [ngTemplateOutlet]="activeTemplate()!" />
167
+ }
168
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
169
+ }
170
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RouteView, decorators: [{
171
+ type: Component,
172
+ args: [{
173
+ selector: "route-view",
174
+ template: `
175
+ @if (activeTemplate()) {
176
+ <ng-container [ngTemplateOutlet]="activeTemplate()!" />
177
+ }
178
+ `,
179
+ imports: [NgTemplateOutlet],
180
+ }]
181
+ }], propDecorators: { nodeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "routeNode", required: false }] }], matches: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => RouteMatch), { ...{ descendants: true }, isSignal: true }] }], notFounds: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => RouteNotFound), { ...{ descendants: true }, isSignal: true }] }] } });
182
+
183
+ class RouterErrorBoundary {
184
+ errorTemplate = input(...(ngDevMode ? [undefined, { debugName: "errorTemplate" }] : /* istanbul ignore next */ []));
185
+ onError = output();
186
+ visibleError = computed(() => {
187
+ const snap = this.snapshot();
188
+ return snap.version > this.dismissedVersion() ? snap.error : null;
189
+ }, ...(ngDevMode ? [{ debugName: "visibleError" }] : /* istanbul ignore next */ []));
190
+ errorContext = computed(() => {
191
+ const error = this.visibleError();
192
+ if (!error) {
193
+ return null;
194
+ }
195
+ return {
196
+ $implicit: error,
197
+ resetError: this.resetError,
198
+ };
199
+ }, ...(ngDevMode ? [{ debugName: "errorContext" }] : /* istanbul ignore next */ []));
200
+ router = injectRouter();
201
+ snapshot = sourceToSignal(createErrorSource(this.router));
202
+ dismissedVersion = signal(-1, ...(ngDevMode ? [{ debugName: "dismissedVersion" }] : /* istanbul ignore next */ []));
203
+ constructor() {
204
+ effect(() => {
205
+ const snap = this.snapshot();
206
+ if (snap.error) {
207
+ this.onError.emit({
208
+ error: snap.error,
209
+ toRoute: snap.toRoute,
210
+ fromRoute: snap.fromRoute,
211
+ });
212
+ }
213
+ });
214
+ }
215
+ resetError = () => {
216
+ this.dismissedVersion.set(this.snapshot().version);
217
+ };
218
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RouterErrorBoundary, deps: [], target: i0.ɵɵFactoryTarget.Component });
219
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: RouterErrorBoundary, isStandalone: true, selector: "router-error-boundary", inputs: { errorTemplate: { classPropertyName: "errorTemplate", publicName: "errorTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onError: "onError" }, ngImport: i0, template: `
220
+ <ng-content />
221
+ @if (errorContext() && errorTemplate()) {
222
+ <ng-container
223
+ [ngTemplateOutlet]="errorTemplate()!"
224
+ [ngTemplateOutletContext]="errorContext()!"
225
+ />
226
+ }
227
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
228
+ }
229
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RouterErrorBoundary, decorators: [{
230
+ type: Component,
231
+ args: [{
232
+ selector: "router-error-boundary",
233
+ template: `
234
+ <ng-content />
235
+ @if (errorContext() && errorTemplate()) {
236
+ <ng-container
237
+ [ngTemplateOutlet]="errorTemplate()!"
238
+ [ngTemplateOutletContext]="errorContext()!"
239
+ />
240
+ }
241
+ `,
242
+ imports: [NgTemplateOutlet],
243
+ }]
244
+ }], ctorParameters: () => [], propDecorators: { errorTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorTemplate", required: false }] }], onError: [{ type: i0.Output, args: ["onError"] }] } });
245
+
246
+ const CLEAR_DELAY = 7000;
247
+ const SAFARI_READY_DELAY = 100;
248
+ const ANNOUNCER_ATTR = "data-real-router-announcer";
249
+ const INTERNAL_ROUTE_PREFIX = "@@";
250
+ const VISUALLY_HIDDEN = "position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0";
251
+ function createRouteAnnouncer(router, options) {
252
+ const prefix = options?.prefix ?? "Navigated to ";
253
+ const getCustomText = options?.getAnnouncementText;
254
+ let isInitialNavigation = true;
255
+ let isReady = false;
256
+ let isDestroyed = false;
257
+ let lastAnnouncedText = "";
258
+ let pendingText = null;
259
+ let clearTimeoutId;
260
+ const announcer = getOrCreateAnnouncer();
261
+ const doAnnounce = (text, h1) => {
262
+ lastAnnouncedText = text;
263
+ clearTimeout(clearTimeoutId);
264
+ announcer.textContent = text;
265
+ clearTimeoutId = setTimeout(() => {
266
+ announcer.textContent = "";
267
+ lastAnnouncedText = "";
268
+ }, CLEAR_DELAY);
269
+ manageFocus(h1);
270
+ };
271
+ // Safari-ready delay: announcing before VoiceOver wires up the aria-live region
272
+ // causes the first announcement to be silently dropped. Wait SAFARI_READY_DELAY ms
273
+ // before marking the announcer "ready" — any navigation during that window is
274
+ // buffered in pendingText and flushed once the delay expires.
275
+ const safariTimeoutId = setTimeout(() => {
276
+ isReady = true;
277
+ if (pendingText !== null && !isDestroyed) {
278
+ const text = pendingText;
279
+ pendingText = null;
280
+ doAnnounce(text, document.querySelector("h1"));
281
+ }
282
+ }, SAFARI_READY_DELAY);
283
+ const unsubscribe = router.subscribe(({ route }) => {
284
+ if (isInitialNavigation) {
285
+ isInitialNavigation = false;
286
+ return;
287
+ }
288
+ // Double rAF: waits for two paint frames so the incoming route's DOM
289
+ // (including the new <h1>) is fully rendered before resolveText reads it.
290
+ // Single rAF fires before the new route's template has been attached,
291
+ // which would cause resolveText to pick up the OLD h1 or fall back to
292
+ // document.title / route.name prematurely.
293
+ requestAnimationFrame(() => {
294
+ requestAnimationFrame(() => {
295
+ if (isDestroyed) {
296
+ return;
297
+ }
298
+ const h1 = document.querySelector("h1");
299
+ const text = resolveText(route, prefix, getCustomText, h1);
300
+ if (!text || text === lastAnnouncedText) {
301
+ return;
302
+ }
303
+ if (!isReady) {
304
+ // Defer announcement until Safari-ready window elapses (see safariTimeoutId).
305
+ pendingText = text;
306
+ return;
307
+ }
308
+ doAnnounce(text, h1);
309
+ });
310
+ });
311
+ });
312
+ return {
313
+ destroy() {
314
+ isDestroyed = true;
315
+ unsubscribe();
316
+ clearTimeout(clearTimeoutId);
317
+ clearTimeout(safariTimeoutId);
318
+ removeAnnouncer();
319
+ },
320
+ };
321
+ }
322
+ function getOrCreateAnnouncer() {
323
+ const existing = document.querySelector(`[${ANNOUNCER_ATTR}]`);
324
+ if (existing) {
325
+ return existing;
326
+ }
327
+ const element = document.createElement("div");
328
+ element.setAttribute("style", VISUALLY_HIDDEN);
329
+ element.setAttribute("aria-live", "assertive");
330
+ element.setAttribute("aria-atomic", "true");
331
+ element.setAttribute(ANNOUNCER_ATTR, "");
332
+ document.body.prepend(element);
333
+ return element;
334
+ }
335
+ function removeAnnouncer() {
336
+ document.querySelector(`[${ANNOUNCER_ATTR}]`)?.remove();
337
+ }
338
+ function resolveText(route, prefix, getCustomText, h1) {
339
+ if (getCustomText) {
340
+ return getCustomText(route);
341
+ }
342
+ const h1Text = h1?.textContent.trim() ?? "";
343
+ const routeName = route.name.startsWith(INTERNAL_ROUTE_PREFIX)
344
+ ? ""
345
+ : route.name;
346
+ const rawText = h1Text || document.title || routeName || globalThis.location.pathname;
347
+ return `${prefix}${rawText}`;
348
+ }
349
+ function manageFocus(h1) {
350
+ if (!h1) {
351
+ return;
352
+ }
353
+ if (!h1.hasAttribute("tabindex")) {
354
+ h1.setAttribute("tabindex", "-1");
355
+ }
356
+ h1.focus({ preventScroll: true });
357
+ }
358
+
359
+ function shouldNavigate(evt) {
360
+ return (evt.button === 0 &&
361
+ !evt.metaKey &&
362
+ !evt.altKey &&
363
+ !evt.ctrlKey &&
364
+ !evt.shiftKey);
365
+ }
366
+ function buildHref(router, routeName, routeParams) {
367
+ try {
368
+ const buildUrl = router.buildUrl;
369
+ if (buildUrl) {
370
+ const url = buildUrl(routeName, routeParams);
371
+ if (url !== undefined) {
372
+ return url;
373
+ }
374
+ }
375
+ return router.buildPath(routeName, routeParams);
376
+ }
377
+ catch {
378
+ console.error(`[real-router] Route "${routeName}" is not defined. The element will render without an href attribute.`);
379
+ return undefined;
380
+ }
381
+ }
382
+ function parseTokens(value) {
383
+ return value ? (value.match(/\S+/g) ?? []) : [];
384
+ }
385
+ function buildActiveClassName(isActive, activeClassName, baseClassName) {
386
+ if (isActive && activeClassName) {
387
+ const activeTokens = parseTokens(activeClassName);
388
+ if (activeTokens.length === 0) {
389
+ return baseClassName ?? undefined;
390
+ }
391
+ if (!baseClassName) {
392
+ return activeTokens.join(" ");
393
+ }
394
+ const baseTokens = parseTokens(baseClassName);
395
+ const seen = new Set(baseTokens);
396
+ for (const token of activeTokens) {
397
+ if (!seen.has(token)) {
398
+ seen.add(token);
399
+ baseTokens.push(token);
400
+ }
401
+ }
402
+ return baseTokens.join(" ");
403
+ }
404
+ return baseClassName ?? undefined;
405
+ }
406
+ function shallowEqual(prev, next) {
407
+ if (Object.is(prev, next)) {
408
+ return true;
409
+ }
410
+ if (!prev || !next) {
411
+ return false;
412
+ }
413
+ const prevKeys = Object.keys(prev);
414
+ if (prevKeys.length !== Object.keys(next).length) {
415
+ return false;
416
+ }
417
+ const prevRecord = prev;
418
+ const nextRecord = next;
419
+ for (const key of prevKeys) {
420
+ if (!Object.is(prevRecord[key], nextRecord[key])) {
421
+ return false;
422
+ }
423
+ }
424
+ return true;
425
+ }
426
+ function applyLinkA11y(element) {
427
+ if (!element) {
428
+ return;
429
+ }
430
+ if (element instanceof HTMLAnchorElement ||
431
+ element instanceof HTMLButtonElement) {
432
+ return;
433
+ }
434
+ if (!element.hasAttribute("role")) {
435
+ element.setAttribute("role", "link");
436
+ }
437
+ if (!element.hasAttribute("tabindex")) {
438
+ element.setAttribute("tabindex", "0");
439
+ }
440
+ }
441
+
442
+ class NavigationAnnouncer {
443
+ announcer = createRouteAnnouncer(injectRouter());
444
+ constructor() {
445
+ inject(DestroyRef).onDestroy(() => {
446
+ this.announcer.destroy();
447
+ });
448
+ }
449
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NavigationAnnouncer, deps: [], target: i0.ɵɵFactoryTarget.Component });
450
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: NavigationAnnouncer, isStandalone: true, selector: "navigation-announcer", ngImport: i0, template: "", isInline: true });
451
+ }
452
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NavigationAnnouncer, decorators: [{
453
+ type: Component,
454
+ args: [{
455
+ selector: "navigation-announcer",
456
+ template: "",
457
+ }]
458
+ }], ctorParameters: () => [] });
459
+
460
+ class RealLink {
461
+ routeName = input("", ...(ngDevMode ? [{ debugName: "routeName" }] : /* istanbul ignore next */ []));
462
+ routeParams = input({}, ...(ngDevMode ? [{ debugName: "routeParams" }] : /* istanbul ignore next */ []));
463
+ routeOptions = input({}, ...(ngDevMode ? [{ debugName: "routeOptions" }] : /* istanbul ignore next */ []));
464
+ activeClassName = input("active", ...(ngDevMode ? [{ debugName: "activeClassName" }] : /* istanbul ignore next */ []));
465
+ activeStrict = input(false, ...(ngDevMode ? [{ debugName: "activeStrict" }] : /* istanbul ignore next */ []));
466
+ ignoreQueryParams = input(true, ...(ngDevMode ? [{ debugName: "ignoreQueryParams" }] : /* istanbul ignore next */ []));
467
+ router = injectRouter();
468
+ destroyRef = inject(DestroyRef);
469
+ anchor = inject(ElementRef)
470
+ .nativeElement;
471
+ isActive = signal(false, ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
472
+ href = computed(() => buildHref(this.router, this.routeName(), this.routeParams()), ...(ngDevMode ? [{ debugName: "href" }] : /* istanbul ignore next */ []));
473
+ prevActiveClass = "";
474
+ ngOnInit() {
475
+ const source = createActiveRouteSource(this.router, this.routeName(), this.routeParams(), {
476
+ strict: this.activeStrict(),
477
+ ignoreQueryParams: this.ignoreQueryParams(),
478
+ });
479
+ this.isActive.set(source.getSnapshot());
480
+ this.updateDom();
481
+ const unsub = source.subscribe(() => {
482
+ this.isActive.set(source.getSnapshot());
483
+ this.updateDom();
484
+ });
485
+ this.destroyRef.onDestroy(() => {
486
+ unsub();
487
+ source.destroy();
488
+ });
489
+ }
490
+ onClick(event) {
491
+ if (!shouldNavigate(event) || this.anchor.target === "_blank") {
492
+ return;
493
+ }
494
+ event.preventDefault();
495
+ this.router
496
+ .navigate(this.routeName(), this.routeParams(), this.routeOptions())
497
+ .catch(() => { });
498
+ }
499
+ updateDom() {
500
+ const href = this.href();
501
+ if (href !== undefined) {
502
+ this.anchor.setAttribute("href", href);
503
+ }
504
+ const activeClass = this.activeClassName();
505
+ if (this.prevActiveClass && this.prevActiveClass !== activeClass) {
506
+ this.anchor.classList.remove(this.prevActiveClass);
507
+ }
508
+ if (activeClass) {
509
+ this.anchor.classList.toggle(activeClass, this.isActive());
510
+ }
511
+ this.prevActiveClass = activeClass;
512
+ }
513
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RealLink, deps: [], target: i0.ɵɵFactoryTarget.Directive });
514
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.8", type: RealLink, isStandalone: true, selector: "a[realLink]", inputs: { routeName: { classPropertyName: "routeName", publicName: "routeName", isSignal: true, isRequired: false, transformFunction: null }, routeParams: { classPropertyName: "routeParams", publicName: "routeParams", isSignal: true, isRequired: false, transformFunction: null }, routeOptions: { classPropertyName: "routeOptions", publicName: "routeOptions", isSignal: true, isRequired: false, transformFunction: null }, activeClassName: { classPropertyName: "activeClassName", publicName: "activeClassName", isSignal: true, isRequired: false, transformFunction: null }, activeStrict: { classPropertyName: "activeStrict", publicName: "activeStrict", isSignal: true, isRequired: false, transformFunction: null }, ignoreQueryParams: { classPropertyName: "ignoreQueryParams", publicName: "ignoreQueryParams", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick($event)" } }, ngImport: i0 });
515
+ }
516
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RealLink, decorators: [{
517
+ type: Directive,
518
+ args: [{
519
+ selector: "a[realLink]",
520
+ host: {
521
+ "(click)": "onClick($event)",
522
+ },
523
+ }]
524
+ }], propDecorators: { routeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "routeName", required: false }] }], routeParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "routeParams", required: false }] }], routeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "routeOptions", required: false }] }], activeClassName: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeClassName", required: false }] }], activeStrict: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeStrict", required: false }] }], ignoreQueryParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "ignoreQueryParams", required: false }] }] } });
525
+
526
+ class RealLinkActive {
527
+ realLinkActive = input("", ...(ngDevMode ? [{ debugName: "realLinkActive" }] : /* istanbul ignore next */ []));
528
+ routeName = input("", ...(ngDevMode ? [{ debugName: "routeName" }] : /* istanbul ignore next */ []));
529
+ routeParams = input({}, ...(ngDevMode ? [{ debugName: "routeParams" }] : /* istanbul ignore next */ []));
530
+ activeStrict = input(false, ...(ngDevMode ? [{ debugName: "activeStrict" }] : /* istanbul ignore next */ []));
531
+ ignoreQueryParams = input(true, ...(ngDevMode ? [{ debugName: "ignoreQueryParams" }] : /* istanbul ignore next */ []));
532
+ router = injectRouter();
533
+ destroyRef = inject(DestroyRef);
534
+ element = inject(ElementRef).nativeElement;
535
+ isActive = signal(false, ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
536
+ constructor() {
537
+ applyLinkA11y(this.element);
538
+ }
539
+ ngOnInit() {
540
+ const source = createActiveRouteSource(this.router, this.routeName(), this.routeParams(), {
541
+ strict: this.activeStrict(),
542
+ ignoreQueryParams: this.ignoreQueryParams(),
543
+ });
544
+ this.isActive.set(source.getSnapshot());
545
+ this.updateClass();
546
+ const unsub = source.subscribe(() => {
547
+ this.isActive.set(source.getSnapshot());
548
+ this.updateClass();
549
+ });
550
+ this.destroyRef.onDestroy(() => {
551
+ unsub();
552
+ source.destroy();
553
+ });
554
+ }
555
+ updateClass() {
556
+ const className = this.realLinkActive();
557
+ if (!className) {
558
+ return;
559
+ }
560
+ this.element.classList.toggle(className, this.isActive());
561
+ }
562
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RealLinkActive, deps: [], target: i0.ɵɵFactoryTarget.Directive });
563
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.8", type: RealLinkActive, isStandalone: true, selector: "[realLinkActive]", inputs: { realLinkActive: { classPropertyName: "realLinkActive", publicName: "realLinkActive", isSignal: true, isRequired: false, transformFunction: null }, routeName: { classPropertyName: "routeName", publicName: "routeName", isSignal: true, isRequired: false, transformFunction: null }, routeParams: { classPropertyName: "routeParams", publicName: "routeParams", isSignal: true, isRequired: false, transformFunction: null }, activeStrict: { classPropertyName: "activeStrict", publicName: "activeStrict", isSignal: true, isRequired: false, transformFunction: null }, ignoreQueryParams: { classPropertyName: "ignoreQueryParams", publicName: "ignoreQueryParams", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
564
+ }
565
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RealLinkActive, decorators: [{
566
+ type: Directive,
567
+ args: [{ selector: "[realLinkActive]" }]
568
+ }], ctorParameters: () => [], propDecorators: { realLinkActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "realLinkActive", required: false }] }], routeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "routeName", required: false }] }], routeParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "routeParams", required: false }] }], activeStrict: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeStrict", required: false }] }], ignoreQueryParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "ignoreQueryParams", required: false }] }] } });
569
+
570
+ /**
571
+ * Generated bundle index. Do not edit.
572
+ */
573
+
574
+ export { NAVIGATOR, NavigationAnnouncer, ROUTE, ROUTER, RealLink, RealLinkActive, RouteMatch, RouteNotFound, RouteView, RouterErrorBoundary, injectIsActiveRoute, injectNavigator, injectRoute, injectRouteNode, injectRouteUtils, injectRouter, injectRouterTransition, provideRealRouter, sourceToSignal };
575
+ //# sourceMappingURL=real-router-angular.mjs.map