@atlasng/common 0.0.2

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 ADDED
@@ -0,0 +1,29 @@
1
+ # @atlasng/common
2
+
3
+ A shared Angular library containing utilities, pipes, directives, guards, and services that are used across multiple applications and libraries in the AtlasNG monorepo. This library is the single source of truth for cross-cutting concerns that do not belong to any single feature domain.
4
+
5
+ ## Overview
6
+
7
+ The common library provides:
8
+
9
+ - **Pipes**: Reusable Angular pipes for formatting, transforming, and presenting data in templates
10
+ - **Directives**: Structural and attribute directives for declarative DOM manipulation and behavior
11
+ - **Guards**: Route guards for controlling navigation and access across applications
12
+ - **Shared Services**: Stateless helper services and utility functions consumed by multiple features
13
+ - **Type Utilities**: Shared TypeScript interfaces, enums, and type helpers used across the codebase
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @atlasng/common
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### TODO: Pipes
24
+
25
+ ### TODO: Directives
26
+
27
+ ### TODO: Guards
28
+
29
+ ### TODO: Shared Services
@@ -0,0 +1,637 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, forwardRef, inject, input, booleanAttribute, Injector, ElementRef, HostAttributeToken, computed, effect, Directive, ErrorHandler, InjectionToken, APP_ID } from '@angular/core';
3
+ import { LOCATION, CUSTOM_ELEMENT_REGISTRY } from '@atlasng/core';
4
+ import { Location } from '@angular/common';
5
+ import { UrlSerializer, UrlTree, PRIMARY_OUTLET, Router, ActivatedRoute } from '@angular/router';
6
+
7
+ /**
8
+ * Returns whether an element is a native anchor-like tag (`<a>` or `<area>`).
9
+ *
10
+ * @param element Element to check.
11
+ * @returns `true` when the element tag is `a` or `area`; otherwise `false`.
12
+ */
13
+ function isAnchorElement(element) {
14
+ const tagName = element.tagName.toLowerCase();
15
+ return tagName === 'a' || tagName === 'area';
16
+ }
17
+ /**
18
+ * Returns whether a custom element is anchor-like by checking for an observed `href` attribute.
19
+ *
20
+ * @param element Element to check.
21
+ * @param registry Custom element registry used to resolve the element constructor.
22
+ * @returns `true` if the custom element observes `href`; otherwise `false`.
23
+ */
24
+ function isAnchorLikeCustomElement(element, registry) {
25
+ if (!registry) {
26
+ return false;
27
+ }
28
+ const tagName = element.tagName.toLowerCase();
29
+ const constructor = registry.get(tagName);
30
+ const attributes = constructor?.observedAttributes ?? [];
31
+ return attributes.includes('href');
32
+ }
33
+ /**
34
+ * Returns whether an element can be treated as a link target.
35
+ *
36
+ * Supports native anchors and custom elements that observe `href`.
37
+ *
38
+ * @param element Element to check.
39
+ * @param registry Custom element registry used for custom-element checks.
40
+ * @returns `true` when the element is native-anchor-like or custom-anchor-like.
41
+ */
42
+ function isAnchorLikeElement(element, registry) {
43
+ return element !== undefined && (isAnchorElement(element) || isAnchorLikeCustomElement(element, registry));
44
+ }
45
+ /**
46
+ * Type guard for Angular `UrlTree`-like objects.
47
+ *
48
+ * @param value Value to test.
49
+ * @returns `true` when the value matches the `UrlTree` shape used by this library.
50
+ */
51
+ function isUrlTree(value) {
52
+ return (typeof value === 'object' && value !== null && 'root' in value && 'queryParams' in value && 'fragment' in value);
53
+ }
54
+ /**
55
+ * Normalizes a value into an array.
56
+ *
57
+ * @param value Single item or array of items.
58
+ * @returns The input array unchanged, or a new one-element array for scalar input.
59
+ */
60
+ function castArray(value) {
61
+ return Array.isArray(value) ? value : [value];
62
+ }
63
+ /**
64
+ * Attempts to parse an absolute URL from a command-like value.
65
+ *
66
+ * Supports raw string input and single-item command arrays used by link handlers.
67
+ * Relative paths and non-string values return `null`.
68
+ *
69
+ * @param value Potential absolute URL value.
70
+ * @returns Parsed `URL` instance when successful; otherwise `null`.
71
+ */
72
+ function tryParseAbsoluteUrl(value) {
73
+ if (Array.isArray(value) && value.length === 1) {
74
+ value = value[0];
75
+ }
76
+ if (typeof value !== 'string') {
77
+ return null;
78
+ }
79
+ try {
80
+ return new URL(value);
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ }
86
+ /**
87
+ * Normalizes supported command attribute values into a `LinkCommand` object.
88
+ *
89
+ * @param value Input command value to normalize.
90
+ * @returns A normalized `LinkCommand`, or `undefined` when input is nullish.
91
+ */
92
+ function commandAttribute(value) {
93
+ if (value === null || value === undefined) {
94
+ return undefined;
95
+ }
96
+ else if (typeof value === 'object' && 'command' in value) {
97
+ return value;
98
+ }
99
+ return { command: value };
100
+ }
101
+ /**
102
+ * Merges `source` into `target` while ignoring `undefined` values.
103
+ *
104
+ * @param target Base object to copy and merge into.
105
+ * @param source Object containing override values.
106
+ * @returns A merged object.
107
+ */
108
+ function safeMerge(target, source) {
109
+ const result = { ...target };
110
+ for (const key in source) {
111
+ const value = source[key];
112
+ if (value !== undefined) {
113
+ result[key] = value;
114
+ }
115
+ }
116
+ return result;
117
+ }
118
+ /**
119
+ * Applies query params and fragment options from a link command onto an URL.
120
+ *
121
+ * @param url URL instance to mutate.
122
+ * @param command Link command containing query/fragment options.
123
+ */
124
+ function applyQueryParamsAndFragmentToUrl(url, command) {
125
+ if (!command.queryParamsHandling && command.queryParams) {
126
+ url.search = '';
127
+ }
128
+ if (command.queryParamsHandling !== 'preserve' && command.queryParams) {
129
+ for (const [key, value] of Object.entries(command.queryParams)) {
130
+ const values = castArray(value);
131
+ url.searchParams.delete(key);
132
+ for (const v of values) {
133
+ url.searchParams.append(key, v);
134
+ }
135
+ }
136
+ }
137
+ if (!command.preserveFragment && command.fragment !== undefined) {
138
+ url.hash = command.fragment;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Base strategy for preparing and executing link navigation.
144
+ */
145
+ class LinkHandler {
146
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: LinkHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
147
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: LinkHandler, providedIn: 'root', useExisting: i0.forwardRef(() => RouterlessLinkHandler) });
148
+ }
149
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: LinkHandler, decorators: [{
150
+ type: Injectable,
151
+ args: [{
152
+ providedIn: 'root',
153
+ useExisting: forwardRef(() => RouterlessLinkHandler),
154
+ }]
155
+ }] });
156
+ /**
157
+ * Default link handler that navigates through `window.location` without Angular Router navigation.
158
+ */
159
+ class RouterlessLinkHandler extends LinkHandler {
160
+ /**
161
+ * Browser location object used for hard navigations.
162
+ */
163
+ browserLocation = inject(LOCATION);
164
+ /**
165
+ * Optional registry for resolving custom elements that behave like anchors.
166
+ */
167
+ customElementRegistry = inject(CUSTOM_ELEMENT_REGISTRY);
168
+ /**
169
+ * Angular location service used for path inspection and external URL preparation.
170
+ */
171
+ location = inject(Location);
172
+ /**
173
+ * Angular URL serializer used for parsing and serializing `UrlTree` values.
174
+ */
175
+ serializer = inject(UrlSerializer);
176
+ /**
177
+ * Builds a `PreparedLink` from the given command and element context.
178
+ *
179
+ * @param command Navigation command and URL creation options.
180
+ * @param element Host element associated with the link.
181
+ * @param attributes Optional link attributes.
182
+ * @param _injector Unused injector placeholder for API parity.
183
+ * @returns Prepared link metadata used by `navigateTo`.
184
+ */
185
+ prepareLink(command, element, attributes, _injector) {
186
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
187
+ if (command.relativeTo) {
188
+ // eslint-disable-next-line no-console
189
+ console.warn('The "relativeTo" option is not supported by RouterlessLinkHandler.');
190
+ }
191
+ }
192
+ return {
193
+ href: this.serializeCommand(command),
194
+ attributes,
195
+ handlerContext: {
196
+ isAnchorLikeElement: this.isAnchorLikeElement(element),
197
+ },
198
+ };
199
+ }
200
+ /**
201
+ * Navigates to a prepared link using native anchor behavior or `window.location`.
202
+ *
203
+ * @param link Prepared link to navigate to.
204
+ * @param _event Triggering event (unused).
205
+ * @param options Navigation behavior options.
206
+ * @returns `true` when native navigation should continue, otherwise `false`.
207
+ */
208
+ navigateTo(link, _event, options) {
209
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
210
+ if (options.skipLocationChange) {
211
+ // eslint-disable-next-line no-console
212
+ console.warn('The "skipLocationChange" option is not supported by RouterlessLinkHandler.');
213
+ }
214
+ if (options.state) {
215
+ // eslint-disable-next-line no-console
216
+ console.warn('The "state" option is not supported by RouterlessLinkHandler.');
217
+ }
218
+ if (options.browserUrl) {
219
+ // eslint-disable-next-line no-console
220
+ console.warn('The "browserUrl" option is not supported by RouterlessLinkHandler.');
221
+ }
222
+ }
223
+ if (link.handlerContext.isAnchorLikeElement) {
224
+ return true;
225
+ }
226
+ if (options.replaceUrl) {
227
+ this.browserLocation.replace(link.href);
228
+ }
229
+ else {
230
+ this.browserLocation.assign(link.href);
231
+ }
232
+ return false;
233
+ }
234
+ /**
235
+ * Checks if the given element behaves like an anchor element.
236
+ *
237
+ * @param element Element to check.
238
+ * @returns `true` if the element behaves like an anchor; otherwise `false`.
239
+ */
240
+ isAnchorLikeElement(element) {
241
+ return isAnchorLikeElement(element, this.customElementRegistry);
242
+ }
243
+ /**
244
+ * Serializes a link command into an external URL string.
245
+ *
246
+ * Does not support all features of Angular's `Router` command arrays (e.g. outlets).
247
+ *
248
+ * @param command Navigation command and URL creation options.
249
+ * @returns External URL ready for navigation.
250
+ */
251
+ serializeCommand(command) {
252
+ const absoluteUrl = tryParseAbsoluteUrl(command.command);
253
+ if (absoluteUrl) {
254
+ applyQueryParamsAndFragmentToUrl(absoluteUrl, command);
255
+ return absoluteUrl.toString();
256
+ }
257
+ const urlTree = this.commandToUrlTree(command);
258
+ const url = this.serializer.serialize(urlTree);
259
+ return this.location.prepareExternalUrl(url);
260
+ }
261
+ /**
262
+ * Converts a `LinkCommand` into a `UrlTree`.
263
+ *
264
+ * @param command Navigation command and URL creation options.
265
+ * @returns Normalized `UrlTree` for serialization.
266
+ */
267
+ commandToUrlTree(command) {
268
+ const value = command.command;
269
+ if (isUrlTree(value)) {
270
+ return value;
271
+ }
272
+ const currentUrlTree = this.serializer.parse(this.location.path(true));
273
+ const { queryParamsHandling, preserveFragment } = command;
274
+ let queryParams = command.queryParams ?? {};
275
+ let fragment = command.fragment;
276
+ if (queryParamsHandling === 'preserve') {
277
+ queryParams = currentUrlTree.queryParams;
278
+ }
279
+ else if (queryParamsHandling === 'merge') {
280
+ queryParams = { ...currentUrlTree.queryParams, ...queryParams };
281
+ }
282
+ if (preserveFragment) {
283
+ fragment = currentUrlTree.fragment;
284
+ }
285
+ const path = this.commandToPath(castArray(value), currentUrlTree);
286
+ const pathTree = this.serializer.parse(path);
287
+ return new UrlTree(pathTree.root, queryParams, fragment);
288
+ }
289
+ /**
290
+ * Converts a command array into a URL path.
291
+ *
292
+ * @param command Command array.
293
+ * @param currentUrlTree Current URL tree.
294
+ * @returns URL path.
295
+ */
296
+ commandToPath(command, currentUrlTree) {
297
+ const currentSegments = currentUrlTree.root.children[PRIMARY_OUTLET]?.segments ?? [];
298
+ const paths = currentSegments.map((segment) => segment.path);
299
+ let isAbsolute = false;
300
+ let isFirstPath = true;
301
+ for (const part of command) {
302
+ if (part === null || part === undefined) {
303
+ continue;
304
+ }
305
+ let path = String(part);
306
+ if (typeof part === 'object' && part !== null) {
307
+ if ('segmentPath' in part) {
308
+ path = String(part.segmentPath);
309
+ }
310
+ else {
311
+ if ('outlets' in part && (typeof ngDevMode === 'undefined' || ngDevMode)) {
312
+ // eslint-disable-next-line no-console
313
+ console.warn('Outlets in command arrays are not supported by RouterlessLinkHandler and will be skipped.');
314
+ }
315
+ continue;
316
+ }
317
+ }
318
+ if (path.length === 0) {
319
+ continue;
320
+ }
321
+ if (isFirstPath && path.startsWith('/')) {
322
+ paths.length = 0;
323
+ isAbsolute = true;
324
+ }
325
+ isFirstPath = false;
326
+ for (const token of path.split('/')) {
327
+ if (!token || token === '.') {
328
+ continue;
329
+ }
330
+ if (token === '..') {
331
+ paths.pop();
332
+ continue;
333
+ }
334
+ paths.push(token);
335
+ }
336
+ }
337
+ return isAbsolute || paths.length > 0 ? `/${paths.join('/')}` : '/';
338
+ }
339
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: RouterlessLinkHandler, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
340
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: RouterlessLinkHandler, providedIn: 'root' });
341
+ }
342
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: RouterlessLinkHandler, decorators: [{
343
+ type: Injectable,
344
+ args: [{
345
+ providedIn: 'root',
346
+ }]
347
+ }] });
348
+
349
+ /**
350
+ * Generic navigation directive that works with both internal Angular routes and external URLs.
351
+ */
352
+ class AnyLink {
353
+ /** Primary link command input. */
354
+ command = input(undefined, { ...(ngDevMode ? { debugName: "command" } : /* istanbul ignore next */ {}), alias: 'angAnyLink', transform: commandAttribute });
355
+ /** Optional browsing context target, e.g. `_blank`. */
356
+ target = input(...(ngDevMode ? [undefined, { debugName: "target" }] : /* istanbul ignore next */ []));
357
+ /** Optional `rel` attribute value forwarded to the rendered link. */
358
+ rel = input(...(ngDevMode ? [undefined, { debugName: "rel" }] : /* istanbul ignore next */ []));
359
+ /** Optional `download` attribute value forwarded to the rendered link. */
360
+ download = input(...(ngDevMode ? [undefined, { debugName: "download" }] : /* istanbul ignore next */ []));
361
+ /** Router query params used when the command resolves to an Angular route. */
362
+ queryParams = input(...(ngDevMode ? [undefined, { debugName: "queryParams" }] : /* istanbul ignore next */ []));
363
+ /** Strategy for merging provided query params with the current URL query params. */
364
+ queryParamsHandling = input(...(ngDevMode ? [undefined, { debugName: "queryParamsHandling" }] : /* istanbul ignore next */ []));
365
+ /** URL fragment to apply during navigation. */
366
+ fragment = input(...(ngDevMode ? [undefined, { debugName: "fragment" }] : /* istanbul ignore next */ []));
367
+ /** Preserves the current fragment when true. */
368
+ preserveFragment = input(false, { ...(ngDevMode ? { debugName: "preserveFragment" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
369
+ /** Navigates without pushing a new browser history entry when true. */
370
+ skipLocationChange = input(false, { ...(ngDevMode ? { debugName: "skipLocationChange" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
371
+ /** Route used as a base for relative navigation commands. */
372
+ relativeTo = input(...(ngDevMode ? [undefined, { debugName: "relativeTo" }] : /* istanbul ignore next */ []));
373
+ /** Optional browser-visible URL override for router navigation. */
374
+ browserUrl = input(...(ngDevMode ? [undefined, { debugName: "browserUrl" }] : /* istanbul ignore next */ []));
375
+ /** Replaces the current history entry instead of pushing a new one when true. */
376
+ replaceUrl = input(false, { ...(ngDevMode ? { debugName: "replaceUrl" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
377
+ /** Optional history state object passed to the router. */
378
+ state = input(...(ngDevMode ? [undefined, { debugName: "state" }] : /* istanbul ignore next */ []));
379
+ /** Optional router navigation extras info payload. */
380
+ info = input(...(ngDevMode ? [undefined, { debugName: "info" }] : /* istanbul ignore next */ []));
381
+ /** Injector used to resolve optional dependencies while preparing navigation commands. */
382
+ injector = inject(Injector);
383
+ /** Host DOM element the directive is attached to. */
384
+ element = inject(ElementRef).nativeElement;
385
+ /** Link orchestration service responsible for preparing and executing navigation. */
386
+ handler = inject(LinkHandler);
387
+ /** Initial static `href` attribute from the host element, if present. */
388
+ initialHref = inject(new HostAttributeToken('href'), { optional: true });
389
+ /** Initial static `tabindex` attribute from the host element, if present. */
390
+ initialTabIndex = inject(new HostAttributeToken('tabindex'), { optional: true });
391
+ /** Whether the host behaves as an anchor-like element for native link semantics. */
392
+ isAnchorLikeElement = isAnchorLikeElement(this.element, inject(CUSTOM_ELEMENT_REGISTRY));
393
+ /** Prepared link model derived from command inputs and current directive state. */
394
+ preparedLink = computed(() => {
395
+ const command = this.command();
396
+ if (!command) {
397
+ return undefined;
398
+ }
399
+ return this.handler.prepareLink(safeMerge(command, {
400
+ queryParams: this.queryParams(),
401
+ queryParamsHandling: this.queryParamsHandling(),
402
+ fragment: this.fragment(),
403
+ preserveFragment: this.preserveFragment(),
404
+ relativeTo: this.relativeTo(),
405
+ }), this.element, {
406
+ target: this.target(),
407
+ rel: this.rel(),
408
+ download: this.download(),
409
+ }, this.injector);
410
+ }, ...(ngDevMode ? [{ debugName: "preparedLink" }] : /* istanbul ignore next */ []));
411
+ /** Resolved `href` host attribute value. */
412
+ hrefAttribute = computed(() => {
413
+ if (!this.isAnchorLikeElement) {
414
+ return this.initialHref;
415
+ }
416
+ return this.preparedLink()?.href;
417
+ }, ...(ngDevMode ? [{ debugName: "hrefAttribute" }] : /* istanbul ignore next */ []));
418
+ /** Resolved `target` host attribute value. */
419
+ targetAttribute = computed(() => this.getAttributeValue('target'), ...(ngDevMode ? [{ debugName: "targetAttribute" }] : /* istanbul ignore next */ []));
420
+ /** Resolved `rel` host attribute value. */
421
+ relAttribute = computed(() => this.getAttributeValue('rel'), ...(ngDevMode ? [{ debugName: "relAttribute" }] : /* istanbul ignore next */ []));
422
+ /** Resolved `download` host attribute value. */
423
+ downloadAttribute = computed(() => this.getAttributeValue('download'), ...(ngDevMode ? [{ debugName: "downloadAttribute" }] : /* istanbul ignore next */ []));
424
+ /** Resolved `tabindex` host attribute value for keyboard accessibility on non-anchor hosts. */
425
+ tabIndexAttribute = computed(() => {
426
+ if (this.initialTabIndex !== null || this.isAnchorLikeElement) {
427
+ return this.initialTabIndex;
428
+ }
429
+ return this.preparedLink() ? '0' : null;
430
+ }, ...(ngDevMode ? [{ debugName: "tabIndexAttribute" }] : /* istanbul ignore next */ []));
431
+ /**
432
+ * Initializes the directive and asserts against invalid input combinations in development mode.
433
+ */
434
+ constructor() {
435
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
436
+ effect(() => {
437
+ if (isUrlTree(this.command()?.command) &&
438
+ (this.queryParams() ||
439
+ this.queryParamsHandling() ||
440
+ this.fragment() !== undefined ||
441
+ this.preserveFragment() ||
442
+ this.relativeTo())) {
443
+ throw new Error('Cannot configure queryParams or fragment when using a UrlTree as the angAnyLink input value.');
444
+ }
445
+ });
446
+ }
447
+ }
448
+ /**
449
+ * Delegates click handling to `LinkHandler` when a command is configured.
450
+ *
451
+ * @param event Pointer event emitted by the host click interaction.
452
+ * @returns `true` to keep default browser behavior, or `false` to suppress it.
453
+ */
454
+ onClick(event) {
455
+ const link = this.preparedLink();
456
+ if (!link) {
457
+ return true;
458
+ }
459
+ const result = this.handler.navigateTo(link, event, {
460
+ skipLocationChange: this.skipLocationChange(),
461
+ browserUrl: this.browserUrl(),
462
+ replaceUrl: this.replaceUrl(),
463
+ state: this.state(),
464
+ info: this.info(),
465
+ });
466
+ return result ?? !this.isAnchorLikeElement;
467
+ }
468
+ /**
469
+ * Resolves an attribute value from prepared link metadata, falling back to direct input values.
470
+ *
471
+ * @param name Link attribute name to resolve.
472
+ * @returns Resolved attribute value, `null`, or `undefined` when not provided.
473
+ */
474
+ getAttributeValue(name) {
475
+ const value = this.preparedLink()?.attributes?.[name];
476
+ return value !== undefined ? value : this[name]();
477
+ }
478
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: AnyLink, deps: [], target: i0.ɵɵFactoryTarget.Directive });
479
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.7", type: AnyLink, isStandalone: true, selector: "[angAnyLink]", inputs: { command: { classPropertyName: "command", publicName: "angAnyLink", isSignal: true, isRequired: false, transformFunction: null }, target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, rel: { classPropertyName: "rel", publicName: "rel", isSignal: true, isRequired: false, transformFunction: null }, download: { classPropertyName: "download", publicName: "download", isSignal: true, isRequired: false, transformFunction: null }, queryParams: { classPropertyName: "queryParams", publicName: "queryParams", isSignal: true, isRequired: false, transformFunction: null }, queryParamsHandling: { classPropertyName: "queryParamsHandling", publicName: "queryParamsHandling", isSignal: true, isRequired: false, transformFunction: null }, fragment: { classPropertyName: "fragment", publicName: "fragment", isSignal: true, isRequired: false, transformFunction: null }, preserveFragment: { classPropertyName: "preserveFragment", publicName: "preserveFragment", isSignal: true, isRequired: false, transformFunction: null }, skipLocationChange: { classPropertyName: "skipLocationChange", publicName: "skipLocationChange", isSignal: true, isRequired: false, transformFunction: null }, relativeTo: { classPropertyName: "relativeTo", publicName: "relativeTo", isSignal: true, isRequired: false, transformFunction: null }, browserUrl: { classPropertyName: "browserUrl", publicName: "browserUrl", isSignal: true, isRequired: false, transformFunction: null }, replaceUrl: { classPropertyName: "replaceUrl", publicName: "replaceUrl", 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 } }, host: { listeners: { "click": "onClick($event)" }, properties: { "attr.href": "hrefAttribute()", "attr.target": "targetAttribute()", "attr.rel": "relAttribute()", "attr.download": "downloadAttribute()", "attr.tabindex": "tabIndexAttribute()" } }, exportAs: ["angAnyLink"], ngImport: i0 });
480
+ }
481
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: AnyLink, decorators: [{
482
+ type: Directive,
483
+ args: [{
484
+ selector: '[angAnyLink]',
485
+ host: {
486
+ '[attr.href]': 'hrefAttribute()',
487
+ '[attr.target]': 'targetAttribute()',
488
+ '[attr.rel]': 'relAttribute()',
489
+ '[attr.download]': 'downloadAttribute()',
490
+ '[attr.tabindex]': 'tabIndexAttribute()',
491
+ '(click)': 'onClick($event)',
492
+ },
493
+ exportAs: 'angAnyLink',
494
+ }]
495
+ }], ctorParameters: () => [], propDecorators: { command: [{ type: i0.Input, args: [{ isSignal: true, alias: "angAnyLink", required: false }] }], target: [{ type: i0.Input, args: [{ isSignal: true, alias: "target", required: false }] }], rel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rel", required: false }] }], download: [{ type: i0.Input, args: [{ isSignal: true, alias: "download", required: false }] }], queryParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryParams", required: false }] }], queryParamsHandling: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryParamsHandling", required: false }] }], fragment: [{ type: i0.Input, args: [{ isSignal: true, alias: "fragment", required: false }] }], preserveFragment: [{ type: i0.Input, args: [{ isSignal: true, alias: "preserveFragment", required: false }] }], skipLocationChange: [{ type: i0.Input, args: [{ isSignal: true, alias: "skipLocationChange", required: false }] }], relativeTo: [{ type: i0.Input, args: [{ isSignal: true, alias: "relativeTo", required: false }] }], browserUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "browserUrl", required: false }] }], replaceUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "replaceUrl", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], info: [{ type: i0.Input, args: [{ isSignal: true, alias: "info", required: false }] }] } });
496
+
497
+ /**
498
+ * Link handler that supports Angular Router commands and absolute URLs.
499
+ */
500
+ class RouterLinkHandler extends RouterlessLinkHandler {
501
+ /** Angular Router instance used to build and execute route-based navigation. */
502
+ router = inject(Router);
503
+ /** Angular error handler for reporting navigation failures. */
504
+ errorHandler = inject(ErrorHandler);
505
+ /**
506
+ * Prepares a link payload that supports both absolute URLs and Angular router commands.
507
+ *
508
+ * @param command Navigation command and URL creation options.
509
+ * @param element Host element associated with the link.
510
+ * @param attributes Optional link attributes from directive inputs.
511
+ * @param injector Optional injector used to resolve `ActivatedRoute` for relative commands.
512
+ * @returns Prepared link metadata used by `navigateTo`.
513
+ */
514
+ prepareLink(command, element, attributes, injector) {
515
+ if (tryParseAbsoluteUrl(command.command)) {
516
+ if (command.relativeTo && (typeof ngDevMode === 'undefined' || ngDevMode)) {
517
+ // eslint-disable-next-line no-console
518
+ console.warn('The "relativeTo" option is not supported for absolute URLs in RouterLinkHandler.');
519
+ }
520
+ return super.prepareLink({ ...command, relativeTo: undefined }, element, attributes, injector);
521
+ }
522
+ let urlTree;
523
+ if (isUrlTree(command.command)) {
524
+ urlTree = command.command;
525
+ }
526
+ else {
527
+ const relativeTo = command.relativeTo ?? injector?.get(ActivatedRoute, null);
528
+ urlTree = this.router.createUrlTree(castArray(command.command), { ...command, relativeTo });
529
+ }
530
+ const url = this.router.serializeUrl(urlTree);
531
+ const href = this.location.prepareExternalUrl(url);
532
+ return {
533
+ href,
534
+ attributes,
535
+ handlerContext: {
536
+ isAnchorLikeElement: this.isAnchorLikeElement(element),
537
+ urlTree,
538
+ },
539
+ };
540
+ }
541
+ /**
542
+ * Navigates to a prepared link using Angular Router when a `UrlTree` is available.
543
+ *
544
+ * Preserves native browser behavior for modified clicks and non-`_self` targets
545
+ * on anchor-like hosts.
546
+ *
547
+ * @param link Prepared link metadata from `prepareLink`.
548
+ * @param event Triggering DOM event.
549
+ * @param options Angular Router navigation behavior options.
550
+ * @returns `true` when native navigation should continue, otherwise `false`.
551
+ */
552
+ navigateTo(link, event, options) {
553
+ const { urlTree, isAnchorLikeElement } = link.handlerContext;
554
+ if (!urlTree) {
555
+ return super.navigateTo(link, event, options);
556
+ }
557
+ if (isAnchorLikeElement && event instanceof MouseEvent) {
558
+ const { button, altKey, ctrlKey, metaKey, shiftKey } = event;
559
+ if (button !== 0 || altKey || ctrlKey || metaKey || shiftKey) {
560
+ return true;
561
+ }
562
+ const target = link.attributes?.target;
563
+ if (target && target !== '_self') {
564
+ return true;
565
+ }
566
+ }
567
+ this.router.navigateByUrl(urlTree, options).catch((error) => {
568
+ this.errorHandler.handleError(error);
569
+ });
570
+ return !isAnchorLikeElement;
571
+ }
572
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: RouterLinkHandler, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
573
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: RouterLinkHandler, providedIn: 'root' });
574
+ }
575
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: RouterLinkHandler, decorators: [{
576
+ type: Injectable,
577
+ args: [{
578
+ providedIn: 'root',
579
+ }]
580
+ }] });
581
+
582
+ /** Maximum random value used to build the optional hexadecimal infix segment. */
583
+ const INFIX_MAX = 0xffffff;
584
+ /** Default configuration applied when no custom options are provided. */
585
+ const DEFAULT_GENERATOR_OPTIONS = {
586
+ randomize: true,
587
+ };
588
+ /** Dependency injection token for overriding {@link IdGenerator} options. */
589
+ const ID_GENERATOR_OPTIONS = new InjectionToken('ID_GENERATOR_OPTIONS', {
590
+ providedIn: 'root',
591
+ factory: () => DEFAULT_GENERATOR_OPTIONS,
592
+ });
593
+ /**
594
+ * Generates stable, incrementing DOM-safe IDs.
595
+ */
596
+ class IdGenerator {
597
+ /** Angular application ID. */
598
+ appId = inject(APP_ID);
599
+ /** Effective generator options after merging defaults with user provided overrides. */
600
+ options = { ...DEFAULT_GENERATOR_OPTIONS, ...inject(ID_GENERATOR_OPTIONS) };
601
+ /** Random hexadecimal segment reused across generated IDs when randomization is enabled. */
602
+ infix = Math.floor(INFIX_MAX * Math.random()).toString(16);
603
+ /** Monotonic counter that guarantees uniqueness within this service instance. */
604
+ counter = 0;
605
+ /**
606
+ * Builds a unique ID using the provided prefix and generator configuration.
607
+ *
608
+ * @param prefix Prefix for the ID.
609
+ * @returns A hyphen-delimited identifier.
610
+ */
611
+ getId(prefix) {
612
+ const parts = [prefix];
613
+ if (this.appId !== 'ng') {
614
+ parts.push(this.appId);
615
+ }
616
+ if (this.options.randomize) {
617
+ parts.push(this.infix);
618
+ }
619
+ parts.push(`${this.counter++}`);
620
+ return parts.join('-');
621
+ }
622
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: IdGenerator, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
623
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: IdGenerator, providedIn: 'root' });
624
+ }
625
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: IdGenerator, decorators: [{
626
+ type: Injectable,
627
+ args: [{
628
+ providedIn: 'root',
629
+ }]
630
+ }] });
631
+
632
+ /**
633
+ * Generated bundle index. Do not edit.
634
+ */
635
+
636
+ export { AnyLink, ID_GENERATOR_OPTIONS, IdGenerator, LinkHandler, RouterLinkHandler, RouterlessLinkHandler };
637
+ //# sourceMappingURL=atlasng-common.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atlasng-common.mjs","sources":["../../../../libs/common/src/lib/any-link/utils.ts","../../../../libs/common/src/lib/any-link/link-handler.ts","../../../../libs/common/src/lib/any-link/any-link.ts","../../../../libs/common/src/lib/any-link/router-link-handler.ts","../../../../libs/common/src/lib/id-generator.ts","../../../../libs/common/src/atlasng-common.ts"],"sourcesContent":["import type { UrlTree } from '@angular/router';\nimport type { LinkCommand } from './link-handler';\n\n/**\n * Returns whether an element is a native anchor-like tag (`<a>` or `<area>`).\n *\n * @param element Element to check.\n * @returns `true` when the element tag is `a` or `area`; otherwise `false`.\n */\nexport function isAnchorElement(element: Element): boolean {\n const tagName = element.tagName.toLowerCase();\n return tagName === 'a' || tagName === 'area';\n}\n\n/**\n * Returns whether a custom element is anchor-like by checking for an observed `href` attribute.\n *\n * @param element Element to check.\n * @param registry Custom element registry used to resolve the element constructor.\n * @returns `true` if the custom element observes `href`; otherwise `false`.\n */\nexport function isAnchorLikeCustomElement(element: Element, registry: CustomElementRegistry | undefined): boolean {\n if (!registry) {\n return false;\n }\n\n const tagName = element.tagName.toLowerCase();\n const constructor = registry.get(tagName) as { observedAttributes?: string[] } | undefined;\n const attributes = constructor?.observedAttributes ?? [];\n return attributes.includes('href');\n}\n\n/**\n * Returns whether an element can be treated as a link target.\n *\n * Supports native anchors and custom elements that observe `href`.\n *\n * @param element Element to check.\n * @param registry Custom element registry used for custom-element checks.\n * @returns `true` when the element is native-anchor-like or custom-anchor-like.\n */\nexport function isAnchorLikeElement(\n element: Element | undefined,\n registry: CustomElementRegistry | undefined,\n): boolean {\n return element !== undefined && (isAnchorElement(element) || isAnchorLikeCustomElement(element, registry));\n}\n\n/**\n * Type guard for Angular `UrlTree`-like objects.\n *\n * @param value Value to test.\n * @returns `true` when the value matches the `UrlTree` shape used by this library.\n */\nexport function isUrlTree(value: unknown): value is UrlTree {\n return (\n typeof value === 'object' && value !== null && 'root' in value && 'queryParams' in value && 'fragment' in value\n );\n}\n\n/**\n * Normalizes a value into an array.\n *\n * @param value Single item or array of items.\n * @returns The input array unchanged, or a new one-element array for scalar input.\n */\nexport function castArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * Attempts to parse an absolute URL from a command-like value.\n *\n * Supports raw string input and single-item command arrays used by link handlers.\n * Relative paths and non-string values return `null`.\n *\n * @param value Potential absolute URL value.\n * @returns Parsed `URL` instance when successful; otherwise `null`.\n */\nexport function tryParseAbsoluteUrl(value: unknown): URL | null {\n if (Array.isArray(value) && value.length === 1) {\n value = value[0];\n }\n if (typeof value !== 'string') {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n}\n\n/**\n * Normalizes supported command attribute values into a `LinkCommand` object.\n *\n * @param value Input command value to normalize.\n * @returns A normalized `LinkCommand`, or `undefined` when input is nullish.\n */\nexport function commandAttribute(\n value: string | readonly unknown[] | UrlTree | LinkCommand | null | undefined,\n): LinkCommand | undefined {\n if (value === null || value === undefined) {\n return undefined;\n } else if (typeof value === 'object' && 'command' in value) {\n return value;\n }\n\n return { command: value };\n}\n\n/**\n * Merges `source` into `target` while ignoring `undefined` values.\n *\n * @param target Base object to copy and merge into.\n * @param source Object containing override values.\n * @returns A merged object.\n */\nexport function safeMerge<T, U>(target: T, source: U): T & U {\n const result = { ...target } as T & U;\n\n for (const key in source) {\n const value = source[key];\n if (value !== undefined) {\n result[key] = value as (T & U)[typeof key];\n }\n }\n\n return result;\n}\n\n/**\n * Applies query params and fragment options from a link command onto an URL.\n *\n * @param url URL instance to mutate.\n * @param command Link command containing query/fragment options.\n */\nexport function applyQueryParamsAndFragmentToUrl(url: URL, command: LinkCommand): void {\n if (!command.queryParamsHandling && command.queryParams) {\n url.search = '';\n }\n if (command.queryParamsHandling !== 'preserve' && command.queryParams) {\n for (const [key, value] of Object.entries(command.queryParams)) {\n const values = castArray(value);\n url.searchParams.delete(key);\n for (const v of values) {\n url.searchParams.append(key, v);\n }\n }\n }\n\n if (!command.preserveFragment && command.fragment !== undefined) {\n url.hash = command.fragment;\n }\n}\n","import { Location } from '@angular/common';\nimport { forwardRef, inject, Injectable, Injector } from '@angular/core';\nimport {\n type NavigationBehaviorOptions,\n PRIMARY_OUTLET,\n type UrlCreationOptions,\n UrlSerializer,\n UrlTree,\n} from '@angular/router';\nimport { CUSTOM_ELEMENT_REGISTRY, LOCATION } from '@atlasng/core';\nimport {\n applyQueryParamsAndFragmentToUrl,\n castArray,\n isAnchorLikeElement,\n isUrlTree,\n tryParseAbsoluteUrl,\n} from './utils';\n\n/**\n * Command input for link preparation.\n *\n * Extends Angular URL creation options and adds the target route command.\n */\nexport interface LinkCommand extends UrlCreationOptions {\n /**\n * Route command represented as a path string, command array, or prebuilt `UrlTree`.\n */\n command: string | readonly unknown[] | UrlTree;\n}\n\n/**\n * Anchor-relevant attributes that can be applied to prepared links.\n */\nexport interface LinkAttributes {\n /**\n * Browser target for link navigation.\n */\n target?: string;\n /**\n * Relationship hint used for link security and semantics.\n */\n rel?: string;\n /**\n * Optional filename used for download behavior.\n */\n download?: string;\n}\n\n/**\n * Link attributes where each value may be `null`.\n *\n * `null` indicates that the attribute should be removed from the host element\n * instead of falling back to it's initial value.\n */\nexport type NullableLinkAttributes = {\n [K in keyof LinkAttributes]: LinkAttributes[K] | null;\n};\n\n/**\n * A link value resolved by a `LinkHandler`.\n *\n * Includes the final URL, optional DOM attributes, and handler-specific context.\n */\nexport interface PreparedLink<C = unknown> {\n /**\n * Final navigation URL.\n */\n href: string;\n /**\n * Optional attributes to apply to the host element.\n */\n attributes?: NullableLinkAttributes;\n /**\n * Handler-owned context used during navigation.\n */\n handlerContext: C;\n}\n\n/**\n * Base strategy for preparing and executing link navigation.\n */\n@Injectable({\n providedIn: 'root',\n useExisting: forwardRef(() => RouterlessLinkHandler),\n})\nexport abstract class LinkHandler<C = unknown> {\n /**\n * Resolves command and element inputs into a navigable link descriptor.\n *\n * @param command Navigation command and URL creation options.\n * @param element Host element associated with the link.\n * @param attributes Optional link attributes from directive inputs.\n * @param injector Optional injector for advanced handler scenarios.\n * @returns A prepared link payload consumed by `navigateTo`.\n */\n abstract prepareLink(\n command: LinkCommand,\n element?: Element,\n attributes?: LinkAttributes,\n injector?: Injector,\n ): PreparedLink<C>;\n\n /**\n * Performs navigation for a previously prepared link.\n *\n * @param link Prepared link payload.\n * @param event Triggering DOM event.\n * @param options Navigation behavior options.\n * @returns `true` when native navigation should continue, `false` when handled internally,\n * or `void` for implementations that do not provide a continuation signal.\n */\n abstract navigateTo(link: PreparedLink<C>, event: Event, options: NavigationBehaviorOptions): boolean | void;\n}\n\n/**\n * Runtime context returned by `RouterlessLinkHandler`.\n */\nexport interface RouterlessLinkHandlerContext {\n /**\n * Indicates whether the host element can use native anchor navigation.\n */\n isAnchorLikeElement: boolean;\n}\n\n/**\n * Default link handler that navigates through `window.location` without Angular Router navigation.\n */\n@Injectable({\n providedIn: 'root',\n})\nexport class RouterlessLinkHandler extends LinkHandler<RouterlessLinkHandlerContext> {\n /**\n * Browser location object used for hard navigations.\n */\n private readonly browserLocation = inject(LOCATION);\n\n /**\n * Optional registry for resolving custom elements that behave like anchors.\n */\n private readonly customElementRegistry = inject(CUSTOM_ELEMENT_REGISTRY);\n\n /**\n * Angular location service used for path inspection and external URL preparation.\n */\n protected readonly location = inject(Location);\n\n /**\n * Angular URL serializer used for parsing and serializing `UrlTree` values.\n */\n protected readonly serializer = inject(UrlSerializer);\n\n /**\n * Builds a `PreparedLink` from the given command and element context.\n *\n * @param command Navigation command and URL creation options.\n * @param element Host element associated with the link.\n * @param attributes Optional link attributes.\n * @param _injector Unused injector placeholder for API parity.\n * @returns Prepared link metadata used by `navigateTo`.\n */\n override prepareLink(\n command: LinkCommand,\n element?: Element,\n attributes?: LinkAttributes,\n _injector?: Injector,\n ): PreparedLink<RouterlessLinkHandlerContext> {\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n if (command.relativeTo) {\n // eslint-disable-next-line no-console\n console.warn('The \"relativeTo\" option is not supported by RouterlessLinkHandler.');\n }\n }\n\n return {\n href: this.serializeCommand(command),\n attributes,\n handlerContext: {\n isAnchorLikeElement: this.isAnchorLikeElement(element),\n },\n };\n }\n\n /**\n * Navigates to a prepared link using native anchor behavior or `window.location`.\n *\n * @param link Prepared link to navigate to.\n * @param _event Triggering event (unused).\n * @param options Navigation behavior options.\n * @returns `true` when native navigation should continue, otherwise `false`.\n */\n override navigateTo(\n link: PreparedLink<RouterlessLinkHandlerContext>,\n _event: Event,\n options: NavigationBehaviorOptions,\n ): boolean {\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n if (options.skipLocationChange) {\n // eslint-disable-next-line no-console\n console.warn('The \"skipLocationChange\" option is not supported by RouterlessLinkHandler.');\n }\n\n if (options.state) {\n // eslint-disable-next-line no-console\n console.warn('The \"state\" option is not supported by RouterlessLinkHandler.');\n }\n\n if (options.browserUrl) {\n // eslint-disable-next-line no-console\n console.warn('The \"browserUrl\" option is not supported by RouterlessLinkHandler.');\n }\n }\n\n if (link.handlerContext.isAnchorLikeElement) {\n return true;\n }\n\n if (options.replaceUrl) {\n this.browserLocation.replace(link.href);\n } else {\n this.browserLocation.assign(link.href);\n }\n\n return false;\n }\n\n /**\n * Checks if the given element behaves like an anchor element.\n *\n * @param element Element to check.\n * @returns `true` if the element behaves like an anchor; otherwise `false`.\n */\n protected isAnchorLikeElement(element: Element | undefined): boolean {\n return isAnchorLikeElement(element, this.customElementRegistry);\n }\n\n /**\n * Serializes a link command into an external URL string.\n *\n * Does not support all features of Angular's `Router` command arrays (e.g. outlets).\n *\n * @param command Navigation command and URL creation options.\n * @returns External URL ready for navigation.\n */\n protected serializeCommand(command: LinkCommand): string {\n const absoluteUrl = tryParseAbsoluteUrl(command.command);\n if (absoluteUrl) {\n applyQueryParamsAndFragmentToUrl(absoluteUrl, command);\n return absoluteUrl.toString();\n }\n\n const urlTree = this.commandToUrlTree(command);\n const url = this.serializer.serialize(urlTree);\n return this.location.prepareExternalUrl(url);\n }\n\n /**\n * Converts a `LinkCommand` into a `UrlTree`.\n *\n * @param command Navigation command and URL creation options.\n * @returns Normalized `UrlTree` for serialization.\n */\n private commandToUrlTree(command: LinkCommand): UrlTree {\n const value = command.command;\n if (isUrlTree(value)) {\n return value;\n }\n\n const currentUrlTree = this.serializer.parse(this.location.path(true));\n const { queryParamsHandling, preserveFragment } = command;\n let queryParams = command.queryParams ?? {};\n let fragment: string | null | undefined = command.fragment;\n\n if (queryParamsHandling === 'preserve') {\n queryParams = currentUrlTree.queryParams;\n } else if (queryParamsHandling === 'merge') {\n queryParams = { ...currentUrlTree.queryParams, ...queryParams };\n }\n\n if (preserveFragment) {\n fragment = currentUrlTree.fragment;\n }\n\n const path = this.commandToPath(castArray(value), currentUrlTree);\n const pathTree = this.serializer.parse(path);\n return new UrlTree(pathTree.root, queryParams, fragment);\n }\n\n /**\n * Converts a command array into a URL path.\n *\n * @param command Command array.\n * @param currentUrlTree Current URL tree.\n * @returns URL path.\n */\n private commandToPath(command: readonly unknown[], currentUrlTree: UrlTree): string {\n const currentSegments = currentUrlTree.root.children[PRIMARY_OUTLET]?.segments ?? [];\n const paths = currentSegments.map((segment) => segment.path);\n let isAbsolute = false;\n let isFirstPath = true;\n\n for (const part of command) {\n if (part === null || part === undefined) {\n continue;\n }\n\n let path = String(part);\n if (typeof part === 'object' && part !== null) {\n if ('segmentPath' in part) {\n path = String(part.segmentPath);\n } else {\n if ('outlets' in part && (typeof ngDevMode === 'undefined' || ngDevMode)) {\n // eslint-disable-next-line no-console\n console.warn('Outlets in command arrays are not supported by RouterlessLinkHandler and will be skipped.');\n }\n\n continue;\n }\n }\n\n if (path.length === 0) {\n continue;\n }\n\n if (isFirstPath && path.startsWith('/')) {\n paths.length = 0;\n isAbsolute = true;\n }\n\n isFirstPath = false;\n\n for (const token of path.split('/')) {\n if (!token || token === '.') {\n continue;\n }\n\n if (token === '..') {\n paths.pop();\n continue;\n }\n\n paths.push(token);\n }\n }\n\n return isAbsolute || paths.length > 0 ? `/${paths.join('/')}` : '/';\n }\n}\n","import {\n booleanAttribute,\n computed,\n Directive,\n effect,\n ElementRef,\n HostAttributeToken,\n inject,\n Injector,\n input,\n} from '@angular/core';\nimport type { ActivatedRoute, Params, QueryParamsHandling, UrlTree } from '@angular/router';\nimport { CUSTOM_ELEMENT_REGISTRY } from '@atlasng/core';\nimport { LinkAttributes, LinkCommand, LinkHandler } from './link-handler';\nimport { commandAttribute, isAnchorLikeElement, isUrlTree, safeMerge } from './utils';\n\n/** Valid types for the link command input. */\nexport type AnyLinkCommand = string | readonly unknown[] | UrlTree | LinkCommand | null | undefined;\n\n/**\n * Generic navigation directive that works with both internal Angular routes and external URLs.\n */\n@Directive({\n selector: '[angAnyLink]',\n host: {\n '[attr.href]': 'hrefAttribute()',\n '[attr.target]': 'targetAttribute()',\n '[attr.rel]': 'relAttribute()',\n '[attr.download]': 'downloadAttribute()',\n '[attr.tabindex]': 'tabIndexAttribute()',\n '(click)': 'onClick($event)',\n },\n exportAs: 'angAnyLink',\n})\nexport class AnyLink {\n /** Primary link command input. */\n readonly command = input(undefined, { alias: 'angAnyLink', transform: commandAttribute });\n\n /** Optional browsing context target, e.g. `_blank`. */\n readonly target = input<string>();\n\n /** Optional `rel` attribute value forwarded to the rendered link. */\n readonly rel = input<string>();\n\n /** Optional `download` attribute value forwarded to the rendered link. */\n readonly download = input<string>();\n\n /** Router query params used when the command resolves to an Angular route. */\n readonly queryParams = input<Params | null>();\n\n /** Strategy for merging provided query params with the current URL query params. */\n readonly queryParamsHandling = input<QueryParamsHandling | null>();\n\n /** URL fragment to apply during navigation. */\n readonly fragment = input<string>();\n\n /** Preserves the current fragment when true. */\n readonly preserveFragment = input(false, { transform: booleanAttribute });\n\n /** Navigates without pushing a new browser history entry when true. */\n readonly skipLocationChange = input(false, { transform: booleanAttribute });\n\n /** Route used as a base for relative navigation commands. */\n readonly relativeTo = input<ActivatedRoute | null>();\n\n /** Optional browser-visible URL override for router navigation. */\n readonly browserUrl = input<UrlTree | string>();\n\n /** Replaces the current history entry instead of pushing a new one when true. */\n readonly replaceUrl = input(false, { transform: booleanAttribute });\n\n /** Optional history state object passed to the router. */\n readonly state = input<Record<string, unknown>>();\n\n /** Optional router navigation extras info payload. */\n readonly info = input<unknown>();\n\n /** Injector used to resolve optional dependencies while preparing navigation commands. */\n private readonly injector = inject(Injector);\n\n /** Host DOM element the directive is attached to. */\n private readonly element = inject(ElementRef).nativeElement as Element;\n\n /** Link orchestration service responsible for preparing and executing navigation. */\n private readonly handler = inject(LinkHandler);\n\n /** Initial static `href` attribute from the host element, if present. */\n private readonly initialHref = inject(new HostAttributeToken('href'), { optional: true });\n\n /** Initial static `tabindex` attribute from the host element, if present. */\n private readonly initialTabIndex = inject(new HostAttributeToken('tabindex'), { optional: true });\n\n /** Whether the host behaves as an anchor-like element for native link semantics. */\n private readonly isAnchorLikeElement = isAnchorLikeElement(this.element, inject(CUSTOM_ELEMENT_REGISTRY));\n\n /** Prepared link model derived from command inputs and current directive state. */\n private readonly preparedLink = computed(() => {\n const command = this.command();\n if (!command) {\n return undefined;\n }\n\n return this.handler.prepareLink(\n safeMerge(command, {\n queryParams: this.queryParams(),\n queryParamsHandling: this.queryParamsHandling(),\n fragment: this.fragment(),\n preserveFragment: this.preserveFragment(),\n relativeTo: this.relativeTo(),\n }),\n this.element,\n {\n target: this.target(),\n rel: this.rel(),\n download: this.download(),\n },\n this.injector,\n );\n });\n\n /** Resolved `href` host attribute value. */\n protected readonly hrefAttribute = computed(() => {\n if (!this.isAnchorLikeElement) {\n return this.initialHref;\n }\n\n return this.preparedLink()?.href;\n });\n\n /** Resolved `target` host attribute value. */\n protected readonly targetAttribute = computed(() => this.getAttributeValue('target'));\n\n /** Resolved `rel` host attribute value. */\n protected readonly relAttribute = computed(() => this.getAttributeValue('rel'));\n\n /** Resolved `download` host attribute value. */\n protected readonly downloadAttribute = computed(() => this.getAttributeValue('download'));\n\n /** Resolved `tabindex` host attribute value for keyboard accessibility on non-anchor hosts. */\n protected readonly tabIndexAttribute = computed(() => {\n if (this.initialTabIndex !== null || this.isAnchorLikeElement) {\n return this.initialTabIndex;\n }\n\n return this.preparedLink() ? '0' : null;\n });\n\n /**\n * Initializes the directive and asserts against invalid input combinations in development mode.\n */\n constructor() {\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n effect(() => {\n if (\n isUrlTree(this.command()?.command) &&\n (this.queryParams() ||\n this.queryParamsHandling() ||\n this.fragment() !== undefined ||\n this.preserveFragment() ||\n this.relativeTo())\n ) {\n throw new Error(\n 'Cannot configure queryParams or fragment when using a UrlTree as the angAnyLink input value.',\n );\n }\n });\n }\n }\n\n /**\n * Delegates click handling to `LinkHandler` when a command is configured.\n *\n * @param event Pointer event emitted by the host click interaction.\n * @returns `true` to keep default browser behavior, or `false` to suppress it.\n */\n protected onClick(event: PointerEvent): boolean {\n const link = this.preparedLink();\n if (!link) {\n return true;\n }\n\n const result = this.handler.navigateTo(link, event, {\n skipLocationChange: this.skipLocationChange(),\n browserUrl: this.browserUrl(),\n replaceUrl: this.replaceUrl(),\n state: this.state(),\n info: this.info(),\n });\n\n return result ?? !this.isAnchorLikeElement;\n }\n\n /**\n * Resolves an attribute value from prepared link metadata, falling back to direct input values.\n *\n * @param name Link attribute name to resolve.\n * @returns Resolved attribute value, `null`, or `undefined` when not provided.\n */\n private getAttributeValue(name: keyof LinkAttributes): string | null | undefined {\n const value = this.preparedLink()?.attributes?.[name];\n return value !== undefined ? value : this[name]();\n }\n}\n","import { ErrorHandler, inject, Injectable, Injector } from '@angular/core';\nimport { ActivatedRoute, NavigationBehaviorOptions, Router, UrlTree } from '@angular/router';\nimport {\n LinkAttributes,\n LinkCommand,\n LinkHandler,\n PreparedLink,\n RouterlessLinkHandler,\n RouterlessLinkHandlerContext,\n} from './link-handler';\nimport { castArray, isUrlTree, tryParseAbsoluteUrl } from './utils';\n\n/**\n * Context for router link handling.\n */\nexport interface RouterLinkHandlerContext extends RouterlessLinkHandlerContext {\n /**\n * Parsed router URL used for Angular Router navigation.\n *\n * Omitted when the link resolves to an absolute external URL.\n */\n urlTree?: UrlTree;\n}\n\n/**\n * Link handler that supports Angular Router commands and absolute URLs.\n */\n@Injectable({\n providedIn: 'root',\n})\nexport class RouterLinkHandler extends RouterlessLinkHandler implements LinkHandler<RouterLinkHandlerContext> {\n /** Angular Router instance used to build and execute route-based navigation. */\n protected readonly router = inject(Router);\n\n /** Angular error handler for reporting navigation failures. */\n protected readonly errorHandler = inject(ErrorHandler);\n\n /**\n * Prepares a link payload that supports both absolute URLs and Angular router commands.\n *\n * @param command Navigation command and URL creation options.\n * @param element Host element associated with the link.\n * @param attributes Optional link attributes from directive inputs.\n * @param injector Optional injector used to resolve `ActivatedRoute` for relative commands.\n * @returns Prepared link metadata used by `navigateTo`.\n */\n override prepareLink(\n command: LinkCommand,\n element?: Element,\n attributes?: LinkAttributes,\n injector?: Injector,\n ): PreparedLink<RouterLinkHandlerContext> {\n if (tryParseAbsoluteUrl(command.command)) {\n if (command.relativeTo && (typeof ngDevMode === 'undefined' || ngDevMode)) {\n // eslint-disable-next-line no-console\n console.warn('The \"relativeTo\" option is not supported for absolute URLs in RouterLinkHandler.');\n }\n\n return super.prepareLink({ ...command, relativeTo: undefined }, element, attributes, injector);\n }\n\n let urlTree: UrlTree | undefined;\n if (isUrlTree(command.command)) {\n urlTree = command.command;\n } else {\n const relativeTo = command.relativeTo ?? injector?.get(ActivatedRoute, null);\n urlTree = this.router.createUrlTree(castArray(command.command), { ...command, relativeTo });\n }\n\n const url = this.router.serializeUrl(urlTree);\n const href = this.location.prepareExternalUrl(url);\n\n return {\n href,\n attributes,\n handlerContext: {\n isAnchorLikeElement: this.isAnchorLikeElement(element),\n urlTree,\n },\n };\n }\n\n /**\n * Navigates to a prepared link using Angular Router when a `UrlTree` is available.\n *\n * Preserves native browser behavior for modified clicks and non-`_self` targets\n * on anchor-like hosts.\n *\n * @param link Prepared link metadata from `prepareLink`.\n * @param event Triggering DOM event.\n * @param options Angular Router navigation behavior options.\n * @returns `true` when native navigation should continue, otherwise `false`.\n */\n override navigateTo(\n link: PreparedLink<RouterLinkHandlerContext>,\n event: Event,\n options: NavigationBehaviorOptions,\n ): boolean {\n const { urlTree, isAnchorLikeElement } = link.handlerContext;\n if (!urlTree) {\n return super.navigateTo(link, event, options);\n }\n\n if (isAnchorLikeElement && event instanceof MouseEvent) {\n const { button, altKey, ctrlKey, metaKey, shiftKey } = event;\n if (button !== 0 || altKey || ctrlKey || metaKey || shiftKey) {\n return true;\n }\n\n const target = link.attributes?.target;\n if (target && target !== '_self') {\n return true;\n }\n }\n\n this.router.navigateByUrl(urlTree, options).catch((error) => {\n this.errorHandler.handleError(error);\n });\n\n return !isAnchorLikeElement;\n }\n}\n","import { APP_ID, inject, Injectable, InjectionToken } from '@angular/core';\n\n/**\n * Configures how IDs are composed by {@link IdGenerator}.\n */\nexport interface IdGeneratorOptions {\n /**\n * Includes a random infix segment in generated IDs when enabled. (Enabled by default)\n */\n randomize?: boolean;\n}\n\n/** Maximum random value used to build the optional hexadecimal infix segment. */\nconst INFIX_MAX = 0xffffff;\n\n/** Default configuration applied when no custom options are provided. */\nconst DEFAULT_GENERATOR_OPTIONS: Required<IdGeneratorOptions> = {\n randomize: true,\n};\n\n/** Dependency injection token for overriding {@link IdGenerator} options. */\nexport const ID_GENERATOR_OPTIONS = new InjectionToken<IdGeneratorOptions>('ID_GENERATOR_OPTIONS', {\n providedIn: 'root',\n factory: () => DEFAULT_GENERATOR_OPTIONS,\n});\n\n/**\n * Generates stable, incrementing DOM-safe IDs.\n */\n@Injectable({\n providedIn: 'root',\n})\nexport class IdGenerator {\n /** Angular application ID. */\n private readonly appId = inject(APP_ID);\n\n /** Effective generator options after merging defaults with user provided overrides. */\n private readonly options = { ...DEFAULT_GENERATOR_OPTIONS, ...inject(ID_GENERATOR_OPTIONS) };\n\n /** Random hexadecimal segment reused across generated IDs when randomization is enabled. */\n private readonly infix = Math.floor(INFIX_MAX * Math.random()).toString(16);\n\n /** Monotonic counter that guarantees uniqueness within this service instance. */\n private counter = 0;\n\n /**\n * Builds a unique ID using the provided prefix and generator configuration.\n *\n * @param prefix Prefix for the ID.\n * @returns A hyphen-delimited identifier.\n */\n getId(prefix: string): string {\n const parts = [prefix];\n\n if (this.appId !== 'ng') {\n parts.push(this.appId);\n }\n if (this.options.randomize) {\n parts.push(this.infix);\n }\n\n parts.push(`${this.counter++}`);\n return parts.join('-');\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@atlasng/common",
3
+ "version": "0.0.2",
4
+ "peerDependencies": {
5
+ "@angular/common": "^21.2.6",
6
+ "@angular/core": "^21.2.6",
7
+ "@angular/router": "^21.2.6",
8
+ "@atlasng/core": "0.0.2"
9
+ },
10
+ "sideEffects": false,
11
+ "module": "fesm2022/atlasng-common.mjs",
12
+ "typings": "types/atlasng-common.d.ts",
13
+ "exports": {
14
+ "./package.json": {
15
+ "default": "./package.json"
16
+ },
17
+ ".": {
18
+ "types": "./types/atlasng-common.d.ts",
19
+ "default": "./fesm2022/atlasng-common.mjs"
20
+ }
21
+ },
22
+ "type": "module",
23
+ "dependencies": {
24
+ "tslib": "^2.3.0"
25
+ }
26
+ }
@@ -0,0 +1,332 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { Injector, ErrorHandler, InjectionToken } from '@angular/core';
3
+ import { UrlCreationOptions, UrlTree, NavigationBehaviorOptions, UrlSerializer, Params, QueryParamsHandling, ActivatedRoute, Router } from '@angular/router';
4
+ import { Location } from '@angular/common';
5
+
6
+ /**
7
+ * Command input for link preparation.
8
+ *
9
+ * Extends Angular URL creation options and adds the target route command.
10
+ */
11
+ interface LinkCommand extends UrlCreationOptions {
12
+ /**
13
+ * Route command represented as a path string, command array, or prebuilt `UrlTree`.
14
+ */
15
+ command: string | readonly unknown[] | UrlTree;
16
+ }
17
+ /**
18
+ * Anchor-relevant attributes that can be applied to prepared links.
19
+ */
20
+ interface LinkAttributes {
21
+ /**
22
+ * Browser target for link navigation.
23
+ */
24
+ target?: string;
25
+ /**
26
+ * Relationship hint used for link security and semantics.
27
+ */
28
+ rel?: string;
29
+ /**
30
+ * Optional filename used for download behavior.
31
+ */
32
+ download?: string;
33
+ }
34
+ /**
35
+ * Link attributes where each value may be `null`.
36
+ *
37
+ * `null` indicates that the attribute should be removed from the host element
38
+ * instead of falling back to it's initial value.
39
+ */
40
+ type NullableLinkAttributes = {
41
+ [K in keyof LinkAttributes]: LinkAttributes[K] | null;
42
+ };
43
+ /**
44
+ * A link value resolved by a `LinkHandler`.
45
+ *
46
+ * Includes the final URL, optional DOM attributes, and handler-specific context.
47
+ */
48
+ interface PreparedLink<C = unknown> {
49
+ /**
50
+ * Final navigation URL.
51
+ */
52
+ href: string;
53
+ /**
54
+ * Optional attributes to apply to the host element.
55
+ */
56
+ attributes?: NullableLinkAttributes;
57
+ /**
58
+ * Handler-owned context used during navigation.
59
+ */
60
+ handlerContext: C;
61
+ }
62
+ /**
63
+ * Base strategy for preparing and executing link navigation.
64
+ */
65
+ declare abstract class LinkHandler<C = unknown> {
66
+ /**
67
+ * Resolves command and element inputs into a navigable link descriptor.
68
+ *
69
+ * @param command Navigation command and URL creation options.
70
+ * @param element Host element associated with the link.
71
+ * @param attributes Optional link attributes from directive inputs.
72
+ * @param injector Optional injector for advanced handler scenarios.
73
+ * @returns A prepared link payload consumed by `navigateTo`.
74
+ */
75
+ abstract prepareLink(command: LinkCommand, element?: Element, attributes?: LinkAttributes, injector?: Injector): PreparedLink<C>;
76
+ /**
77
+ * Performs navigation for a previously prepared link.
78
+ *
79
+ * @param link Prepared link payload.
80
+ * @param event Triggering DOM event.
81
+ * @param options Navigation behavior options.
82
+ * @returns `true` when native navigation should continue, `false` when handled internally,
83
+ * or `void` for implementations that do not provide a continuation signal.
84
+ */
85
+ abstract navigateTo(link: PreparedLink<C>, event: Event, options: NavigationBehaviorOptions): boolean | void;
86
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<LinkHandler<any>, never>;
87
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<LinkHandler<any>>;
88
+ }
89
+ /**
90
+ * Runtime context returned by `RouterlessLinkHandler`.
91
+ */
92
+ interface RouterlessLinkHandlerContext {
93
+ /**
94
+ * Indicates whether the host element can use native anchor navigation.
95
+ */
96
+ isAnchorLikeElement: boolean;
97
+ }
98
+ /**
99
+ * Default link handler that navigates through `window.location` without Angular Router navigation.
100
+ */
101
+ declare class RouterlessLinkHandler extends LinkHandler<RouterlessLinkHandlerContext> {
102
+ /**
103
+ * Browser location object used for hard navigations.
104
+ */
105
+ private readonly browserLocation;
106
+ /**
107
+ * Optional registry for resolving custom elements that behave like anchors.
108
+ */
109
+ private readonly customElementRegistry;
110
+ /**
111
+ * Angular location service used for path inspection and external URL preparation.
112
+ */
113
+ protected readonly location: Location;
114
+ /**
115
+ * Angular URL serializer used for parsing and serializing `UrlTree` values.
116
+ */
117
+ protected readonly serializer: UrlSerializer;
118
+ /**
119
+ * Builds a `PreparedLink` from the given command and element context.
120
+ *
121
+ * @param command Navigation command and URL creation options.
122
+ * @param element Host element associated with the link.
123
+ * @param attributes Optional link attributes.
124
+ * @param _injector Unused injector placeholder for API parity.
125
+ * @returns Prepared link metadata used by `navigateTo`.
126
+ */
127
+ prepareLink(command: LinkCommand, element?: Element, attributes?: LinkAttributes, _injector?: Injector): PreparedLink<RouterlessLinkHandlerContext>;
128
+ /**
129
+ * Navigates to a prepared link using native anchor behavior or `window.location`.
130
+ *
131
+ * @param link Prepared link to navigate to.
132
+ * @param _event Triggering event (unused).
133
+ * @param options Navigation behavior options.
134
+ * @returns `true` when native navigation should continue, otherwise `false`.
135
+ */
136
+ navigateTo(link: PreparedLink<RouterlessLinkHandlerContext>, _event: Event, options: NavigationBehaviorOptions): boolean;
137
+ /**
138
+ * Checks if the given element behaves like an anchor element.
139
+ *
140
+ * @param element Element to check.
141
+ * @returns `true` if the element behaves like an anchor; otherwise `false`.
142
+ */
143
+ protected isAnchorLikeElement(element: Element | undefined): boolean;
144
+ /**
145
+ * Serializes a link command into an external URL string.
146
+ *
147
+ * Does not support all features of Angular's `Router` command arrays (e.g. outlets).
148
+ *
149
+ * @param command Navigation command and URL creation options.
150
+ * @returns External URL ready for navigation.
151
+ */
152
+ protected serializeCommand(command: LinkCommand): string;
153
+ /**
154
+ * Converts a `LinkCommand` into a `UrlTree`.
155
+ *
156
+ * @param command Navigation command and URL creation options.
157
+ * @returns Normalized `UrlTree` for serialization.
158
+ */
159
+ private commandToUrlTree;
160
+ /**
161
+ * Converts a command array into a URL path.
162
+ *
163
+ * @param command Command array.
164
+ * @param currentUrlTree Current URL tree.
165
+ * @returns URL path.
166
+ */
167
+ private commandToPath;
168
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<RouterlessLinkHandler, never>;
169
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<RouterlessLinkHandler>;
170
+ }
171
+
172
+ /** Valid types for the link command input. */
173
+ type AnyLinkCommand = string | readonly unknown[] | UrlTree | LinkCommand | null | undefined;
174
+ /**
175
+ * Generic navigation directive that works with both internal Angular routes and external URLs.
176
+ */
177
+ declare class AnyLink {
178
+ /** Primary link command input. */
179
+ readonly command: _angular_core.InputSignalWithTransform<LinkCommand | undefined, string | UrlTree | LinkCommand | readonly unknown[] | null | undefined>;
180
+ /** Optional browsing context target, e.g. `_blank`. */
181
+ readonly target: _angular_core.InputSignal<string | undefined>;
182
+ /** Optional `rel` attribute value forwarded to the rendered link. */
183
+ readonly rel: _angular_core.InputSignal<string | undefined>;
184
+ /** Optional `download` attribute value forwarded to the rendered link. */
185
+ readonly download: _angular_core.InputSignal<string | undefined>;
186
+ /** Router query params used when the command resolves to an Angular route. */
187
+ readonly queryParams: _angular_core.InputSignal<Params | null | undefined>;
188
+ /** Strategy for merging provided query params with the current URL query params. */
189
+ readonly queryParamsHandling: _angular_core.InputSignal<QueryParamsHandling | null | undefined>;
190
+ /** URL fragment to apply during navigation. */
191
+ readonly fragment: _angular_core.InputSignal<string | undefined>;
192
+ /** Preserves the current fragment when true. */
193
+ readonly preserveFragment: _angular_core.InputSignalWithTransform<boolean, unknown>;
194
+ /** Navigates without pushing a new browser history entry when true. */
195
+ readonly skipLocationChange: _angular_core.InputSignalWithTransform<boolean, unknown>;
196
+ /** Route used as a base for relative navigation commands. */
197
+ readonly relativeTo: _angular_core.InputSignal<ActivatedRoute | null | undefined>;
198
+ /** Optional browser-visible URL override for router navigation. */
199
+ readonly browserUrl: _angular_core.InputSignal<string | UrlTree | undefined>;
200
+ /** Replaces the current history entry instead of pushing a new one when true. */
201
+ readonly replaceUrl: _angular_core.InputSignalWithTransform<boolean, unknown>;
202
+ /** Optional history state object passed to the router. */
203
+ readonly state: _angular_core.InputSignal<Record<string, unknown> | undefined>;
204
+ /** Optional router navigation extras info payload. */
205
+ readonly info: _angular_core.InputSignal<unknown>;
206
+ /** Injector used to resolve optional dependencies while preparing navigation commands. */
207
+ private readonly injector;
208
+ /** Host DOM element the directive is attached to. */
209
+ private readonly element;
210
+ /** Link orchestration service responsible for preparing and executing navigation. */
211
+ private readonly handler;
212
+ /** Initial static `href` attribute from the host element, if present. */
213
+ private readonly initialHref;
214
+ /** Initial static `tabindex` attribute from the host element, if present. */
215
+ private readonly initialTabIndex;
216
+ /** Whether the host behaves as an anchor-like element for native link semantics. */
217
+ private readonly isAnchorLikeElement;
218
+ /** Prepared link model derived from command inputs and current directive state. */
219
+ private readonly preparedLink;
220
+ /** Resolved `href` host attribute value. */
221
+ protected readonly hrefAttribute: _angular_core.Signal<string | null | undefined>;
222
+ /** Resolved `target` host attribute value. */
223
+ protected readonly targetAttribute: _angular_core.Signal<string | null | undefined>;
224
+ /** Resolved `rel` host attribute value. */
225
+ protected readonly relAttribute: _angular_core.Signal<string | null | undefined>;
226
+ /** Resolved `download` host attribute value. */
227
+ protected readonly downloadAttribute: _angular_core.Signal<string | null | undefined>;
228
+ /** Resolved `tabindex` host attribute value for keyboard accessibility on non-anchor hosts. */
229
+ protected readonly tabIndexAttribute: _angular_core.Signal<string | null>;
230
+ /**
231
+ * Initializes the directive and asserts against invalid input combinations in development mode.
232
+ */
233
+ constructor();
234
+ /**
235
+ * Delegates click handling to `LinkHandler` when a command is configured.
236
+ *
237
+ * @param event Pointer event emitted by the host click interaction.
238
+ * @returns `true` to keep default browser behavior, or `false` to suppress it.
239
+ */
240
+ protected onClick(event: PointerEvent): boolean;
241
+ /**
242
+ * Resolves an attribute value from prepared link metadata, falling back to direct input values.
243
+ *
244
+ * @param name Link attribute name to resolve.
245
+ * @returns Resolved attribute value, `null`, or `undefined` when not provided.
246
+ */
247
+ private getAttributeValue;
248
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AnyLink, never>;
249
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<AnyLink, "[angAnyLink]", ["angAnyLink"], { "command": { "alias": "angAnyLink"; "required": false; "isSignal": true; }; "target": { "alias": "target"; "required": false; "isSignal": true; }; "rel": { "alias": "rel"; "required": false; "isSignal": true; }; "download": { "alias": "download"; "required": false; "isSignal": true; }; "queryParams": { "alias": "queryParams"; "required": false; "isSignal": true; }; "queryParamsHandling": { "alias": "queryParamsHandling"; "required": false; "isSignal": true; }; "fragment": { "alias": "fragment"; "required": false; "isSignal": true; }; "preserveFragment": { "alias": "preserveFragment"; "required": false; "isSignal": true; }; "skipLocationChange": { "alias": "skipLocationChange"; "required": false; "isSignal": true; }; "relativeTo": { "alias": "relativeTo"; "required": false; "isSignal": true; }; "browserUrl": { "alias": "browserUrl"; "required": false; "isSignal": true; }; "replaceUrl": { "alias": "replaceUrl"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "info": { "alias": "info"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
250
+ }
251
+
252
+ /**
253
+ * Context for router link handling.
254
+ */
255
+ interface RouterLinkHandlerContext extends RouterlessLinkHandlerContext {
256
+ /**
257
+ * Parsed router URL used for Angular Router navigation.
258
+ *
259
+ * Omitted when the link resolves to an absolute external URL.
260
+ */
261
+ urlTree?: UrlTree;
262
+ }
263
+ /**
264
+ * Link handler that supports Angular Router commands and absolute URLs.
265
+ */
266
+ declare class RouterLinkHandler extends RouterlessLinkHandler implements LinkHandler<RouterLinkHandlerContext> {
267
+ /** Angular Router instance used to build and execute route-based navigation. */
268
+ protected readonly router: Router;
269
+ /** Angular error handler for reporting navigation failures. */
270
+ protected readonly errorHandler: ErrorHandler;
271
+ /**
272
+ * Prepares a link payload that supports both absolute URLs and Angular router commands.
273
+ *
274
+ * @param command Navigation command and URL creation options.
275
+ * @param element Host element associated with the link.
276
+ * @param attributes Optional link attributes from directive inputs.
277
+ * @param injector Optional injector used to resolve `ActivatedRoute` for relative commands.
278
+ * @returns Prepared link metadata used by `navigateTo`.
279
+ */
280
+ prepareLink(command: LinkCommand, element?: Element, attributes?: LinkAttributes, injector?: Injector): PreparedLink<RouterLinkHandlerContext>;
281
+ /**
282
+ * Navigates to a prepared link using Angular Router when a `UrlTree` is available.
283
+ *
284
+ * Preserves native browser behavior for modified clicks and non-`_self` targets
285
+ * on anchor-like hosts.
286
+ *
287
+ * @param link Prepared link metadata from `prepareLink`.
288
+ * @param event Triggering DOM event.
289
+ * @param options Angular Router navigation behavior options.
290
+ * @returns `true` when native navigation should continue, otherwise `false`.
291
+ */
292
+ navigateTo(link: PreparedLink<RouterLinkHandlerContext>, event: Event, options: NavigationBehaviorOptions): boolean;
293
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<RouterLinkHandler, never>;
294
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<RouterLinkHandler>;
295
+ }
296
+
297
+ /**
298
+ * Configures how IDs are composed by {@link IdGenerator}.
299
+ */
300
+ interface IdGeneratorOptions {
301
+ /**
302
+ * Includes a random infix segment in generated IDs when enabled. (Enabled by default)
303
+ */
304
+ randomize?: boolean;
305
+ }
306
+ /** Dependency injection token for overriding {@link IdGenerator} options. */
307
+ declare const ID_GENERATOR_OPTIONS: InjectionToken<IdGeneratorOptions>;
308
+ /**
309
+ * Generates stable, incrementing DOM-safe IDs.
310
+ */
311
+ declare class IdGenerator {
312
+ /** Angular application ID. */
313
+ private readonly appId;
314
+ /** Effective generator options after merging defaults with user provided overrides. */
315
+ private readonly options;
316
+ /** Random hexadecimal segment reused across generated IDs when randomization is enabled. */
317
+ private readonly infix;
318
+ /** Monotonic counter that guarantees uniqueness within this service instance. */
319
+ private counter;
320
+ /**
321
+ * Builds a unique ID using the provided prefix and generator configuration.
322
+ *
323
+ * @param prefix Prefix for the ID.
324
+ * @returns A hyphen-delimited identifier.
325
+ */
326
+ getId(prefix: string): string;
327
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<IdGenerator, never>;
328
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<IdGenerator>;
329
+ }
330
+
331
+ export { AnyLink, ID_GENERATOR_OPTIONS, IdGenerator, LinkHandler, RouterLinkHandler, RouterlessLinkHandler };
332
+ export type { AnyLinkCommand, IdGeneratorOptions, LinkAttributes, LinkCommand, NullableLinkAttributes, PreparedLink, RouterLinkHandlerContext, RouterlessLinkHandlerContext };