@c8y/ngx-components 1024.14.1 → 1024.15.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.
@@ -1,12 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, input, effect, Directive, Pipe, createEnvironmentInjector, InjectionToken, Optional, Inject, EventEmitter, NgModule, LOCALE_ID, NgModuleRef, createNgModule, isDevMode, inject, DOCUMENT, EnvironmentInjector, Injector, Input, HostListener, Component, HostBinding, Output, forwardRef, DestroyRef, ViewChild, signal, viewChild, ChangeDetectionStrategy, contentChildren, untracked, SecurityContext, TemplateRef, ElementRef, provideAppInitializer, Self, SkipSelf, Attribute, ContentChild, ContentChildren, ViewContainerRef, output, ChangeDetectorRef, ViewChildren, createComponent, runInInjectionContext, importProvidersFrom, SimpleChange, reflectComponentType, ViewEncapsulation, computed, Type } from '@angular/core';
2
+ import { Injectable, input, effect, Directive, Pipe, createEnvironmentInjector, InjectionToken, Optional, Inject, EventEmitter, NgModule, LOCALE_ID, NgModuleRef, createNgModule, isDevMode, inject, DOCUMENT, EnvironmentInjector, Injector, Input, HostListener, Component, HostBinding, Output, forwardRef, DestroyRef, ViewChild, signal, viewChild, ChangeDetectionStrategy, contentChildren, untracked, SecurityContext, TemplateRef, ElementRef, provideAppInitializer, Self, SkipSelf, Attribute, ContentChild, computed, ContentChildren, ViewContainerRef, output, ChangeDetectorRef, ViewChildren, createComponent, runInInjectionContext, importProvidersFrom, SimpleChange, reflectComponentType, ViewEncapsulation, Type } from '@angular/core';
3
3
  import * as i1$2 from 'ngx-bootstrap/dropdown';
4
4
  import { BsDropdownDirective, BsDropdownModule } from 'ngx-bootstrap/dropdown';
5
5
  import * as i4$2 from '@angular/cdk/a11y';
6
6
  import { ConfigurableFocusTrapFactory, CdkTrapFocus, A11yModule } from '@angular/cdk/a11y';
7
7
  import { of, Observable, map, startWith, distinctUntilChanged, shareReplay, merge, defer, combineLatest, race, isObservable, from, Subject, BehaviorSubject, ReplaySubject, firstValueFrom, lastValueFrom, NEVER, filter as filter$1, tap as tap$1, mergeMap, fromEvent, pipe, throwError, interval, take as take$1, takeUntil as takeUntil$1, concat, fromEventPattern, withLatestFrom, catchError as catchError$1, empty, timer, forkJoin, debounceTime as debounceTime$1, switchMap as switchMap$1, skip as skip$1 } from 'rxjs';
8
8
  import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
9
- import { castArray, flatten, uniq, sortBy, groupBy, camelCase, isEqual, isUndefined, throttle as throttle$1, each, mapValues, mapKeys, keys, get, isNaN as isNaN$1, isFinite, forEach, assign, min, every, first, map as map$2, find, negate, upperFirst, memoize as memoize$1, isEmpty, property, some, entries, omitBy, isDate, pick, flatMap, orderBy, filter as filter$2, snakeCase, matches, escape, escapeRegExp, assignWith, set, toNumber, isEqualWith, clone, omit, cloneDeep, uniqBy, has, transform, identity, unset, flow, findIndex as findIndex$1, isNil, chunk, values, union, without, indexOf, kebabCase, forOwn } from 'lodash-es';
9
+ import { castArray, flatten, uniq, sortBy, groupBy, camelCase, isEqual, isUndefined, throttle as throttle$1, each, mapValues, mapKeys, keys, get, isNaN as isNaN$1, isFinite, forEach, assign, min, every, first, map as map$2, find, negate, upperFirst, memoize as memoize$1, isEmpty, property, some, entries, omitBy, isDate, pick, flatMap, orderBy, filter as filter$2, snakeCase, matches, isNil, escape, escapeRegExp, assignWith, set, toNumber, isEqualWith, clone, omit, cloneDeep, uniqBy, has, transform, identity, unset, flow, findIndex as findIndex$1, chunk, values, union, without, indexOf, kebabCase, forOwn } from 'lodash-es';
10
10
  import { map as map$1, distinctUntilChanged as distinctUntilChanged$1, filter, startWith as startWith$1, switchMap, shareReplay as shareReplay$1, take, scan, takeUntil, tap, catchError, debounceTime, share, first as first$1, retryWhen, delay, concatMap, debounce, sample, withLatestFrom as withLatestFrom$1, mergeMap as mergeMap$1, every as every$1, toArray, merge as merge$1, mapTo, skip, reduce, finalize, combineLatestWith } from 'rxjs/operators';
11
11
  import * as i1 from '@c8y/client';
12
12
  import { InventoryService, OperationStatus, TenantLoginOptionType, UserManagementSource, GrantType, ApplicationType, BasicAuth, CookieAuth, Realtime, FetchClient, BearerAuthFromSessionStorage, QueriesUtil, Client, PasswordStrength, ApplicationAvailability, AlarmService, TenantService, ApplicationService, UserService, aggregationType, Service, Paging, FeatureService } from '@c8y/client';
@@ -9257,30 +9257,74 @@ class AssetHierarchyService {
9257
9257
  if (!parentIdsOfAsset || parentIdsOfAsset.length === 0) {
9258
9258
  return [[asset]];
9259
9259
  }
9260
- const { data: parents } = await this.inventory.list({
9261
- pageSize: 2000,
9262
- ids: parentIdsOfAsset.join(','),
9263
- withParents: true,
9264
- withChildren: true
9265
- });
9266
- // Deduplicate managed objects by ID to handle self-referencing parents and circular relationships
9267
- const allManagedObjects = [asset, ...parents];
9268
- const uniqueManagedObjectsMap = new Map(allManagedObjects.map(mo => [mo.id, mo]));
9269
- const parentsAndAsset = Array.from(uniqueManagedObjectsMap.values());
9270
- const roots = parentsAndAsset.filter(parent => this.getParentIds(parent).length === 0);
9271
- // Find all paths from each root to the target asset
9272
- const breadcrumbPaths = [];
9273
- roots.forEach(root => {
9274
- const paths = this.findAllPathsToAsset(root, assetId, parentsAndAsset);
9275
- breadcrumbPaths.push(...paths);
9276
- });
9277
- return breadcrumbPaths;
9260
+ return await this.resolvePathsFromParents(asset, assetId, parentIdsOfAsset);
9278
9261
  }
9279
9262
  catch (e) {
9280
9263
  this.alertService.addServerFailure(e);
9281
9264
  return [];
9282
9265
  }
9283
9266
  }
9267
+ /**
9268
+ * Resolves ancestor paths only when the asset has at most one direct parent
9269
+ * (the cheap, common single-path case). Returns null when the asset is directly
9270
+ * assigned to multiple parents — the caller should keep the lazy click-to-load
9271
+ * flow rather than auto-paying the full hierarchy traversal.
9272
+ *
9273
+ * Costs a single `inventory.detail` call to count direct parents; only proceeds
9274
+ * to the heavier `inventory.list` traversal for single-parent assets.
9275
+ *
9276
+ * @param assetId - The ID of the target asset
9277
+ * @returns Ancestor paths (root → asset) for single-parent assets, or `null`
9278
+ * when the asset has multiple direct parents.
9279
+ */
9280
+ async getAncestorPathsForSingleParent(assetId) {
9281
+ if (!assetId) {
9282
+ return [];
9283
+ }
9284
+ try {
9285
+ const { data: asset } = await this.inventory.detail(assetId, {
9286
+ withParents: true
9287
+ });
9288
+ const parentIdsOfAsset = this.getParentIds(asset).filter(id => id !== assetId);
9289
+ if (parentIdsOfAsset.length === 0) {
9290
+ return [[asset]];
9291
+ }
9292
+ if (parentIdsOfAsset.length > 1) {
9293
+ return null;
9294
+ }
9295
+ return await this.resolvePathsFromParents(asset, assetId, parentIdsOfAsset);
9296
+ }
9297
+ catch {
9298
+ // Eager auto-resolve is a background enhancement — on failure fall back to
9299
+ // the lazy ellipsis flow instead of surfacing a danger alert for a crumb
9300
+ // the user never asked to expand (e.g. the managed object no longer exists).
9301
+ return null;
9302
+ }
9303
+ }
9304
+ /**
9305
+ * Fetches the given parents and walks every path from each root down to the
9306
+ * target asset.
9307
+ */
9308
+ async resolvePathsFromParents(asset, assetId, parentIds) {
9309
+ const { data: parents } = await this.inventory.list({
9310
+ pageSize: 2000,
9311
+ ids: parentIds.join(','),
9312
+ withParents: true,
9313
+ withChildren: true
9314
+ });
9315
+ // Deduplicate managed objects by ID to handle self-referencing parents and circular relationships
9316
+ const allManagedObjects = [asset, ...parents];
9317
+ const uniqueManagedObjectsMap = new Map(allManagedObjects.map(mo => [mo.id, mo]));
9318
+ const parentsAndAsset = Array.from(uniqueManagedObjectsMap.values());
9319
+ const roots = parentsAndAsset.filter(parent => this.getParentIds(parent).length === 0);
9320
+ // Find all paths from each root to the target asset
9321
+ const breadcrumbPaths = [];
9322
+ roots.forEach(root => {
9323
+ const paths = this.findAllPathsToAsset(root, assetId, parentsAndAsset);
9324
+ breadcrumbPaths.push(...paths);
9325
+ });
9326
+ return breadcrumbPaths;
9327
+ }
9284
9328
  /**
9285
9329
  * Gets all parent IDs of a managed object.
9286
9330
  */
@@ -14153,7 +14197,7 @@ class BreadcrumbService extends ExtensionPointForPlugins {
14153
14197
  return this.state$.value;
14154
14198
  }
14155
14199
  /**
14156
- * Adds a new item to the action bar in the header.
14200
+ * Adds a breadcrumb path to the breadcrumb trail.
14157
14201
  * @param item The item to add.
14158
14202
  */
14159
14203
  add(item) {
@@ -14161,7 +14205,7 @@ class BreadcrumbService extends ExtensionPointForPlugins {
14161
14205
  this.emitNewState();
14162
14206
  }
14163
14207
  /**
14164
- * Removes an action bar item from the header.
14208
+ * Removes a breadcrumb path from the breadcrumb trail.
14165
14209
  * @param item The item to remove.
14166
14210
  */
14167
14211
  remove(item) {
@@ -14177,17 +14221,50 @@ class BreadcrumbService extends ExtensionPointForPlugins {
14177
14221
  this.refresh();
14178
14222
  }
14179
14223
  sortByPreferredPath(breadcrumbs) {
14180
- if (this.preferredPath) {
14181
- return breadcrumbs.sort(bc => bc.items.find((item) => !!item.path?.match(this.preferredPath)) ? -1 : 1);
14224
+ if (!this.preferredPath) {
14225
+ return breadcrumbs;
14182
14226
  }
14183
- return breadcrumbs;
14227
+ // Stable partition: crumbs containing the preferred path move to the front,
14228
+ // every other crumb keeps its original relative order. (A single-argument
14229
+ // Array.sort comparator never inspects the second element, so it cannot do this.)
14230
+ const matchesPreferred = (bc) => bc.items.some((item) => !!item.path?.match(this.preferredPath));
14231
+ return [
14232
+ ...breadcrumbs.filter(matchesPreferred),
14233
+ ...breadcrumbs.filter(bc => !matchesPreferred(bc))
14234
+ ];
14184
14235
  }
14185
14236
  setupItemsObservable() {
14186
14237
  return fromTrigger(this.router, this.refresh$, [
14187
14238
  getInjectedHooks(HOOK_BREADCRUMB, this.injectors),
14188
14239
  () => this.factories,
14189
14240
  stateToFactory(this.state$)
14190
- ]).pipe(distinctUntilChanged$1(), map$1(breadcrumbs => this.sortByPreferredPath(breadcrumbs)), shareReplay$1(1));
14241
+ ]).pipe(distinctUntilChanged$1(), map$1(breadcrumbs => this.sortByPreferredPath(this.dedupeByContent(breadcrumbs))), shareReplay$1(1));
14242
+ }
14243
+ /**
14244
+ * Removes breadcrumbs that resolve to the same crumbs. The same factory can be
14245
+ * picked up from more than one injector (e.g. provided directly and via a plugin
14246
+ * module), which would otherwise surface as duplicate paths in the dropdown.
14247
+ * Genuinely distinct paths (a node reachable through different parents) are kept.
14248
+ *
14249
+ * Only fully label-based crumbs are de-duplicated. Crumbs carrying a `component`
14250
+ * or `template` item are always kept: a `Type` or `TemplateRef` has no stable,
14251
+ * comparable identity here (minified differently across bundles, or a fresh
14252
+ * reference per render), so they cannot be reliably matched.
14253
+ */
14254
+ dedupeByContent(breadcrumbs) {
14255
+ const seen = new Set();
14256
+ return breadcrumbs.filter(breadcrumb => {
14257
+ const items = breadcrumb.items ?? [];
14258
+ if (!items.length || items.some(item => item.component || item.template)) {
14259
+ return true;
14260
+ }
14261
+ const signature = items.map(item => `${item.label ?? ''}|${item.path ?? ''}`).join('>');
14262
+ if (seen.has(signature)) {
14263
+ return false;
14264
+ }
14265
+ seen.add(signature);
14266
+ return true;
14267
+ });
14191
14268
  }
14192
14269
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: BreadcrumbService, deps: [{ token: i0.Injector }, { token: i1$3.Router }, { token: PluginsResolveService }], target: i0.ɵɵFactoryTarget.Injectable }); }
14193
14270
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: BreadcrumbService, providedIn: 'root' }); }
@@ -14296,8 +14373,24 @@ class DocsService extends ExtensionPointWithoutStateForPlugins {
14296
14373
  this.DEFAULT_DOCS_BASE_URL = 'https://cumulocity.com';
14297
14374
  this.items$ = this.setupItemsObservable();
14298
14375
  }
14376
+ /**
14377
+ * Indicates whether documentation links should be displayed at all. Setting `docsBaseUrl` to
14378
+ * `null` or to an empty string switches the links off, whereas `'/'` keeps them enabled and
14379
+ * resolves to links relative to the host the application is served from.
14380
+ */
14381
+ isDocsEnabled() {
14382
+ const docsBaseUrl = this.options.get('docsBaseUrl', this.DEFAULT_DOCS_BASE_URL);
14383
+ return !isNil(docsBaseUrl) && docsBaseUrl !== '';
14384
+ }
14385
+ /**
14386
+ * Returns the base URL for documentation links, or `null` if the links are switched off with
14387
+ * `docsBaseUrl: null`.
14388
+ */
14299
14389
  getBaseUrl(uiVersion) {
14300
14390
  const docsBaseUrl = this.options.get('docsBaseUrl', this.DEFAULT_DOCS_BASE_URL);
14391
+ if (isNil(docsBaseUrl)) {
14392
+ return null;
14393
+ }
14301
14394
  return this.getUrlWithDocsVersion(docsBaseUrl, uiVersion);
14302
14395
  }
14303
14396
  /**
@@ -14318,11 +14411,15 @@ class DocsService extends ExtensionPointWithoutStateForPlugins {
14318
14411
  get templateStr() {
14319
14412
  return this.options.get('guideHrefTemplate', '${docsBaseUrl}${partialUrl}');
14320
14413
  }
14414
+ /**
14415
+ * Builds a link to a documentation page, or returns `null` if documentation links are switched
14416
+ * off. Absolute links are returned as they are.
14417
+ */
14321
14418
  getUserGuideLink(link) {
14322
14419
  if (/^https?:/.test(link)) {
14323
14420
  return link;
14324
14421
  }
14325
- if (this.getBaseUrl === null) {
14422
+ if (!this.isDocsEnabled()) {
14326
14423
  return null;
14327
14424
  }
14328
14425
  return this.getLink(this.templateStr, link);
@@ -14339,7 +14436,7 @@ class DocsService extends ExtensionPointWithoutStateForPlugins {
14339
14436
  ? []
14340
14437
  : documentationItems
14341
14438
  .map((item) => ({ ...item, url: this.getUserGuideLink(item.url) }))
14342
- .filter(({ url }) => !excludeDefault.some(e => new RegExp(e).test(url)));
14439
+ .filter(({ url }) => !!url && !excludeDefault.some(e => new RegExp(e).test(url)));
14343
14440
  if (links) {
14344
14441
  // backwards compatibility
14345
14442
  links.map((lnk) => {
@@ -14602,39 +14699,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
14602
14699
 
14603
14700
  class BreadcrumbOutletComponent {
14604
14701
  constructor() {
14605
- this.showAll = false;
14606
- this.breadcrumbs = [];
14607
- this.dropdownOpen = false;
14702
+ this.breadcrumbs = input([], ...(ngDevMode ? [{ debugName: "breadcrumbs" }] : /* istanbul ignore next */ []));
14608
14703
  this.GROUP_ICON = 'c8y-group';
14704
+ /**
14705
+ * Breadcrumbs that represent a fully resolved path (every crumb has a label).
14706
+ * The group breadcrumb factory also emits an un-resolved breadcrumb that carries
14707
+ * a component placeholder (the "…" ellipsis) instead of labels; that one is not a
14708
+ * real alternate path and must not count towards the dropdown.
14709
+ */
14710
+ this.resolvedBreadcrumbs = computed(() => (this.breadcrumbs() ?? []).filter(b => this.isResolvedPath(b)), ...(ngDevMode ? [{ debugName: "resolvedBreadcrumbs" }] : /* istanbul ignore next */ []));
14711
+ /**
14712
+ * The breadcrumb shown inline. Prefers a resolved path; falls back to the first
14713
+ * breadcrumb (the ellipsis breadcrumb) while ancestors are not loaded yet.
14714
+ */
14715
+ this.inlineBreadcrumb = computed(() => this.resolvedBreadcrumbs()[0] ?? (this.breadcrumbs() ?? [])[0], ...(ngDevMode ? [{ debugName: "inlineBreadcrumb" }] : /* istanbul ignore next */ []));
14716
+ /** Resolved breadcrumbs other than the inline one — these populate the dropdown. */
14717
+ this.alternateBreadcrumbs = computed(() => this.resolvedBreadcrumbs().filter(b => b !== this.inlineBreadcrumb()), ...(ngDevMode ? [{ debugName: "alternateBreadcrumbs" }] : /* istanbul ignore next */ []));
14718
+ /** The toggle is only relevant when the node belongs to more than one path. */
14719
+ this.showToggle = computed(() => this.alternateBreadcrumbs().length > 0, ...(ngDevMode ? [{ debugName: "showToggle" }] : /* istanbul ignore next */ []));
14720
+ this.dropdownOpen = computed(() => this.showToggle() && this.resolvedBreadcrumbs().some(b => b.forceDropdownOpen), ...(ngDevMode ? [{ debugName: "dropdownOpen" }] : /* istanbul ignore next */ []));
14609
14721
  }
14610
14722
  /**
14611
14723
  * For upgrade only. Old angularjs routes start with hash, new ones not.
14612
14724
  */
14613
14725
  normalizePath(path) {
14614
- return path?.replace(/^#\/?/, '');
14726
+ return path?.replace(/^#\/?/, '') ?? '';
14615
14727
  }
14616
- ngOnChanges() {
14617
- this.dropdownOpen =
14618
- this.breadcrumbs?.length > 1 && this.breadcrumbs.some(b => b.forceDropdownOpen);
14728
+ isResolvedPath(breadcrumb) {
14729
+ return !!breadcrumb?.items?.length && breadcrumb.items.every(item => item.label != null);
14619
14730
  }
14620
14731
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: BreadcrumbOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
14621
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: BreadcrumbOutletComponent, isStandalone: true, selector: "c8y-breadcrumb-outlet", inputs: { breadcrumbs: "breadcrumbs", dropdownOpen: "dropdownOpen" }, usesOnChanges: true, ngImport: i0, template: "@if (breadcrumbs && breadcrumbs.length > 0) {\n <div class=\"breadcrumbs-container d-flex\">\n @if (breadcrumbs.length > 1) {\n <div\n class=\"l-h-1\"\n container=\"body\"\n dropdown\n #breadcrumbDropdown=\"bs-dropdown\"\n [isOpen]=\"dropdownOpen\"\n >\n <button\n class=\"btn-clean btn-xs btn p-l-4 p-r-4 p-t-0 p-b-0\"\n title=\"{{ 'Show all paths' | translate }}\"\n type=\"button\"\n dropdownToggle\n >\n <i [c8yIcon]=\"breadcrumbDropdown.isOpen ? 'caret-down' : 'caret-right'\"></i>\n </button>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n >\n @for (breadcrumb of breadcrumbs; track breadcrumb; let i = $index; let first = $first) {\n <!-- in dropdown menu, show all breadcrumbs except the first one -->\n @if (!first) {\n <div class=\"p-t-8 p-r-16 p-b-8 p-l-16 separator-bottom\">\n <ul class=\"breadcrumbs text-muted m-l-4\">\n @for (\n item of breadcrumb.items;\n track item;\n let isLast = $last;\n let isFirst = $first\n ) {\n <li class=\"text-muted\">\n @if (!isFirst) {\n <span><i [c8yIcon]=\"'forward'\"></i></span>\n }\n @if (!isLast) {\n <a\n class=\"word-break\"\n [routerLink]=\"item.path\"\n >\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n ></i>\n }\n <span>{{ item.label | translate }}</span>\n </a>\n } @else {\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n ></i>\n }\n <span>{{ item.label | translate }}</span>\n }\n </li>\n }\n </ul>\n </div>\n }\n }\n </div>\n </div>\n }\n <ul\n class=\"breadcrumbs text-muted\"\n [attr.role]=\"'navigation'\"\n >\n <!-- show only first breadcrumb- applicable both for single and multiple breadcrumb cases (for multiple breadcrumbs, rest are in dropdown) -->\n @for (item of breadcrumbs[0].items; track item; let firstItem = $first) {\n <li class=\"text-muted\">\n @if (!firstItem) {\n <span class=\"l-h-1 a-s-stretch d-flex a-i-center\"><i c8yIcon=\"forward\"></i></span>\n }\n <ng-container\n *c8yOutlet=\"item.component || item.template; injector: breadcrumbs[0].injector\"\n ></ng-container>\n @if (item.label && item.path) {\n <a\n class=\"word-break\"\n title=\"{{ item.label | translate }}\"\n [attr.aria-label]=\"'breadcrumb'\"\n [routerLink]=\"normalizePath(item.path)\"\n >\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n ></i>\n <span>{{ item.label | translate }}</span>\n </a>\n }\n @if (item.label && !item.path) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n ></i>\n <span title=\"{{ item.label | translate }}\">{{ item.label | translate }}</span>\n }\n </li>\n }\n </ul>\n </div>\n}\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: OutletDirective, selector: "[c8yOutlet]", inputs: ["c8yOutlet", "c8yOutletProperties", "c8yOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: BsDropdownModule }, { kind: "directive", type: i1$2.BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: i1$2.BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: i1$2.BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
14732
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: BreadcrumbOutletComponent, isStandalone: true, selector: "c8y-breadcrumb-outlet", inputs: { breadcrumbs: { classPropertyName: "breadcrumbs", publicName: "breadcrumbs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (breadcrumbs() && breadcrumbs().length > 0) {\n <div class=\"breadcrumbs-container d-flex\">\n @if (showToggle()) {\n <div\n class=\"l-h-1\"\n container=\"body\"\n dropdown\n #breadcrumbDropdown=\"bs-dropdown\"\n [isOpen]=\"dropdownOpen()\"\n >\n <button\n class=\"btn-clean btn-xs btn p-l-4 p-r-4 p-t-0 p-b-0\"\n title=\"{{ 'Show all paths' | translate }}\"\n [attr.aria-label]=\"'Show all paths' | translate\"\n type=\"button\"\n dropdownToggle\n >\n <i\n [c8yIcon]=\"breadcrumbDropdown.isOpen ? 'caret-down' : 'caret-right'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n >\n @for (breadcrumb of alternateBreadcrumbs(); track breadcrumb) {\n <div class=\"p-t-8 p-r-16 p-b-8 p-l-16 separator-bottom\">\n <ul class=\"breadcrumbs text-muted m-l-4\">\n @for (\n item of breadcrumb.items;\n track item;\n let isLast = $last;\n let isFirst = $first\n ) {\n <li class=\"text-muted\">\n @if (!isFirst) {\n <span\n ><i\n [c8yIcon]=\"'forward'\"\n aria-hidden=\"true\"\n ></i\n ></span>\n }\n @if (!isLast) {\n <a\n class=\"word-break dropdown-item\"\n [routerLink]=\"item.path\"\n >\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n <span>{{ item.label | translate }}</span>\n </a>\n } @else {\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n <span>{{ item.label | translate }}</span>\n }\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n }\n <ul\n class=\"breadcrumbs text-muted\"\n [attr.aria-label]=\"'Breadcrumb' | translate\"\n [attr.role]=\"'navigation'\"\n >\n <!-- show only the inline breadcrumb; alternate paths (if any) live in the dropdown -->\n @for (\n item of inlineBreadcrumb().items;\n track item;\n let firstItem = $first;\n let isLast = $last\n ) {\n <li class=\"text-muted\">\n @if (!firstItem) {\n <span class=\"l-h-1 a-s-stretch d-flex a-i-center\"\n ><i\n c8yIcon=\"forward\"\n aria-hidden=\"true\"\n ></i\n ></span>\n }\n <ng-container\n *c8yOutlet=\"item.component || item.template; injector: inlineBreadcrumb().injector\"\n ></ng-container>\n @if (item.label && item.path) {\n <a\n class=\"word-break\"\n title=\"{{ item.label | translate }}\"\n [attr.aria-label]=\"item.label | translate\"\n [attr.aria-current]=\"isLast ? 'page' : null\"\n [routerLink]=\"normalizePath(item.path)\"\n >\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n <span>{{ item.label | translate }}</span>\n </a>\n }\n @if (item.label && !item.path) {\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n <span\n title=\"{{ item.label | translate }}\"\n [attr.aria-current]=\"isLast ? 'page' : null\"\n >{{ item.label | translate }}</span\n >\n }\n </li>\n }\n </ul>\n </div>\n}\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: OutletDirective, selector: "[c8yOutlet]", inputs: ["c8yOutlet", "c8yOutletProperties", "c8yOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: BsDropdownModule }, { kind: "directive", type: i1$2.BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: i1$2.BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: i1$2.BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
14622
14733
  }
14623
14734
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: BreadcrumbOutletComponent, decorators: [{
14624
14735
  type: Component,
14625
- args: [{ selector: 'c8y-breadcrumb-outlet', standalone: true, imports: [
14736
+ args: [{ selector: 'c8y-breadcrumb-outlet', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
14626
14737
  IconDirective,
14627
14738
  TooltipModule,
14628
14739
  OutletDirective,
14629
14740
  RouterLink,
14630
14741
  C8yTranslatePipe,
14631
14742
  BsDropdownModule
14632
- ], template: "@if (breadcrumbs && breadcrumbs.length > 0) {\n <div class=\"breadcrumbs-container d-flex\">\n @if (breadcrumbs.length > 1) {\n <div\n class=\"l-h-1\"\n container=\"body\"\n dropdown\n #breadcrumbDropdown=\"bs-dropdown\"\n [isOpen]=\"dropdownOpen\"\n >\n <button\n class=\"btn-clean btn-xs btn p-l-4 p-r-4 p-t-0 p-b-0\"\n title=\"{{ 'Show all paths' | translate }}\"\n type=\"button\"\n dropdownToggle\n >\n <i [c8yIcon]=\"breadcrumbDropdown.isOpen ? 'caret-down' : 'caret-right'\"></i>\n </button>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n >\n @for (breadcrumb of breadcrumbs; track breadcrumb; let i = $index; let first = $first) {\n <!-- in dropdown menu, show all breadcrumbs except the first one -->\n @if (!first) {\n <div class=\"p-t-8 p-r-16 p-b-8 p-l-16 separator-bottom\">\n <ul class=\"breadcrumbs text-muted m-l-4\">\n @for (\n item of breadcrumb.items;\n track item;\n let isLast = $last;\n let isFirst = $first\n ) {\n <li class=\"text-muted\">\n @if (!isFirst) {\n <span><i [c8yIcon]=\"'forward'\"></i></span>\n }\n @if (!isLast) {\n <a\n class=\"word-break\"\n [routerLink]=\"item.path\"\n >\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n ></i>\n }\n <span>{{ item.label | translate }}</span>\n </a>\n } @else {\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n ></i>\n }\n <span>{{ item.label | translate }}</span>\n }\n </li>\n }\n </ul>\n </div>\n }\n }\n </div>\n </div>\n }\n <ul\n class=\"breadcrumbs text-muted\"\n [attr.role]=\"'navigation'\"\n >\n <!-- show only first breadcrumb- applicable both for single and multiple breadcrumb cases (for multiple breadcrumbs, rest are in dropdown) -->\n @for (item of breadcrumbs[0].items; track item; let firstItem = $first) {\n <li class=\"text-muted\">\n @if (!firstItem) {\n <span class=\"l-h-1 a-s-stretch d-flex a-i-center\"><i c8yIcon=\"forward\"></i></span>\n }\n <ng-container\n *c8yOutlet=\"item.component || item.template; injector: breadcrumbs[0].injector\"\n ></ng-container>\n @if (item.label && item.path) {\n <a\n class=\"word-break\"\n title=\"{{ item.label | translate }}\"\n [attr.aria-label]=\"'breadcrumb'\"\n [routerLink]=\"normalizePath(item.path)\"\n >\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n ></i>\n <span>{{ item.label | translate }}</span>\n </a>\n }\n @if (item.label && !item.path) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n ></i>\n <span title=\"{{ item.label | translate }}\">{{ item.label | translate }}</span>\n }\n </li>\n }\n </ul>\n </div>\n}\n" }]
14633
- }], propDecorators: { breadcrumbs: [{
14634
- type: Input
14635
- }], dropdownOpen: [{
14636
- type: Input
14637
- }] } });
14743
+ ], template: "@if (breadcrumbs() && breadcrumbs().length > 0) {\n <div class=\"breadcrumbs-container d-flex\">\n @if (showToggle()) {\n <div\n class=\"l-h-1\"\n container=\"body\"\n dropdown\n #breadcrumbDropdown=\"bs-dropdown\"\n [isOpen]=\"dropdownOpen()\"\n >\n <button\n class=\"btn-clean btn-xs btn p-l-4 p-r-4 p-t-0 p-b-0\"\n title=\"{{ 'Show all paths' | translate }}\"\n [attr.aria-label]=\"'Show all paths' | translate\"\n type=\"button\"\n dropdownToggle\n >\n <i\n [c8yIcon]=\"breadcrumbDropdown.isOpen ? 'caret-down' : 'caret-right'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n >\n @for (breadcrumb of alternateBreadcrumbs(); track breadcrumb) {\n <div class=\"p-t-8 p-r-16 p-b-8 p-l-16 separator-bottom\">\n <ul class=\"breadcrumbs text-muted m-l-4\">\n @for (\n item of breadcrumb.items;\n track item;\n let isLast = $last;\n let isFirst = $first\n ) {\n <li class=\"text-muted\">\n @if (!isFirst) {\n <span\n ><i\n [c8yIcon]=\"'forward'\"\n aria-hidden=\"true\"\n ></i\n ></span>\n }\n @if (!isLast) {\n <a\n class=\"word-break dropdown-item\"\n [routerLink]=\"item.path\"\n >\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n <span>{{ item.label | translate }}</span>\n </a>\n } @else {\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n <span>{{ item.label | translate }}</span>\n }\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n }\n <ul\n class=\"breadcrumbs text-muted\"\n [attr.aria-label]=\"'Breadcrumb' | translate\"\n [attr.role]=\"'navigation'\"\n >\n <!-- show only the inline breadcrumb; alternate paths (if any) live in the dropdown -->\n @for (\n item of inlineBreadcrumb().items;\n track item;\n let firstItem = $first;\n let isLast = $last\n ) {\n <li class=\"text-muted\">\n @if (!firstItem) {\n <span class=\"l-h-1 a-s-stretch d-flex a-i-center\"\n ><i\n c8yIcon=\"forward\"\n aria-hidden=\"true\"\n ></i\n ></span>\n }\n <ng-container\n *c8yOutlet=\"item.component || item.template; injector: inlineBreadcrumb().injector\"\n ></ng-container>\n @if (item.label && item.path) {\n <a\n class=\"word-break\"\n title=\"{{ item.label | translate }}\"\n [attr.aria-label]=\"item.label | translate\"\n [attr.aria-current]=\"isLast ? 'page' : null\"\n [routerLink]=\"normalizePath(item.path)\"\n >\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n <span>{{ item.label | translate }}</span>\n </a>\n }\n @if (item.label && !item.path) {\n @if (item.icon) {\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n <span\n title=\"{{ item.label | translate }}\"\n [attr.aria-current]=\"isLast ? 'page' : null\"\n >{{ item.label | translate }}</span\n >\n }\n </li>\n }\n </ul>\n </div>\n}\n" }]
14744
+ }], propDecorators: { breadcrumbs: [{ type: i0.Input, args: [{ isSignal: true, alias: "breadcrumbs", required: false }] }] } });
14638
14745
 
14639
14746
  class SearchOutletComponent {
14640
14747
  constructor(alertService) {
@@ -14724,7 +14831,7 @@ class HeaderBarComponent {
14724
14831
  this.headerService.closeRightDrawer();
14725
14832
  }
14726
14833
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: HeaderBarComponent, deps: [{ token: HeaderService }, { token: ActionService }, { token: BreadcrumbService }, { token: SearchService }, { token: AppStateService }, { token: UserMenuService }, { token: DocsService }, { token: i4.ApiService }], target: i0.ɵɵFactoryTarget.Component }); }
14727
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: HeaderBarComponent, isStandalone: true, selector: "c8y-header-bar", inputs: { simple: "simple" }, ngImport: i0, template: "<div\n class=\"app-main-header\"\n [ngClass]=\"{\n open: (headerService.navigatorOpen$ | async) && !simple,\n drawerOpen: headerService.rightDrawerOpen$ | async\n }\"\n>\n <div\n class=\"header-bar\"\n role=\"banner\"\n >\n @if ((headerService.canToggleNavigator$ | async) && !simple) {\n <button\n class=\"navigator-toggle main-header-button\"\n title=\"{{ 'Toggle navigation bar' | translate }}\"\n [attr.aria-expanded]=\"headerService.navigatorOpen$ | async\"\n [attr.aria-controls]=\"'navigator'\"\n type=\"button\"\n data-cy=\"header-bar--main-header-button\"\n (click)=\"headerService.toggleNavigator()\"\n >\n @if (!(headerService.navigatorOpen$ | async)) {\n <i [c8yIcon]=\"'outdent'\"></i>\n }\n @if (headerService.navigatorOpen$ | async) {\n <i [c8yIcon]=\"'dedent-right'\"></i>\n }\n </button>\n }\n <div class=\"app-view\">\n <c8y-app-icon\n [name]=\"(appState$ | async).app?.name\"\n [contextPath]=\"(appState$ | async).app?.contextPath\"\n [app]=\"(app$ | async) || (appState$ | async).app\"\n ></c8y-app-icon>\n\n <span class=\"page-header\">\n <c8y-title-outlet></c8y-title-outlet>\n @if (!simple) {\n <c8y-breadcrumb-outlet\n class=\"app-breadcrumbs\"\n [breadcrumbs]=\"breadcrumbService.items$ | async\"\n ></c8y-breadcrumb-outlet>\n }\n </span>\n </div>\n @if (!simple) {\n <c8y-search-outlet\n class=\"main-header-item\"\n [search]=\"searchService.items$ | async\"\n ></c8y-search-outlet>\n }\n @if (!simple) {\n <c8y-action-outlet [items]=\"actionService.items$ | async\"></c8y-action-outlet>\n }\n @if (appState.currentUser.value) {\n <c8y-app-switcher\n class=\"main-header-item\"\n title=\"{{ 'Application switcher' | translate }}\"\n ></c8y-app-switcher>\n }\n @if (showNotification$ | async) {\n <button\n class=\"main-header-button drawer-toggle\"\n [attr.aria-label]=\"appState.currentUser | async | shortenUserName\"\n tooltip=\"{{ appState.currentUser | async | shortenUserName }}\n{{ 'New features available' | translate }}\"\n placement=\"left\"\n [attr.aria-expanded]=\"headerService.rightDrawerOpen$ | async\"\n [attr.aria-controls]=\"'right-drawer'\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"toggleDrawer()\"\n [attr.data-cy]=\"'right-drawer-toggle-button'\"\n >\n @if (appState.currentUser | async) {\n <span\n class=\"user-dot user-dot-notification\"\n data-cy=\"header-bar--user-dot\"\n >\n {{ appState.currentUser | async | userNameInitials }}\n </span>\n }\n <span class=\"close-dot\">&times;</span>\n </button>\n @if (!(headerService.rightDrawerOpen$ | async)) {\n <div\n class=\"p-relative a-s-stretch no-pointer\"\n style=\"order: 4\"\n >\n @if (showNotification$ | async) {\n <span class=\"c8y-pulse c8y-pulse--md active\"></span>\n }\n </div>\n }\n } @else {\n <button\n class=\"main-header-button drawer-toggle\"\n [attr.aria-label]=\"appState.currentUser | async | shortenUserName\"\n tooltip=\"{{ appState.currentUser | async | shortenUserName }}\"\n placement=\"left\"\n [attr.aria-expanded]=\"headerService.rightDrawerOpen$ | async\"\n [attr.aria-controls]=\"'right-drawer'\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"toggleDrawer()\"\n [attr.data-cy]=\"'right-drawer-toggle-button'\"\n >\n @if (appState.currentUser | async) {\n <span\n class=\"user-dot user-dot-notification\"\n data-cy=\"header-bar--user-dot\"\n >\n {{ appState.currentUser | async | userNameInitials }}\n </span>\n }\n <span class=\"close-dot\">&times;</span>\n </button>\n }\n </div>\n <div class=\"head-toggler\">\n <button\n title=\"{{ 'Toggle' | translate }}\"\n type=\"button\"\n data-cy=\"header-bar--toggle\"\n (click)=\"headerService.toggle()\"\n >\n <i [c8yIcon]=\"'angle-right'\"></i>\n </button>\n </div>\n <c8y-drawer-outlet\n id=\"right-drawer\"\n [tabindex]=\"(headerService.rightDrawerOpen$ | async) ? '0' : '-1'\"\n [attr.aria-hidden]=\"!(headerService.rightDrawerOpen$ | async)\"\n position=\"right\"\n [open]=\"headerService.rightDrawerOpen$ | async\"\n ></c8y-drawer-outlet>\n <div\n class=\"loading-bar\"\n [ngClass]=\"loadingClass$ | async\"\n ></div>\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: AppIconComponent, selector: "c8y-app-icon", inputs: ["contextPath", "name", "app"] }, { kind: "component", type: TitleOutletComponent, selector: "c8y-title-outlet" }, { kind: "component", type: BreadcrumbOutletComponent, selector: "c8y-breadcrumb-outlet", inputs: ["breadcrumbs", "dropdownOpen"] }, { kind: "component", type: SearchOutletComponent, selector: "c8y-search-outlet", inputs: ["search"] }, { kind: "component", type: ActionOutletComponent, selector: "c8y-action-outlet", inputs: ["items"] }, { kind: "component", type: AppSwitcherComponent, selector: "c8y-app-switcher" }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i1$4.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "component", type: DrawerOutletComponent, selector: "c8y-drawer-outlet", inputs: ["position", "open"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: ShortenUserNamePipe, name: "shortenUserName" }, { kind: "pipe", type: UserNameInitialsPipe, name: "userNameInitials" }] }); }
14834
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: HeaderBarComponent, isStandalone: true, selector: "c8y-header-bar", inputs: { simple: "simple" }, ngImport: i0, template: "<div\n class=\"app-main-header\"\n [ngClass]=\"{\n open: (headerService.navigatorOpen$ | async) && !simple,\n drawerOpen: headerService.rightDrawerOpen$ | async\n }\"\n>\n <div\n class=\"header-bar\"\n role=\"banner\"\n >\n @if ((headerService.canToggleNavigator$ | async) && !simple) {\n <button\n class=\"navigator-toggle main-header-button\"\n title=\"{{ 'Toggle navigation bar' | translate }}\"\n [attr.aria-expanded]=\"headerService.navigatorOpen$ | async\"\n [attr.aria-controls]=\"'navigator'\"\n type=\"button\"\n data-cy=\"header-bar--main-header-button\"\n (click)=\"headerService.toggleNavigator()\"\n >\n @if (!(headerService.navigatorOpen$ | async)) {\n <i [c8yIcon]=\"'outdent'\"></i>\n }\n @if (headerService.navigatorOpen$ | async) {\n <i [c8yIcon]=\"'dedent-right'\"></i>\n }\n </button>\n }\n <div class=\"app-view\">\n <c8y-app-icon\n [name]=\"(appState$ | async).app?.name\"\n [contextPath]=\"(appState$ | async).app?.contextPath\"\n [app]=\"(app$ | async) || (appState$ | async).app\"\n ></c8y-app-icon>\n\n <span class=\"page-header\">\n <c8y-title-outlet></c8y-title-outlet>\n @if (!simple) {\n <c8y-breadcrumb-outlet\n class=\"app-breadcrumbs\"\n [breadcrumbs]=\"breadcrumbService.items$ | async\"\n ></c8y-breadcrumb-outlet>\n }\n </span>\n </div>\n @if (!simple) {\n <c8y-search-outlet\n class=\"main-header-item\"\n [search]=\"searchService.items$ | async\"\n ></c8y-search-outlet>\n }\n @if (!simple) {\n <c8y-action-outlet [items]=\"actionService.items$ | async\"></c8y-action-outlet>\n }\n @if (appState.currentUser.value) {\n <c8y-app-switcher\n class=\"main-header-item\"\n title=\"{{ 'Application switcher' | translate }}\"\n ></c8y-app-switcher>\n }\n @if (showNotification$ | async) {\n <button\n class=\"main-header-button drawer-toggle\"\n [attr.aria-label]=\"appState.currentUser | async | shortenUserName\"\n tooltip=\"{{ appState.currentUser | async | shortenUserName }}\n{{ 'New features available' | translate }}\"\n placement=\"left\"\n [attr.aria-expanded]=\"headerService.rightDrawerOpen$ | async\"\n [attr.aria-controls]=\"'right-drawer'\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"toggleDrawer()\"\n [attr.data-cy]=\"'right-drawer-toggle-button'\"\n >\n @if (appState.currentUser | async) {\n <span\n class=\"user-dot user-dot-notification\"\n data-cy=\"header-bar--user-dot\"\n >\n {{ appState.currentUser | async | userNameInitials }}\n </span>\n }\n <span class=\"close-dot\">&times;</span>\n </button>\n @if (!(headerService.rightDrawerOpen$ | async)) {\n <div\n class=\"p-relative a-s-stretch no-pointer\"\n style=\"order: 4\"\n >\n @if (showNotification$ | async) {\n <span class=\"c8y-pulse c8y-pulse--md active\"></span>\n }\n </div>\n }\n } @else {\n <button\n class=\"main-header-button drawer-toggle\"\n [attr.aria-label]=\"appState.currentUser | async | shortenUserName\"\n tooltip=\"{{ appState.currentUser | async | shortenUserName }}\"\n placement=\"left\"\n [attr.aria-expanded]=\"headerService.rightDrawerOpen$ | async\"\n [attr.aria-controls]=\"'right-drawer'\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"toggleDrawer()\"\n [attr.data-cy]=\"'right-drawer-toggle-button'\"\n >\n @if (appState.currentUser | async) {\n <span\n class=\"user-dot user-dot-notification\"\n data-cy=\"header-bar--user-dot\"\n >\n {{ appState.currentUser | async | userNameInitials }}\n </span>\n }\n <span class=\"close-dot\">&times;</span>\n </button>\n }\n </div>\n <div class=\"head-toggler\">\n <button\n title=\"{{ 'Toggle' | translate }}\"\n type=\"button\"\n data-cy=\"header-bar--toggle\"\n (click)=\"headerService.toggle()\"\n >\n <i [c8yIcon]=\"'angle-right'\"></i>\n </button>\n </div>\n <c8y-drawer-outlet\n id=\"right-drawer\"\n [tabindex]=\"(headerService.rightDrawerOpen$ | async) ? '0' : '-1'\"\n [attr.aria-hidden]=\"!(headerService.rightDrawerOpen$ | async)\"\n position=\"right\"\n [open]=\"headerService.rightDrawerOpen$ | async\"\n ></c8y-drawer-outlet>\n <div\n class=\"loading-bar\"\n [ngClass]=\"loadingClass$ | async\"\n ></div>\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: AppIconComponent, selector: "c8y-app-icon", inputs: ["contextPath", "name", "app"] }, { kind: "component", type: TitleOutletComponent, selector: "c8y-title-outlet" }, { kind: "component", type: BreadcrumbOutletComponent, selector: "c8y-breadcrumb-outlet", inputs: ["breadcrumbs"] }, { kind: "component", type: SearchOutletComponent, selector: "c8y-search-outlet", inputs: ["search"] }, { kind: "component", type: ActionOutletComponent, selector: "c8y-action-outlet", inputs: ["items"] }, { kind: "component", type: AppSwitcherComponent, selector: "c8y-app-switcher" }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i1$4.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "component", type: DrawerOutletComponent, selector: "c8y-drawer-outlet", inputs: ["position", "open"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: ShortenUserNamePipe, name: "shortenUserName" }, { kind: "pipe", type: UserNameInitialsPipe, name: "userNameInitials" }] }); }
14728
14835
  }
14729
14836
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: HeaderBarComponent, decorators: [{
14730
14837
  type: Component,
@@ -16171,11 +16278,11 @@ class BreadcrumbItemComponent {
16171
16278
  this.injector = _injector;
16172
16279
  }
16173
16280
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: BreadcrumbItemComponent, deps: [{ token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Component }); }
16174
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.19", type: BreadcrumbItemComponent, isStandalone: true, selector: "c8y-breadcrumb-item", inputs: { icon: "icon", translate: "translate", label: "label", path: "path", injector: "injector" }, viewQueries: [{ propertyName: "template", first: true, predicate: ["template"], descendants: true }], ngImport: i0, template: "<ng-template #template>\n <ng-content></ng-content>\n</ng-template>" }); }
16281
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.19", type: BreadcrumbItemComponent, isStandalone: true, selector: "c8y-breadcrumb-item", inputs: { icon: "icon", translate: "translate", label: "label", path: "path", injector: "injector" }, viewQueries: [{ propertyName: "template", first: true, predicate: ["template"], descendants: true }], ngImport: i0, template: "<ng-template #template>\n <ng-content></ng-content>\n</ng-template>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
16175
16282
  }
16176
16283
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: BreadcrumbItemComponent, decorators: [{
16177
16284
  type: Component,
16178
- args: [{ selector: 'c8y-breadcrumb-item', standalone: true, template: "<ng-template #template>\n <ng-content></ng-content>\n</ng-template>" }]
16285
+ args: [{ selector: 'c8y-breadcrumb-item', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #template>\n <ng-content></ng-content>\n</ng-template>\n" }]
16179
16286
  }], ctorParameters: () => [{ type: i0.EnvironmentInjector }], propDecorators: { icon: [{
16180
16287
  type: Input
16181
16288
  }], translate: [{
@@ -16204,11 +16311,11 @@ class BreadcrumbComponent {
16204
16311
  this.breadcrumbService.remove(this);
16205
16312
  }
16206
16313
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: BreadcrumbComponent, deps: [{ token: BreadcrumbService }], target: i0.ɵɵFactoryTarget.Component }); }
16207
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.19", type: BreadcrumbComponent, isStandalone: true, selector: "c8y-breadcrumb", queries: [{ propertyName: "contentChildItems", predicate: BreadcrumbItemComponent }], ngImport: i0, template: "<ng-template>\n <ng-content></ng-content>\n</ng-template>" }); }
16314
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.19", type: BreadcrumbComponent, isStandalone: true, selector: "c8y-breadcrumb", queries: [{ propertyName: "contentChildItems", predicate: BreadcrumbItemComponent }], ngImport: i0, template: "<ng-template>\n <ng-content></ng-content>\n</ng-template>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
16208
16315
  }
16209
16316
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: BreadcrumbComponent, decorators: [{
16210
16317
  type: Component,
16211
- args: [{ selector: 'c8y-breadcrumb', standalone: true, template: "<ng-template>\n <ng-content></ng-content>\n</ng-template>" }]
16318
+ args: [{ selector: 'c8y-breadcrumb', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template>\n <ng-content></ng-content>\n</ng-template>\n" }]
16212
16319
  }], ctorParameters: () => [{ type: BreadcrumbService }], propDecorators: { contentChildItems: [{
16213
16320
  type: ContentChildren,
16214
16321
  args: [BreadcrumbItemComponent]
@@ -27246,19 +27353,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
27246
27353
  }], ctorParameters: () => [{ type: RouterService }] });
27247
27354
 
27248
27355
  class GuideHrefDirective {
27249
- set baseHref(href) {
27250
- this.htmlAnchor.href = `${href}/${this.c8yGuideHref}`;
27251
- }
27252
- constructor(elementRef) {
27356
+ constructor(elementRef, docsService) {
27253
27357
  this.elementRef = elementRef;
27358
+ this.docsService = docsService;
27254
27359
  this.htmlAnchor = this.elementRef.nativeElement;
27255
27360
  if (!this.htmlAnchor.hasAttribute('target')) {
27256
27361
  this.htmlAnchor.setAttribute('target', '_blank');
27257
27362
  this.htmlAnchor.setAttribute('rel', 'noopener noreferrer');
27258
27363
  }
27259
27364
  }
27260
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: GuideHrefDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
27261
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.19", type: GuideHrefDirective, isStandalone: true, selector: "[c8y-guide-href]", inputs: { c8yGuideHref: ["c8y-guide-href", "c8yGuideHref"] }, ngImport: i0 }); }
27365
+ ngOnChanges() {
27366
+ const guideHref = this.docsService.getUserGuideLink(this.c8yGuideHref);
27367
+ if (!guideHref) {
27368
+ // documentation links are switched off, or no link can be built from `guideHrefTemplate`
27369
+ this.htmlAnchor.removeAttribute('href');
27370
+ return;
27371
+ }
27372
+ this.htmlAnchor.href = guideHref;
27373
+ }
27374
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: GuideHrefDirective, deps: [{ token: i0.ElementRef }, { token: DocsService }], target: i0.ɵɵFactoryTarget.Directive }); }
27375
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.19", type: GuideHrefDirective, isStandalone: true, selector: "[c8y-guide-href]", inputs: { c8yGuideHref: ["c8y-guide-href", "c8yGuideHref"] }, usesOnChanges: true, ngImport: i0 }); }
27262
27376
  }
27263
27377
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: GuideHrefDirective, decorators: [{
27264
27378
  type: Directive,
@@ -27266,7 +27380,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
27266
27380
  selector: '[c8y-guide-href]',
27267
27381
  standalone: true
27268
27382
  }]
27269
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { c8yGuideHref: [{
27383
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: DocsService }], propDecorators: { c8yGuideHref: [{
27270
27384
  type: Input,
27271
27385
  args: ['c8y-guide-href']
27272
27386
  }] } });
@@ -27285,12 +27399,14 @@ class GuideDocsComponent {
27285
27399
  this.docs = docs;
27286
27400
  this.el = el;
27287
27401
  this.renderer = renderer;
27402
+ /**
27403
+ * Whether the projected content should be displayed. It only becomes `true` once the anchor
27404
+ * attributes have been resolved, and stays `false` if documentation links are switched off.
27405
+ */
27406
+ this.ready = false;
27288
27407
  this.destroyed$ = new ReplaySubject(1);
27289
27408
  this.translateDirective = translateDirective;
27290
27409
  }
27291
- ngOnInit() {
27292
- this.baseUrl = this.docs.getBaseUrl();
27293
- }
27294
27410
  /**
27295
27411
  * After content is initialized we subscribe for translation events, then we are able to modify
27296
27412
  * anchor just as C8yGuideHrefDirective does.
@@ -27308,9 +27424,7 @@ class GuideDocsComponent {
27308
27424
  else {
27309
27425
  this.replaceAnchorAttributes();
27310
27426
  }
27311
- this.links.forEach(guide => {
27312
- guide.baseHref = this.baseUrl;
27313
- });
27427
+ this.ready = this.docs.isDocsEnabled();
27314
27428
  }
27315
27429
  /**
27316
27430
  * Find and modify anchor elements
@@ -27318,7 +27432,12 @@ class GuideDocsComponent {
27318
27432
  replaceAnchorAttributes() {
27319
27433
  const anchors = this.findGuideAnchors(this.el.nativeElement);
27320
27434
  anchors.forEach(a => {
27321
- const guideHref = `${this.baseUrl}/${a.getAttribute('c8y-guide-href')}`;
27435
+ const guideHref = this.docs.getUserGuideLink(a.getAttribute('c8y-guide-href'));
27436
+ if (!guideHref) {
27437
+ // e.g. `guideHrefTemplate` is empty, so no link can be built: keep the text, drop the link
27438
+ this.renderer.removeAttribute(a, 'href');
27439
+ return;
27440
+ }
27322
27441
  this.renderer.setAttribute(a, 'href', guideHref);
27323
27442
  if (!a.hasAttribute('target')) {
27324
27443
  this.renderer.setAttribute(a, 'target', '_blank');
@@ -27352,17 +27471,14 @@ class GuideDocsComponent {
27352
27471
  this.destroyed$.complete();
27353
27472
  }
27354
27473
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: GuideDocsComponent, deps: [{ token: DocsService }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: C8yTranslateDirective, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
27355
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: GuideDocsComponent, isStandalone: true, selector: "[c8y-guide-docs]", queries: [{ propertyName: "translateDirective", first: true, predicate: C8yTranslateDirective, descendants: true, static: true }, { propertyName: "links", predicate: GuideHrefDirective, descendants: true }], ngImport: i0, template: "@if (!!baseUrl) {\n <ng-content></ng-content>\n}\n" }); }
27474
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: GuideDocsComponent, isStandalone: true, selector: "[c8y-guide-docs]", queries: [{ propertyName: "translateDirective", first: true, predicate: C8yTranslateDirective, descendants: true, static: true }], ngImport: i0, template: "@if (ready) {\n <ng-content></ng-content>\n}\n" }); }
27356
27475
  }
27357
27476
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: GuideDocsComponent, decorators: [{
27358
27477
  type: Component,
27359
- args: [{ selector: '[c8y-guide-docs]', standalone: true, template: "@if (!!baseUrl) {\n <ng-content></ng-content>\n}\n" }]
27478
+ args: [{ selector: '[c8y-guide-docs]', standalone: true, template: "@if (ready) {\n <ng-content></ng-content>\n}\n" }]
27360
27479
  }], ctorParameters: () => [{ type: DocsService }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: C8yTranslateDirective, decorators: [{
27361
27480
  type: Optional
27362
- }] }], propDecorators: { links: [{
27363
- type: ContentChildren,
27364
- args: [GuideHrefDirective, { descendants: true }]
27365
- }], translateDirective: [{
27481
+ }] }], propDecorators: { translateDirective: [{
27366
27482
  type: ContentChild,
27367
27483
  args: [C8yTranslateDirective, { static: true }]
27368
27484
  }] } });