@c8y/ngx-components 1023.14.176 → 1023.14.178

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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, Component, inject, InjectionToken, EventEmitter, Output, Input, TemplateRef, ViewChild, Optional, Inject, HostListener, input, HostBinding, NgModule } from '@angular/core';
2
+ import { Injectable, Component, inject, InjectionToken, EventEmitter, Output, Input, TemplateRef, ViewChild, Injector, signal, afterNextRender, Optional, Inject, HostListener, input, HostBinding, NgModule } from '@angular/core';
3
3
  import * as i2 from '@c8y/ngx-components';
4
4
  import { IconDirective, C8yTranslatePipe, Permissions, ContextRouteService, Status, NavigatorNode, NEW_DASHBOARD_ROUTER_STATE_PROP, getActivatedRoute, ViewContext, TabsOutletComponent, memoize, hookGeneric, ExtensionPointForPlugins, fromTriggerOnce, getInjectedHooks, stateToFactory, ListGroupModule, C8yComponentOutlet, AlertService, C8yTranslateDirective, DynamicComponentModule, DynamicComponentComponent, sortByPriority, isPromise, CoreModule, WIDGET_CONFIGURATION_GRID_SIZE, HighlightComponent, EmptyStateComponent, FormGroupComponent, RequiredInputPlaceholderDirective, ProductExperienceDirective, ResizableGridComponent, DashboardChildChange, CopyDashboardDisabledReason, TitleComponent, ActionBarItemComponent, HelpComponent, WidgetsDashboardComponent, DatePipe, hookRoute, hookTab, PreviewService, hookNavigator } from '@c8y/ngx-components';
5
5
  import * as i1 from '@angular/router';
@@ -272,6 +272,24 @@ const PRODUCT_EXPERIENCE = {
272
272
  }
273
273
  }
274
274
  };
275
+ const FOUNDATION_CLASSES = [
276
+ 'c8y-dark-theme',
277
+ 'c8y-light-theme',
278
+ 'panel-content-branded'
279
+ ];
280
+ const THEME_FOUNDATION_MAP = {
281
+ 'dashboard-theme-dark': 'c8y-dark-theme',
282
+ 'dashboard-theme-white': 'c8y-light-theme',
283
+ 'dashboard-theme-branded': 'panel-content-branded'
284
+ };
285
+ const WIDGET_FOUNDATION_MAP = {
286
+ 'panel-content-dark': 'c8y-dark-theme',
287
+ 'panel-content-white': 'c8y-light-theme'
288
+ };
289
+ const DASHBOARD_FOUNDATION_MAP = {
290
+ 'dashboard-theme-dark': 'c8y-dark-theme',
291
+ 'dashboard-theme-white': 'c8y-light-theme'
292
+ };
275
293
  const REPORT_DEFAULT_NAVIGATION_NODE_PRIORITY = 30;
276
294
  const DASHBOARD_DETAILS_OUTLET = 'dashboard-details';
277
295
  const DASHBOARD_DETAILS_TABS_OUTLET_NAME = 'dashboardTabs';
@@ -380,7 +398,7 @@ class ContextDashboardService {
380
398
  this.tabs.refresh();
381
399
  });
382
400
  }
383
- catch (ex) {
401
+ catch {
384
402
  // intended empty
385
403
  }
386
404
  }
@@ -400,17 +418,17 @@ class ContextDashboardService {
400
418
  }
401
419
  return dashboard;
402
420
  }
403
- activateDashboards(route, types) {
421
+ activateDashboards(route, types, options) {
404
422
  const { dashboardId } = route.params;
405
423
  if (dashboardId) {
406
- return this.getDashboard$(dashboardId, types, route.parent.data.contextData).pipe(tap(dashboard => {
424
+ return this.getDashboard$(dashboardId, types, route.parent.data.contextData, options).pipe(tap(dashboard => {
407
425
  route.data = { dashboard };
408
426
  }), map(() => true), catchError(() => {
409
427
  return of(false);
410
428
  }));
411
429
  }
412
430
  const { contextData: mo } = this.contextRouteService.getContextData(route);
413
- this.dashboardTabs$ = this.getTabs$(mo, types, route?.parent?.data);
431
+ this.dashboardTabs$ = this.getTabs$(mo, types, route?.parent?.data, options);
414
432
  return this.dashboardTabs$;
415
433
  }
416
434
  getDashboard(name, defaultWidgets) {
@@ -537,10 +555,10 @@ class ContextDashboardService {
537
555
  return widget;
538
556
  }), 'id');
539
557
  }
540
- getDashboard$(dashboardIdOrName, dashboardType, mo) {
558
+ getDashboard$(dashboardIdOrName, dashboardType, mo, options) {
541
559
  const cache = this.cache.get(dashboardIdOrName);
542
560
  const dashboards = mo
543
- ? this.getContextDashboards(mo, dashboardType)
561
+ ? this.getContextDashboards(mo, dashboardType, options)
544
562
  : this.getNamedDashboard(dashboardIdOrName);
545
563
  const cacheRefresh = this.getContextDashboards$(dashboards).pipe(tap(dashboard => this.cacheDashboard(dashboard)), filter(dashboard => dashboard.id === dashboardIdOrName ||
546
564
  has(dashboard, `${this.FRAGMENT_NAME}${this.INDEX_SPLIT}${ContextDashboardType.Named}${this.INDEX_SPLIT}${dashboardIdOrName}`)));
@@ -657,7 +675,7 @@ class ContextDashboardService {
657
675
  return null;
658
676
  }
659
677
  }
660
- async getContextDashboards(mo, dashboardType) {
678
+ async getContextDashboards(mo, dashboardType, options) {
661
679
  const filterCriteria = dashboardType.map(t => ({
662
680
  // it's necessary to wrap fragment in quotes because dashboard type can contain spaces
663
681
  __has: `'${this.createDashboardFragment(mo, t)}'`
@@ -675,9 +693,11 @@ class ContextDashboardService {
675
693
  ? [...filterCriteria, typeFilterCriteria]
676
694
  : filterCriteria;
677
695
  const query = this.queriesUtil.buildQuery({ __filter: { __or: finalFilterCriteria } });
696
+ const strictAppFilter = !!options?.strictAppFilter;
678
697
  const now = Date.now();
679
698
  const cacheHasValidResponse = this.contextDashboardsCache &&
680
699
  this.contextDashboardsCache.query === query &&
700
+ this.contextDashboardsCache.strictAppFilter === strictAppFilter &&
681
701
  now - this.contextDashboardsCache.timestamp < this.CACHE_TIMEOUT;
682
702
  if (cacheHasValidResponse) {
683
703
  return this.contextDashboardsCache.result;
@@ -687,7 +707,8 @@ class ContextDashboardService {
687
707
  }
688
708
  this.contextDashboardsCache = {
689
709
  query,
690
- result: this.listAndFilterByApplication(query),
710
+ strictAppFilter,
711
+ result: this.listAndFilterByApplication(query, strictAppFilter),
691
712
  timestamp: now
692
713
  };
693
714
  return this.contextDashboardsCache.result;
@@ -887,8 +908,8 @@ class ContextDashboardService {
887
908
  replaceContextInIdsObj(ids, newContext, oldContext) {
888
909
  return Object.fromEntries(Object.entries(ids).map(([key, id]) => [key, id === oldContext.id ? newContext.id : id]));
889
910
  }
890
- getTabs$(mo, dashboardType, context) {
891
- const dashboards = this.getContextDashboards(mo, dashboardType);
911
+ getTabs$(mo, dashboardType, context, options) {
912
+ const dashboards = this.getContextDashboards(mo, dashboardType, options);
892
913
  return this.getContextDashboards$(dashboards).pipe(mergeMap(dashboard => this.verifyDashboardAvailability$(dashboard)),
893
914
  // Due to MTM-62321 named context dashboards included fragments for device and groups.
894
915
  // therefore some device-info dashboards (from DM app) might occur in cockpit on device level.
@@ -1011,10 +1032,15 @@ class ContextDashboardService {
1011
1032
  * For dashboard views within applications, filter dashboards by application association.
1012
1033
  * Do it on the client side as the inventory query doesn't perform well on not indexed fragments.
1013
1034
  */
1014
- async listAndFilterByApplication(query) {
1035
+ async listAndFilterByApplication(query, strictAppFilter = false) {
1015
1036
  const dashboards = await this.inventory.list({ query, pageSize: this.DEFAULT_PAGESIZE });
1037
+ const appKey = this.appStateService?.currentApplication?.value?.key;
1016
1038
  dashboards.data = dashboards.data.filter(dashboard => {
1017
- return (Array.from(dashboard[this.APPLIED_TO_FRAGMENT] || []).includes(this.appStateService?.currentApplication?.value?.key) || !dashboard[this.APPLIED_TO_FRAGMENT]);
1039
+ const appliedTo = Array.from(dashboard[this.APPLIED_TO_FRAGMENT] || []);
1040
+ if (strictAppFilter) {
1041
+ return !!appKey && appliedTo.includes(appKey);
1042
+ }
1043
+ return appliedTo.includes(appKey) || !dashboard[this.APPLIED_TO_FRAGMENT];
1018
1044
  });
1019
1045
  return dashboards;
1020
1046
  }
@@ -2388,20 +2414,43 @@ class WidgetPreviewComponent {
2388
2414
  constructor() {
2389
2415
  this.widgetConfigService = inject(WidgetConfigService);
2390
2416
  this.widgetConfig = inject(WidgetConfigComponent);
2391
- this.previewClasses = {
2417
+ this.injector = inject(Injector);
2418
+ // charts don't observe CSS vars — force destroy/recreate when theme changes
2419
+ this.showContent = signal(true, ...(ngDevMode ? [{ debugName: "showContent" }] : []));
2420
+ this.previousClassKey = '';
2421
+ this._previewClasses = {
2392
2422
  'dashboard-theme-light': true,
2393
2423
  'panel-title-regular': true
2394
2424
  };
2395
2425
  }
2426
+ set previewClasses(value) {
2427
+ this._previewClasses = value;
2428
+ const classKey = this.activeClassKey(value);
2429
+ if (this.previousClassKey && classKey !== this.previousClassKey) {
2430
+ this.showContent.set(false);
2431
+ afterNextRender(() => this.showContent.set(true), { injector: this.injector });
2432
+ }
2433
+ this.previousClassKey = classKey;
2434
+ }
2435
+ get previewClasses() {
2436
+ return this._previewClasses;
2437
+ }
2396
2438
  get translateWidgetTitle() {
2397
2439
  return this.widgetConfig?.mo.c8y_Dashboard.translateWidgetTitle ?? false;
2398
2440
  }
2441
+ activeClassKey(classes) {
2442
+ return Object.entries(classes ?? {})
2443
+ .filter(([, v]) => v)
2444
+ .map(([k]) => k)
2445
+ .sort()
2446
+ .join(' ');
2447
+ }
2399
2448
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: WidgetPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2400
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: WidgetPreviewComponent, isStandalone: true, selector: "c8y-widget-preview", inputs: { previewClasses: "previewClasses" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset p-l-24 p-r-24 p-t-16 p-b-16\">\n <legend>{{ 'Preview' | translate }}</legend>\n <c8y-dashboard\n class=\"dashboard-preview-slot\"\n [columns]=\"1\"\n [ngClass]=\"previewClasses\"\n >\n <c8y-dashboard-child\n [isFrozen]=\"true\"\n [width]=\"1\"\n [height]=\"6\"\n >\n <c8y-dashboard-child-title>\n <span>\n {{\n (widgetConfigService.selected$ | async)?.data?.title\n | translate: { noTranslateRemoveContext: !translateWidgetTitle }\n }}\n </span>\n </c8y-dashboard-child-title>\n\n @if ((widgetConfigService.previewViewState$ | async) === 'noPreview') {\n <div class=\"fit-h d-flex d-col j-c-center a-i-center\">\n <c8y-ui-empty-state\n [icon]=\"'visibility-off'\"\n [title]=\"'No preview available.' | translate\"\n [subtitle]=\"'This widget does not provide any preview.' | translate\"\n [horizontal]=\"false\"\n ></c8y-ui-empty-state>\n </div>\n }\n\n @if ((widgetConfigService.previewViewState$ | async) === 'configPreview') {\n <c8y-dynamic-component\n [componentId]=\"(widgetConfigService.selected$ | async)?.id\"\n [config]=\"(widgetConfigService.currentConfig$ | async)?.config\"\n ></c8y-dynamic-component>\n }\n\n @if ((widgetConfigService.previewViewState$ | async) === 'templatePreview') {\n <ng-template [ngTemplateOutlet]=\"widgetConfigService.preview$ | async\"></ng-template>\n }\n </c8y-dashboard-child>\n </c8y-dashboard>\n</fieldset>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: CoreModule }, { kind: "component", type: i2.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "component", type: i2.DynamicComponentComponent, selector: "c8y-dynamic-component", inputs: ["componentId", "config", "mode", "notFoundError", "executeResolvers"], outputs: ["updateWidgetClasses"] }, { kind: "component", type: i2.DashboardComponent, selector: "c8y-dashboard", inputs: ["columns", "gap", "rows"], outputs: ["dashboardChange"] }, { kind: "component", type: i2.DashboardChildComponent, selector: "c8y-dashboard-child", inputs: ["x", "y", "width", "height", "data", "margin", "useIntersection", "isFrozen", "canToggleFullscreen", "editMode", "class"], outputs: ["changeStart", "changeEnd", "toggleFullscreen"] }, { kind: "component", type: i2.DashboardChildTitleComponent, selector: "c8y-dashboard-child-title" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); }
2449
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: WidgetPreviewComponent, isStandalone: true, selector: "c8y-widget-preview", inputs: { previewClasses: "previewClasses" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset p-l-24 p-r-24 p-t-16 p-b-16 flex-grow d-flex d-col min-height-0\">\n <legend>{{ 'Preview' | translate }}</legend>\n <c8y-dashboard\n class=\"dashboard-preview-slot flex-grow min-height-0\"\n [columns]=\"1\"\n [ngClass]=\"previewClasses\"\n >\n @if (showContent()) {\n <c8y-dashboard-child\n [isFrozen]=\"true\"\n [width]=\"1\"\n [height]=\"6\"\n >\n @let widgetTitle =\n (widgetConfigService.selected$ | async)?.data?.title\n | translate: { noTranslateRemoveContext: !translateWidgetTitle };\n <c8y-dashboard-child-title [attr.title]=\"widgetTitle\">\n <span>\n {{ widgetTitle }}\n </span>\n </c8y-dashboard-child-title>\n\n @if ((widgetConfigService.previewViewState$ | async) === 'noPreview') {\n <div class=\"fit-h d-flex d-col j-c-center a-i-center\">\n <c8y-ui-empty-state\n [icon]=\"'visibility-off'\"\n [title]=\"'No preview available.' | translate\"\n [subtitle]=\"'This widget does not provide any preview.' | translate\"\n [horizontal]=\"false\"\n ></c8y-ui-empty-state>\n </div>\n }\n\n @if ((widgetConfigService.previewViewState$ | async) === 'configPreview') {\n <c8y-dynamic-component\n [componentId]=\"(widgetConfigService.selected$ | async)?.id\"\n [config]=\"(widgetConfigService.currentConfig$ | async)?.config\"\n ></c8y-dynamic-component>\n }\n\n @if ((widgetConfigService.previewViewState$ | async) === 'templatePreview') {\n <ng-template [ngTemplateOutlet]=\"widgetConfigService.preview$ | async\"></ng-template>\n }\n </c8y-dashboard-child>\n }\n </c8y-dashboard>\n</fieldset>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: CoreModule }, { kind: "component", type: i2.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "component", type: i2.DynamicComponentComponent, selector: "c8y-dynamic-component", inputs: ["componentId", "config", "mode", "notFoundError", "executeResolvers"], outputs: ["updateWidgetClasses"] }, { kind: "component", type: i2.DashboardComponent, selector: "c8y-dashboard", inputs: ["columns", "gap", "rows"], outputs: ["dashboardChange"] }, { kind: "component", type: i2.DashboardChildComponent, selector: "c8y-dashboard-child", inputs: ["x", "y", "width", "height", "data", "margin", "useIntersection", "isFrozen", "canToggleFullscreen", "editMode", "class"], outputs: ["changeStart", "changeEnd", "toggleFullscreen"] }, { kind: "component", type: i2.DashboardChildTitleComponent, selector: "c8y-dashboard-child-title" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); }
2401
2450
  }
2402
2451
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: WidgetPreviewComponent, decorators: [{
2403
2452
  type: Component,
2404
- args: [{ selector: 'c8y-widget-preview', standalone: true, imports: [NgClass, C8yTranslatePipe, CommonModule, CoreModule], template: "<fieldset class=\"c8y-fieldset p-l-24 p-r-24 p-t-16 p-b-16\">\n <legend>{{ 'Preview' | translate }}</legend>\n <c8y-dashboard\n class=\"dashboard-preview-slot\"\n [columns]=\"1\"\n [ngClass]=\"previewClasses\"\n >\n <c8y-dashboard-child\n [isFrozen]=\"true\"\n [width]=\"1\"\n [height]=\"6\"\n >\n <c8y-dashboard-child-title>\n <span>\n {{\n (widgetConfigService.selected$ | async)?.data?.title\n | translate: { noTranslateRemoveContext: !translateWidgetTitle }\n }}\n </span>\n </c8y-dashboard-child-title>\n\n @if ((widgetConfigService.previewViewState$ | async) === 'noPreview') {\n <div class=\"fit-h d-flex d-col j-c-center a-i-center\">\n <c8y-ui-empty-state\n [icon]=\"'visibility-off'\"\n [title]=\"'No preview available.' | translate\"\n [subtitle]=\"'This widget does not provide any preview.' | translate\"\n [horizontal]=\"false\"\n ></c8y-ui-empty-state>\n </div>\n }\n\n @if ((widgetConfigService.previewViewState$ | async) === 'configPreview') {\n <c8y-dynamic-component\n [componentId]=\"(widgetConfigService.selected$ | async)?.id\"\n [config]=\"(widgetConfigService.currentConfig$ | async)?.config\"\n ></c8y-dynamic-component>\n }\n\n @if ((widgetConfigService.previewViewState$ | async) === 'templatePreview') {\n <ng-template [ngTemplateOutlet]=\"widgetConfigService.preview$ | async\"></ng-template>\n }\n </c8y-dashboard-child>\n </c8y-dashboard>\n</fieldset>\n" }]
2453
+ args: [{ selector: 'c8y-widget-preview', standalone: true, imports: [NgClass, C8yTranslatePipe, CommonModule, CoreModule], template: "<fieldset class=\"c8y-fieldset p-l-24 p-r-24 p-t-16 p-b-16 flex-grow d-flex d-col min-height-0\">\n <legend>{{ 'Preview' | translate }}</legend>\n <c8y-dashboard\n class=\"dashboard-preview-slot flex-grow min-height-0\"\n [columns]=\"1\"\n [ngClass]=\"previewClasses\"\n >\n @if (showContent()) {\n <c8y-dashboard-child\n [isFrozen]=\"true\"\n [width]=\"1\"\n [height]=\"6\"\n >\n @let widgetTitle =\n (widgetConfigService.selected$ | async)?.data?.title\n | translate: { noTranslateRemoveContext: !translateWidgetTitle };\n <c8y-dashboard-child-title [attr.title]=\"widgetTitle\">\n <span>\n {{ widgetTitle }}\n </span>\n </c8y-dashboard-child-title>\n\n @if ((widgetConfigService.previewViewState$ | async) === 'noPreview') {\n <div class=\"fit-h d-flex d-col j-c-center a-i-center\">\n <c8y-ui-empty-state\n [icon]=\"'visibility-off'\"\n [title]=\"'No preview available.' | translate\"\n [subtitle]=\"'This widget does not provide any preview.' | translate\"\n [horizontal]=\"false\"\n ></c8y-ui-empty-state>\n </div>\n }\n\n @if ((widgetConfigService.previewViewState$ | async) === 'configPreview') {\n <c8y-dynamic-component\n [componentId]=\"(widgetConfigService.selected$ | async)?.id\"\n [config]=\"(widgetConfigService.currentConfig$ | async)?.config\"\n ></c8y-dynamic-component>\n }\n\n @if ((widgetConfigService.previewViewState$ | async) === 'templatePreview') {\n <ng-template [ngTemplateOutlet]=\"widgetConfigService.preview$ | async\"></ng-template>\n }\n </c8y-dashboard-child>\n }\n </c8y-dashboard>\n</fieldset>\n" }]
2405
2454
  }], propDecorators: { previewClasses: [{
2406
2455
  type: Input
2407
2456
  }] } });
@@ -2641,7 +2690,37 @@ class WidgetConfigComponent {
2641
2690
  cssClasses[this.styling.contentClass] = true;
2642
2691
  }
2643
2692
  if (isPreview) {
2644
- cssClasses[`dashboard-theme-${this.defaultStyling.contentClass.split('-').pop()}`] = true;
2693
+ // When the widget has its own explicit theme (dark/white/branded), the preview
2694
+ // should use the widget's theme — not the dashboard's. In the actual dashboard,
2695
+ // dark/white widgets only get their own theme tokens, not the dashboard's branded
2696
+ // overrides. "Match dashboard" (panel-content-light) and non-theme content classes
2697
+ // (e.g. transparent) inherit the dashboard theme.
2698
+ const widgetSuffix = this.styling.contentClass.split('-').pop();
2699
+ const explicitWidgetThemes = new Set(['dark', 'white', 'branded']);
2700
+ const themeSuffix = explicitWidgetThemes.has(widgetSuffix)
2701
+ ? widgetSuffix
2702
+ : this.defaultStyling.contentClass.split('-').pop();
2703
+ cssClasses[`dashboard-theme-${themeSuffix}`] = true;
2704
+ }
2705
+ // Add foundation theme class so branding overrides cascade into the preview.
2706
+ // Widget-level content class takes priority over dashboard-level theme.
2707
+ let foundationClass = null;
2708
+ for (const [cls, fc] of Object.entries(WIDGET_FOUNDATION_MAP)) {
2709
+ if (cssClasses[cls]) {
2710
+ foundationClass = fc;
2711
+ break;
2712
+ }
2713
+ }
2714
+ if (!foundationClass) {
2715
+ for (const [cls, fc] of Object.entries(DASHBOARD_FOUNDATION_MAP)) {
2716
+ if (cssClasses[cls]) {
2717
+ foundationClass = fc;
2718
+ break;
2719
+ }
2720
+ }
2721
+ }
2722
+ if (foundationClass) {
2723
+ cssClasses[foundationClass] = true;
2645
2724
  }
2646
2725
  return cssClasses;
2647
2726
  }
@@ -2792,6 +2871,7 @@ class ContextDashboardComponent {
2792
2871
  other instances.`);
2793
2872
  this.hasPermissionToEditDashboard = false;
2794
2873
  this.destroy$ = new Subject();
2874
+ this.actionBarFoundationClass = null;
2795
2875
  }
2796
2876
  ngOnInit() {
2797
2877
  if (!this.name) {
@@ -2831,6 +2911,7 @@ class ContextDashboardComponent {
2831
2911
  if (this.dataSub) {
2832
2912
  this.dataSub.unsubscribe();
2833
2913
  }
2914
+ this.removeActionBarFoundationClass();
2834
2915
  this.contextDashboardStateService.setSelectedDashboard(null);
2835
2916
  }
2836
2917
  /**
@@ -3024,7 +3105,7 @@ class ContextDashboardComponent {
3024
3105
  ]);
3025
3106
  }
3026
3107
  }
3027
- catch (ex) {
3108
+ catch {
3028
3109
  // intended empty
3029
3110
  }
3030
3111
  }
@@ -3108,7 +3189,9 @@ class ContextDashboardComponent {
3108
3189
  */
3109
3190
  addDashboardClassToBody() {
3110
3191
  this.class.split(' ').forEach(cssClass => {
3111
- this.renderer.addClass(document.body, cssClass);
3192
+ if (!FOUNDATION_CLASSES.includes(cssClass)) {
3193
+ this.renderer.addClass(document.body, cssClass);
3194
+ }
3112
3195
  });
3113
3196
  }
3114
3197
  /**
@@ -3117,7 +3200,9 @@ class ContextDashboardComponent {
3117
3200
  */
3118
3201
  removeDashboardClassFromBody() {
3119
3202
  this.class.split(' ').forEach(cssClass => {
3120
- this.renderer.removeClass(document.body, cssClass);
3203
+ if (!FOUNDATION_CLASSES.includes(cssClass)) {
3204
+ this.renderer.removeClass(document.body, cssClass);
3205
+ }
3121
3206
  });
3122
3207
  }
3123
3208
  async onLoad(trackExperience) {
@@ -3136,6 +3221,8 @@ class ContextDashboardComponent {
3136
3221
  dashboard: true,
3137
3222
  ...this.dashboard?.classes
3138
3223
  };
3224
+ this.addFoundationClasses(dashboardClasses);
3225
+ this.updateActionBarTheme(dashboardClasses);
3139
3226
  this.isCopyDisabled = this.dashboard
3140
3227
  ? this.getDashboardCopyPermissionState()
3141
3228
  : { state: false, reason: null };
@@ -3164,6 +3251,8 @@ class ContextDashboardComponent {
3164
3251
  dashboard: true,
3165
3252
  ...layoutChanges.classes
3166
3253
  };
3254
+ this.addFoundationClasses(dashboardClasses);
3255
+ this.updateActionBarTheme(dashboardClasses);
3167
3256
  this.class = Object.keys(dashboardClasses).join(' ');
3168
3257
  }
3169
3258
  else if (this.dashboard) {
@@ -3267,6 +3356,45 @@ class ContextDashboardComponent {
3267
3356
  }
3268
3357
  return { state: true };
3269
3358
  }
3359
+ /**
3360
+ * Adds a CSS foundation theme class (c8y-dark-theme / c8y-light-theme)
3361
+ * to dashboard classes based on the selected dashboard theme.
3362
+ * This enables branding overrides to cascade via the standard theme selectors.
3363
+ */
3364
+ addFoundationClasses(dashboardClasses) {
3365
+ for (const [themeClass, foundationClass] of Object.entries(THEME_FOUNDATION_MAP)) {
3366
+ if (dashboardClasses[themeClass]) {
3367
+ dashboardClasses[foundationClass] = true;
3368
+ }
3369
+ }
3370
+ }
3371
+ /**
3372
+ * Adds/removes the foundation theme class on the action bar element,
3373
+ * which lives outside the dashboard container and cannot inherit tokens from it.
3374
+ */
3375
+ updateActionBarTheme(dashboardClasses) {
3376
+ const actionBar = document.querySelector('.c8y-ui-action-bar');
3377
+ if (!actionBar) {
3378
+ return;
3379
+ }
3380
+ this.removeActionBarFoundationClass();
3381
+ for (const [themeClass, foundationClass] of Object.entries(THEME_FOUNDATION_MAP)) {
3382
+ if (dashboardClasses[themeClass]) {
3383
+ this.renderer.addClass(actionBar, foundationClass);
3384
+ this.actionBarFoundationClass = foundationClass;
3385
+ break;
3386
+ }
3387
+ }
3388
+ }
3389
+ removeActionBarFoundationClass() {
3390
+ if (this.actionBarFoundationClass) {
3391
+ const actionBar = document.querySelector('.c8y-ui-action-bar');
3392
+ if (actionBar) {
3393
+ this.renderer.removeClass(actionBar, this.actionBarFoundationClass);
3394
+ }
3395
+ this.actionBarFoundationClass = null;
3396
+ }
3397
+ }
3270
3398
  setNewState(state, descriptionProp, widgetsChanged) {
3271
3399
  const description = this.getDescriptionForNewState(descriptionProp, widgetsChanged);
3272
3400
  this.editModeService.newState({ ...state, changeHistory: description });
@@ -4213,7 +4341,9 @@ const canActivateDeviceInfo = (route, _) => {
4213
4341
  return previewFeatureService.getState$('ui.dm-dashboard-manager').pipe(switchMap$1(isPreviewEnabled => {
4214
4342
  return isPreviewEnabled
4215
4343
  ? contextDashboardService
4216
- .activateDashboards(route, [ContextDashboardType.Device, ContextDashboardType.Type])
4344
+ .activateDashboards(route, [ContextDashboardType.Device, ContextDashboardType.Type], {
4345
+ strictAppFilter: true
4346
+ })
4217
4347
  .pipe(map$1(tabs => !(Array.isArray(tabs) && tabs.length > 0)))
4218
4348
  : of(true);
4219
4349
  }), take(1));
@@ -4390,5 +4520,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
4390
4520
  * Generated bundle index. Do not edit.
4391
4521
  */
4392
4522
 
4393
- export { ALL_GLOBAL_ROLES_SELECTED, AddDashboardComponent, AddDashboardFactory, AppearanceSettingsComponent, CONTEXT_DASHBOARD_CONFIG, ContextDashboardComponent, ContextDashboardModule, ContextDashboardService, ContextDashboardType, DASHBOARD_CHILDREN_STATE_NAME, DASHBOARD_DETAILS_OUTLET, DASHBOARD_DETAILS_TABS_OUTLET_NAME, DASHBOARD_SETTINGS_CHANGES, DASHBOARD_THEME_CLASSES, DashboardActionBarFactory, DashboardDetailComponent, DashboardDetailService, DashboardDetailsTabId, DeviceInfoDashboardComponent, DeviceInfoDashboardModule, DeviceManagementHomeDashboardComponent, DeviceManagementHomeDashboardModule, GlobalContextSectionComponent, HOOK_WIDGET_CONFIG, NewDashboardGuard, PRODUCT_EXPERIENCE, PasteDashboardActionComponent, REPORT_DEFAULT_NAVIGATION_NODE_PRIORITY, STYLING_CLASS_PREFIXES, TypeDashboardInfoComponent, WIDGET_CONTENT_CLASSES, WIDGET_HEADER_CLASSES, WidgetAssetSelectorComponent, WidgetConfigAppearanceComponent, WidgetConfigComponent, WidgetConfigFeedbackComponent, WidgetConfigGeneralComponent, WidgetConfigSectionComponent, WidgetConfigSectionService, WidgetConfigService, WidgetPreviewComponent, WidgetPreviewWrapperComponent, WidgetService, hookWidgetConfig, newDashboardTab };
4523
+ export { ALL_GLOBAL_ROLES_SELECTED, AddDashboardComponent, AddDashboardFactory, AppearanceSettingsComponent, CONTEXT_DASHBOARD_CONFIG, ContextDashboardComponent, ContextDashboardModule, ContextDashboardService, ContextDashboardType, DASHBOARD_CHILDREN_STATE_NAME, DASHBOARD_DETAILS_OUTLET, DASHBOARD_DETAILS_TABS_OUTLET_NAME, DASHBOARD_FOUNDATION_MAP, DASHBOARD_SETTINGS_CHANGES, DASHBOARD_THEME_CLASSES, DashboardActionBarFactory, DashboardDetailComponent, DashboardDetailService, DashboardDetailsTabId, DeviceInfoDashboardComponent, DeviceInfoDashboardModule, DeviceManagementHomeDashboardComponent, DeviceManagementHomeDashboardModule, FOUNDATION_CLASSES, GlobalContextSectionComponent, HOOK_WIDGET_CONFIG, NewDashboardGuard, PRODUCT_EXPERIENCE, PasteDashboardActionComponent, REPORT_DEFAULT_NAVIGATION_NODE_PRIORITY, STYLING_CLASS_PREFIXES, THEME_FOUNDATION_MAP, TypeDashboardInfoComponent, WIDGET_CONTENT_CLASSES, WIDGET_FOUNDATION_MAP, WIDGET_HEADER_CLASSES, WidgetAssetSelectorComponent, WidgetConfigAppearanceComponent, WidgetConfigComponent, WidgetConfigFeedbackComponent, WidgetConfigGeneralComponent, WidgetConfigSectionComponent, WidgetConfigSectionService, WidgetConfigService, WidgetPreviewComponent, WidgetPreviewWrapperComponent, WidgetService, hookWidgetConfig, newDashboardTab };
4394
4524
  //# sourceMappingURL=c8y-ngx-components-context-dashboard.mjs.map