@scion/workbench 21.0.0-beta.3 → 21.0.0-beta.5

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,15 +1,15 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { effect, untracked, Injectable, inject, Injector, signal, booleanAttribute, numberAttribute, DestroyRef, DOCUMENT, computed, InjectionToken, assertNotInReactiveContext, isSignal, createEnvironmentInjector, EnvironmentInjector, runInInjectionContext, Pipe, isDevMode, ChangeDetectionStrategy, Component, makeEnvironmentProviders, NgZone, ɵZONELESS_ENABLED as _ZONELESS_ENABLED, ApplicationInitStatus, forwardRef, Directive, createComponent, ApplicationRef, ElementRef, viewChild, afterRenderEffect, input, output, ViewContainerRef, TemplateRef, Renderer2, ChangeDetectorRef, linkedSignal, HostListener, HostBinding, viewChildren, IterableDiffers, CUSTOM_ELEMENTS_SCHEMA, provideEnvironmentInitializer } from '@angular/core';
3
3
  import { RouterOutlet, Router, PRIMARY_OUTLET, ChildrenOutletContexts, NavigationStart, ActivationStart, ActivationEnd, UrlSegment, RouterEvent, NavigationEnd, NavigationCancel, NavigationError, ActivatedRoute } from '@angular/router';
4
- import { Arrays, Objects as Objects$1, Defined, Observables, Maps, Dictionaries } from '@scion/toolkit/util';
4
+ import { Arrays, Objects, Defined, Observables, Maps, Dictionaries } from '@scion/toolkit/util';
5
5
  import { toObservable, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
6
- import { skipUntil, mergeMap, filter, observeOn, catchError, map, startWith, take, switchMap as switchMap$1, skip, subscribeOn, finalize, distinctUntilChanged, takeUntil, first, combineLatestWith } from 'rxjs/operators';
6
+ import { skipUntil, mergeMap, observeOn, catchError, map, startWith, take, filter, switchMap as switchMap$1, skip, subscribeOn, finalize, distinctUntilChanged, takeUntil, first, combineLatestWith } from 'rxjs/operators';
7
7
  import { coerceElement } from '@angular/cdk/coercion';
8
8
  import { UUID } from '@scion/toolkit/uuid';
9
- import { Observable, Subject, asapScheduler, AsyncSubject, lastValueFrom, iif, switchMap, race, pairwise, EMPTY, of, firstValueFrom, from, animationFrameScheduler, merge, fromEvent, noop, BehaviorSubject, mergeWith, mergeMap as mergeMap$1, delay, identity, concatWith, withLatestFrom, NEVER, map as map$1, share, timer, ReplaySubject, combineLatest, tap, asyncScheduler } from 'rxjs';
9
+ import { Observable, Subject, asapScheduler, AsyncSubject, lastValueFrom, iif, switchMap, race, pairwise, EMPTY, of, firstValueFrom, from, animationFrameScheduler, merge, fromEvent, noop, BehaviorSubject, mergeWith, mergeMap as mergeMap$1, delay, identity, concatWith, withLatestFrom, NEVER, map as map$1, share, timer, ReplaySubject, combineLatest, tap } from 'rxjs';
10
10
  import { ManifestService, MicrofrontendPlatform, PlatformState, QualifierMatcher, ObservableDecorator, MicrofrontendPlatformConfig, MicrofrontendPlatformHost, APP_IDENTITY, MessageClient, mapToBody, OutletRouter, MessageHeaders, ResponseStatusCodes, ParamMatcher, CapabilityInterceptor, HostManifestInterceptor, IntentInterceptor, IntentClient, PlatformPropertyService } from '@scion/microfrontend-platform';
11
11
  import { tapFirst, subscribeIn, observeIn, filterArray } from '@scion/toolkit/operators';
12
- import { WorkbenchCapabilities, WorkbenchRouter as WorkbenchRouter$1, ɵWorkbenchRouter as _WorkbenchRouter, WorkbenchDialogService as WorkbenchDialogService$1, ɵWorkbenchDialogService as _WorkbenchDialogService, WorkbenchMessageBoxService as WorkbenchMessageBoxService$1, ɵWorkbenchMessageBoxService as _WorkbenchMessageBoxService, WorkbenchPopupService as WorkbenchPopupService$1, ɵWorkbenchPopupService as _WorkbenchPopupService, WorkbenchNotificationService as WorkbenchNotificationService$1, ɵWorkbenchNotificationService as _WorkbenchNotificationService, WorkbenchTextService, ɵWorkbenchTextService as _WorkbenchTextService, ɵTHEME_CONTEXT_KEY as _THEME_CONTEXT_KEY, ɵVIEW_ID_CONTEXT_KEY as _VIEW_ID_CONTEXT_KEY, ɵWorkbenchCommands as _WorkbenchCommands, ɵVIEW_CAPABILITY_ID_PARAM_NAME as _VIEW_CAPABILITY_ID_PARAM_NAME, ɵWORKBENCH_PART_CONTEXT as _WORKBENCH_PART_CONTEXT, eNOTIFICATION_MESSAGE_PARAM, ɵDIALOG_CONTEXT as _DIALOG_CONTEXT, ɵWorkbenchDialogMessageHeaders as _WorkbenchDialogMessageHeaders, ɵMESSAGE_BOX_CONTEXT as _MESSAGE_BOX_CONTEXT, eMESSAGE_BOX_MESSAGE_PARAM, WorkbenchClient, ɵPOPUP_CONTEXT as _POPUP_CONTEXT, ɵWorkbenchPopupMessageHeaders as _WorkbenchPopupMessageHeaders } from '@scion/workbench-client';
12
+ import { WorkbenchCapabilities, WorkbenchRouter as WorkbenchRouter$1, ɵWorkbenchRouter as _WorkbenchRouter, WorkbenchDialogService as WorkbenchDialogService$1, ɵWorkbenchDialogService as _WorkbenchDialogService, WorkbenchMessageBoxService as WorkbenchMessageBoxService$1, ɵWorkbenchMessageBoxService as _WorkbenchMessageBoxService, WorkbenchPopupService as WorkbenchPopupService$1, ɵWorkbenchPopupService as _WorkbenchPopupService, WorkbenchNotificationService as WorkbenchNotificationService$1, ɵWorkbenchNotificationService as _WorkbenchNotificationService, WorkbenchTextService, ɵWorkbenchTextService as _WorkbenchTextService, ɵTHEME_CONTEXT_KEY as _THEME_CONTEXT_KEY, ɵVIEW_ID_CONTEXT_KEY as _VIEW_ID_CONTEXT_KEY, ɵWorkbenchCommands as _WorkbenchCommands, ɵVIEW_CAPABILITY_ID_PARAM_NAME as _VIEW_CAPABILITY_ID_PARAM_NAME, ɵWORKBENCH_PART_CONTEXT as _WORKBENCH_PART_CONTEXT, eNOTIFICATION_MESSAGE_PARAM, ɵNOTIFICATION_CONTEXT as _NOTIFICATION_CONTEXT, ɵDIALOG_CONTEXT as _DIALOG_CONTEXT, ɵWorkbenchDialogMessageHeaders as _WorkbenchDialogMessageHeaders, ɵMESSAGE_BOX_CONTEXT as _MESSAGE_BOX_CONTEXT, eMESSAGE_BOX_MESSAGE_PARAM, WorkbenchClient, ɵPOPUP_CONTEXT as _POPUP_CONTEXT, ɵWorkbenchPopupMessageHeaders as _WorkbenchPopupMessageHeaders } from '@scion/workbench-client';
13
13
  import { fromMutation$, fromResize$ } from '@scion/toolkit/observable';
14
14
  import { boundingClientRect, SciDimensionDirective, dimension } from '@scion/components/dimension';
15
15
  import { SciViewportComponent } from '@scion/components/viewport';
@@ -511,12 +511,6 @@ const WORKBENCH_ROUTE = new InjectionToken('WORKBENCH_ROUTE');
511
511
  function serializeExecution(fn) {
512
512
  return mergeMap(element => fn(element), 1);
513
513
  }
514
- /**
515
- * Mirrors the source except for `null` emissions.
516
- */
517
- function filterNull() {
518
- return filter((item) => item !== null);
519
- }
520
514
 
521
515
  /*
522
516
  * Copyright (c) 2018-2024 Swiss Federal Railways
@@ -1376,7 +1370,7 @@ function provideManifestObjectCache() {
1376
1370
  }
1377
1371
 
1378
1372
  /*
1379
- * Copyright (c) 2018-2025 Swiss Federal Railways
1373
+ * Copyright (c) 2018-2026 Swiss Federal Railways
1380
1374
  *
1381
1375
  * This program and the accompanying materials are made
1382
1376
  * available under the terms of the Eclipse Public License 2.0
@@ -1559,6 +1553,41 @@ function canMatchWorkbenchMessageBoxCapability(qualifier) {
1559
1553
  function canMatchWorkbenchPopupCapability(qualifier) {
1560
1554
  return canMatchWorkbenchCapability('popup', WorkbenchCapabilities.Popup, qualifier);
1561
1555
  }
1556
+ /**
1557
+ * Configures a route to only match workbench notifications navigated to the specified notification capability.
1558
+ *
1559
+ * Use this guard to differentiate microfrontend routes, which must all have an empty path.
1560
+ *
1561
+ * @example - Route matching a notification capability with qualifier {notification: 'info'}
1562
+ * ```ts
1563
+ * import {Routes} from '@angular/router';
1564
+ * import {canMatchWorkbenchNotificationCapability} from '@scion/workbench';
1565
+ *
1566
+ * const routes: Routes = [
1567
+ * {path: '', canMatch: [canMatchWorkbenchNotificationCapability({notification: 'info'})], component: InfoComponent},
1568
+ * ];
1569
+ * ```
1570
+ *
1571
+ * The above route matches the following notification capability:
1572
+ *
1573
+ * ```json
1574
+ * {
1575
+ * "type": "notification",
1576
+ * "qualifier": {
1577
+ * "notification": "info"
1578
+ * },
1579
+ * "properties": {
1580
+ * "path": ""
1581
+ * }
1582
+ * }
1583
+ * ```
1584
+ *
1585
+ * @param qualifier - Identifies the notification capability.
1586
+ * @return guard matching the specified notification capability.
1587
+ */
1588
+ function canMatchWorkbenchNotificationCapability(qualifier) {
1589
+ return canMatchWorkbenchCapability('notification', WorkbenchCapabilities.Notification, qualifier);
1590
+ }
1562
1591
  /**
1563
1592
  * Matches a route if navigated to the specified capability displayed in the specified workbench element.
1564
1593
  */
@@ -1673,6 +1702,12 @@ function isDialogId(dialogId) {
1673
1702
  function isPopupId(popupId) {
1674
1703
  return popupId?.startsWith(POPUP_ID_PREFIX) ?? false;
1675
1704
  }
1705
+ /**
1706
+ * Tests if the given id matches the format of a notification identifier.
1707
+ */
1708
+ function isNotificationId(notificationId) {
1709
+ return notificationId?.startsWith(NOTIFICATION_ID_PREFIX) ?? false;
1710
+ }
1676
1711
  /**
1677
1712
  * Tests if the given id matches the format of an activity identifier.
1678
1713
  */
@@ -1993,7 +2028,7 @@ class UrlSegmentMatcher {
1993
2028
  if (checkPath && segment.path !== this._pattern[index].path) {
1994
2029
  return false;
1995
2030
  }
1996
- if (checkMatrixParams && !Objects$1.isEqual(segment.parameters, this._pattern[index].parameters)) {
2031
+ if (checkMatrixParams && !Objects.isEqual(segment.parameters, this._pattern[index].parameters)) {
1997
2032
  return false;
1998
2033
  }
1999
2034
  return true;
@@ -2001,51 +2036,6 @@ class UrlSegmentMatcher {
2001
2036
  }
2002
2037
  }
2003
2038
 
2004
- /*
2005
- * Copyright (c) 2018-2024 Swiss Federal Railways
2006
- *
2007
- * This program and the accompanying materials are made
2008
- * available under the terms of the Eclipse Public License 2.0
2009
- * which is available at https://www.eclipse.org/legal/epl-2.0/
2010
- *
2011
- * SPDX-License-Identifier: EPL-2.0
2012
- */
2013
- /**
2014
- * Provides helper functions for working with objects.
2015
- */
2016
- const Objects = {
2017
- /**
2018
- * Like {@link Object.keys}, but preserving the data type of keys.
2019
- */
2020
- keys: (object) => {
2021
- return Object.keys(object);
2022
- },
2023
- /**
2024
- * Like {@link Object.values}, but preserving the data type of values and supporting optional properties.
2025
- */
2026
- values: (object) => {
2027
- return Object.values(object);
2028
- },
2029
- /**
2030
- * Like {@link Object.entries}, but preserving the data type of keys and supporting optional properties.
2031
- */
2032
- entries: (object) => {
2033
- return Object.entries(object);
2034
- },
2035
- /**
2036
- * Stringifies given object to matrix notation: a=b;c=d;e=f
2037
- */
2038
- toMatrixNotation: (object) => {
2039
- return Object.entries(object ?? {}).map(([key, value]) => `${key}=${value}`).join(';');
2040
- },
2041
- /**
2042
- * Compares the two objects for shallow equality, ignoring the propery order.
2043
- */
2044
- isEqual: (a, b) => {
2045
- return Objects$1.isEqual(a, b);
2046
- },
2047
- };
2048
-
2049
2039
  /*
2050
2040
  * Copyright (c) 2018-2023 Swiss Federal Railways
2051
2041
  *
@@ -6376,7 +6366,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
6376
6366
  }], ctorParameters: () => [] });
6377
6367
 
6378
6368
  /*
6379
- * Copyright (c) 2018-2025 Swiss Federal Railways
6369
+ * Copyright (c) 2018-2026 Swiss Federal Railways
6380
6370
  *
6381
6371
  * This program and the accompanying materials are made
6382
6372
  * available under the terms of the Eclipse Public License 2.0
@@ -6435,7 +6425,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
6435
6425
  }] });
6436
6426
 
6437
6427
  /*
6438
- * Copyright (c) 2018-2023 Swiss Federal Railways
6428
+ * Copyright (c) 2018-2026 Swiss Federal Railways
6439
6429
  *
6440
6430
  * This program and the accompanying materials are made
6441
6431
  * available under the terms of the Eclipse Public License 2.0
@@ -6789,7 +6779,7 @@ function removeEmptyEntries(map) {
6789
6779
  * Compares two arrays of strings for equality, ignoring element order.
6790
6780
  */
6791
6781
  function isEqualArray(a, b) {
6792
- return Arrays.isEqual(a, b, { exactOrder: false });
6782
+ return Objects.isEqual(a, b, { ignoreArrayOrder: true });
6793
6783
  }
6794
6784
 
6795
6785
  /**
@@ -6863,7 +6853,7 @@ class WbComponentPortal {
6863
6853
  this.detach();
6864
6854
  }
6865
6855
  this._viewContainerRef.set(viewContainerRef);
6866
- this._componentRef.update(componentRef => componentRef ?? createPortalComponent(this._componentType, { providers: this._options?.providers, injector: viewContainerRef.injector }));
6856
+ this._componentRef.update(componentRef => componentRef ?? createPortalComponent(this._componentType, { providers: this._options?.providers, injector: this._options?.injector ?? viewContainerRef.injector }));
6867
6857
  this._logger.debug(() => 'Attaching portal', LoggerNames.LIFECYCLE, this._options?.debugName ?? this._componentType.name);
6868
6858
  this._componentRef().changeDetectorRef.reattach();
6869
6859
  this._viewContainerRef().insert(this._componentRef().hostView);
@@ -8339,7 +8329,7 @@ class MainAreaPartComponent {
8339
8329
  });
8340
8330
  }
8341
8331
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MainAreaPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8342
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: MainAreaPartComponent, isStandalone: true, selector: "wb-part[data-partid=\"part.main-area\"]", host: { properties: { "attr.data-grid": "dasherize(part.gridName())", "attr.data-active": "part.active() ? '' : null" } }, ngImport: i0, template: "@if (mainAreaGrid().root.visible) {\n <wb-grid [grid]=\"mainAreaGrid()\"\n [gridDropZone]=\"{\n dropRegionSize: 100,\n dropPlaceholderSize: 100,\n dropZoneAttributes: {\n 'data-grid': 'main-area',\n 'data-partid': part.id,\n }\n }\"\n [attr.data-grid]=\"'main-area'\"\n class=\"e2e-content\"/>\n} @else {\n <div wbViewDropZone\n [wbViewDropZoneRegions]=\"canDrop() && {center: true, north: false, south: false, west: false, east: false}\"\n [wbViewDropZoneAttributes]=\"{\n 'data-desktop': '',\n 'data-partid': part.id,\n }\"\n (wbViewDropZoneDrop)=\"onDesktopViewDrop($event)\"\n class=\"desktop e2e-part-content e2e-content\">\n @if (part.navigation()) {\n <ng-container *wbPortalOutlet=\"part.slot.portal; destroyOnDetach: false\"/>\n } @else {\n <ng-container *wbPortalOutlet=\"desktop.slot.portal; destroyOnDetach: false\"/>\n }\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column}:host>wb-grid{flex:auto}:host>div.desktop{flex:auto;display:grid;position:relative}\n"], dependencies: [{ kind: "component", type: GridComponent, selector: "wb-grid", inputs: ["grid", "gridDropZone"] }, { kind: "directive", type: ViewDropZoneDirective, selector: "[wbViewDropZone]", inputs: ["wbViewDropZoneRegions", "wbViewDropZoneAttributes", "wbViewDropZoneRegionSize", "wbViewDropZonePlaceholderSize"], outputs: ["wbViewDropZoneDrop"] }, { kind: "directive", type: WorkbenchPortalOutletDirective, selector: "ng-template[wbPortalOutlet]", inputs: ["wbPortalOutlet", "wbPortalOutletDestroyOnDetach"] }] });
8332
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: MainAreaPartComponent, isStandalone: true, selector: "wb-part[data-partid=\"part.main-area\"]", host: { properties: { "attr.data-grid": "dasherize(part.gridName())", "attr.data-active": "part.active() ? '' : null" } }, ngImport: i0, template: "@if (mainAreaGrid().root.visible) {\n <wb-grid [grid]=\"mainAreaGrid()\"\n [gridDropZone]=\"{\n dropRegionSize: 100,\n dropPlaceholderSize: 100,\n dropZoneAttributes: {\n 'data-grid': 'main-area',\n 'data-partid': part.id,\n }\n }\"\n [attr.data-grid]=\"'main-area'\"\n class=\"e2e-slot\"/>\n} @else {\n <div wbViewDropZone\n [wbViewDropZoneRegions]=\"canDrop() && {center: true, north: false, south: false, west: false, east: false}\"\n [wbViewDropZoneAttributes]=\"{\n 'data-desktop': '',\n 'data-partid': part.id,\n }\"\n (wbViewDropZoneDrop)=\"onDesktopViewDrop($event)\"\n class=\"desktop e2e-part-slot e2e-slot\">\n @if (part.navigation()) {\n <ng-container *wbPortalOutlet=\"part.slot.portal; destroyOnDetach: false\"/>\n } @else {\n <ng-container *wbPortalOutlet=\"desktop.slot.portal; destroyOnDetach: false\"/>\n }\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column}:host>wb-grid{flex:auto}:host>div.desktop{flex:auto;display:grid;position:relative}\n"], dependencies: [{ kind: "component", type: GridComponent, selector: "wb-grid", inputs: ["grid", "gridDropZone"] }, { kind: "directive", type: ViewDropZoneDirective, selector: "[wbViewDropZone]", inputs: ["wbViewDropZoneRegions", "wbViewDropZoneAttributes", "wbViewDropZoneRegionSize", "wbViewDropZonePlaceholderSize"], outputs: ["wbViewDropZoneDrop"] }, { kind: "directive", type: WorkbenchPortalOutletDirective, selector: "ng-template[wbPortalOutlet]", inputs: ["wbPortalOutlet", "wbPortalOutletDestroyOnDetach"] }] });
8343
8333
  }
8344
8334
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MainAreaPartComponent, decorators: [{
8345
8335
  type: Component,
@@ -8350,7 +8340,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
8350
8340
  ], host: {
8351
8341
  '[attr.data-grid]': 'dasherize(part.gridName())',
8352
8342
  '[attr.data-active]': `part.active() ? '' : null`,
8353
- }, template: "@if (mainAreaGrid().root.visible) {\n <wb-grid [grid]=\"mainAreaGrid()\"\n [gridDropZone]=\"{\n dropRegionSize: 100,\n dropPlaceholderSize: 100,\n dropZoneAttributes: {\n 'data-grid': 'main-area',\n 'data-partid': part.id,\n }\n }\"\n [attr.data-grid]=\"'main-area'\"\n class=\"e2e-content\"/>\n} @else {\n <div wbViewDropZone\n [wbViewDropZoneRegions]=\"canDrop() && {center: true, north: false, south: false, west: false, east: false}\"\n [wbViewDropZoneAttributes]=\"{\n 'data-desktop': '',\n 'data-partid': part.id,\n }\"\n (wbViewDropZoneDrop)=\"onDesktopViewDrop($event)\"\n class=\"desktop e2e-part-content e2e-content\">\n @if (part.navigation()) {\n <ng-container *wbPortalOutlet=\"part.slot.portal; destroyOnDetach: false\"/>\n } @else {\n <ng-container *wbPortalOutlet=\"desktop.slot.portal; destroyOnDetach: false\"/>\n }\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column}:host>wb-grid{flex:auto}:host>div.desktop{flex:auto;display:grid;position:relative}\n"] }]
8343
+ }, template: "@if (mainAreaGrid().root.visible) {\n <wb-grid [grid]=\"mainAreaGrid()\"\n [gridDropZone]=\"{\n dropRegionSize: 100,\n dropPlaceholderSize: 100,\n dropZoneAttributes: {\n 'data-grid': 'main-area',\n 'data-partid': part.id,\n }\n }\"\n [attr.data-grid]=\"'main-area'\"\n class=\"e2e-slot\"/>\n} @else {\n <div wbViewDropZone\n [wbViewDropZoneRegions]=\"canDrop() && {center: true, north: false, south: false, west: false, east: false}\"\n [wbViewDropZoneAttributes]=\"{\n 'data-desktop': '',\n 'data-partid': part.id,\n }\"\n (wbViewDropZoneDrop)=\"onDesktopViewDrop($event)\"\n class=\"desktop e2e-part-slot e2e-slot\">\n @if (part.navigation()) {\n <ng-container *wbPortalOutlet=\"part.slot.portal; destroyOnDetach: false\"/>\n } @else {\n <ng-container *wbPortalOutlet=\"desktop.slot.portal; destroyOnDetach: false\"/>\n }\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column}:host>wb-grid{flex:auto}:host>div.desktop{flex:auto;display:grid;position:relative}\n"] }]
8354
8344
  }] });
8355
8345
 
8356
8346
  /*
@@ -9732,7 +9722,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
9732
9722
  }] });
9733
9723
 
9734
9724
  /*
9735
- * Copyright (c) 2018-2023 Swiss Federal Railways
9725
+ * Copyright (c) 2018-2026 Swiss Federal Railways
9736
9726
  *
9737
9727
  * This program and the accompanying materials are made
9738
9728
  * available under the terms of the Eclipse Public License 2.0
@@ -9754,7 +9744,8 @@ class WorkbenchDialogComponent {
9754
9744
  headerHeight = signal(undefined, { ...(ngDevMode ? { debugName: "headerHeight" } : {}) });
9755
9745
  transformTranslateX = signal(0, { ...(ngDevMode ? { debugName: "transformTranslateX" } : {}) });
9756
9746
  transformTranslateY = signal(0, { ...(ngDevMode ? { debugName: "transformTranslateY" } : {}) });
9757
- dialogContent = viewChild.required(SciViewportComponent, { read: (ElementRef) });
9747
+ slotAnchorName = this.dialog.id.replace('.', '_'); // Anchor must not contain a dot.
9748
+ dialogSlotBounds = viewChild.required('slot_bounds', { read: (ElementRef) });
9758
9749
  constructor() {
9759
9750
  this.setDialogOffset();
9760
9751
  this.trackFocus();
@@ -9856,7 +9847,7 @@ class WorkbenchDialogComponent {
9856
9847
  this.focus();
9857
9848
  }
9858
9849
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9859
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: WorkbenchDialogComponent, isStandalone: true, selector: "wb-dialog", host: { listeners: { "keydown.escape": "onEscape($event)" }, properties: { "attr.data-dialogid": "dialog.id", "class.justified": "!dialog.padding()", "style.--\u0275dialog-transform-translate-x": "transformTranslateX()", "style.--\u0275dialog-transform-translate-y": "transformTranslateY()", "style.--\u0275dialog-min-height": "dialog.size.minHeight() ?? headerHeight()", "style.--\u0275dialog-height": "dialog.size.height()", "style.--\u0275dialog-max-height": "dialog.size.maxHeight()", "style.--\u0275dialog-min-width": "dialog.size.minWidth() ?? '100px'", "style.--\u0275dialog-width": "dialog.size.width()", "style.--\u0275dialog-max-width": "dialog.size.maxWidth()", "class": "dialog.cssClass()" } }, viewQueries: [{ propertyName: "_cdkTrapFocus", first: true, predicate: CdkTrapFocus, descendants: true, isSignal: true }, { propertyName: "_dialogElement", first: true, predicate: ["dialog_element"], descendants: true, isSignal: true }, { propertyName: "dialogContent", first: true, predicate: SciViewportComponent, descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<div class=\"dialog e2e-dialog\"\n [class.blinking]=\"dialog.blinking$ | async\"\n [tabindex]=\"-1\"\n wbMovable [wbHandle]=\"header\" (wbMovableMove)=\"onMove($event)\"\n (mousedown)=\"onDialogMouseDown()\"\n wbResizable [wbResizableEnabled]=\"dialog.resizable()\" (wbResizableResize)=\"onResize($event)\"\n @enter\n [@.disabled]=\"!dialog.animate\"\n wbGlassPane\n #dialog_element>\n <div class=\"dialog-box e2e-dialog-box\" cdkTrapFocus>\n <header #header\n class=\"e2e-dialog-header\"\n [class.divider]=\"dialog.header?.divider() ?? true\"\n sciDimension (sciDimensionChange)=\"onHeaderDimensionChange($event)\">\n <ng-container *ngTemplateOutlet=\"dialog.header?.template ?? default_dialog_header\"/>\n </header>\n\n <sci-viewport class=\"content e2e-dialog-content\">\n <ng-container *ngComponentOutlet=\"dialog.component; inputs: dialog.inputs\"/>\n </sci-viewport>\n\n @if (dialog.footer || dialog.actions.length) {\n <footer class=\"e2e-dialog-footer\" [class.divider]=\"dialog.footer?.divider() ?? true\">\n <ng-container *ngTemplateOutlet=\"dialog.footer?.template ?? default_dialog_footer\"/>\n </footer>\n }\n </div>\n</div>\n\n<ng-template #default_dialog_header>\n <wb-dialog-header/>\n</ng-template>\n\n<ng-template #default_dialog_footer>\n <wb-dialog-footer/>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{--\\275 dialog-transform-translate-x: 0;--\\275 dialog-transform-translate-y: 0;--\\275 dialog-min-height: initial;--\\275 dialog-height: initial;--\\275 dialog-max-height: initial;--\\275 dialog-min-width: initial;--\\275 dialog-width: initial;--\\275 dialog-max-width: initial;--\\275 dialog-padding: var(--sci-workbench-dialog-padding)}:host.justified{--\\275 dialog-padding: 0}:host{display:flex;flex-direction:column;align-items:center;position:relative}:host>div.dialog{display:flex;flex-direction:column;position:absolute;top:3%;color:var(--sci-color-text);transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x))) translateY(calc(1px * var(--\\275 dialog-transform-translate-y)));min-height:var(--\\275 dialog-min-height);height:var(--\\275 dialog-height);max-height:var(--\\275 dialog-max-height);min-width:var(--\\275 dialog-min-width);width:var(--\\275 dialog-width);max-width:var(--\\275 dialog-max-width);outline:none;pointer-events:auto}:host>div.dialog>div.dialog-box{flex:auto;display:flex;flex-direction:column;gap:calc(1.25 * var(--\\275 dialog-padding));border:1px solid var(--sci-color-border);border-radius:var(--sci-corner);background-color:var(--sci-color-background-elevation);box-shadow:var(--sci-elevation) var(--sci-static-color-black);overflow:hidden}:host>div.dialog>div.dialog-box>header{flex:none}:host>div.dialog>div.dialog-box>header.divider{border-bottom:1px solid var(--sci-color-border)}:host>div.dialog>div.dialog-box>sci-viewport{flex:auto}:host>div.dialog>div.dialog-box>sci-viewport::part(content){padding-inline:var(--\\275 dialog-padding)}:host>div.dialog>div.dialog-box>footer{flex:none}:host>div.dialog>div.dialog-box>footer.divider{border-top:1px solid var(--sci-color-border)}:host>div.dialog.blinking{animation-duration:50ms;animation-iteration-count:infinite;animation-name:blink-animation}@keyframes blink-animation{0%{transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x) - 2px)) translateY(calc(1px * var(--\\275 dialog-transform-translate-y) - 1px))}to{transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x) + 2px)) translateY(calc(1px * var(--\\275 dialog-transform-translate-y) + 1px))}}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: MovableDirective, selector: "[wbMovable]", inputs: ["wbHandle"], outputs: ["wbMovableMove"] }, { kind: "directive", type: ResizableDirective, selector: "[wbResizable]", inputs: ["wbResizableEnabled"], outputs: ["wbResizableResize"] }, { kind: "component", type: SciViewportComponent, selector: "sci-viewport", inputs: ["scrollbarStyle"], outputs: ["scroll"] }, { kind: "directive", type: SciDimensionDirective, selector: "[sciDimension]", inputs: ["emitOutsideAngular"], outputs: ["sciDimensionChange"] }, { kind: "component", type: DialogHeaderComponent, selector: "wb-dialog-header" }, { kind: "component", type: DialogFooterComponent, selector: "wb-dialog-footer" }, { kind: "directive", type: GlassPaneDirective, selector: "[wbGlassPane]" }, { kind: "pipe", type: AsyncPipe, name: "async" }], viewProviders: [
9850
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: WorkbenchDialogComponent, isStandalone: true, selector: "wb-dialog", host: { listeners: { "keydown.escape": "onEscape($event)" }, properties: { "attr.data-dialogid": "dialog.id", "class.justified": "!dialog.padding()", "style.--\u0275dialog-transform-translate-x": "transformTranslateX()", "style.--\u0275dialog-transform-translate-y": "transformTranslateY()", "style.--\u0275dialog-min-height": "dialog.size.minHeight() ?? headerHeight()", "style.--\u0275dialog-height": "dialog.size.height()", "style.--\u0275dialog-max-height": "dialog.size.maxHeight()", "style.--\u0275dialog-min-width": "dialog.size.minWidth() ?? '100px'", "style.--\u0275dialog-width": "dialog.size.width()", "style.--\u0275dialog-max-width": "dialog.size.maxWidth()", "style.--\u0275slot-anchor": "`--${slotAnchorName}`", "class": "dialog.cssClass()" } }, viewQueries: [{ propertyName: "_cdkTrapFocus", first: true, predicate: CdkTrapFocus, descendants: true, isSignal: true }, { propertyName: "_dialogElement", first: true, predicate: ["dialog_element"], descendants: true, isSignal: true }, { propertyName: "dialogSlotBounds", first: true, predicate: ["slot_bounds"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<div class=\"dialog e2e-dialog\"\n [class.blinking]=\"dialog.blinking$ | async\"\n [tabindex]=\"-1\"\n wbMovable [wbHandle]=\"header\" (wbMovableMove)=\"onMove($event)\"\n (mousedown)=\"onDialogMouseDown()\"\n wbResizable [wbResizableEnabled]=\"dialog.resizable()\" (wbResizableResize)=\"onResize($event)\"\n @enter\n [@.disabled]=\"!dialog.animate\"\n wbGlassPane\n #dialog_element>\n <div class=\"dialog-box e2e-dialog-box\" cdkTrapFocus>\n <header #header\n class=\"e2e-dialog-header\"\n [class.divider]=\"dialog.header?.divider() ?? true\"\n sciDimension (sciDimensionChange)=\"onHeaderDimensionChange($event)\">\n <ng-container *ngTemplateOutlet=\"dialog.header?.template ?? default_dialog_header\"/>\n </header>\n\n <sci-viewport class=\"e2e-dialog-slot\">\n <ng-container *ngComponentOutlet=\"dialog.component; inputs: dialog.inputs\"/>\n </sci-viewport>\n\n <!-- Extra DIV to capture bounds available to slotted content, excluding viewport content padding. May differ from the actual content size if content overflows or does not fill the slot. -->\n <div class=\"slot-bounds e2e-dialog-slot-bounds\" #slot_bounds></div>\n\n @if (dialog.footer || dialog.actions.length) {\n <footer class=\"e2e-dialog-footer\" [class.divider]=\"dialog.footer?.divider() ?? true\">\n <ng-container *ngTemplateOutlet=\"dialog.footer?.template ?? default_dialog_footer\"/>\n </footer>\n }\n </div>\n</div>\n\n<ng-template #default_dialog_header>\n <wb-dialog-header/>\n</ng-template>\n\n<ng-template #default_dialog_footer>\n <wb-dialog-footer/>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{--\\275 dialog-transform-translate-x: 0;--\\275 dialog-transform-translate-y: 0;--\\275 dialog-min-height: initial;--\\275 dialog-height: initial;--\\275 dialog-max-height: initial;--\\275 dialog-min-width: initial;--\\275 dialog-width: initial;--\\275 dialog-max-width: initial;--\\275 dialog-padding: var(--sci-workbench-dialog-padding)}:host.justified{--\\275 dialog-padding: 0}:host{display:flex;flex-direction:column;align-items:center;position:relative}:host>div.dialog{display:flex;flex-direction:column;position:absolute;top:3%;color:var(--sci-color-text);transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x))) translateY(calc(1px * var(--\\275 dialog-transform-translate-y)));min-height:var(--\\275 dialog-min-height);height:var(--\\275 dialog-height);max-height:var(--\\275 dialog-max-height);min-width:var(--\\275 dialog-min-width);width:var(--\\275 dialog-width);max-width:var(--\\275 dialog-max-width);outline:none;pointer-events:auto}:host>div.dialog>div.dialog-box{flex:auto;display:flex;flex-direction:column;gap:calc(1.25 * var(--\\275 dialog-padding));border:1px solid var(--sci-color-border);border-radius:var(--sci-corner);background-color:var(--sci-color-background-elevation);box-shadow:var(--sci-elevation) var(--sci-static-color-black);overflow:hidden}:host>div.dialog>div.dialog-box>header{flex:none}:host>div.dialog>div.dialog-box>header.divider{border-bottom:1px solid var(--sci-color-border)}:host>div.dialog>div.dialog-box>sci-viewport{flex:auto;anchor-name:var(--\\275slot-anchor)}:host>div.dialog>div.dialog-box>sci-viewport::part(content){padding-inline:var(--\\275 dialog-padding)}:host>div.dialog>div.dialog-box>div.slot-bounds{position:absolute;position-anchor:var(--\\275slot-anchor);inset:anchor(top) anchor(right) anchor(bottom) anchor(left);margin-inline:var(--\\275 dialog-padding);visibility:hidden}:host>div.dialog>div.dialog-box>footer{flex:none}:host>div.dialog>div.dialog-box>footer.divider{border-top:1px solid var(--sci-color-border)}:host>div.dialog.blinking{animation-duration:50ms;animation-iteration-count:infinite;animation-name:blink-animation}@keyframes blink-animation{0%{transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x) - 2px)) translateY(calc(1px * var(--\\275 dialog-transform-translate-y) - 1px))}to{transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x) + 2px)) translateY(calc(1px * var(--\\275 dialog-transform-translate-y) + 1px))}}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: MovableDirective, selector: "[wbMovable]", inputs: ["wbHandle"], outputs: ["wbMovableMove"] }, { kind: "directive", type: ResizableDirective, selector: "[wbResizable]", inputs: ["wbResizableEnabled"], outputs: ["wbResizableResize"] }, { kind: "component", type: SciViewportComponent, selector: "sci-viewport", inputs: ["scrollbarStyle"], outputs: ["scroll"] }, { kind: "directive", type: SciDimensionDirective, selector: "[sciDimension]", inputs: ["emitOutsideAngular"], outputs: ["sciDimensionChange"] }, { kind: "component", type: DialogHeaderComponent, selector: "wb-dialog-header" }, { kind: "component", type: DialogFooterComponent, selector: "wb-dialog-footer" }, { kind: "directive", type: GlassPaneDirective, selector: "[wbGlassPane]" }, { kind: "pipe", type: AsyncPipe, name: "async" }], viewProviders: [
9860
9851
  configureDialogGlassPane(),
9861
9852
  ], animations: [
9862
9853
  trigger('enter', provideEnterAnimation()),
@@ -9891,9 +9882,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
9891
9882
  '[style.--ɵdialog-min-width]': 'dialog.size.minWidth() ?? \'100px\'',
9892
9883
  '[style.--ɵdialog-width]': 'dialog.size.width()',
9893
9884
  '[style.--ɵdialog-max-width]': 'dialog.size.maxWidth()',
9885
+ '[style.--ɵslot-anchor]': '`--${slotAnchorName}`',
9894
9886
  '[class]': 'dialog.cssClass()',
9895
- }, template: "<div class=\"dialog e2e-dialog\"\n [class.blinking]=\"dialog.blinking$ | async\"\n [tabindex]=\"-1\"\n wbMovable [wbHandle]=\"header\" (wbMovableMove)=\"onMove($event)\"\n (mousedown)=\"onDialogMouseDown()\"\n wbResizable [wbResizableEnabled]=\"dialog.resizable()\" (wbResizableResize)=\"onResize($event)\"\n @enter\n [@.disabled]=\"!dialog.animate\"\n wbGlassPane\n #dialog_element>\n <div class=\"dialog-box e2e-dialog-box\" cdkTrapFocus>\n <header #header\n class=\"e2e-dialog-header\"\n [class.divider]=\"dialog.header?.divider() ?? true\"\n sciDimension (sciDimensionChange)=\"onHeaderDimensionChange($event)\">\n <ng-container *ngTemplateOutlet=\"dialog.header?.template ?? default_dialog_header\"/>\n </header>\n\n <sci-viewport class=\"content e2e-dialog-content\">\n <ng-container *ngComponentOutlet=\"dialog.component; inputs: dialog.inputs\"/>\n </sci-viewport>\n\n @if (dialog.footer || dialog.actions.length) {\n <footer class=\"e2e-dialog-footer\" [class.divider]=\"dialog.footer?.divider() ?? true\">\n <ng-container *ngTemplateOutlet=\"dialog.footer?.template ?? default_dialog_footer\"/>\n </footer>\n }\n </div>\n</div>\n\n<ng-template #default_dialog_header>\n <wb-dialog-header/>\n</ng-template>\n\n<ng-template #default_dialog_footer>\n <wb-dialog-footer/>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{--\\275 dialog-transform-translate-x: 0;--\\275 dialog-transform-translate-y: 0;--\\275 dialog-min-height: initial;--\\275 dialog-height: initial;--\\275 dialog-max-height: initial;--\\275 dialog-min-width: initial;--\\275 dialog-width: initial;--\\275 dialog-max-width: initial;--\\275 dialog-padding: var(--sci-workbench-dialog-padding)}:host.justified{--\\275 dialog-padding: 0}:host{display:flex;flex-direction:column;align-items:center;position:relative}:host>div.dialog{display:flex;flex-direction:column;position:absolute;top:3%;color:var(--sci-color-text);transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x))) translateY(calc(1px * var(--\\275 dialog-transform-translate-y)));min-height:var(--\\275 dialog-min-height);height:var(--\\275 dialog-height);max-height:var(--\\275 dialog-max-height);min-width:var(--\\275 dialog-min-width);width:var(--\\275 dialog-width);max-width:var(--\\275 dialog-max-width);outline:none;pointer-events:auto}:host>div.dialog>div.dialog-box{flex:auto;display:flex;flex-direction:column;gap:calc(1.25 * var(--\\275 dialog-padding));border:1px solid var(--sci-color-border);border-radius:var(--sci-corner);background-color:var(--sci-color-background-elevation);box-shadow:var(--sci-elevation) var(--sci-static-color-black);overflow:hidden}:host>div.dialog>div.dialog-box>header{flex:none}:host>div.dialog>div.dialog-box>header.divider{border-bottom:1px solid var(--sci-color-border)}:host>div.dialog>div.dialog-box>sci-viewport{flex:auto}:host>div.dialog>div.dialog-box>sci-viewport::part(content){padding-inline:var(--\\275 dialog-padding)}:host>div.dialog>div.dialog-box>footer{flex:none}:host>div.dialog>div.dialog-box>footer.divider{border-top:1px solid var(--sci-color-border)}:host>div.dialog.blinking{animation-duration:50ms;animation-iteration-count:infinite;animation-name:blink-animation}@keyframes blink-animation{0%{transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x) - 2px)) translateY(calc(1px * var(--\\275 dialog-transform-translate-y) - 1px))}to{transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x) + 2px)) translateY(calc(1px * var(--\\275 dialog-transform-translate-y) + 1px))}}\n"] }]
9896
- }], ctorParameters: () => [], propDecorators: { _cdkTrapFocus: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkTrapFocus), { isSignal: true }] }], _dialogElement: [{ type: i0.ViewChild, args: ['dialog_element', { isSignal: true }] }], dialogContent: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SciViewportComponent), { ...{ read: (ElementRef) }, isSignal: true }] }], onEscape: [{
9887
+ }, template: "<div class=\"dialog e2e-dialog\"\n [class.blinking]=\"dialog.blinking$ | async\"\n [tabindex]=\"-1\"\n wbMovable [wbHandle]=\"header\" (wbMovableMove)=\"onMove($event)\"\n (mousedown)=\"onDialogMouseDown()\"\n wbResizable [wbResizableEnabled]=\"dialog.resizable()\" (wbResizableResize)=\"onResize($event)\"\n @enter\n [@.disabled]=\"!dialog.animate\"\n wbGlassPane\n #dialog_element>\n <div class=\"dialog-box e2e-dialog-box\" cdkTrapFocus>\n <header #header\n class=\"e2e-dialog-header\"\n [class.divider]=\"dialog.header?.divider() ?? true\"\n sciDimension (sciDimensionChange)=\"onHeaderDimensionChange($event)\">\n <ng-container *ngTemplateOutlet=\"dialog.header?.template ?? default_dialog_header\"/>\n </header>\n\n <sci-viewport class=\"e2e-dialog-slot\">\n <ng-container *ngComponentOutlet=\"dialog.component; inputs: dialog.inputs\"/>\n </sci-viewport>\n\n <!-- Extra DIV to capture bounds available to slotted content, excluding viewport content padding. May differ from the actual content size if content overflows or does not fill the slot. -->\n <div class=\"slot-bounds e2e-dialog-slot-bounds\" #slot_bounds></div>\n\n @if (dialog.footer || dialog.actions.length) {\n <footer class=\"e2e-dialog-footer\" [class.divider]=\"dialog.footer?.divider() ?? true\">\n <ng-container *ngTemplateOutlet=\"dialog.footer?.template ?? default_dialog_footer\"/>\n </footer>\n }\n </div>\n</div>\n\n<ng-template #default_dialog_header>\n <wb-dialog-header/>\n</ng-template>\n\n<ng-template #default_dialog_footer>\n <wb-dialog-footer/>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{--\\275 dialog-transform-translate-x: 0;--\\275 dialog-transform-translate-y: 0;--\\275 dialog-min-height: initial;--\\275 dialog-height: initial;--\\275 dialog-max-height: initial;--\\275 dialog-min-width: initial;--\\275 dialog-width: initial;--\\275 dialog-max-width: initial;--\\275 dialog-padding: var(--sci-workbench-dialog-padding)}:host.justified{--\\275 dialog-padding: 0}:host{display:flex;flex-direction:column;align-items:center;position:relative}:host>div.dialog{display:flex;flex-direction:column;position:absolute;top:3%;color:var(--sci-color-text);transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x))) translateY(calc(1px * var(--\\275 dialog-transform-translate-y)));min-height:var(--\\275 dialog-min-height);height:var(--\\275 dialog-height);max-height:var(--\\275 dialog-max-height);min-width:var(--\\275 dialog-min-width);width:var(--\\275 dialog-width);max-width:var(--\\275 dialog-max-width);outline:none;pointer-events:auto}:host>div.dialog>div.dialog-box{flex:auto;display:flex;flex-direction:column;gap:calc(1.25 * var(--\\275 dialog-padding));border:1px solid var(--sci-color-border);border-radius:var(--sci-corner);background-color:var(--sci-color-background-elevation);box-shadow:var(--sci-elevation) var(--sci-static-color-black);overflow:hidden}:host>div.dialog>div.dialog-box>header{flex:none}:host>div.dialog>div.dialog-box>header.divider{border-bottom:1px solid var(--sci-color-border)}:host>div.dialog>div.dialog-box>sci-viewport{flex:auto;anchor-name:var(--\\275slot-anchor)}:host>div.dialog>div.dialog-box>sci-viewport::part(content){padding-inline:var(--\\275 dialog-padding)}:host>div.dialog>div.dialog-box>div.slot-bounds{position:absolute;position-anchor:var(--\\275slot-anchor);inset:anchor(top) anchor(right) anchor(bottom) anchor(left);margin-inline:var(--\\275 dialog-padding);visibility:hidden}:host>div.dialog>div.dialog-box>footer{flex:none}:host>div.dialog>div.dialog-box>footer.divider{border-top:1px solid var(--sci-color-border)}:host>div.dialog.blinking{animation-duration:50ms;animation-iteration-count:infinite;animation-name:blink-animation}@keyframes blink-animation{0%{transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x) - 2px)) translateY(calc(1px * var(--\\275 dialog-transform-translate-y) - 1px))}to{transform:translate(calc(1px * var(--\\275 dialog-transform-translate-x) + 2px)) translateY(calc(1px * var(--\\275 dialog-transform-translate-y) + 1px))}}\n"] }]
9888
+ }], ctorParameters: () => [], propDecorators: { _cdkTrapFocus: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkTrapFocus), { isSignal: true }] }], _dialogElement: [{ type: i0.ViewChild, args: ['dialog_element', { isSignal: true }] }], dialogSlotBounds: [{ type: i0.ViewChild, args: ['slot_bounds', { ...{ read: (ElementRef) }, isSignal: true }] }], onEscape: [{
9897
9889
  type: HostListener,
9898
9890
  args: ['keydown.escape', ['$event']]
9899
9891
  }] } });
@@ -10175,7 +10167,7 @@ class MessageBoxHeaderComponent {
10175
10167
  title = input(undefined, { ...(ngDevMode ? { debugName: "title" } : {}) });
10176
10168
  severity = input.required({ ...(ngDevMode ? { debugName: "severity" } : {}) });
10177
10169
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10178
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: MessageBoxHeaderComponent, isStandalone: true, selector: "wb-message-box-header", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-severity": "severity()" } }, ngImport: i0, template: "@if (title()) {\n <span class=\"title e2e-title\">{{(title() | wbText)()}}</span>\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:grid;border-top:var(--sci-workbench-messagebox-severity-indicator-size) solid var(--\\275message-box-severity-color);padding-inline:var(--sci-workbench-messagebox-padding);padding-top:var(--sci-workbench-messagebox-padding);-webkit-user-select:none;user-select:none}:host[data-severity=info]{--\\275message-box-severity-color: var(--sci-color-accent)}:host[data-severity=warn]{--\\275message-box-severity-color: var(--sci-color-notice)}:host[data-severity=error]{--\\275message-box-severity-color: var(--sci-color-negative)}:host>span.title{overflow-wrap:break-word;white-space:pre-line;font-family:var(--sci-workbench-messagebox-title-font-family),sans-serif;font-size:var(--sci-workbench-messagebox-title-font-size);font-weight:var(--sci-workbench-messagebox-title-font-weight);text-align:var(--sci-workbench-messagebox-title-align)}\n"], dependencies: [{ kind: "pipe", type: TextPipe, name: "wbText" }] });
10170
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: MessageBoxHeaderComponent, isStandalone: true, selector: "wb-message-box-header", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-severity": "severity()" } }, ngImport: i0, template: "@if (title()) {\n <span class=\"title e2e-title\">{{(title() | wbText)()}}</span>\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:grid;border-top:var(--sci-workbench-messagebox-severity-indicator-size) solid var(--\\275message-box-severity-color);padding-inline:var(--sci-workbench-messagebox-padding);padding-top:var(--sci-workbench-messagebox-padding);-webkit-user-select:none;user-select:none}:host[data-severity=info]{--\\275message-box-severity-color: var(--sci-color-accent)}:host[data-severity=warn]{--\\275message-box-severity-color: var(--sci-color-notice)}:host[data-severity=error]{--\\275message-box-severity-color: var(--sci-color-negative)}:host>span.title{word-break:break-word;white-space:pre-line;font-family:var(--sci-workbench-messagebox-title-font-family),sans-serif;font-size:var(--sci-workbench-messagebox-title-font-size);font-weight:var(--sci-workbench-messagebox-title-font-weight);text-align:var(--sci-workbench-messagebox-title-align)}\n"], dependencies: [{ kind: "pipe", type: TextPipe, name: "wbText" }] });
10179
10171
  }
10180
10172
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxHeaderComponent, decorators: [{
10181
10173
  type: Component,
@@ -10183,7 +10175,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
10183
10175
  TextPipe,
10184
10176
  ], host: {
10185
10177
  '[attr.data-severity]': 'severity()',
10186
- }, template: "@if (title()) {\n <span class=\"title e2e-title\">{{(title() | wbText)()}}</span>\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:grid;border-top:var(--sci-workbench-messagebox-severity-indicator-size) solid var(--\\275message-box-severity-color);padding-inline:var(--sci-workbench-messagebox-padding);padding-top:var(--sci-workbench-messagebox-padding);-webkit-user-select:none;user-select:none}:host[data-severity=info]{--\\275message-box-severity-color: var(--sci-color-accent)}:host[data-severity=warn]{--\\275message-box-severity-color: var(--sci-color-notice)}:host[data-severity=error]{--\\275message-box-severity-color: var(--sci-color-negative)}:host>span.title{overflow-wrap:break-word;white-space:pre-line;font-family:var(--sci-workbench-messagebox-title-font-family),sans-serif;font-size:var(--sci-workbench-messagebox-title-font-size);font-weight:var(--sci-workbench-messagebox-title-font-weight);text-align:var(--sci-workbench-messagebox-title-align)}\n"] }]
10178
+ }, template: "@if (title()) {\n <span class=\"title e2e-title\">{{(title() | wbText)()}}</span>\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:grid;border-top:var(--sci-workbench-messagebox-severity-indicator-size) solid var(--\\275message-box-severity-color);padding-inline:var(--sci-workbench-messagebox-padding);padding-top:var(--sci-workbench-messagebox-padding);-webkit-user-select:none;user-select:none}:host[data-severity=info]{--\\275message-box-severity-color: var(--sci-color-accent)}:host[data-severity=warn]{--\\275message-box-severity-color: var(--sci-color-notice)}:host[data-severity=error]{--\\275message-box-severity-color: var(--sci-color-negative)}:host>span.title{word-break:break-word;white-space:pre-line;font-family:var(--sci-workbench-messagebox-title-font-family),sans-serif;font-size:var(--sci-workbench-messagebox-title-font-size);font-weight:var(--sci-workbench-messagebox-title-font-weight);text-align:var(--sci-workbench-messagebox-title-align)}\n"] }]
10187
10179
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: true }] }] } });
10188
10180
 
10189
10181
  /*
@@ -10228,20 +10220,17 @@ class WorkbenchMessageBoxComponent {
10228
10220
  message = input.required({ ...(ngDevMode ? { debugName: "message" } : {}), transform: nullIfEmptyMessage });
10229
10221
  options = input(undefined, { ...(ngDevMode ? { debugName: "options" } : {}) });
10230
10222
  _dialog = inject(ɵWorkbenchDialog);
10231
- // Ensure host element to be focusable in order to close the message box on Escape keystroke.
10232
- tabindex = -1;
10233
- empty = false;
10234
- get contentSelectable() {
10235
- return this.options()?.contentSelectable;
10236
- }
10237
- get hasTitle() {
10238
- return !!this.options()?.title;
10239
- }
10223
+ empty = signal(false, { ...(ngDevMode ? { debugName: "empty" } : {}) });
10240
10224
  constructor() {
10241
10225
  this._dialog.closable = false;
10242
10226
  this._dialog.resizable = false;
10243
10227
  this._dialog.padding = false;
10244
- this._dialog.size.maxWidth = 'var(--sci-workbench-messagebox-max-width)';
10228
+ // Limit the maximum messagebox width if text message to break the message.
10229
+ effect(() => {
10230
+ if (typeof this.message() === 'string' || this.message() === null) {
10231
+ this._dialog.size.maxWidth = 'var(--sci-workbench-messagebox-max-width)';
10232
+ }
10233
+ });
10245
10234
  }
10246
10235
  onAction(action) {
10247
10236
  this._dialog.close(action);
@@ -10255,10 +10244,10 @@ class WorkbenchMessageBoxComponent {
10255
10244
  this._dialog.size.minWidth = `${preferredSize}px`;
10256
10245
  }
10257
10246
  onContentDimensionChange(dimension) {
10258
- this.empty = !dimension.offsetHeight;
10247
+ this.empty.set(!dimension.offsetHeight);
10259
10248
  }
10260
10249
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10261
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: WorkbenchMessageBoxComponent, isStandalone: true, selector: "wb-message-box", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown.escape": "onEscape()" }, properties: { "attr.tabindex": "this.tabindex", "class.empty": "this.empty", "class.content-selectable": "this.contentSelectable", "class.has-title": "this.hasTitle" } }, ngImport: i0, template: "@let options = this.options() ?? {};\n<ng-template wbDialogHeader [divider]=\"false\">\n <wb-message-box-header [title]=\"options.title\" [severity]=\"options.severity ?? 'info'\"/>\n</ng-template>\n\n@let message = this.message();\n<div class=\"message e2e-message\" [class.text]=\"message | wbTypeof:'string'\" sciDimension (sciDimensionChange)=\"onContentDimensionChange($event)\">\n @if (message | wbTypeof:'string') {\n {{($any(message) | wbText)()}}\n } @else {\n <ng-container *ngComponentOutlet=\"message; inputs: options.inputs\"/>\n }\n</div>\n\n<ng-template wbDialogFooter>\n <wb-message-box-footer [actions]=\"options.actions ?? {ok: '%workbench.ok.action'}\"\n [severity]=\"options.severity ?? 'info'\"\n (action)=\"onAction($event)\"\n (keydown.escape)=\"onEscape()\"\n (preferredSizeChange)=\"onFooterPreferredSizeChange($event)\"/>\n</ng-template>\n", styles: [":host{display:grid;overflow:hidden;outline:none;padding-inline:var(--sci-workbench-messagebox-padding);padding-bottom:var(--sci-workbench-messagebox-padding)}:host.has-title:not(.empty){padding-top:var(--sci-workbench-messagebox-padding)}:host:not(.content-selectable){-webkit-user-select:none;user-select:none}:host>div.message.text{overflow-wrap:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: SciDimensionDirective, selector: "[sciDimension]", inputs: ["emitOutsideAngular"], outputs: ["sciDimensionChange"] }, { kind: "directive", type: WorkbenchDialogHeaderDirective, selector: "ng-template[wbDialogHeader]", inputs: ["divider"] }, { kind: "directive", type: WorkbenchDialogFooterDirective, selector: "ng-template[wbDialogFooter]", inputs: ["divider"] }, { kind: "component", type: MessageBoxHeaderComponent, selector: "wb-message-box-header", inputs: ["title", "severity"] }, { kind: "component", type: MessageBoxFooterComponent, selector: "wb-message-box-footer", inputs: ["actions", "severity"], outputs: ["action", "preferredSizeChange"] }, { kind: "pipe", type: TypeofPipe, name: "wbTypeof" }, { kind: "pipe", type: TextPipe, name: "wbText" }] });
10250
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: WorkbenchMessageBoxComponent, isStandalone: true, selector: "wb-message-box", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown.escape": "onEscape()" }, properties: { "attr.tabindex": "-1", "class.empty": "empty()", "class.content-selectable": "options()?.contentSelectable", "class.has-title": "!!this.options()?.title" } }, ngImport: i0, template: "@let options = this.options() ?? {};\n<ng-template wbDialogHeader [divider]=\"false\">\n <wb-message-box-header [title]=\"options.title\" [severity]=\"options.severity ?? 'info'\"/>\n</ng-template>\n\n@let message = this.message();\n<div class=\"slot e2e-slot\" [class.text]=\"message | wbTypeof:'string'\" sciDimension (sciDimensionChange)=\"onContentDimensionChange($event)\">\n @if (message | wbTypeof:'string') {\n {{($any(message) | wbText)()}}\n } @else {\n <ng-container *ngComponentOutlet=\"message; inputs: options.inputs\"/>\n }\n</div>\n\n<ng-template wbDialogFooter>\n <wb-message-box-footer [actions]=\"options.actions ?? {ok: '%workbench.ok.action'}\"\n [severity]=\"options.severity ?? 'info'\"\n (action)=\"onAction($event)\"\n (keydown.escape)=\"onEscape()\"\n (preferredSizeChange)=\"onFooterPreferredSizeChange($event)\"/>\n</ng-template>\n", styles: [":host{display:grid;outline:none;padding-inline:var(--sci-workbench-messagebox-padding);padding-bottom:var(--sci-workbench-messagebox-padding)}:host.has-title:not(.empty){padding-top:var(--sci-workbench-messagebox-padding)}:host:not(.content-selectable){-webkit-user-select:none;user-select:none}:host>div.slot.text{word-break:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: SciDimensionDirective, selector: "[sciDimension]", inputs: ["emitOutsideAngular"], outputs: ["sciDimensionChange"] }, { kind: "directive", type: WorkbenchDialogHeaderDirective, selector: "ng-template[wbDialogHeader]", inputs: ["divider"] }, { kind: "directive", type: WorkbenchDialogFooterDirective, selector: "ng-template[wbDialogFooter]", inputs: ["divider"] }, { kind: "component", type: MessageBoxHeaderComponent, selector: "wb-message-box-header", inputs: ["title", "severity"] }, { kind: "component", type: MessageBoxFooterComponent, selector: "wb-message-box-footer", inputs: ["actions", "severity"], outputs: ["action", "preferredSizeChange"] }, { kind: "pipe", type: TypeofPipe, name: "wbTypeof" }, { kind: "pipe", type: TextPipe, name: "wbText" }] });
10262
10251
  }
10263
10252
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxComponent, decorators: [{
10264
10253
  type: Component,
@@ -10271,23 +10260,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
10271
10260
  MessageBoxFooterComponent,
10272
10261
  TypeofPipe,
10273
10262
  TextPipe,
10274
- ], template: "@let options = this.options() ?? {};\n<ng-template wbDialogHeader [divider]=\"false\">\n <wb-message-box-header [title]=\"options.title\" [severity]=\"options.severity ?? 'info'\"/>\n</ng-template>\n\n@let message = this.message();\n<div class=\"message e2e-message\" [class.text]=\"message | wbTypeof:'string'\" sciDimension (sciDimensionChange)=\"onContentDimensionChange($event)\">\n @if (message | wbTypeof:'string') {\n {{($any(message) | wbText)()}}\n } @else {\n <ng-container *ngComponentOutlet=\"message; inputs: options.inputs\"/>\n }\n</div>\n\n<ng-template wbDialogFooter>\n <wb-message-box-footer [actions]=\"options.actions ?? {ok: '%workbench.ok.action'}\"\n [severity]=\"options.severity ?? 'info'\"\n (action)=\"onAction($event)\"\n (keydown.escape)=\"onEscape()\"\n (preferredSizeChange)=\"onFooterPreferredSizeChange($event)\"/>\n</ng-template>\n", styles: [":host{display:grid;overflow:hidden;outline:none;padding-inline:var(--sci-workbench-messagebox-padding);padding-bottom:var(--sci-workbench-messagebox-padding)}:host.has-title:not(.empty){padding-top:var(--sci-workbench-messagebox-padding)}:host:not(.content-selectable){-webkit-user-select:none;user-select:none}:host>div.message.text{overflow-wrap:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"] }]
10275
- }], ctorParameters: () => [], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], tabindex: [{
10276
- type: HostBinding,
10277
- args: ['attr.tabindex']
10278
- }], empty: [{
10279
- type: HostBinding,
10280
- args: ['class.empty']
10281
- }], contentSelectable: [{
10282
- type: HostBinding,
10283
- args: ['class.content-selectable']
10284
- }], hasTitle: [{
10285
- type: HostBinding,
10286
- args: ['class.has-title']
10287
- }], onEscape: [{
10288
- type: HostListener,
10289
- args: ['keydown.escape']
10290
- }] } });
10263
+ ], host: {
10264
+ // Ensure host element to be focusable in order to close the message box on Escape keystroke.
10265
+ '[attr.tabindex]': '-1',
10266
+ '[class.empty]': 'empty()',
10267
+ '[class.content-selectable]': 'options()?.contentSelectable',
10268
+ '[class.has-title]': '!!this.options()?.title',
10269
+ '(keydown.escape)': 'onEscape()',
10270
+ }, template: "@let options = this.options() ?? {};\n<ng-template wbDialogHeader [divider]=\"false\">\n <wb-message-box-header [title]=\"options.title\" [severity]=\"options.severity ?? 'info'\"/>\n</ng-template>\n\n@let message = this.message();\n<div class=\"slot e2e-slot\" [class.text]=\"message | wbTypeof:'string'\" sciDimension (sciDimensionChange)=\"onContentDimensionChange($event)\">\n @if (message | wbTypeof:'string') {\n {{($any(message) | wbText)()}}\n } @else {\n <ng-container *ngComponentOutlet=\"message; inputs: options.inputs\"/>\n }\n</div>\n\n<ng-template wbDialogFooter>\n <wb-message-box-footer [actions]=\"options.actions ?? {ok: '%workbench.ok.action'}\"\n [severity]=\"options.severity ?? 'info'\"\n (action)=\"onAction($event)\"\n (keydown.escape)=\"onEscape()\"\n (preferredSizeChange)=\"onFooterPreferredSizeChange($event)\"/>\n</ng-template>\n", styles: [":host{display:grid;outline:none;padding-inline:var(--sci-workbench-messagebox-padding);padding-bottom:var(--sci-workbench-messagebox-padding)}:host.has-title:not(.empty){padding-top:var(--sci-workbench-messagebox-padding)}:host:not(.content-selectable){-webkit-user-select:none;user-select:none}:host>div.slot.text{word-break:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"] }]
10271
+ }], ctorParameters: () => [], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }] } });
10291
10272
  function nullIfEmptyMessage(message) {
10292
10273
  return message !== '' ? message : null;
10293
10274
  }
@@ -10424,7 +10405,7 @@ class WorkbenchPopupComponent {
10424
10405
  }, { ...(ngDevMode ? { debugName: "effectRef" } : {}) });
10425
10406
  }
10426
10407
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchPopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10427
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.1", type: WorkbenchPopupComponent, isStandalone: true, selector: "wb-popup", host: { properties: { "attr.data-popupid": "popup.id", "style.width": "popup.size?.width()", "style.min-width": "popup.size?.minWidth()", "style.max-width": "popup.size?.maxWidth()", "style.height": "popup.size?.height()", "style.min-height": "popup.size?.minHeight()", "style.max-height": "popup.size?.maxHeight()", "class": "popup.cssClass()" } }, providers: [
10408
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.1", type: WorkbenchPopupComponent, isStandalone: true, selector: "wb-popup", host: { properties: { "attr.data-popupid": "popup.id", "style.width": "popup.size.width()", "style.min-width": "popup.size.minWidth()", "style.max-width": "popup.size.maxWidth()", "style.height": "popup.size.height()", "style.min-height": "popup.size.minHeight()", "style.max-height": "popup.size.maxHeight()", "class": "popup.cssClass()" } }, providers: [
10428
10409
  configurePopupGlassPane(),
10429
10410
  ], viewQueries: [{ propertyName: "_cdkTrapFocus", first: true, predicate: ["focus_trap"], descendants: true, read: CdkTrapFocus, isSignal: true }], hostDirectives: [{ directive: GlassPaneDirective }], ngImport: i0, template: "<sci-viewport cdkTrapFocus class=\"e2e-popup-viewport\" #focus_trap>\n <ng-container *ngComponentOutlet=\"popup.component; inputs: popup.inputs\"/>\n</sci-viewport>\n", styles: [":host{display:grid;grid-template-columns:100%;grid-template-rows:100%;outline:none;border-radius:var(--sci-corner);overflow:hidden}\n"], dependencies: [{ kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: SciViewportComponent, selector: "sci-viewport", inputs: ["scrollbarStyle"], outputs: ["scroll"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }] });
10430
10411
  }
@@ -10440,12 +10421,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
10440
10421
  configurePopupGlassPane(),
10441
10422
  ], host: {
10442
10423
  '[attr.data-popupid]': 'popup.id',
10443
- '[style.width]': 'popup.size?.width()',
10444
- '[style.min-width]': 'popup.size?.minWidth()',
10445
- '[style.max-width]': 'popup.size?.maxWidth()',
10446
- '[style.height]': 'popup.size?.height()',
10447
- '[style.min-height]': 'popup.size?.minHeight()',
10448
- '[style.max-height]': 'popup.size?.maxHeight()',
10424
+ '[style.width]': 'popup.size.width()',
10425
+ '[style.min-width]': 'popup.size.minWidth()',
10426
+ '[style.max-width]': 'popup.size.maxWidth()',
10427
+ '[style.height]': 'popup.size.height()',
10428
+ '[style.min-height]': 'popup.size.minHeight()',
10429
+ '[style.max-height]': 'popup.size.maxHeight()',
10449
10430
  '[class]': 'popup.cssClass()',
10450
10431
  }, template: "<sci-viewport cdkTrapFocus class=\"e2e-popup-viewport\" #focus_trap>\n <ng-container *ngComponentOutlet=\"popup.component; inputs: popup.inputs\"/>\n</sci-viewport>\n", styles: [":host{display:grid;grid-template-columns:100%;grid-template-rows:100%;outline:none;border-radius:var(--sci-corner);overflow:hidden}\n"] }]
10451
10432
  }], ctorParameters: () => [], propDecorators: { _cdkTrapFocus: [{ type: i0.ViewChild, args: ['focus_trap', { ...{ read: CdkTrapFocus }, isSignal: true }] }] } });
@@ -10943,6 +10924,38 @@ const LEGACY_POPUP_INPUT = new InjectionToken('LEGACY_POPUP_INPUT');
10943
10924
  *
10944
10925
  * SPDX-License-Identifier: EPL-2.0
10945
10926
  */
10927
+ /**
10928
+ * Registry for {@link WorkbenchNotification} elements.
10929
+ */
10930
+ class WorkbenchNotificationRegistry extends WorkbenchElementRegistry {
10931
+ /**
10932
+ * Gets the most recently opened notification.
10933
+ */
10934
+ top;
10935
+ constructor() {
10936
+ super({
10937
+ nullElementErrorFn: notificationId => Error(`[NullNotificationError] Notification '${notificationId}' not found.`),
10938
+ onUnregister: notification => notification.destroy(),
10939
+ });
10940
+ this.top = computed(() => this.elements().at(-1), { ...(ngDevMode ? { debugName: "top" } : {}) });
10941
+ }
10942
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10943
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationRegistry, providedIn: 'root' });
10944
+ }
10945
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationRegistry, decorators: [{
10946
+ type: Injectable,
10947
+ args: [{ providedIn: 'root' }]
10948
+ }], ctorParameters: () => [] });
10949
+
10950
+ /*
10951
+ * Copyright (c) 2018-2026 Swiss Federal Railways
10952
+ *
10953
+ * This program and the accompanying materials are made
10954
+ * available under the terms of the Eclipse Public License 2.0
10955
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
10956
+ *
10957
+ * SPDX-License-Identifier: EPL-2.0
10958
+ */
10946
10959
  /**
10947
10960
  * Creates the invocation context for given element.
10948
10961
  *
@@ -10963,7 +10976,7 @@ function createInvocationContext(elementId, options) {
10963
10976
  return {
10964
10977
  elementId: part.id,
10965
10978
  attached: part.slot.portal.attached,
10966
- bounds: computed(() => constrainClientRect(part.slot.bounds(), part.bounds())),
10979
+ bounds: computed(() => constrainClientRect(part.slot.bounds(), part.partBounds())),
10967
10980
  destroyed: part.slot.portal.destroyed,
10968
10981
  peripheral: part.peripheral,
10969
10982
  };
@@ -10973,7 +10986,7 @@ function createInvocationContext(elementId, options) {
10973
10986
  return {
10974
10987
  elementId: view.id,
10975
10988
  attached: view.slot.portal.attached,
10976
- bounds: computed(() => constrainClientRect(view.slot.bounds(), view.part().bounds())),
10989
+ bounds: computed(() => constrainClientRect(view.slot.bounds(), view.part().partBounds())),
10977
10990
  destroyed: view.slot.portal.destroyed,
10978
10991
  peripheral: computed(() => view.part().peripheral()),
10979
10992
  };
@@ -10998,6 +11011,16 @@ function createInvocationContext(elementId, options) {
10998
11011
  peripheral: signal(false),
10999
11012
  };
11000
11013
  }
11014
+ else if (isNotificationId(contextualElementId)) {
11015
+ const notification = injector.get(WorkbenchNotificationRegistry).get(contextualElementId);
11016
+ return {
11017
+ elementId: notification.id,
11018
+ attached: computed(() => !notification.destroyed()),
11019
+ bounds: notification.bounds,
11020
+ destroyed: notification.destroyed,
11021
+ peripheral: signal(true),
11022
+ };
11023
+ }
11001
11024
  return null;
11002
11025
  }
11003
11026
 
@@ -11195,7 +11218,7 @@ function provideWorkbenchDialogContext() {
11195
11218
  }
11196
11219
 
11197
11220
  /*
11198
- * Copyright (c) 2018-2023 Swiss Federal Railways
11221
+ * Copyright (c) 2018-2026 Swiss Federal Railways
11199
11222
  *
11200
11223
  * This program and the accompanying materials are made
11201
11224
  * available under the terms of the Eclipse Public License 2.0
@@ -11230,7 +11253,7 @@ class ɵWorkbenchDialog {
11230
11253
  focused = computed(() => this._focusMonitor.activeElement()?.id === this.id, { ...(ngDevMode ? { debugName: "focused" } : {}) });
11231
11254
  attached;
11232
11255
  destroyed = signal(false, { ...(ngDevMode ? { debugName: "destroyed" } : {}) });
11233
- bounds = boundingClientRect(computed(() => this._componentRef()?.instance.dialogContent()));
11256
+ bounds = boundingClientRect(computed(() => this._componentRef()?.instance.dialogSlotBounds()));
11234
11257
  modal;
11235
11258
  blinking$ = new BehaviorSubject(false);
11236
11259
  header;
@@ -11760,6 +11783,7 @@ class ɵWorkbenchView {
11760
11783
  focused = computed(() => this._focusMonitor.activeElement()?.id === this.id, { ...(ngDevMode ? { debugName: "focused" } : {}) });
11761
11784
  menuItems;
11762
11785
  blockedBy;
11786
+ bounds = computed(() => this.slot.bounds(), { ...(ngDevMode ? { debugName: "bounds" } : {}) });
11763
11787
  slot;
11764
11788
  classList = new ClassList();
11765
11789
  isClosable = computed(() => this._closable() && !this.blockedBy(), { ...(ngDevMode ? { debugName: "isClosable" } : {}) });
@@ -12005,7 +12029,7 @@ class ɵWorkbenchView {
12005
12029
  changes?.forEachAddedItem(({ item: fn }) => menuItems.set(fn, computed(() => runInInjectionContext(constructInjector(this, this.part()), () => fn(this)))));
12006
12030
  changes?.forEachRemovedItem(({ item: fn }) => menuItems.delete(fn));
12007
12031
  return Array.from(menuItems.values()).map(menuItem => menuItem()).filter(menuItem => !!menuItem);
12008
- }, { equal: (a, b) => Arrays.isEqual(a, b) });
12032
+ }, { equal: (a, b) => Objects.isEqual(a, b) });
12009
12033
  function constructInjector(view, part) {
12010
12034
  return Injector.create({
12011
12035
  parent: injector,
@@ -12463,7 +12487,7 @@ class ViewMenuService {
12463
12487
  }
12464
12488
  const key = accelerator.at(-1).toLocaleLowerCase();
12465
12489
  const modifierKeys = accelerator.slice(0, -1);
12466
- return key === eventKey && Arrays.isEqual(modifierKeys, eventModifierKeys, { exactOrder: false });
12490
+ return key === eventKey && Objects.isEqual(modifierKeys, eventModifierKeys, { ignoreArrayOrder: true });
12467
12491
  });
12468
12492
  }
12469
12493
  }
@@ -12657,18 +12681,12 @@ class ViewTabComponent {
12657
12681
  }
12658
12682
  onClose(event) {
12659
12683
  event.stopPropagation(); // prevent the view from being activated
12660
- if (event.altKey) {
12661
- void this.view().close('other-views');
12662
- }
12663
- else {
12664
- void this.view().close();
12665
- }
12684
+ this.closeView(event);
12666
12685
  }
12667
- onMousedown(event) {
12668
- if (event.buttons === AUXILARY_MOUSE_BUTTON$1) {
12669
- void this.view().close();
12670
- event.stopPropagation();
12671
- event.preventDefault();
12686
+ onAuxClick(event) {
12687
+ if (event.button === 1) { // primary aux button
12688
+ event.preventDefault(); // prevent user-agent default action
12689
+ this.closeView(event);
12672
12690
  }
12673
12691
  }
12674
12692
  onContextmenu(event) {
@@ -12713,6 +12731,17 @@ class ViewTabComponent {
12713
12731
  onDragEnd() {
12714
12732
  this.viewDragService.unsetViewDragData();
12715
12733
  }
12734
+ /**
12735
+ * Closes the current view or other views if the 'Alt' key is pressed.
12736
+ */
12737
+ closeView(event) {
12738
+ if (event.altKey) {
12739
+ void this.view().close('other-views');
12740
+ }
12741
+ else {
12742
+ void this.view().close();
12743
+ }
12744
+ }
12716
12745
  installMenuAccelerators() {
12717
12746
  this._viewMenuService.installMenuAccelerators(this.host, this.view);
12718
12747
  }
@@ -12729,7 +12758,7 @@ class ViewTabComponent {
12729
12758
  });
12730
12759
  }
12731
12760
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ViewTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12732
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: ViewTabComponent, isStandalone: true, selector: "wb-view-tab", inputs: { view: { classPropertyName: "view", publicName: "viewId", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "click": "onClick()", "mousedown": "onMousedown($event)", "contextmenu": "onContextmenu($event)", "dragstart": "onDragStart($event)", "dragend": "onDragEnd()" }, properties: { "attr.data-viewid": "view().id", "attr.data-active": "view().active() ? '' : null", "attr.data-dirty": "view().dirty() ? '' : null", "attr.data-focus-within-view": "view().focused() ? '' : null", "attr.draggable": "true", "attr.tabindex": "-1", "class.view-drag": "viewDragService.dragging()", "class": "view().classList.asList()", "style.--sci-workbench-tab-title-offset-right": "viewTitleOffsetRight()" } }, ngImport: i0, template: "<!-- IMPORTANT: THIS HTML FILE IS ALSO USED BY `ViewTabDragImageComponent` -->\n\n@if (view().active()) {\n <div class=\"corner-radius start\">\n <div class=\"circle\"></div>\n </div>\n}\n\n<div class=\"content\">\n <ng-container *cdkPortalOutlet=\"viewTabContentPortal()\"/>\n</div>\n\n@if (view().active()) {\n <div class=\"corner-radius end\">\n <div class=\"circle\"></div>\n </div>\n}\n\n@if (view().closable()) {\n <button (click)=\"onClose($event)\"\n [title]=\"('%workbench.close_tab.tooltip;close_others_modifier=Alt' | wbText)()\"\n [disabled]=\"!view().isClosable()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n </button>\n}\n", styles: [":host{display:grid;align-items:center;padding-left:var(--sci-workbench-tab-padding-inline);padding-right:var(--sci-workbench-tab-padding-inline);position:relative;-webkit-user-select:none;user-select:none;cursor:var(--sci-workbench-tab-cursor);box-sizing:border-box;outline:none;border-top:var(--sci-workbench-tab-border-top-width) solid transparent;border-left:var(--sci-workbench-tab-border-width) solid transparent;border-right:var(--sci-workbench-tab-border-width) solid transparent;border-top-left-radius:var(--sci-workbench-tab-border-radius);border-top-right-radius:var(--sci-workbench-tab-border-radius)}:host[data-active]{cursor:default;border-left-color:var(--sci-workbench-tab-border-color);border-right-color:var(--sci-workbench-tab-border-color);border-top-color:var(--sci-workbench-tab-border-color);background-color:var(--sci-workbench-view-background-color)}wb-part[data-peripheral] :host[data-active]{background-color:var(--sci-workbench-view-peripheral-background-color)}:host[data-active]>div.content{color:var(--sci-workbench-tab-text-color-active)}:host[data-active][data-focus-within-view]>div.content{color:var(--sci-workbench-part-active-tab-text-color-active)}:host>div.content{display:inline-grid;font-family:var(--sci-workbench-tab-font-family),sans-serif;font-size:var(--sci-workbench-tab-font-size);font-weight:var(--sci-workbench-tab-font-weight);min-width:var(--sci-workbench-tab-min-width);max-width:var(--sci-workbench-tab-max-width);isolation:isolate;transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;right:calc(var(--sci-workbench-tab-padding-inline) - .125em);visibility:hidden;padding:.125em;border-radius:var(--sci-corner-small);transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host[data-active]>button.close:is(button,#sci-reset),:host:hover:not(.view-drag)>button.close:is(button,#sci-reset){visibility:visible}@container style(--sci-workbench-tab-background-color-hover){:host:hover:not([data-active]):not(.view-drag):before{content:\"\";position:absolute;place-self:start center;box-sizing:border-box;height:calc(100% - var(--sci-workbench-tab-padding-block-hover) - var(--sci-workbench-part-bar-border-bottom-width));width:calc(100% - var(--sci-workbench-tab-padding-inline-hover) + 2 * var(--sci-workbench-tab-border-width));background-color:var(--sci-workbench-tab-background-color-hover);border:var(--sci-workbench-tab-border-width) solid var(--sci-workbench-tab-background-color-hover);border-radius:var(--sci-workbench-tab-border-radius);pointer-events:none}:host:hover:not([data-active]):not(.view-drag)>button.close:is(button,#sci-reset):not(:disabled):hover{background-color:color-mix(in srgb,var(--sci-workbench-tab-background-color-hover) 90%,light-dark(var(--sci-static-color-black),var(--sci-static-color-white)))}}:host>div.corner-radius{height:var(--sci-workbench-tab-border-radius);width:var(--sci-workbench-tab-border-radius);overflow:hidden;position:absolute;bottom:0}:host>div.corner-radius>div.circle{position:absolute;top:calc(-2 * var(--sci-workbench-tab-border-radius));width:calc(2 * var(--sci-workbench-tab-border-radius));height:calc(2 * var(--sci-workbench-tab-border-radius));border:var(--sci-workbench-tab-border-radius) solid var(--sci-workbench-view-background-color);border-radius:50%;box-shadow:inset 0 0 0 var(--sci-workbench-tab-border-width) var(--sci-workbench-tab-border-color);box-sizing:content-box}wb-part[data-peripheral] :host>div.corner-radius>div.circle{border-color:var(--sci-workbench-view-peripheral-background-color)}:host>div.corner-radius.start{left:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.start>div.circle{left:calc(-2 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end{right:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end>div.circle{right:calc(-2 * var(--sci-workbench-tab-border-radius))}@container viewtab (height >= 3.5rem){:host>button.close:is(button,#sci-reset){top:5px;right:5px}}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "component", type: IconComponent, selector: "wb-icon", inputs: ["icon"] }, { kind: "pipe", type: TextPipe, name: "wbText" }] });
12761
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: ViewTabComponent, isStandalone: true, selector: "wb-view-tab", inputs: { view: { classPropertyName: "view", publicName: "viewId", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "click": "onClick()", "auxclick": "onAuxClick($event)", "contextmenu": "onContextmenu($event)", "dragstart": "onDragStart($event)", "dragend": "onDragEnd()" }, properties: { "attr.data-viewid": "view().id", "attr.data-active": "view().active() ? '' : null", "attr.data-dirty": "view().dirty() ? '' : null", "attr.data-focus-within-view": "view().focused() ? '' : null", "attr.draggable": "true", "attr.tabindex": "-1", "class.view-drag": "viewDragService.dragging()", "class": "view().classList.asList()", "style.--sci-workbench-tab-title-offset-right": "viewTitleOffsetRight()" } }, ngImport: i0, template: "<!-- IMPORTANT: THIS HTML FILE IS ALSO USED BY `ViewTabDragImageComponent` -->\n\n@if (view().active()) {\n <div class=\"corner-radius start\">\n <div class=\"circle\"></div>\n </div>\n}\n\n<div class=\"content\">\n <ng-container *cdkPortalOutlet=\"viewTabContentPortal()\"/>\n</div>\n\n@if (view().active()) {\n <div class=\"corner-radius end\">\n <div class=\"circle\"></div>\n </div>\n}\n\n@if (view().closable()) {\n <button (click)=\"onClose($event)\"\n [title]=\"('%workbench.close_tab.tooltip;close_others_modifier=Alt' | wbText)()\"\n [disabled]=\"!view().isClosable()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n </button>\n}\n", styles: [":host{display:grid;align-items:center;padding-left:var(--sci-workbench-tab-padding-inline);padding-right:var(--sci-workbench-tab-padding-inline);position:relative;-webkit-user-select:none;user-select:none;cursor:var(--sci-workbench-tab-cursor);box-sizing:border-box;outline:none;border-top:var(--sci-workbench-tab-border-top-width) solid transparent;border-left:var(--sci-workbench-tab-border-width) solid transparent;border-right:var(--sci-workbench-tab-border-width) solid transparent;border-top-left-radius:var(--sci-workbench-tab-border-radius);border-top-right-radius:var(--sci-workbench-tab-border-radius)}:host[data-active]{cursor:default;border-left-color:var(--sci-workbench-tab-border-color);border-right-color:var(--sci-workbench-tab-border-color);border-top-color:var(--sci-workbench-tab-border-color);background-color:var(--sci-workbench-view-background-color)}wb-part[data-peripheral] :host[data-active]{background-color:var(--sci-workbench-view-peripheral-background-color)}:host[data-active]>div.content{color:var(--sci-workbench-tab-text-color-active)}:host[data-active][data-focus-within-view]>div.content{color:var(--sci-workbench-part-active-tab-text-color-active)}:host>div.content{display:inline-grid;font-family:var(--sci-workbench-tab-font-family),sans-serif;font-size:var(--sci-workbench-tab-font-size);font-weight:var(--sci-workbench-tab-font-weight);min-width:var(--sci-workbench-tab-min-width);max-width:var(--sci-workbench-tab-max-width);isolation:isolate;transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;right:calc(var(--sci-workbench-tab-padding-inline) - .125em);visibility:hidden;padding:.125em;border-radius:var(--sci-corner-small);transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host[data-active]>button.close:is(button,#sci-reset),:host:hover:not(.view-drag)>button.close:is(button,#sci-reset){visibility:visible}@container style(--sci-workbench-tab-background-color-hover){:host:hover:not([data-active]):not(.view-drag):before{content:\"\";position:absolute;place-self:start center;box-sizing:border-box;height:calc(100% - var(--sci-workbench-tab-padding-block-hover) - var(--sci-workbench-part-bar-border-bottom-width));width:calc(100% - var(--sci-workbench-tab-padding-inline-hover) + 2 * var(--sci-workbench-tab-border-width));background-color:var(--sci-workbench-tab-background-color-hover);border:var(--sci-workbench-tab-border-width) solid var(--sci-workbench-tab-background-color-hover);border-radius:var(--sci-workbench-tab-border-radius);pointer-events:none}:host:hover:not([data-active]):not(.view-drag)>button.close:is(button,#sci-reset):not(:disabled):hover{background-color:color-mix(in srgb,var(--sci-workbench-tab-background-color-hover) 90%,light-dark(var(--sci-static-color-black),var(--sci-static-color-white)))}}:host>div.corner-radius{height:var(--sci-workbench-tab-border-radius);width:var(--sci-workbench-tab-border-radius);overflow:hidden;position:absolute;bottom:0}:host>div.corner-radius>div.circle{position:absolute;top:calc(-2 * var(--sci-workbench-tab-border-radius));width:calc(2 * var(--sci-workbench-tab-border-radius));height:calc(2 * var(--sci-workbench-tab-border-radius));border:var(--sci-workbench-tab-border-radius) solid var(--sci-workbench-view-background-color);border-radius:50%;box-shadow:inset 0 0 0 var(--sci-workbench-tab-border-width) var(--sci-workbench-tab-border-color);box-sizing:content-box}wb-part[data-peripheral] :host>div.corner-radius>div.circle{border-color:var(--sci-workbench-view-peripheral-background-color)}:host>div.corner-radius.start{left:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.start>div.circle{left:calc(-2 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end{right:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end>div.circle{right:calc(-2 * var(--sci-workbench-tab-border-radius))}@container viewtab (height >= 3.5rem){:host>button.close:is(button,#sci-reset){top:5px;right:5px}}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "component", type: IconComponent, selector: "wb-icon", inputs: ["icon"] }, { kind: "pipe", type: TextPipe, name: "wbText" }] });
12733
12762
  }
12734
12763
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ViewTabComponent, decorators: [{
12735
12764
  type: Component,
@@ -12747,27 +12776,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
12747
12776
  '[class.view-drag]': 'viewDragService.dragging()',
12748
12777
  '[class]': 'view().classList.asList()',
12749
12778
  '[style.--sci-workbench-tab-title-offset-right]': 'viewTitleOffsetRight()',
12779
+ '(click)': 'onClick()',
12780
+ '(auxclick)': 'onAuxClick($event)',
12781
+ '(contextmenu)': 'onContextmenu($event)',
12782
+ '(dragstart)': 'onDragStart($event)',
12783
+ '(dragend)': 'onDragEnd()',
12750
12784
  }, template: "<!-- IMPORTANT: THIS HTML FILE IS ALSO USED BY `ViewTabDragImageComponent` -->\n\n@if (view().active()) {\n <div class=\"corner-radius start\">\n <div class=\"circle\"></div>\n </div>\n}\n\n<div class=\"content\">\n <ng-container *cdkPortalOutlet=\"viewTabContentPortal()\"/>\n</div>\n\n@if (view().active()) {\n <div class=\"corner-radius end\">\n <div class=\"circle\"></div>\n </div>\n}\n\n@if (view().closable()) {\n <button (click)=\"onClose($event)\"\n [title]=\"('%workbench.close_tab.tooltip;close_others_modifier=Alt' | wbText)()\"\n [disabled]=\"!view().isClosable()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n </button>\n}\n", styles: [":host{display:grid;align-items:center;padding-left:var(--sci-workbench-tab-padding-inline);padding-right:var(--sci-workbench-tab-padding-inline);position:relative;-webkit-user-select:none;user-select:none;cursor:var(--sci-workbench-tab-cursor);box-sizing:border-box;outline:none;border-top:var(--sci-workbench-tab-border-top-width) solid transparent;border-left:var(--sci-workbench-tab-border-width) solid transparent;border-right:var(--sci-workbench-tab-border-width) solid transparent;border-top-left-radius:var(--sci-workbench-tab-border-radius);border-top-right-radius:var(--sci-workbench-tab-border-radius)}:host[data-active]{cursor:default;border-left-color:var(--sci-workbench-tab-border-color);border-right-color:var(--sci-workbench-tab-border-color);border-top-color:var(--sci-workbench-tab-border-color);background-color:var(--sci-workbench-view-background-color)}wb-part[data-peripheral] :host[data-active]{background-color:var(--sci-workbench-view-peripheral-background-color)}:host[data-active]>div.content{color:var(--sci-workbench-tab-text-color-active)}:host[data-active][data-focus-within-view]>div.content{color:var(--sci-workbench-part-active-tab-text-color-active)}:host>div.content{display:inline-grid;font-family:var(--sci-workbench-tab-font-family),sans-serif;font-size:var(--sci-workbench-tab-font-size);font-weight:var(--sci-workbench-tab-font-weight);min-width:var(--sci-workbench-tab-min-width);max-width:var(--sci-workbench-tab-max-width);isolation:isolate;transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;right:calc(var(--sci-workbench-tab-padding-inline) - .125em);visibility:hidden;padding:.125em;border-radius:var(--sci-corner-small);transform:translateY(calc(-1 * var(--sci-workbench-tab-border-top-width)))}:host[data-active]>button.close:is(button,#sci-reset),:host:hover:not(.view-drag)>button.close:is(button,#sci-reset){visibility:visible}@container style(--sci-workbench-tab-background-color-hover){:host:hover:not([data-active]):not(.view-drag):before{content:\"\";position:absolute;place-self:start center;box-sizing:border-box;height:calc(100% - var(--sci-workbench-tab-padding-block-hover) - var(--sci-workbench-part-bar-border-bottom-width));width:calc(100% - var(--sci-workbench-tab-padding-inline-hover) + 2 * var(--sci-workbench-tab-border-width));background-color:var(--sci-workbench-tab-background-color-hover);border:var(--sci-workbench-tab-border-width) solid var(--sci-workbench-tab-background-color-hover);border-radius:var(--sci-workbench-tab-border-radius);pointer-events:none}:host:hover:not([data-active]):not(.view-drag)>button.close:is(button,#sci-reset):not(:disabled):hover{background-color:color-mix(in srgb,var(--sci-workbench-tab-background-color-hover) 90%,light-dark(var(--sci-static-color-black),var(--sci-static-color-white)))}}:host>div.corner-radius{height:var(--sci-workbench-tab-border-radius);width:var(--sci-workbench-tab-border-radius);overflow:hidden;position:absolute;bottom:0}:host>div.corner-radius>div.circle{position:absolute;top:calc(-2 * var(--sci-workbench-tab-border-radius));width:calc(2 * var(--sci-workbench-tab-border-radius));height:calc(2 * var(--sci-workbench-tab-border-radius));border:var(--sci-workbench-tab-border-radius) solid var(--sci-workbench-view-background-color);border-radius:50%;box-shadow:inset 0 0 0 var(--sci-workbench-tab-border-width) var(--sci-workbench-tab-border-color);box-sizing:content-box}wb-part[data-peripheral] :host>div.corner-radius>div.circle{border-color:var(--sci-workbench-view-peripheral-background-color)}:host>div.corner-radius.start{left:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.start>div.circle{left:calc(-2 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end{right:calc(-1 * var(--sci-workbench-tab-border-radius))}:host>div.corner-radius.end>div.circle{right:calc(-2 * var(--sci-workbench-tab-border-radius))}@container viewtab (height >= 3.5rem){:host>button.close:is(button,#sci-reset){top:5px;right:5px}}\n"] }]
12751
- }], ctorParameters: () => [], propDecorators: { view: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewId", required: true }] }], onClick: [{
12752
- type: HostListener,
12753
- args: ['click']
12754
- }], onMousedown: [{
12755
- type: HostListener,
12756
- args: ['mousedown', ['$event']]
12757
- }], onContextmenu: [{
12758
- type: HostListener,
12759
- args: ['contextmenu', ['$event']]
12760
- }], onDragStart: [{
12761
- type: HostListener,
12762
- args: ['dragstart', ['$event']]
12763
- }], onDragEnd: [{
12764
- type: HostListener,
12765
- args: ['dragend']
12766
- }] } });
12767
- /**
12768
- * Indicates that the auxilary mouse button is pressed (usually the mouse wheel button or middle button).
12769
- */
12770
- const AUXILARY_MOUSE_BUTTON$1 = 4;
12785
+ }], ctorParameters: () => [], propDecorators: { view: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewId", required: true }] }] } });
12771
12786
 
12772
12787
  /*
12773
12788
  * Copyright (c) 2018-2022 Swiss Federal Railways
@@ -13027,6 +13042,7 @@ class DragImageWorkbenchView {
13027
13042
  menuItems = signal([], { ...(ngDevMode ? { debugName: "menuItems" } : {}) });
13028
13043
  isClosable;
13029
13044
  activationInstant = signal(0, { ...(ngDevMode ? { debugName: "activationInstant" } : {}) });
13045
+ bounds = signal(undefined, { ...(ngDevMode ? { debugName: "bounds" } : {}) });
13030
13046
  constructor(dragData) {
13031
13047
  this.id = dragData.viewId;
13032
13048
  this.alternativeId = dragData.alternativeViewId;
@@ -13707,7 +13723,7 @@ class PartComponent {
13707
13723
  inject(DestroyRef).onDestroy(() => logger.debug(() => `Destroying PartComponent [partId=${this.part.id}]'`, LoggerNames.LIFECYCLE));
13708
13724
  }
13709
13725
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: PartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13710
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: PartComponent, isStandalone: true, selector: "wb-part", host: { properties: { "attr.data-partid": "part.id", "attr.data-peripheral": "part.peripheral() ? '' : null", "attr.data-grid": "dasherize(part.gridName())", "attr.data-active": "part.active() ? '' : null", "attr.data-referencepart": "part.referencePart() ? '' : null", "attr.tabindex": "-1", "class": "part.classList.asList()" } }, ngImport: i0, template: "@if (part.title() || part.views().length || part.actions().length || part.canMinimize()) {\n <wb-part-bar/>\n}\n\n@if (part.views().length) {\n <!-- Prevent splitting if there is no active view, i.e, when dragging the last view out of the tabbar. -->\n @let canSplit = !!part.activeView();\n <div wbViewDropZone\n [wbViewDropZoneRegionSize]=\".25\"\n [wbViewDropZonePlaceholderSize]=\".5\"\n [wbViewDropZoneAttributes]=\"{'data-partid': part.id}\"\n [wbViewDropZoneRegions]=\"canDrop() && {center: true, north: canSplit, south: canSplit, west: canSplit, east: canSplit}\"\n (wbViewDropZoneDrop)=\"onViewDrop($event)\"\n class=\"content e2e-content e2e-view-content\">\n <ng-container *wbPortalOutlet=\"part.activeView()?.slot!.portal; destroyOnDetach: false\"/>\n </div>\n} @else {\n <div wbViewDropZone\n [wbViewDropZoneRegions]=\"canDrop() && {center: false, north: true, south: true, west: true, east: true}\"\n [wbViewDropZoneAttributes]=\"{'data-partid': part.id}\"\n (wbViewDropZoneDrop)=\"onViewDrop($event)\"\n class=\"content e2e-content e2e-part-content\">\n <ng-container *wbPortalOutlet=\"part.slot.portal; destroyOnDetach: false\"/>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;outline:none;background-color:var(--sci-workbench-part-background-color);overflow:hidden}:host[data-peripheral]{background-color:var(--sci-workbench-part-peripheral-background-color)}:host>wb-part-bar{flex:none}:host>div.content{flex:auto;display:grid;position:relative}\n"], dependencies: [{ kind: "component", type: PartBarComponent, selector: "wb-part-bar" }, { kind: "directive", type: ViewDropZoneDirective, selector: "[wbViewDropZone]", inputs: ["wbViewDropZoneRegions", "wbViewDropZoneAttributes", "wbViewDropZoneRegionSize", "wbViewDropZonePlaceholderSize"], outputs: ["wbViewDropZoneDrop"] }, { kind: "directive", type: WorkbenchPortalOutletDirective, selector: "ng-template[wbPortalOutlet]", inputs: ["wbPortalOutlet", "wbPortalOutletDestroyOnDetach"] }] });
13726
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: PartComponent, isStandalone: true, selector: "wb-part", host: { properties: { "attr.data-partid": "part.id", "attr.data-peripheral": "part.peripheral() ? '' : null", "attr.data-grid": "dasherize(part.gridName())", "attr.data-active": "part.active() ? '' : null", "attr.data-referencepart": "part.referencePart() ? '' : null", "attr.tabindex": "-1", "class": "part.classList.asList()" } }, ngImport: i0, template: "@if (part.title() || part.views().length || part.actions().length || part.canMinimize()) {\n <wb-part-bar/>\n}\n\n@if (part.views().length) {\n <!-- Prevent splitting if there is no active view, i.e, when dragging the last view out of the tabbar. -->\n @let canSplit = !!part.activeView();\n <div wbViewDropZone\n [wbViewDropZoneRegionSize]=\".25\"\n [wbViewDropZonePlaceholderSize]=\".5\"\n [wbViewDropZoneAttributes]=\"{'data-partid': part.id}\"\n [wbViewDropZoneRegions]=\"canDrop() && {center: true, north: canSplit, south: canSplit, west: canSplit, east: canSplit}\"\n (wbViewDropZoneDrop)=\"onViewDrop($event)\"\n class=\"slot e2e-slot e2e-view-slot\">\n <ng-container *wbPortalOutlet=\"part.activeView()?.slot!.portal; destroyOnDetach: false\"/>\n </div>\n} @else {\n <div wbViewDropZone\n [wbViewDropZoneRegions]=\"canDrop() && {center: false, north: true, south: true, west: true, east: true}\"\n [wbViewDropZoneAttributes]=\"{'data-partid': part.id}\"\n (wbViewDropZoneDrop)=\"onViewDrop($event)\"\n class=\"slot e2e-slot e2e-part-slot\">\n <ng-container *wbPortalOutlet=\"part.slot.portal; destroyOnDetach: false\"/>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;outline:none;background-color:var(--sci-workbench-part-background-color);overflow:hidden}:host[data-peripheral]{background-color:var(--sci-workbench-part-peripheral-background-color)}:host>wb-part-bar{flex:none}:host>div.slot{flex:auto;display:grid;position:relative}\n"], dependencies: [{ kind: "component", type: PartBarComponent, selector: "wb-part-bar" }, { kind: "directive", type: ViewDropZoneDirective, selector: "[wbViewDropZone]", inputs: ["wbViewDropZoneRegions", "wbViewDropZoneAttributes", "wbViewDropZoneRegionSize", "wbViewDropZonePlaceholderSize"], outputs: ["wbViewDropZoneDrop"] }, { kind: "directive", type: WorkbenchPortalOutletDirective, selector: "ng-template[wbPortalOutlet]", inputs: ["wbPortalOutlet", "wbPortalOutletDestroyOnDetach"] }] });
13711
13727
  }
13712
13728
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: PartComponent, decorators: [{
13713
13729
  type: Component,
@@ -13723,7 +13739,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
13723
13739
  '[attr.data-referencepart]': `part.referencePart() ? '' : null`,
13724
13740
  '[attr.tabindex]': '-1',
13725
13741
  '[class]': 'part.classList.asList()',
13726
- }, template: "@if (part.title() || part.views().length || part.actions().length || part.canMinimize()) {\n <wb-part-bar/>\n}\n\n@if (part.views().length) {\n <!-- Prevent splitting if there is no active view, i.e, when dragging the last view out of the tabbar. -->\n @let canSplit = !!part.activeView();\n <div wbViewDropZone\n [wbViewDropZoneRegionSize]=\".25\"\n [wbViewDropZonePlaceholderSize]=\".5\"\n [wbViewDropZoneAttributes]=\"{'data-partid': part.id}\"\n [wbViewDropZoneRegions]=\"canDrop() && {center: true, north: canSplit, south: canSplit, west: canSplit, east: canSplit}\"\n (wbViewDropZoneDrop)=\"onViewDrop($event)\"\n class=\"content e2e-content e2e-view-content\">\n <ng-container *wbPortalOutlet=\"part.activeView()?.slot!.portal; destroyOnDetach: false\"/>\n </div>\n} @else {\n <div wbViewDropZone\n [wbViewDropZoneRegions]=\"canDrop() && {center: false, north: true, south: true, west: true, east: true}\"\n [wbViewDropZoneAttributes]=\"{'data-partid': part.id}\"\n (wbViewDropZoneDrop)=\"onViewDrop($event)\"\n class=\"content e2e-content e2e-part-content\">\n <ng-container *wbPortalOutlet=\"part.slot.portal; destroyOnDetach: false\"/>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;outline:none;background-color:var(--sci-workbench-part-background-color);overflow:hidden}:host[data-peripheral]{background-color:var(--sci-workbench-part-peripheral-background-color)}:host>wb-part-bar{flex:none}:host>div.content{flex:auto;display:grid;position:relative}\n"] }]
13742
+ }, template: "@if (part.title() || part.views().length || part.actions().length || part.canMinimize()) {\n <wb-part-bar/>\n}\n\n@if (part.views().length) {\n <!-- Prevent splitting if there is no active view, i.e, when dragging the last view out of the tabbar. -->\n @let canSplit = !!part.activeView();\n <div wbViewDropZone\n [wbViewDropZoneRegionSize]=\".25\"\n [wbViewDropZonePlaceholderSize]=\".5\"\n [wbViewDropZoneAttributes]=\"{'data-partid': part.id}\"\n [wbViewDropZoneRegions]=\"canDrop() && {center: true, north: canSplit, south: canSplit, west: canSplit, east: canSplit}\"\n (wbViewDropZoneDrop)=\"onViewDrop($event)\"\n class=\"slot e2e-slot e2e-view-slot\">\n <ng-container *wbPortalOutlet=\"part.activeView()?.slot!.portal; destroyOnDetach: false\"/>\n </div>\n} @else {\n <div wbViewDropZone\n [wbViewDropZoneRegions]=\"canDrop() && {center: false, north: true, south: true, west: true, east: true}\"\n [wbViewDropZoneAttributes]=\"{'data-partid': part.id}\"\n (wbViewDropZoneDrop)=\"onViewDrop($event)\"\n class=\"slot e2e-slot e2e-part-slot\">\n <ng-container *wbPortalOutlet=\"part.slot.portal; destroyOnDetach: false\"/>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;outline:none;background-color:var(--sci-workbench-part-background-color);overflow:hidden}:host[data-peripheral]{background-color:var(--sci-workbench-part-peripheral-background-color)}:host>wb-part-bar{flex:none}:host>div.slot{flex:auto;display:grid;position:relative}\n"] }]
13727
13743
  }], ctorParameters: () => [] });
13728
13744
 
13729
13745
  /*
@@ -13794,7 +13810,9 @@ class ɵWorkbenchPart {
13794
13810
  views;
13795
13811
  classList = new ClassList();
13796
13812
  portal;
13797
- bounds;
13813
+ partBounds;
13814
+ /** Represents the bounds of the slot. Used for public API. Internally, use {@link slot.bounds()} instead. */
13815
+ bounds = computed(() => this.slot.bounds(), { ...(ngDevMode ? { debugName: "bounds" } : {}) });
13798
13816
  blockedBy;
13799
13817
  slot;
13800
13818
  _isInMainArea;
@@ -13807,7 +13825,7 @@ class ɵWorkbenchPart {
13807
13825
  this.views = computeViews(this.mPart);
13808
13826
  this.alternativeId = this.mPart().alternativeId;
13809
13827
  this.portal = this.createPartPortal();
13810
- this.bounds = boundingClientRect(computed(() => this.portal.element()));
13828
+ this.partBounds = boundingClientRect(computed(() => this.portal.element()));
13811
13829
  this.blockedBy = inject(WorkbenchDialogRegistry).top(this.id);
13812
13830
  this.slot = {
13813
13831
  portal: this.createPartSlotPortal(),
@@ -14069,7 +14087,7 @@ function computePartActions(part) {
14069
14087
  changes?.forEachAddedItem(({ item: fn }) => partActions.set(fn, computed(() => constructPartAction(fn, injector))));
14070
14088
  changes?.forEachRemovedItem(({ item: fn }) => partActions.delete(fn));
14071
14089
  return Array.from(partActions.values()).map(partAction => partAction()).filter(partAction => !!partAction);
14072
- }, { equal: (a, b) => Arrays.isEqual(a, b) });
14090
+ }, { equal: (a, b) => Objects.isEqual(a, b) });
14073
14091
  function constructPartAction(factoryFn, injector) {
14074
14092
  const action = runInInjectionContext(injector, () => factoryFn(inject(ɵWorkbenchPart)));
14075
14093
  if (action instanceof TemplateRef || typeof action === 'function') {
@@ -14090,7 +14108,7 @@ function computeActiveView(mPart) {
14090
14108
  */
14091
14109
  function computeViews(mPart) {
14092
14110
  const viewRegistry = inject(WorkbenchViewRegistry);
14093
- return computed(() => mPart().views.map(mView => viewRegistry.get(mView.id)), { equal: (a, b) => Arrays.isEqual(a, b, { exactOrder: true }) });
14111
+ return computed(() => mPart().views.map(mView => viewRegistry.get(mView.id)), { equal: (a, b) => Objects.isEqual(a, b) });
14094
14112
  }
14095
14113
 
14096
14114
  /*
@@ -15244,8 +15262,7 @@ class MicrofrontendViewComponent {
15244
15262
  installNavigator() {
15245
15263
  // Use a root effect to emit even if detached from change detection.
15246
15264
  toRootObservable(this.navigationContext)
15247
- .pipe(tap(context => this.onPreNavigate(context)), filter((context) => !!context.capability), delayIfLazy(), serializeExecution(context => this.onNavigate(context)), subscribeOn(asyncScheduler), // subscribe asynchronously because `onCapabilityChange` triggers a manual change detection cycle for inactive views; would error if called during component construction otherwise.
15248
- takeUntilDestroyed())
15265
+ .pipe(tap(context => this.onPreNavigate(context)), filter((context) => !!context.capability), delayIfLazy(), serializeExecution(context => this.onNavigate(context)), takeUntilDestroyed())
15249
15266
  .subscribe();
15250
15267
  }
15251
15268
  /**
@@ -15268,6 +15285,7 @@ class MicrofrontendViewComponent {
15268
15285
  }),
15269
15286
  referrer: navigationData.referrer,
15270
15287
  }),
15288
+ equal: (a, b) => a.capability?.metadata.id === b.capability?.metadata.id && Objects.isEqual(a.params, b.params), // do not create new navigation context when navigating to the same capability with the same params
15271
15289
  });
15272
15290
  }
15273
15291
  /**
@@ -15483,7 +15501,7 @@ class MicrofrontendViewComponent {
15483
15501
  }
15484
15502
  }))
15485
15503
  .map(accelerator => `keydown.${accelerator.join('.')}{preventDefault=true}`);
15486
- }, { equal: (a, b) => Arrays.isEqual(a, b, { exactOrder: false }) });
15504
+ }, { equal: (a, b) => Objects.isEqual(a, b, { ignoreArrayOrder: true }) });
15487
15505
  }
15488
15506
  propagateWorkbenchTheme() {
15489
15507
  Microfrontends.propagateTheme(this._routerOutletElement);
@@ -15570,6 +15588,12 @@ class ActivatedMicrofrontend {
15570
15588
  * Embeds the microfrontend of a capability provided by the workbench host application.
15571
15589
  */
15572
15590
  class MicrofrontendHostComponent {
15591
+ /**
15592
+ * Parameters passed to the microfrontend, required to reduce inputs for notification capabilities.
15593
+ *
15594
+ * @see MicrofrontendNotificationIntentHandler
15595
+ */
15596
+ params = input(undefined, { ...(ngDevMode ? { debugName: "params" } : {}) });
15573
15597
  _logger = inject(Logger);
15574
15598
  workbenchElement = inject(WORKBENCH_ELEMENT);
15575
15599
  capability = inject(ActivatedMicrofrontend).capability;
@@ -15605,7 +15629,7 @@ class MicrofrontendHostComponent {
15605
15629
  });
15606
15630
  }
15607
15631
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendHostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
15608
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.1", type: MicrofrontendHostComponent, isStandalone: true, selector: "wb-microfrontend-host", host: { properties: { "attr.data-capabilityid": "capability().metadata!.id", "attr.data-app": "capability().metadata!.appSymbolicName", "attr.data-focus": "workbenchElement.focused() ? '' : null" } }, providers: [
15632
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.1", type: MicrofrontendHostComponent, isStandalone: true, selector: "wb-microfrontend-host", inputs: { params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-capabilityid": "capability().metadata!.id", "attr.data-app": "capability().metadata!.appSymbolicName", "attr.data-focus": "workbenchElement.focused() ? '' : null" } }, providers: [
15609
15633
  // Provide `ActivatedMicrofrontend` for DI in router outlet. Otherwise, `ActivatedMicrofrontend` would not be available, most likely because provided on the route level.
15610
15634
  // TODO [Angular 22] Check if still required. If not, remove this TODO.
15611
15635
  { provide: ActivatedMicrofrontend, useFactory: () => inject(ActivatedMicrofrontend, { skipSelf: true }) },
@@ -15625,7 +15649,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
15625
15649
  // TODO [Angular 22] Check if still required. If not, remove this TODO.
15626
15650
  { provide: ActivatedMicrofrontend, useFactory: () => inject(ActivatedMicrofrontend, { skipSelf: true }) },
15627
15651
  ], template: "<router-outlet [name]=\"outlet()\" wbRouterOutletRootContext/>\n", styles: [":host{display:grid}:host>router-outlet{position:absolute}\n"] }]
15628
- }], ctorParameters: () => [] });
15652
+ }], ctorParameters: () => [], propDecorators: { params: [{ type: i0.Input, args: [{ isSignal: true, alias: "params", required: false }] }] } });
15629
15653
 
15630
15654
  /*
15631
15655
  * Copyright (c) 2018-2025 Swiss Federal Railways
@@ -15758,7 +15782,7 @@ function provideMicrofrontendViewRoute() {
15758
15782
  path: '',
15759
15783
  component: MicrofrontendHostComponent,
15760
15784
  canMatch: [canMatchMicrofrontendView({ host: true })], // use a single matcher because Angular evaluates matchers in parallel
15761
- providers: [provideActivatedMicrofrontend$4()],
15785
+ providers: [provideActivatedMicrofrontend$5()],
15762
15786
  }),
15763
15787
  multi: true,
15764
15788
  },
@@ -15792,7 +15816,7 @@ function canMatchMicrofrontendView(matcher) {
15792
15816
  /**
15793
15817
  * Provides {@link ActivatedMicrofrontend} for injection in the host microfrontend.
15794
15818
  */
15795
- function provideActivatedMicrofrontend$4() {
15819
+ function provideActivatedMicrofrontend$5() {
15796
15820
  return {
15797
15821
  provide: ActivatedMicrofrontend,
15798
15822
  useFactory: () => {
@@ -16078,7 +16102,7 @@ function provideMicrofrontendPartRoute() {
16078
16102
  path: '',
16079
16103
  component: MicrofrontendHostComponent,
16080
16104
  canMatch: [canMatchMicrofrontendPart({ host: true })], // use a single matcher because Angular evaluates matchers in parallel
16081
- providers: [provideActivatedMicrofrontend$3()],
16105
+ providers: [provideActivatedMicrofrontend$4()],
16082
16106
  }),
16083
16107
  multi: true,
16084
16108
  },
@@ -16112,7 +16136,7 @@ function canMatchMicrofrontendPart(matcher) {
16112
16136
  /**
16113
16137
  * Provides {@link ActivatedMicrofrontend} for injection in the host microfrontend.
16114
16138
  */
16115
- function provideActivatedMicrofrontend$3() {
16139
+ function provideActivatedMicrofrontend$4() {
16116
16140
  return {
16117
16141
  provide: ActivatedMicrofrontend,
16118
16142
  useFactory: () => {
@@ -16145,9 +16169,9 @@ class ParamValidator {
16145
16169
  validatePartParams(params, partCapability, context) {
16146
16170
  const { perspectiveCapability, partId } = context;
16147
16171
  return this.validateParams(params, partCapability, {
16148
- deprecated: param => `[PerspectiveDefinitionWarning] Perspective '${qualifier$7(perspectiveCapability)}' of app '${app$7(perspectiveCapability)}' passes the deprecated parameter '${param}' to part '${partId}'. Migrate deprecated parameters as specified in the capability documentation of part '${qualifier$7(partCapability)}'.`,
16149
- missing: param => `[PerspectiveDefinitionError] Perspective '${qualifier$7(perspectiveCapability)}' of app '${app$7(perspectiveCapability)}' does not pass the required parameter '${param}' to part '${partId}'. Pass required parameters as specified in the capability documentation of part '${qualifier$7(partCapability)}'. Ignoring part.`,
16150
- unexpected: param => `[PerspectiveDefinitionError] Perspective '${qualifier$7(perspectiveCapability)}' of app '${app$7(perspectiveCapability)}' passes the unexpected parameter '${param}' to part '${partId}'. Pass parameters as specified in the capability documentation of part '${qualifier$7(partCapability)}'. Ignoring part.`,
16172
+ deprecated: param => `[PerspectiveDefinitionWarning] Perspective '${qualifier$8(perspectiveCapability)}' of app '${app$8(perspectiveCapability)}' passes the deprecated parameter '${param}' to part '${partId}'. Migrate deprecated parameters as specified in the capability documentation of part '${qualifier$8(partCapability)}'.`,
16173
+ missing: param => `[PerspectiveDefinitionError] Perspective '${qualifier$8(perspectiveCapability)}' of app '${app$8(perspectiveCapability)}' does not pass the required parameter '${param}' to part '${partId}'. Pass required parameters as specified in the capability documentation of part '${qualifier$8(partCapability)}'. Ignoring part.`,
16174
+ unexpected: param => `[PerspectiveDefinitionError] Perspective '${qualifier$8(perspectiveCapability)}' of app '${app$8(perspectiveCapability)}' passes the unexpected parameter '${param}' to part '${partId}'. Pass parameters as specified in the capability documentation of part '${qualifier$8(partCapability)}'. Ignoring part.`,
16151
16175
  });
16152
16176
  }
16153
16177
  /**
@@ -16158,9 +16182,9 @@ class ParamValidator {
16158
16182
  validateViewParams(params, viewCapability, context) {
16159
16183
  const { partCapability } = context;
16160
16184
  return this.validateParams(params, viewCapability, {
16161
- deprecated: param => `[PartDefinitionWarning] Part '${qualifier$7(partCapability)}' of app '${app$7(partCapability)}' passes the deprecated parameter '${param}' to view '${qualifier$7(viewCapability)}'. Migrate deprecated parameters as specified in the capability documentation.`,
16162
- missing: param => `[PartDefinitionError] Part '${qualifier$7(partCapability)}' of app '${app$7(partCapability)}' does not pass the required parameter '${param}' to view '${qualifier$7(viewCapability)}'. Pass required parameters as specified in the capability documentation. Ignoring view.`,
16163
- unexpected: param => `[PartDefinitionError] Part '${qualifier$7(partCapability)}' of app '${app$7(partCapability)}' passes the unexpected parameter '${param}' to view '${qualifier$7(viewCapability)}'. Pass parameters as specified in the capability documentation. Ignoring view.`,
16185
+ deprecated: param => `[PartDefinitionWarning] Part '${qualifier$8(partCapability)}' of app '${app$8(partCapability)}' passes the deprecated parameter '${param}' to view '${qualifier$8(viewCapability)}'. Migrate deprecated parameters as specified in the capability documentation.`,
16186
+ missing: param => `[PartDefinitionError] Part '${qualifier$8(partCapability)}' of app '${app$8(partCapability)}' does not pass the required parameter '${param}' to view '${qualifier$8(viewCapability)}'. Pass required parameters as specified in the capability documentation. Ignoring view.`,
16187
+ unexpected: param => `[PartDefinitionError] Part '${qualifier$8(partCapability)}' of app '${app$8(partCapability)}' passes the unexpected parameter '${param}' to view '${qualifier$8(viewCapability)}'. Pass parameters as specified in the capability documentation. Ignoring view.`,
16164
16188
  });
16165
16189
  }
16166
16190
  validateParams(params, capability, messageFactory) {
@@ -16188,13 +16212,13 @@ class ParamValidator {
16188
16212
  /**
16189
16213
  * Returns the qualifier as string.
16190
16214
  */
16191
- function qualifier$7(capability) {
16215
+ function qualifier$8(capability) {
16192
16216
  return Objects.toMatrixNotation(capability.qualifier);
16193
16217
  }
16194
16218
  /**
16195
16219
  * Returns the app symbolic name.
16196
16220
  */
16197
- function app$7(capability) {
16221
+ function app$8(capability) {
16198
16222
  return capability.metadata.appSymbolicName;
16199
16223
  }
16200
16224
 
@@ -16303,7 +16327,7 @@ class MicrofrontendPerspectiveInstaller {
16303
16327
  // If a relative aligned part and the reference part cannot be found, ignore the part.
16304
16328
  // Do not log an error to support conditional parts, i.e., if aligned relative to a conditional docked part.
16305
16329
  if (typeof partRef.position === 'object' && partRef.position.relativeTo && !layout.hasPart(partRef.position.relativeTo)) {
16306
- this._logger.debug(`[PerspectiveDefinitionInfo] Perspective '${qualifier$6(perspectiveCapability)}' of app '${app$6(perspectiveCapability)}' aligns part '${partRef.id}' relative to missing part '${partRef.position.relativeTo}'. The reference part may not be available. Ignoring part.`, LoggerNames.MICROFRONTEND);
16330
+ this._logger.debug(`[PerspectiveDefinitionInfo] Perspective '${qualifier$7(perspectiveCapability)}' of app '${app$7(perspectiveCapability)}' aligns part '${partRef.id}' relative to missing part '${partRef.position.relativeTo}'. The reference part may not be available. Ignoring part.`, LoggerNames.MICROFRONTEND);
16307
16331
  return layout;
16308
16332
  }
16309
16333
  // If capability cannot be found, ignore the part if docked, or add an empty part otherwise, required to support layouts with conditional reference parts.
@@ -16420,13 +16444,13 @@ class MicrofrontendPerspectiveInstaller {
16420
16444
  }
16421
16445
  }
16422
16446
  if (visibleCapabilities.length > 1) {
16423
- this._logger.error(`[PerspectiveDefinitionError] Multiple ${type} capabilities found for qualifier '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}' of app '${app$6(requester)}'. Defaulting to first. Ensure ${type} capabilities to have a unique qualifier.`, LoggerNames.MICROFRONTEND);
16447
+ this._logger.error(`[PerspectiveDefinitionError] Multiple ${type} capabilities found for qualifier '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}' of app '${app$7(requester)}'. Defaulting to first. Ensure ${type} capabilities to have a unique qualifier.`, LoggerNames.MICROFRONTEND);
16424
16448
  }
16425
16449
  else if (!visibleCapabilities.length && capabilities.length) {
16426
- this._logger.error(`[PerspectiveDefinitionError] Application '${app$6(requester)}' is not qualified to use ${type} capability '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}'. Ensure to have declared an intention and the capability is not private.`, LoggerNames.MICROFRONTEND);
16450
+ this._logger.error(`[PerspectiveDefinitionError] Application '${app$7(requester)}' is not qualified to use ${type} capability '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}'. Ensure to have declared an intention and the capability is not private.`, LoggerNames.MICROFRONTEND);
16427
16451
  }
16428
16452
  else if (!visibleCapabilities.length) {
16429
- const message = `No ${type} capability found for qualifier '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}' of app '${app$6(requester)}'. The qualifier may be incorrect, the capability not registered, or the providing application not available.`;
16453
+ const message = `No ${type} capability found for qualifier '${Objects.toMatrixNotation(qualifier)}' in ${requester.type} '${Objects.toMatrixNotation(requester.qualifier)}' of app '${app$7(requester)}'. The qualifier may be incorrect, the capability not registered, or the providing application not available.`;
16430
16454
  if (context.logLevelIfEmpty === 'error') {
16431
16455
  this._logger.error(`[PerspectiveDefinitionError] ${message}`, LoggerNames.MICROFRONTEND);
16432
16456
  }
@@ -16447,14 +16471,14 @@ class MicrofrontendPerspectiveInstaller {
16447
16471
  // Validate main area part not to have views.
16448
16472
  const isMainAreaPart = partRef.id === MAIN_AREA || partRef.id === MAIN_AREA_ALTERNATIVE_ID;
16449
16473
  if (isMainAreaPart && partCapability.properties?.views?.length) {
16450
- this._logger.error(`[PerspectiveDefinitionError] Part '${qualifier$6(partCapability)}' of app '${app$6(partCapability)}' is used as main area part in perspective '${qualifier$6(context.perspectiveCapability)}' and defines views. Views cannot be added to the main area of a perspective. Ignoring part.`, LoggerNames.MICROFRONTEND);
16474
+ this._logger.error(`[PerspectiveDefinitionError] Part '${qualifier$7(partCapability)}' of app '${app$7(partCapability)}' is used as main area part in perspective '${qualifier$7(context.perspectiveCapability)}' and defines views. Views cannot be added to the main area of a perspective. Ignoring part.`, LoggerNames.MICROFRONTEND);
16451
16475
  return false;
16452
16476
  }
16453
16477
  // Validate extras of docked part.
16454
16478
  if ('position' in partRef && typeof partRef.position === 'string') {
16455
16479
  const dockedPartExtras = partCapability.properties?.extras;
16456
16480
  if (!dockedPartExtras?.label || !dockedPartExtras.icon) {
16457
- this._logger.error(`[PerspectiveDefinitionError] Part '${qualifier$6(partCapability)}' of app '${app$6(partCapability)}' is used as a docked part in perspective '${qualifier$6(context.perspectiveCapability)}' but does not define an icon and label. A docked part must define both an icon and a label: { properties: { extras: { icon: '<icon-name>', label: '<text>' } } }. Ignoring part.`, LoggerNames.MICROFRONTEND);
16481
+ this._logger.error(`[PerspectiveDefinitionError] Part '${qualifier$7(partCapability)}' of app '${app$7(partCapability)}' is used as a docked part in perspective '${qualifier$7(context.perspectiveCapability)}' but does not define an icon and label. A docked part must define both an icon and a label: { properties: { extras: { icon: '<icon-name>', label: '<text>' } } }. Ignoring part.`, LoggerNames.MICROFRONTEND);
16458
16482
  return false;
16459
16483
  }
16460
16484
  }
@@ -16476,13 +16500,13 @@ function createCapabilityRemoteTranslatable(translatable, capability, params) {
16476
16500
  /**
16477
16501
  * Returns the qualifier as string.
16478
16502
  */
16479
- function qualifier$6(capability) {
16503
+ function qualifier$7(capability) {
16480
16504
  return Objects.toMatrixNotation(capability.qualifier);
16481
16505
  }
16482
16506
  /**
16483
16507
  * Returns the app symbolic name.
16484
16508
  */
16485
- function app$6(capability) {
16509
+ function app$7(capability) {
16486
16510
  return capability.metadata.appSymbolicName;
16487
16511
  }
16488
16512
 
@@ -16636,61 +16660,61 @@ class MicrofrontendPerspectiveCapabilityValidator {
16636
16660
  const perspectiveCapability = capability;
16637
16661
  // Assert the perspective capability to have a qualifier.
16638
16662
  if (!Object.keys(perspectiveCapability.qualifier ?? {}).length) {
16639
- throw Error(`[PerspectiveDefinitionError] Perspective capability requires a qualifier [app=${app$5(perspectiveCapability)}, perspective=${JSON.stringify(perspectiveCapability)}]`);
16663
+ throw Error(`[PerspectiveDefinitionError] Perspective capability requires a qualifier [app=${app$6(perspectiveCapability)}, perspective=${JSON.stringify(perspectiveCapability)}]`);
16640
16664
  }
16641
16665
  // Assert the perspective capability to have properties.
16642
16666
  if (!perspectiveCapability.properties) {
16643
- throw Error(`[PerspectiveDefinitionError] Perspective capability requires properties [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16667
+ throw Error(`[PerspectiveDefinitionError] Perspective capability requires properties [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16644
16668
  }
16645
16669
  // Assert the perspective capability to have parts.
16646
16670
  if (!perspectiveCapability.properties.parts) {
16647
- throw Error(`[PerspectiveDefinitionError] Perspective capability requires the 'parts' property [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16671
+ throw Error(`[PerspectiveDefinitionError] Perspective capability requires the 'parts' property [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16648
16672
  }
16649
16673
  // Assert parts to have an id.
16650
16674
  const parts = perspectiveCapability.properties.parts;
16651
16675
  parts.forEach((part, index) => {
16652
16676
  if (!part.id) {
16653
- throw Error(`[PerspectiveDefinitionError] Missing required 'id' property of part at index '${index}' [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16677
+ throw Error(`[PerspectiveDefinitionError] Missing required 'id' property of part at index '${index}' [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16654
16678
  }
16655
16679
  });
16656
16680
  // Assert unique part ids.
16657
16681
  if (new Set(parts.map(part => part.id)).size !== parts.length) {
16658
- throw Error(`[PerspectiveDefinitionError] Parts of perspective must have a unique id [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16682
+ throw Error(`[PerspectiveDefinitionError] Parts of perspective must have a unique id [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16659
16683
  }
16660
16684
  // Assert the perspective capability to have an initial part.
16661
16685
  const [initialPart, ...otherParts] = parts;
16662
16686
  if (!initialPart) {
16663
- throw Error(`[PerspectiveDefinitionError] Perspective capability requires an initial part [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16687
+ throw Error(`[PerspectiveDefinitionError] Perspective capability requires an initial part [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16664
16688
  }
16665
16689
  // Assert initial part not to be positioned.
16666
16690
  if (initialPart.position) {
16667
- throw Error(`[PerspectiveDefinitionError] Initial part '${initialPart.id}' of perspective must not have the 'position' property [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16691
+ throw Error(`[PerspectiveDefinitionError] Initial part '${initialPart.id}' of perspective must not have the 'position' property [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16668
16692
  }
16669
16693
  // Assert other parts to be positioned.
16670
16694
  otherParts.forEach(part => {
16671
16695
  if (!part.position) {
16672
- throw Error(`[PerspectiveDefinitionError] Missing required 'position' property in part '${part.id}' [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16696
+ throw Error(`[PerspectiveDefinitionError] Missing required 'position' property in part '${part.id}' [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16673
16697
  }
16674
16698
  if (typeof part.position === 'string' && !dockingAreas.has(part.position)) {
16675
- throw Error(`[PerspectiveDefinitionError] Illegal position in docked part '${part.id}': '${part.position}'. Must be one of [${[...dockingAreas].join(',')}] [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16699
+ throw Error(`[PerspectiveDefinitionError] Illegal position in docked part '${part.id}': '${part.position}'. Must be one of [${[...dockingAreas].join(',')}] [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16676
16700
  }
16677
16701
  if (typeof part.position === 'object' && !part.position.align) {
16678
- throw Error(`[PerspectiveDefinitionError] Missing required 'align' property in part '${part.id}': { position: { align: 'left|right|top|bottom' } } [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16702
+ throw Error(`[PerspectiveDefinitionError] Missing required 'align' property in part '${part.id}': { position: { align: 'left|right|top|bottom' } } [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16679
16703
  }
16680
16704
  if (typeof part.position === 'object' && !align.has(part.position.align)) {
16681
- throw Error(`[PerspectiveDefinitionError] Illegal alignment of part '${part.id}': '${part.position.align}'. Must be one of [${[...align].join(',')}] [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16705
+ throw Error(`[PerspectiveDefinitionError] Illegal alignment of part '${part.id}': '${part.position.align}'. Must be one of [${[...align].join(',')}] [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16682
16706
  }
16683
16707
  if (typeof part.position === 'object' && part.position.relativeTo && !parts.some(otherPart => otherPart.id === part.position.relativeTo)) {
16684
- throw Error(`[PerspectiveDefinitionError] Illegal part '${part.position.relativeTo}' referenced in 'relativeTo' of part '${part.id}'. Part not found. [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16708
+ throw Error(`[PerspectiveDefinitionError] Illegal part '${part.position.relativeTo}' referenced in 'relativeTo' of part '${part.id}'. Part not found. [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16685
16709
  }
16686
16710
  });
16687
16711
  // Assert parts to have a qualifier.
16688
16712
  parts.forEach(part => {
16689
16713
  if (!Object.keys(part.qualifier ?? {}).length) {
16690
- throw Error(`[PerspectiveDefinitionError] Missing required qualifier for part '${part.id}' [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16714
+ throw Error(`[PerspectiveDefinitionError] Missing required qualifier for part '${part.id}' [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16691
16715
  }
16692
16716
  if (Object.entries(part.qualifier).some(([key, value]) => key === '*' || value === '*')) {
16693
- throw Error(`[PerspectiveDefinitionError] Qualifier for part '${part.id}' must be explicit and not contain wildcards: '${Objects.toMatrixNotation(part.qualifier)}' [app=${app$5(perspectiveCapability)}, perspective=${qualifier$5(perspectiveCapability)}]`);
16717
+ throw Error(`[PerspectiveDefinitionError] Qualifier for part '${part.id}' must be explicit and not contain wildcards: '${Objects.toMatrixNotation(part.qualifier)}' [app=${app$6(perspectiveCapability)}, perspective=${qualifier$6(perspectiveCapability)}]`);
16694
16718
  }
16695
16719
  });
16696
16720
  return capability;
@@ -16704,13 +16728,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
16704
16728
  /**
16705
16729
  * Returns the qualifier as string.
16706
16730
  */
16707
- function qualifier$5(capability) {
16731
+ function qualifier$6(capability) {
16708
16732
  return Objects.toMatrixNotation(capability.qualifier);
16709
16733
  }
16710
16734
  /**
16711
16735
  * Returns the app symbolic name.
16712
16736
  */
16713
- function app$5(capability) {
16737
+ function app$6(capability) {
16714
16738
  return capability.metadata.appSymbolicName;
16715
16739
  }
16716
16740
  const dockingAreas = new Set()
@@ -16840,24 +16864,24 @@ class MicrofrontendPartCapabilityValidator {
16840
16864
  const partCapability = capability;
16841
16865
  // Assert the part capability to have a qualifier.
16842
16866
  if (!Object.keys(partCapability.qualifier ?? {}).length) {
16843
- throw Error(`[PartDefinitionError] Part capability requires a qualifier [app=${app$4(partCapability)}, part=${JSON.stringify(partCapability)}]`);
16867
+ throw Error(`[PartDefinitionError] Part capability requires a qualifier [app=${app$5(partCapability)}, part=${JSON.stringify(partCapability)}]`);
16844
16868
  }
16845
16869
  // Assert docked part extras, if set.
16846
16870
  if (partCapability.properties?.extras) {
16847
16871
  if (!partCapability.properties.extras.icon) {
16848
- throw Error(`[PartDefinitionError] Missing required 'icon' property in docked part extras [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16872
+ throw Error(`[PartDefinitionError] Missing required 'icon' property in docked part extras [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16849
16873
  }
16850
16874
  if (!partCapability.properties.extras.label) {
16851
- throw Error(`[PartDefinitionError] Missing required 'label' property in docked part extras [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16875
+ throw Error(`[PartDefinitionError] Missing required 'label' property in docked part extras [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16852
16876
  }
16853
16877
  }
16854
16878
  // Assert referenced views to have a qualifier.
16855
16879
  partCapability.properties?.views?.forEach((view) => {
16856
16880
  if (!Object.keys(view.qualifier ?? {}).length) {
16857
- throw Error(`[PartDefinitionError] Missing required qualifier for view [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16881
+ throw Error(`[PartDefinitionError] Missing required qualifier for view [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16858
16882
  }
16859
16883
  if (Object.entries(view.qualifier).some(([key, value]) => key === '*' || value === '*')) {
16860
- throw Error(`[PartDefinitionError] View qualifier must be explicit and not contain wildcards: '${Objects.toMatrixNotation(view.qualifier)}' [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16884
+ throw Error(`[PartDefinitionError] View qualifier must be explicit and not contain wildcards: '${Objects.toMatrixNotation(view.qualifier)}' [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16861
16885
  }
16862
16886
  });
16863
16887
  // Assert the path of the part capability.
@@ -16866,11 +16890,11 @@ class MicrofrontendPartCapabilityValidator {
16866
16890
  }
16867
16891
  // Assert host part capabilities not to define the "showSplash" property.
16868
16892
  if (Microfrontends.isHostProvider(capability) && partCapability.properties?.showSplash !== undefined) {
16869
- throw Error(`[PartDefinitionError] Property "showSplash" not supported for part capabilities of the host application [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16893
+ throw Error(`[PartDefinitionError] Property "showSplash" not supported for part capabilities of the host application [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16870
16894
  }
16871
16895
  // Assert "showSplash" property not to be defined if part has no path.
16872
16896
  if (partCapability.properties?.path === undefined && partCapability.properties?.showSplash !== undefined) {
16873
- throw Error(`[PartDefinitionError] Property "showSplash" only supported for part capabilities with a path [app=${app$4(partCapability)}, part=${qualifier$4(partCapability)}]`);
16897
+ throw Error(`[PartDefinitionError] Property "showSplash" only supported for part capabilities with a path [app=${app$5(partCapability)}, part=${qualifier$5(partCapability)}]`);
16874
16898
  }
16875
16899
  return capability;
16876
16900
  }
@@ -16878,12 +16902,12 @@ class MicrofrontendPartCapabilityValidator {
16878
16902
  const path = capability.properties?.path;
16879
16903
  if (Microfrontends.isHostProvider(capability)) {
16880
16904
  if (path === null || path.length) {
16881
- throw Error(`[PartDefinitionError] Part capabilities of the host application require an empty path. [app=${app$4(capability)}, part=${qualifier$4(capability)}]. Change the path '${path}' to empty and add 'canMatchWorkbenchPartCapability(${JSON.stringify(capability.qualifier)})' guard to the route.\n\nExample:\nCapability: { type: 'part', qualifier: ${JSON.stringify(capability.qualifier)}, properties: {path: ''} }\nRoute: { path: '', canMatch: [canMatchWorkbenchPartCapability(${JSON.stringify(capability.qualifier)})], component: PartComponent }`);
16905
+ throw Error(`[PartDefinitionError] Part capabilities of the host application require an empty path. [app=${app$5(capability)}, part=${qualifier$5(capability)}]. Change the path '${path}' to empty and add 'canMatchWorkbenchPartCapability(${JSON.stringify(capability.qualifier)})' guard to the route.\n\nExample:\nCapability: { type: 'part', qualifier: ${JSON.stringify(capability.qualifier)}, properties: {path: ''} }\nRoute: { path: '', canMatch: [canMatchWorkbenchPartCapability(${JSON.stringify(capability.qualifier)})], component: PartComponent }`);
16882
16906
  }
16883
16907
  }
16884
16908
  else {
16885
16909
  if (path === null) {
16886
- throw Error(`[PartDefinitionError] Part capabilities require a path. [app=${app$4(capability)}, part=${qualifier$4(capability)}]`);
16910
+ throw Error(`[PartDefinitionError] Part capabilities require a path. [app=${app$5(capability)}, part=${qualifier$5(capability)}]`);
16887
16911
  }
16888
16912
  }
16889
16913
  }
@@ -16896,13 +16920,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
16896
16920
  /**
16897
16921
  * Returns the qualifier as string.
16898
16922
  */
16899
- function qualifier$4(capability) {
16923
+ function qualifier$5(capability) {
16900
16924
  return Objects.toMatrixNotation(capability.qualifier);
16901
16925
  }
16902
16926
  /**
16903
16927
  * Returns the app symbolic name.
16904
16928
  */
16905
- function app$4(capability) {
16929
+ function app$5(capability) {
16906
16930
  return capability.metadata.appSymbolicName;
16907
16931
  }
16908
16932
 
@@ -17391,21 +17415,21 @@ class MicrofrontendViewCapabilityValidator {
17391
17415
  const viewCapability = capability;
17392
17416
  // Assert the view capability to have a qualifier.
17393
17417
  if (!Object.keys(viewCapability.qualifier ?? {}).length) {
17394
- throw Error(`[ViewDefinitionError] View capability requires a qualifier [app=${app$3(viewCapability)}, view=${qualifier$3(viewCapability)}]`);
17418
+ throw Error(`[ViewDefinitionError] View capability requires a qualifier [app=${app$4(viewCapability)}, view=${qualifier$4(viewCapability)}]`);
17395
17419
  }
17396
17420
  // Assert the view capability to have properties.
17397
17421
  if (!viewCapability.properties) {
17398
- throw Error(`[ViewDefinitionError] View capability requires properties [app=${app$3(viewCapability)}, view=${qualifier$3(viewCapability)}]`);
17422
+ throw Error(`[ViewDefinitionError] View capability requires properties [app=${app$4(viewCapability)}, view=${qualifier$4(viewCapability)}]`);
17399
17423
  }
17400
17424
  // Assert the view capability to have a path, unless provided by the host application.
17401
17425
  this.assertPath(viewCapability);
17402
17426
  // Assert host view capabilities not to define the "lazy" property.
17403
17427
  if (Microfrontends.isHostProvider(capability) && viewCapability.properties.lazy !== undefined) {
17404
- throw Error(`[ViewDefinitionError] Property "lazy" not supported for view capabilities of the host application [app=${app$3(viewCapability)}, view=${qualifier$3(viewCapability)}]`);
17428
+ throw Error(`[ViewDefinitionError] Property "lazy" not supported for view capabilities of the host application [app=${app$4(viewCapability)}, view=${qualifier$4(viewCapability)}]`);
17405
17429
  }
17406
17430
  // Assert host view capabilities not to define the "showSplash" property.
17407
17431
  if (Microfrontends.isHostProvider(capability) && viewCapability.properties.showSplash !== undefined) {
17408
- throw Error(`[ViewDefinitionError] Property "showSplash" not supported for view capabilities of the host application [app=${app$3(viewCapability)}, view=${qualifier$3(viewCapability)}]`);
17432
+ throw Error(`[ViewDefinitionError] Property "showSplash" not supported for view capabilities of the host application [app=${app$4(viewCapability)}, view=${qualifier$4(viewCapability)}]`);
17409
17433
  }
17410
17434
  return capability;
17411
17435
  }
@@ -17413,12 +17437,12 @@ class MicrofrontendViewCapabilityValidator {
17413
17437
  const path = capability.properties?.path;
17414
17438
  if (Microfrontends.isHostProvider(capability)) {
17415
17439
  if (path !== '') {
17416
- throw Error(`[ViewDefinitionError] View capabilities of the host application require an empty path. [app=${app$3(capability)}, view=${qualifier$3(capability)}]. Change the path '${path}' to empty and add 'canMatchWorkbenchViewCapability(${JSON.stringify(capability.qualifier)})' guard to the route.\n\nExample:\nCapability: { type: 'view', qualifier: ${JSON.stringify(capability.qualifier)}, properties: {path: ''} }\nRoute: { path: '', canMatch: [canMatchWorkbenchViewCapability(${JSON.stringify(capability.qualifier)})], component: ViewComponent }`);
17440
+ throw Error(`[ViewDefinitionError] View capabilities of the host application require an empty path. [app=${app$4(capability)}, view=${qualifier$4(capability)}]. Change the path '${path}' to empty and add 'canMatchWorkbenchViewCapability(${JSON.stringify(capability.qualifier)})' guard to the route.\n\nExample:\nCapability: { type: 'view', qualifier: ${JSON.stringify(capability.qualifier)}, properties: {path: ''} }\nRoute: { path: '', canMatch: [canMatchWorkbenchViewCapability(${JSON.stringify(capability.qualifier)})], component: ViewComponent }`);
17417
17441
  }
17418
17442
  }
17419
17443
  else {
17420
17444
  if (path === null || path == undefined) {
17421
- throw Error(`[ViewDefinitionError] View capabilities require a path. [app=${app$3(capability)}, view=${qualifier$3(capability)}]`);
17445
+ throw Error(`[ViewDefinitionError] View capabilities require a path. [app=${app$4(capability)}, view=${qualifier$4(capability)}]`);
17422
17446
  }
17423
17447
  }
17424
17448
  }
@@ -17431,13 +17455,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
17431
17455
  /**
17432
17456
  * Returns the qualifier as string.
17433
17457
  */
17434
- function qualifier$3(capability) {
17458
+ function qualifier$4(capability) {
17435
17459
  return Objects.toMatrixNotation(capability.qualifier);
17436
17460
  }
17437
17461
  /**
17438
17462
  * Returns the app symbolic name.
17439
17463
  */
17440
- function app$3(capability) {
17464
+ function app$4(capability) {
17441
17465
  return capability.metadata.appSymbolicName;
17442
17466
  }
17443
17467
 
@@ -17637,9 +17661,107 @@ function provideMicrofrontendView() {
17637
17661
  }
17638
17662
  }
17639
17663
 
17664
+ /*
17665
+ * Copyright (c) 2018-2026 Swiss Federal Railways
17666
+ *
17667
+ * This program and the accompanying materials are made
17668
+ * available under the terms of the Eclipse Public License 2.0
17669
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
17670
+ *
17671
+ * SPDX-License-Identifier: EPL-2.0
17672
+ */
17673
+ /**
17674
+ * Displays the notification for the built-in notification capability.
17675
+ *
17676
+ * This component is designed to be displayed in {@link MicrofrontendHostComponent}.
17677
+ */
17678
+ class NotificationTextMessageComponent {
17679
+ message;
17680
+ constructor() {
17681
+ const { params, referrer } = inject(ActivatedMicrofrontend);
17682
+ const translatable = params().get(eNOTIFICATION_MESSAGE_PARAM);
17683
+ this.message = createRemoteTranslatable(translatable, { appSymbolicName: referrer() });
17684
+ }
17685
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: NotificationTextMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
17686
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.1", type: NotificationTextMessageComponent, isStandalone: true, selector: "wb-notification-text-message", host: { properties: { "class.empty": "!message?.length" } }, ngImport: i0, template: "{{(message | wbText)()}}\n", styles: [":host{word-break:break-word;white-space:pre-line}:host.empty{display:none}\n"], dependencies: [{ kind: "pipe", type: TextPipe, name: "wbText" }] });
17687
+ }
17688
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: NotificationTextMessageComponent, decorators: [{
17689
+ type: Component,
17690
+ args: [{ selector: 'wb-notification-text-message', imports: [
17691
+ TextPipe,
17692
+ ], host: {
17693
+ '[class.empty]': '!message?.length',
17694
+ }, template: "{{(message | wbText)()}}\n", styles: [":host{word-break:break-word;white-space:pre-line}:host.empty{display:none}\n"] }]
17695
+ }], ctorParameters: () => [] });
17696
+ /**
17697
+ * Property to identify the built-in text notification capability.
17698
+ */
17699
+ const TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY = 'ɵidentity';
17700
+ /**
17701
+ * Value to identify the built-in text notification capability.
17702
+ */
17703
+ const TEXT_NOTIFICATION_CAPABILITY_IDENTITY = UUID.randomUUID();
17704
+
17705
+ var notificationTextMessage_component = /*#__PURE__*/Object.freeze({
17706
+ __proto__: null,
17707
+ TEXT_NOTIFICATION_CAPABILITY_IDENTITY: TEXT_NOTIFICATION_CAPABILITY_IDENTITY,
17708
+ TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY: TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY,
17709
+ default: NotificationTextMessageComponent
17710
+ });
17711
+
17712
+ /*
17713
+ * Copyright (c) 2018-2026 Swiss Federal Railways
17714
+ *
17715
+ * This program and the accompanying materials are made
17716
+ * available under the terms of the Eclipse Public License 2.0
17717
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
17718
+ *
17719
+ * SPDX-License-Identifier: EPL-2.0
17720
+ */
17721
+ /**
17722
+ * Intercepts the host manifest, registering the built-in text notification capability.
17723
+ */
17724
+ class MicrofrontendTextNotificationCapabilityProvider {
17725
+ intercept(hostManifest) {
17726
+ hostManifest.capabilities = [
17727
+ ...hostManifest.capabilities ?? [],
17728
+ provideBuiltInTextNotificationCapability(),
17729
+ ];
17730
+ }
17731
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
17732
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider });
17733
+ }
17734
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider, decorators: [{
17735
+ type: Injectable
17736
+ }] });
17737
+ /**
17738
+ * Provides the built-in notification capability to display text.
17739
+ *
17740
+ * @see MicrofrontendNotificationIntentHandler
17741
+ */
17742
+ function provideBuiltInTextNotificationCapability() {
17743
+ return {
17744
+ type: WorkbenchCapabilities.Notification,
17745
+ qualifier: {},
17746
+ params: [
17747
+ {
17748
+ name: eNOTIFICATION_MESSAGE_PARAM,
17749
+ required: false,
17750
+ description: 'Text to display in the notification.',
17751
+ },
17752
+ ],
17753
+ properties: {
17754
+ path: '',
17755
+ [TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY]: TEXT_NOTIFICATION_CAPABILITY_IDENTITY,
17756
+ },
17757
+ private: false,
17758
+ description: 'Displays a text notification.',
17759
+ };
17760
+ }
17761
+
17640
17762
  /**
17641
- * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered.
17642
- * It informs about system events, task completion or errors. The severity indicates importance or urgency.
17763
+ * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered or focused.
17764
+ * It informs about system events, task completion, or errors. Severity indicates importance or urgency.
17643
17765
  *
17644
17766
  * The notification component can inject this handle to interact with the notification.
17645
17767
  *
@@ -17736,47 +17858,211 @@ class Notification {
17736
17858
  * SPDX-License-Identifier: EPL-2.0
17737
17859
  */
17738
17860
  /**
17739
- * Registry for {@link WorkbenchNotification} elements.
17861
+ * TODO [Angular 22] Remove with Angular 22. Used for backward compatiblity.
17740
17862
  */
17741
- class WorkbenchNotificationRegistry extends WorkbenchElementRegistry {
17742
- /**
17743
- * Gets the most recently opened notification.
17744
- */
17745
- top;
17746
- constructor() {
17747
- super({
17748
- nullElementErrorFn: notificationId => Error(`[NullNotificationError] Notification '${notificationId}' not found.`),
17749
- onUnregister: notification => notification.destroy(),
17863
+ class RemoveLegacyInputPipe {
17864
+ transform(inputs) {
17865
+ const inputsCopy = { ...inputs ?? {} };
17866
+ delete inputsCopy[LEGACY_NOTIFICATION_INPUT]; // eslint-disable-line @typescript-eslint/no-dynamic-delete
17867
+ return inputsCopy;
17868
+ }
17869
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: RemoveLegacyInputPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
17870
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.1", ngImport: i0, type: RemoveLegacyInputPipe, isStandalone: true, name: "wbRemoveLegacyInput" });
17871
+ }
17872
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: RemoveLegacyInputPipe, decorators: [{
17873
+ type: Pipe,
17874
+ args: [{ name: 'wbRemoveLegacyInput' }]
17875
+ }] });
17876
+
17877
+ /*
17878
+ * Copyright (c) 2018-2026 Swiss Federal Railways
17879
+ *
17880
+ * This program and the accompanying materials are made
17881
+ * available under the terms of the Eclipse Public License 2.0
17882
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
17883
+ *
17884
+ * SPDX-License-Identifier: EPL-2.0
17885
+ */
17886
+ /**
17887
+ * Renders the content of a workbench notification.
17888
+ */
17889
+ class WorkbenchNotificationComponent {
17890
+ notification = inject(ɵWorkbenchNotification);
17891
+ hover = signal(false, { ...(ngDevMode ? { debugName: "hover" } : {}) });
17892
+ slotAnchorName = this.notification.id.replace('.', '_'); // Anchor must not contain a dot.
17893
+ notificationSlotBounds = viewChild('slot_bounds', { ...(ngDevMode ? { debugName: "notificationSlotBounds" } : {}), read: (ElementRef) });
17894
+ constructor() {
17895
+ this.installAutoCloseTimer();
17896
+ this.closeOnEscapeIfOnTop();
17897
+ trackFocus(inject(ElementRef).nativeElement, this.notification);
17898
+ }
17899
+ onClose() {
17900
+ this.notification.close();
17901
+ }
17902
+ onEscape(event) {
17903
+ if (this.notification.focused()) {
17904
+ event.stopPropagation(); // stop propagation to prevent closing the most recently displayed notification
17905
+ this.notification.close();
17906
+ }
17907
+ }
17908
+ onAuxClick(event) {
17909
+ if (event.button === 1) { // primary aux button
17910
+ event.preventDefault(); // prevent user-agent default action
17911
+ this.notification.close();
17912
+ }
17913
+ }
17914
+ /**
17915
+ * Closes this notification when pressing escape if it is the most recently displayed notification.
17916
+ */
17917
+ closeOnEscapeIfOnTop() {
17918
+ const zone = inject(NgZone);
17919
+ const document = inject(DOCUMENT);
17920
+ effect(onCleanup => {
17921
+ if (!this.notification.top()) {
17922
+ return;
17923
+ }
17924
+ const subscription = fromEvent(document, 'keydown')
17925
+ .pipe(subscribeIn(fn => zone.runOutsideAngular(fn)), filter((event) => event.key === 'Escape'), observeIn(fn => zone.run(fn)))
17926
+ .subscribe(() => this.notification.close());
17927
+ onCleanup(() => subscription.unsubscribe());
17750
17928
  });
17751
- this.top = computed(() => this.elements().at(-1), { ...(ngDevMode ? { debugName: "top" } : {}) });
17752
17929
  }
17753
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
17754
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationRegistry, providedIn: 'root' });
17930
+ /**
17931
+ * Installs a timer to close the notification.
17932
+ */
17933
+ installAutoCloseTimer() {
17934
+ effect(onCleanup => {
17935
+ const duration = this.notification.duration();
17936
+ const focus = this.notification.focused();
17937
+ const blockedBy = this.notification.blockedBy();
17938
+ const hover = this.hover();
17939
+ if (hover || focus || blockedBy) {
17940
+ return;
17941
+ }
17942
+ untracked(() => {
17943
+ const subscription = fromDuration$(duration).subscribe(() => this.notification.close());
17944
+ onCleanup(() => subscription.unsubscribe());
17945
+ });
17946
+ });
17947
+ function fromDuration$(duration) {
17948
+ switch (duration) {
17949
+ case 'short':
17950
+ return timer(7000);
17951
+ case 'medium':
17952
+ return timer(15000);
17953
+ case 'long':
17954
+ return timer(30000);
17955
+ default:
17956
+ if (typeof duration === 'number') {
17957
+ return timer(duration);
17958
+ }
17959
+ return NEVER;
17960
+ }
17961
+ }
17962
+ }
17963
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
17964
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: WorkbenchNotificationComponent, isStandalone: true, selector: "wb-notification", host: { listeners: { "mouseenter": "hover.set(true)", "mouseleave": "hover.set(false)", "auxclick": "onAuxClick($event)", "keydown.escape": "onEscape($event)" }, properties: { "attr.data-notificationid": "notification.id", "attr.data-severity": "notification.severity()", "style.min-height": "notification.size.minHeight()", "style.height": "notification.size.height()", "style.max-height": "notification.size.maxHeight()", "style.--\u0275slot-anchor": "`--${slotAnchorName}`", "attr.tabindex": "-1", "class": "notification.cssClass()" } }, providers: [
17965
+ configureNotificationGlassPane(),
17966
+ ], viewQueries: [{ propertyName: "notificationSlotBounds", first: true, predicate: ["slot_bounds"], descendants: true, read: ElementRef, isSignal: true }], hostDirectives: [{ directive: GlassPaneDirective }], ngImport: i0, template: "<!-- Title -->\n@if (notification.title(); as title) {\n <header class=\"e2e-title\">{{(title | wbText)()}}</header>\n}\n\n<!-- Message -->\n<div class=\"slot e2e-slot\" [class.text]=\"!!notification.slot.text?.length\">\n @if (notification.slot.text?.length) {\n {{(notification.slot.text | wbText)()}}\n } @else if (notification.slot.component) {\n <sci-viewport class=\"e2e-notification-slot\">\n <ng-container *ngComponentOutlet=\"notification.slot.component; inputs: notification.inputs | wbRemoveLegacyInput;\"/>\n </sci-viewport>\n\n <!-- Extra DIV to capture bounds available to slotted content, excluding viewport content padding. May differ from the actual content size if content overflows or does not fill the slot. -->\n <div class=\"slot-bounds e2e-notification-slot-bounds\" #slot_bounds></div>\n }\n</div>\n\n<button (click)=\"onClose()\"\n [title]=\"('%workbench.close.tooltip' | wbText)()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n</button>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:.75em;background-color:var(--sci-color-background-elevation);color:var(--sci-color-text);font-size:.9em;border:1px solid var(--sci-color-border);border-radius:var(--sci-corner);box-shadow:var(--sci-elevation) var(--sci-static-color-black);padding:1em 0;overflow:hidden;outline:none;position:relative}:host:before{content:\"\";position:absolute;top:0;left:0;bottom:0;width:var(--sci-workbench-notification-severity-indicator-size)}:host[data-severity=info]:before{background-color:var(--sci-color-accent)}:host[data-severity=warn]:before{background-color:var(--sci-color-notice)}:host[data-severity=error]:before{background-color:var(--sci-color-negative)}:host>header{flex:none;font-weight:700;padding:0 var(--sci-workbench-notification-padding);word-break:break-word;white-space:pre-line}:host>div.slot{flex:auto;overflow:hidden;display:grid}:host>div.slot.text{word-break:break-word;white-space:pre-line;padding:0 var(--sci-workbench-notification-padding)}:host>div.slot>sci-viewport{anchor-name:var(--\\275slot-anchor)}:host>div.slot>sci-viewport::part(content){padding-inline:var(--sci-workbench-notification-padding)}:host>div.slot>div.slot-bounds{position:absolute;position-anchor:var(--\\275slot-anchor);inset:anchor(top) anchor(right) anchor(bottom) anchor(left);margin-inline:var(--sci-workbench-notification-padding);visibility:hidden}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;top:.275em;right:.275em;padding:.125em;border-radius:var(--sci-corner-small);font-size:1rem}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "wb-icon", inputs: ["icon"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: SciViewportComponent, selector: "sci-viewport", inputs: ["scrollbarStyle"], outputs: ["scroll"] }, { kind: "pipe", type: TextPipe, name: "wbText" }, { kind: "pipe", type: RemoveLegacyInputPipe, name: "wbRemoveLegacyInput" }] });
17967
+ }
17968
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationComponent, decorators: [{
17969
+ type: Component,
17970
+ args: [{ selector: 'wb-notification', imports: [
17971
+ TextPipe,
17972
+ IconComponent,
17973
+ NgComponentOutlet,
17974
+ RemoveLegacyInputPipe,
17975
+ SciViewportComponent,
17976
+ ], hostDirectives: [
17977
+ GlassPaneDirective,
17978
+ ], providers: [
17979
+ configureNotificationGlassPane(),
17980
+ ], host: {
17981
+ '[attr.data-notificationid]': 'notification.id',
17982
+ '[attr.data-severity]': 'notification.severity()',
17983
+ '[style.min-height]': 'notification.size.minHeight()',
17984
+ '[style.height]': 'notification.size.height()',
17985
+ '[style.max-height]': 'notification.size.maxHeight()',
17986
+ '[style.--ɵslot-anchor]': '`--${slotAnchorName}`',
17987
+ '[attr.tabindex]': '-1',
17988
+ '[class]': 'notification.cssClass()',
17989
+ '(mouseenter)': 'hover.set(true)',
17990
+ '(mouseleave)': 'hover.set(false)',
17991
+ '(auxclick)': 'onAuxClick($event)',
17992
+ '(keydown.escape)': 'onEscape($event)',
17993
+ }, template: "<!-- Title -->\n@if (notification.title(); as title) {\n <header class=\"e2e-title\">{{(title | wbText)()}}</header>\n}\n\n<!-- Message -->\n<div class=\"slot e2e-slot\" [class.text]=\"!!notification.slot.text?.length\">\n @if (notification.slot.text?.length) {\n {{(notification.slot.text | wbText)()}}\n } @else if (notification.slot.component) {\n <sci-viewport class=\"e2e-notification-slot\">\n <ng-container *ngComponentOutlet=\"notification.slot.component; inputs: notification.inputs | wbRemoveLegacyInput;\"/>\n </sci-viewport>\n\n <!-- Extra DIV to capture bounds available to slotted content, excluding viewport content padding. May differ from the actual content size if content overflows or does not fill the slot. -->\n <div class=\"slot-bounds e2e-notification-slot-bounds\" #slot_bounds></div>\n }\n</div>\n\n<button (click)=\"onClose()\"\n [title]=\"('%workbench.close.tooltip' | wbText)()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n</button>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:.75em;background-color:var(--sci-color-background-elevation);color:var(--sci-color-text);font-size:.9em;border:1px solid var(--sci-color-border);border-radius:var(--sci-corner);box-shadow:var(--sci-elevation) var(--sci-static-color-black);padding:1em 0;overflow:hidden;outline:none;position:relative}:host:before{content:\"\";position:absolute;top:0;left:0;bottom:0;width:var(--sci-workbench-notification-severity-indicator-size)}:host[data-severity=info]:before{background-color:var(--sci-color-accent)}:host[data-severity=warn]:before{background-color:var(--sci-color-notice)}:host[data-severity=error]:before{background-color:var(--sci-color-negative)}:host>header{flex:none;font-weight:700;padding:0 var(--sci-workbench-notification-padding);word-break:break-word;white-space:pre-line}:host>div.slot{flex:auto;overflow:hidden;display:grid}:host>div.slot.text{word-break:break-word;white-space:pre-line;padding:0 var(--sci-workbench-notification-padding)}:host>div.slot>sci-viewport{anchor-name:var(--\\275slot-anchor)}:host>div.slot>sci-viewport::part(content){padding-inline:var(--sci-workbench-notification-padding)}:host>div.slot>div.slot-bounds{position:absolute;position-anchor:var(--\\275slot-anchor);inset:anchor(top) anchor(right) anchor(bottom) anchor(left);margin-inline:var(--sci-workbench-notification-padding);visibility:hidden}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;top:.275em;right:.275em;padding:.125em;border-radius:var(--sci-corner-small);font-size:1rem}\n"] }]
17994
+ }], ctorParameters: () => [], propDecorators: { notificationSlotBounds: [{ type: i0.ViewChild, args: ['slot_bounds', { ...{ read: (ElementRef) }, isSignal: true }] }] } });
17995
+ /**
17996
+ * Blocks this notification when dialog(s) overlay it.
17997
+ */
17998
+ function configureNotificationGlassPane() {
17999
+ return [
18000
+ {
18001
+ provide: GLASS_PANE_BLOCKABLE,
18002
+ useFactory: () => inject(ɵWorkbenchNotification),
18003
+ },
18004
+ {
18005
+ provide: GLASS_PANE_OPTIONS,
18006
+ useFactory: () => ({ attributes: { 'data-notificationid': inject(ɵWorkbenchNotification).id } }),
18007
+ },
18008
+ ];
18009
+ }
18010
+
18011
+ /*
18012
+ * Copyright (c) 2018-2026 Swiss Federal Railways
18013
+ *
18014
+ * This program and the accompanying materials are made
18015
+ * available under the terms of the Eclipse Public License 2.0
18016
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
18017
+ *
18018
+ * SPDX-License-Identifier: EPL-2.0
18019
+ */
18020
+ /**
18021
+ * DI token to register providers available for DI if in the context of a workbench notification.
18022
+ */
18023
+ const WORKBENCH_NOTIFICATION_CONTEXT = new InjectionToken('WORKBENCH_NOTIFICATION_CONTEXT');
18024
+ /**
18025
+ * Provides providers available for DI if in the context of a workbench notification.
18026
+ */
18027
+ function provideWorkbenchNotificationContext() {
18028
+ return {
18029
+ provide: WORKBENCH_NOTIFICATION_CONTEXT,
18030
+ useFactory: () => [
18031
+ provideWorkbenchDialogService(),
18032
+ provideWorkbenchMessageBoxService(),
18033
+ provideWorkbenchPopupService(),
18034
+ ],
18035
+ multi: true,
18036
+ };
17755
18037
  }
17756
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationRegistry, decorators: [{
17757
- type: Injectable,
17758
- args: [{ providedIn: 'root' }]
17759
- }], ctorParameters: () => [] });
17760
18038
 
17761
18039
  /** @inheritDoc */
17762
18040
  class ɵWorkbenchNotification {
17763
18041
  id;
17764
18042
  _options;
17765
18043
  /** Injector for the notification; destroyed when the notification is closed. */
17766
- _injector = inject(Injector);
18044
+ injector = inject(Injector);
17767
18045
  slot;
17768
18046
  _notificationRegistry = inject(WorkbenchNotificationRegistry);
18047
+ _focusMonitor = inject(WorkbenchFocusMonitor);
17769
18048
  _title;
17770
18049
  _severity;
17771
18050
  _duration;
17772
18051
  _cssClass;
18052
+ portal;
18053
+ size = new ɵWorkbenchNotificationSize();
18054
+ focused = computed(() => this._focusMonitor.activeElement()?.id === this.id, { ...(ngDevMode ? { debugName: "focused" } : {}) });
18055
+ /** Checks if this notification is the most recently displayed notification. */
18056
+ top = computed(() => this._notificationRegistry.top() === this, { ...(ngDevMode ? { debugName: "top" } : {}) });
17773
18057
  destroyed = signal(false, { ...(ngDevMode ? { debugName: "destroyed" } : {}) });
18058
+ bounds;
18059
+ blockedBy;
17774
18060
  group;
17775
18061
  constructor(id, content, _options) {
17776
18062
  this.id = id;
17777
18063
  this._options = _options;
18064
+ this.portal = this.createPortal();
17778
18065
  this.slot = {
17779
- injector: this.createInjector(),
17780
18066
  component: typeof content === 'function' ? content : undefined,
17781
18067
  text: typeof content === 'string' ? content : undefined,
17782
18068
  };
@@ -17785,24 +18071,25 @@ class ɵWorkbenchNotification {
17785
18071
  this._duration = signal(this._options.duration ?? 'medium', { ...(ngDevMode ? { debugName: "_duration" } : {}) });
17786
18072
  this._cssClass = signal(Arrays.coerce(this._options.cssClass), { ...(ngDevMode ? { debugName: "_cssClass" } : {}) });
17787
18073
  this.group = this._options.group;
17788
- this.closeOnEscape();
18074
+ this.blockedBy = inject(WorkbenchDialogRegistry).top(this.id);
18075
+ this.bounds = boundingClientRect(computed(() => this.portal.componentRef()?.instance.notificationSlotBounds()));
17789
18076
  inject(DestroyRef).onDestroy(() => this.destroyed.set(true));
17790
18077
  }
17791
18078
  /**
17792
- * Creates an injector to render content in the notification's injection context.
18079
+ * Creates a portal to render {@link WorkbenchNotificationComponent} in the notification's injection context.
17793
18080
  */
17794
- createInjector() {
17795
- const injector = Injector.create({
17796
- parent: this._options.injector ?? inject(Injector),
18081
+ createPortal() {
18082
+ return new WbComponentPortal(WorkbenchNotificationComponent, {
18083
+ injector: this._options.injector,
17797
18084
  providers: [
17798
18085
  { provide: ɵWorkbenchNotification, useValue: this },
17799
18086
  { provide: WorkbenchNotification, useExisting: ɵWorkbenchNotification },
17800
18087
  { provide: Notification, useClass: ɵNotification },
18088
+ { provide: WORKBENCH_ELEMENT, useExisting: ɵWorkbenchNotification },
18089
+ inject(WORKBENCH_NOTIFICATION_CONTEXT, { optional: true }) ?? [],
17801
18090
  ...this._options.providers ?? [],
17802
18091
  ],
17803
18092
  });
17804
- inject(DestroyRef).onDestroy(() => injector.destroy());
17805
- return injector;
17806
18093
  }
17807
18094
  /** @inheritDoc */
17808
18095
  get title() {
@@ -17838,6 +18125,9 @@ class ɵWorkbenchNotification {
17838
18125
  }
17839
18126
  /** @inheritDoc */
17840
18127
  close() {
18128
+ if (this.blockedBy()) {
18129
+ return;
18130
+ }
17841
18131
  this.destroy();
17842
18132
  }
17843
18133
  /**
@@ -17846,33 +18136,175 @@ class ɵWorkbenchNotification {
17846
18136
  get inputs() {
17847
18137
  return this._options.inputs;
17848
18138
  }
17849
- /**
17850
- * Closes the notification on escape keystroke, but only if this is the topmost notification.
17851
- */
17852
- closeOnEscape() {
17853
- const zone = inject(NgZone);
17854
- const document = inject(DOCUMENT);
17855
- const top = inject(WorkbenchNotificationRegistry).top;
17856
- effect(onCleanup => {
17857
- if (top() !== this) {
17858
- return;
17859
- }
17860
- const subscription = fromEvent(document, 'keydown')
17861
- .pipe(subscribeIn(fn => zone.runOutsideAngular(fn)), filter((event) => event.key === 'Escape'), observeIn(fn => zone.run(fn)))
17862
- .subscribe(() => this.close());
17863
- onCleanup(() => subscription.unsubscribe());
17864
- });
17865
- }
17866
18139
  /**
17867
18140
  * Destroys this dialog and associated resources.
17868
18141
  */
17869
18142
  destroy() {
17870
18143
  if (!this.destroyed()) {
17871
- this._injector.destroy();
18144
+ this.injector.destroy();
17872
18145
  this._notificationRegistry.unregister(this.id);
17873
18146
  }
17874
18147
  }
17875
18148
  }
18149
+ /** @inheritDoc */
18150
+ class ɵWorkbenchNotificationSize {
18151
+ _height = signal(undefined, { ...(ngDevMode ? { debugName: "_height" } : {}) });
18152
+ _minHeight = signal(undefined, { ...(ngDevMode ? { debugName: "_minHeight" } : {}) });
18153
+ _maxHeight = signal(undefined, { ...(ngDevMode ? { debugName: "_maxHeight" } : {}) });
18154
+ /** @inheritDoc */
18155
+ get height() {
18156
+ return this._height;
18157
+ }
18158
+ /** @inheritDoc */
18159
+ set height(height) {
18160
+ untracked(() => this._height.set(height));
18161
+ }
18162
+ /** @inheritDoc */
18163
+ get minHeight() {
18164
+ return this._minHeight;
18165
+ }
18166
+ /** @inheritDoc */
18167
+ set minHeight(minHeight) {
18168
+ untracked(() => this._minHeight.set(minHeight));
18169
+ }
18170
+ /** @inheritDoc */
18171
+ get maxHeight() {
18172
+ return this._maxHeight;
18173
+ }
18174
+ /** @inheritDoc */
18175
+ set maxHeight(maxHeight) {
18176
+ untracked(() => this._maxHeight.set(maxHeight));
18177
+ }
18178
+ }
18179
+
18180
+ /*
18181
+ * Copyright (c) 2018-2026 Swiss Federal Railways
18182
+ *
18183
+ * This program and the accompanying materials are made
18184
+ * available under the terms of the Eclipse Public License 2.0
18185
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
18186
+ *
18187
+ * SPDX-License-Identifier: EPL-2.0
18188
+ */
18189
+ /**
18190
+ * Displays the microfrontend of a given {@link WorkbenchNotficationCapability}.
18191
+ *
18192
+ * This component is designed to be displayed in a workbench notification.
18193
+ */
18194
+ class MicrofrontendNotificationComponent {
18195
+ capability = input.required({ ...(ngDevMode ? { debugName: "capability" } : {}) });
18196
+ params = input.required({ ...(ngDevMode ? { debugName: "params" } : {}) });
18197
+ referrer = input.required({ ...(ngDevMode ? { debugName: "referrer" } : {}) });
18198
+ _host = inject(ElementRef).nativeElement;
18199
+ _outletRouter = inject(OutletRouter);
18200
+ _messageClient = inject(MessageClient);
18201
+ _logger = inject(Logger);
18202
+ _routerOutletElement = viewChild.required('router_outlet');
18203
+ /** Splash to display until the microfrontend signals readiness. */
18204
+ splash = inject(MicrofrontendPlatformConfig).splash ?? MicrofrontendSplashComponent;
18205
+ workbenchLayoutService = inject(WorkbenchLayoutService);
18206
+ notification = inject(ɵWorkbenchNotification);
18207
+ focusWithin = signal(false, { ...(ngDevMode ? { debugName: "focusWithin" } : {}) });
18208
+ constructor() {
18209
+ this._logger.debug(() => 'Constructing MicrofrontendNotificationComponent.', LoggerNames.MICROFRONTEND);
18210
+ this.installNavigator();
18211
+ this.installNotificationCloseListener();
18212
+ this.installNotificationFocusedPublisher();
18213
+ this.setNotificationProperties();
18214
+ this.propagateNotificationContext();
18215
+ this.propagateWorkbenchTheme();
18216
+ inject(DestroyRef).onDestroy(() => {
18217
+ // Clear the outlet.
18218
+ void this._outletRouter.navigate(null, { outlet: this.notification.id });
18219
+ // Delete retained messages to free resources.
18220
+ void this._messageClient.publish(_WorkbenchCommands.notificationFocusedTopic(this.notification.id), undefined, { retain: true });
18221
+ });
18222
+ }
18223
+ installNavigator() {
18224
+ const manifestService = inject(ManifestService);
18225
+ const injector = inject(Injector);
18226
+ effect(() => {
18227
+ const capability = this.capability();
18228
+ const params = this.params();
18229
+ void untracked(async () => {
18230
+ const application = manifestService.getApplication(capability.metadata.appSymbolicName);
18231
+ this._logger.debug(() => `Loading microfrontend into workbench notification [app=${capability.metadata.appSymbolicName}, baseUrl=${application.baseUrl}, path=${capability.properties.path}].`, LoggerNames.MICROFRONTEND, params, capability);
18232
+ // Wait for the context to be set on the router outlet, as @scion/workbench-client expects it to be available on startup.
18233
+ await Microfrontends.waitForContext(this._routerOutletElement, _NOTIFICATION_CONTEXT, { injector });
18234
+ void this._outletRouter.navigate(capability.properties.path, {
18235
+ outlet: this.notification.id,
18236
+ relativeTo: application.baseUrl,
18237
+ params: params,
18238
+ pushStateToSessionHistoryStack: false,
18239
+ showSplash: capability.properties.showSplash,
18240
+ });
18241
+ });
18242
+ });
18243
+ }
18244
+ installNotificationCloseListener() {
18245
+ this._messageClient.observe$(_WorkbenchCommands.notificationCloseTopic(this.notification.id))
18246
+ .pipe(takeUntilDestroyed())
18247
+ .subscribe(() => {
18248
+ this.notification.close();
18249
+ });
18250
+ }
18251
+ installNotificationFocusedPublisher() {
18252
+ effect(() => {
18253
+ const focused = this.notification.focused();
18254
+ untracked(() => {
18255
+ const commandTopic = _WorkbenchCommands.notificationFocusedTopic(this.notification.id);
18256
+ void this._messageClient.publish(commandTopic, focused, { retain: true });
18257
+ });
18258
+ });
18259
+ }
18260
+ setNotificationProperties() {
18261
+ effect(() => {
18262
+ const properties = this.capability().properties;
18263
+ untracked(() => {
18264
+ this.notification.size.height = properties.size?.height;
18265
+ this.notification.size.minHeight = properties.size?.minHeight;
18266
+ this.notification.size.maxHeight = properties.size?.maxHeight;
18267
+ });
18268
+ });
18269
+ }
18270
+ onFocusWithin(event) {
18271
+ const { detail: focusWithin } = event;
18272
+ this.focusWithin.set(focusWithin);
18273
+ if (focusWithin) {
18274
+ this._host.dispatchEvent(new CustomEvent('sci-microfrontend-focusin', { bubbles: true }));
18275
+ }
18276
+ }
18277
+ /**
18278
+ * Provides the notification context to embedded content.
18279
+ */
18280
+ propagateNotificationContext() {
18281
+ effect(() => {
18282
+ const context = {
18283
+ notificationId: this.notification.id,
18284
+ capability: this.capability(),
18285
+ params: this.params(),
18286
+ referrer: {
18287
+ appSymbolicName: this.referrer(),
18288
+ },
18289
+ };
18290
+ const routerOutletElement = this._routerOutletElement().nativeElement;
18291
+ untracked(() => routerOutletElement.setContextValue(_NOTIFICATION_CONTEXT, context));
18292
+ });
18293
+ }
18294
+ propagateWorkbenchTheme() {
18295
+ Microfrontends.propagateTheme(this._routerOutletElement);
18296
+ }
18297
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18298
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.1", type: MicrofrontendNotificationComponent, isStandalone: true, selector: "wb-microfrontend-notification", inputs: { capability: { classPropertyName: "capability", publicName: "capability", isSignal: true, isRequired: true, transformFunction: null }, params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: true, transformFunction: null }, referrer: { classPropertyName: "referrer", publicName: "referrer", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.workbench-drag": "workbenchLayoutService.dragging()" } }, viewQueries: [{ propertyName: "_routerOutletElement", first: true, predicate: ["router_outlet"], descendants: true, isSignal: true }], ngImport: i0, template: "<sci-router-outlet #router_outlet\n [name]=\"notification.id\"\n [attr.data-capabilityid]=\"capability().metadata!.id\"\n [attr.data-app]=\"capability().metadata!.appSymbolicName\"\n [attr.data-focus]=\"focusWithin() ? '' : null\"\n keystrokes=\"keydown.escape\"\n [class]=\"notification.cssClass()\"\n (focuswithin)=\"onFocusWithin($event)\">\n <ng-container *ngComponentOutlet=\"splash\"/>\n</sci-router-outlet>\n", styles: [":host{display:grid}:host.workbench-drag>sci-router-outlet{pointer-events:none}:host>sci-router-outlet::part(splash){display:grid}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }] });
18299
+ }
18300
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationComponent, decorators: [{
18301
+ type: Component,
18302
+ args: [{ selector: 'wb-microfrontend-notification', imports: [
18303
+ NgComponentOutlet,
18304
+ ], schemas: [CUSTOM_ELEMENTS_SCHEMA], host: {
18305
+ '[class.workbench-drag]': 'workbenchLayoutService.dragging()',
18306
+ }, template: "<sci-router-outlet #router_outlet\n [name]=\"notification.id\"\n [attr.data-capabilityid]=\"capability().metadata!.id\"\n [attr.data-app]=\"capability().metadata!.appSymbolicName\"\n [attr.data-focus]=\"focusWithin() ? '' : null\"\n keystrokes=\"keydown.escape\"\n [class]=\"notification.cssClass()\"\n (focuswithin)=\"onFocusWithin($event)\">\n <ng-container *ngComponentOutlet=\"splash\"/>\n</sci-router-outlet>\n", styles: [":host{display:grid}:host.workbench-drag>sci-router-outlet{pointer-events:none}:host>sci-router-outlet::part(splash){display:grid}\n"] }]
18307
+ }], ctorParameters: () => [], propDecorators: { capability: [{ type: i0.Input, args: [{ isSignal: true, alias: "capability", required: true }] }], params: [{ type: i0.Input, args: [{ isSignal: true, alias: "params", required: true }] }], referrer: [{ type: i0.Input, args: [{ isSignal: true, alias: "referrer", required: true }] }], _routerOutletElement: [{ type: i0.ViewChild, args: ['router_outlet', { isSignal: true }] }] } });
17876
18308
 
17877
18309
  /*
17878
18310
  * Copyright (c) 2018-2025 Swiss Federal Railways
@@ -17887,6 +18319,7 @@ class ɵWorkbenchNotification {
17887
18319
  class ɵWorkbenchNotificationService {
17888
18320
  _rootInjector = inject(ApplicationRef).injector;
17889
18321
  _notificationRegistry = inject(WorkbenchNotificationRegistry);
18322
+ _mutex = new SingleTaskExecutor();
17890
18323
  _zone = inject(NgZone);
17891
18324
  /** @inheritDoc */
17892
18325
  show(message, options) {
@@ -17896,17 +18329,20 @@ class ɵWorkbenchNotificationService {
17896
18329
  this._zone.run(() => this.show(message, options));
17897
18330
  return;
17898
18331
  }
17899
- // Replace previous notification of the same group, or add it otherwise.
17900
- const previousNotification = options?.group ? this._notificationRegistry.elements().find(element => element.group === options.group) : undefined;
17901
- if (previousNotification) {
17902
- const reducedInputs = options?.groupInputReduceFn ? options.groupInputReduceFn(previousNotification.inputs ?? {}, options.inputs ?? {}) : options?.inputs;
17903
- const notification = this.createNotification(message, { ...options, inputs: reducedInputs });
17904
- this._notificationRegistry.replace(previousNotification.id, { key: notification.id, element: notification });
17905
- }
17906
- else {
17907
- const notification = this.createNotification(message, options);
17908
- this._notificationRegistry.register(notification.id, notification);
17909
- }
18332
+ // Prevent race conditions with asynchronous group reducer, ensuring to not have a "stale" previous notification.
18333
+ void this._mutex.submit(async () => {
18334
+ const previousNotification = options?.group ? this._notificationRegistry.elements().find(element => element.group === options.group) : undefined;
18335
+ // Replace previous notification of the same group, or add it otherwise.
18336
+ if (previousNotification) {
18337
+ const reducedInputs = options?.groupInputReduceFn ? await options.groupInputReduceFn(previousNotification.inputs ?? {}, options.inputs ?? {}) : options?.inputs;
18338
+ const notification = this.createNotification(message, { ...options, inputs: reducedInputs });
18339
+ this._notificationRegistry.replace(previousNotification.id, { key: notification.id, element: notification });
18340
+ }
18341
+ else {
18342
+ const notification = this.createNotification(message, options);
18343
+ this._notificationRegistry.register(notification.id, notification);
18344
+ }
18345
+ });
17910
18346
  }
17911
18347
  /**
17912
18348
  * Creates the notification handle.
@@ -17941,8 +18377,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
17941
18377
  /**
17942
18378
  * Shows a notification.
17943
18379
  *
17944
- * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered.
17945
- * It informs about system events, task completion or errors. The severity indicates importance or urgency.
18380
+ * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered or focused.
18381
+ * It informs about system events, task completion, or errors. Severity indicates importance or urgency.
17946
18382
  *
17947
18383
  * Notifications can be grouped. Only the most recent notification within a group is displayed.
17948
18384
  *
@@ -17958,7 +18394,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
17958
18394
  }] });
17959
18395
 
17960
18396
  /*
17961
- * Copyright (c) 2018-2022 Swiss Federal Railways
18397
+ * Copyright (c) 2018-2026 Swiss Federal Railways
18398
+ *
18399
+ * This program and the accompanying materials are made
18400
+ * available under the terms of the Eclipse Public License 2.0
18401
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
18402
+ *
18403
+ * SPDX-License-Identifier: EPL-2.0
18404
+ */
18405
+ /** @inheritDoc */
18406
+ class MicrofrontendHostNotification {
18407
+ _notification;
18408
+ capability;
18409
+ params;
18410
+ referrer;
18411
+ constructor(_notification, capability, params, referrer) {
18412
+ this._notification = _notification;
18413
+ this.capability = signal(capability).asReadonly();
18414
+ this.params = signal(params).asReadonly();
18415
+ this.referrer = signal(referrer).asReadonly();
18416
+ this.setNotificationProperties();
18417
+ }
18418
+ setNotificationProperties() {
18419
+ const properties = this.capability().properties;
18420
+ this._notification.size.height = properties.size?.height;
18421
+ this._notification.size.minHeight = properties.size?.minHeight;
18422
+ this._notification.size.maxHeight = properties.size?.maxHeight;
18423
+ }
18424
+ }
18425
+
18426
+ /*
18427
+ * Copyright (c) 2018-2026 Swiss Federal Railways
17962
18428
  *
17963
18429
  * This program and the accompanying materials are made
17964
18430
  * available under the terms of the Eclipse Public License 2.0
@@ -17967,77 +18433,196 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
17967
18433
  * SPDX-License-Identifier: EPL-2.0
17968
18434
  */
17969
18435
  /**
17970
- * Installs an intent handler to show text notifications.
18436
+ * Handles notification intents, opening a notification based on resolved capability.
18437
+ *
18438
+ * Microfrontends of the host are displayed in {@link MicrofrontendHostComponent}, microfrontends of other applications in {@link MicrofrontendNotificationComponent}.
18439
+ *
18440
+ * Notification intents are handled in this interceptor and are not transported to the providing application to support applications not connected to the SCION Workbench.
17971
18441
  */
17972
- function installNotificationIntentHandler() {
17973
- const intentClient = inject(IntentClient);
17974
- const notificationService = inject(WorkbenchNotificationService);
17975
- const logger = inject(Logger);
17976
- intentClient.onIntent({ type: WorkbenchCapabilities.Notification, qualifier: {} }, request => {
17977
- const command = request.body;
17978
- const referrer = request.headers.get(MessageHeaders.AppSymbolicName);
17979
- const isLegacyApi = !request.intent.params?.has(eNOTIFICATION_MESSAGE_PARAM);
17980
- const message = (isLegacyApi ? command.content : request.intent.params?.get(eNOTIFICATION_MESSAGE_PARAM)) ?? '';
17981
- logger.debug(() => 'Showing notification', LoggerNames.MICROFRONTEND, command);
17982
- notificationService.show(createRemoteTranslatable(message, { appSymbolicName: referrer }), prune({
18442
+ class MicrofrontendNotificationIntentHandler {
18443
+ _notificationService = inject(WorkbenchNotificationService);
18444
+ _logger = inject(Logger);
18445
+ /**
18446
+ * Notification intents are handled in this interceptor and then swallowed.
18447
+ */
18448
+ async intercept(intentMessage, next) {
18449
+ if (intentMessage.intent.type === WorkbenchCapabilities.Notification) {
18450
+ const replyTo = intentMessage.headers.get(MessageHeaders.ReplyTo);
18451
+ await this.showNotification(intentMessage);
18452
+ void Beans.get(MessageClient).publish(replyTo, undefined, { headers: new Map().set(MessageHeaders.Status, ResponseStatusCodes.TERMINAL) });
18453
+ // Do not continue the handler chain to swallow the intent.
18454
+ }
18455
+ else {
18456
+ return next.handle(intentMessage);
18457
+ }
18458
+ }
18459
+ /**
18460
+ * Displays the microfrontend declared by the resolved capability in a notification.
18461
+ */
18462
+ async showNotification(intentMessage) {
18463
+ const command = intentMessage.body;
18464
+ const capability = intentMessage.capability;
18465
+ const capabilityId = capability.metadata.id;
18466
+ const groupParamsReducer = capability.properties.groupParamsReducer;
18467
+ const params = intentMessage.intent.params ?? new Map();
18468
+ const referrer = intentMessage.headers.get(MessageHeaders.AppSymbolicName);
18469
+ const isHostProvider = Microfrontends.isHostProvider(capability);
18470
+ const legacyTextNotification = isLegacyTextNotification(capability, intentMessage);
18471
+ if (legacyTextNotification) {
18472
+ params.set(eNOTIFICATION_MESSAGE_PARAM, command.content);
18473
+ }
18474
+ this._logger.debug(() => 'Handling microfrontend notification intent', LoggerNames.MICROFRONTEND, command);
18475
+ this._notificationService.show(isHostProvider ? MicrofrontendHostComponent : MicrofrontendNotificationComponent, prune({
18476
+ inputs: isHostProvider ? { params } : { capability, params, referrer },
18477
+ providers: isHostProvider ? [provideActivatedMicrofrontend$3(capability, params, referrer)] : undefined,
17983
18478
  title: createRemoteTranslatable(command.title, { appSymbolicName: referrer }),
17984
18479
  severity: command.severity,
17985
- duration: isLegacyApi && typeof command.duration === 'number' ? command.duration * 1000 : command.duration,
17986
- group: command.group,
17987
- cssClass: command.cssClass,
18480
+ duration: legacyTextNotification && typeof command.duration === 'number' ? command.duration * 1000 : command.duration,
18481
+ group: command.group ? `${command.group}_${capabilityId}_${referrer}` : undefined, // Use distinct group name for different referrers and capabilities not to overwrite each other
18482
+ groupInputReduceFn: groupParamsReducer ? createParamsReducerFn(groupParamsReducer) : undefined,
18483
+ cssClass: Arrays.coerce(capability.properties.cssClass).concat(Arrays.coerce(command.cssClass)),
17988
18484
  }));
17989
- });
18485
+ }
18486
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationIntentHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18487
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationIntentHandler });
18488
+ }
18489
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationIntentHandler, decorators: [{
18490
+ type: Injectable
18491
+ }] });
18492
+ /**
18493
+ * Returns a function to reduce inputs by sending a request to passed topic.
18494
+ */
18495
+ function createParamsReducerFn(reducerTopic) {
18496
+ return async (prevInput, currInput) => {
18497
+ const currParams = Object.fromEntries(currInput['params']);
18498
+ const prevParams = Object.fromEntries(prevInput['params']);
18499
+ const reducedParams = await firstValueFrom(Beans.get(MessageClient).request$(reducerTopic, { prevParams, currParams }, { retain: true }));
18500
+ return { ...currInput, params: new Map(Object.entries(reducedParams.body ?? currParams)) };
18501
+ };
18502
+ }
18503
+ /**
18504
+ * Indicates whether the notification is opened by a legacy client.
18505
+ *
18506
+ * TODO [Angular 22] Remove with Angular 22. Used for backward compatiblity.
18507
+ */
18508
+ function isLegacyTextNotification(capability, intentMessage) {
18509
+ const isTextMessage = capability.properties[TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY] === TEXT_NOTIFICATION_CAPABILITY_IDENTITY;
18510
+ return isTextMessage && !intentMessage.intent.params?.has(eNOTIFICATION_MESSAGE_PARAM);
17990
18511
  }
17991
18512
  /**
17992
- * Provides a set of DI providers installing an intent handler to show text notifications.
18513
+ * Provides {@link ActivatedMicrofrontend} for injection in the host microfrontend.
18514
+ */
18515
+ function provideActivatedMicrofrontend$3(capability, params, referrer) {
18516
+ return {
18517
+ provide: ActivatedMicrofrontend,
18518
+ useFactory: () => {
18519
+ const notification = inject(ɵWorkbenchNotification);
18520
+ const reducedParams = notification.inputs?.['params'];
18521
+ // Create in notification's injection context to bind 'MicrofrontendNotification' to the notification's lifecycle.
18522
+ return runInInjectionContext(notification.injector, () => new MicrofrontendHostNotification(notification, capability, reducedParams ?? params, referrer));
18523
+ },
18524
+ };
18525
+ }
18526
+
18527
+ /*
18528
+ * Copyright (c) 2018-2026 Swiss Federal Railways
18529
+ *
18530
+ * This program and the accompanying materials are made
18531
+ * available under the terms of the Eclipse Public License 2.0
18532
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
17993
18533
  *
17994
- * The notification capability is registered in {@link WorkbenchHostManifestInterceptor}.
18534
+ * SPDX-License-Identifier: EPL-2.0
18535
+ */
18536
+ /**
18537
+ * Provides the route for the built-in {@link WorkbenchNotificationCapability}.
17995
18538
  */
17996
- function provideNotificationIntentHandler() {
18539
+ function provideMicrofrontendTextNotificationRoute() {
17997
18540
  return makeEnvironmentProviders([
17998
- provideMicrofrontendPlatformInitializer(() => installNotificationIntentHandler()),
18541
+ {
18542
+ provide: WORKBENCH_ROUTE,
18543
+ useFactory: () => ({
18544
+ path: '',
18545
+ loadComponent: () => Promise.resolve().then(function () { return notificationTextMessage_component; }),
18546
+ canMatch: [canMatchWorkbenchNotificationCapability({})],
18547
+ }),
18548
+ multi: true,
18549
+ },
17999
18550
  ]);
18000
18551
  }
18001
18552
 
18002
18553
  /**
18003
- * Intercepts the host manifest, registering the built-in text notification capability.
18554
+ * Asserts notification capabilities to have required properties.
18004
18555
  */
18005
- class MicrofrontendTextNotificationCapabilityProvider {
18006
- intercept(hostManifest) {
18007
- hostManifest.capabilities = [
18008
- ...hostManifest.capabilities ?? [],
18009
- provideBuiltInTextNotificationCapability(),
18010
- ];
18556
+ class MicrofrontendNotificationCapabilityValidator {
18557
+ async intercept(capability) {
18558
+ if (capability.type !== WorkbenchCapabilities.Notification) {
18559
+ return capability;
18560
+ }
18561
+ const notificationCapability = capability;
18562
+ // Assert capability other than the built-in text notification capability to have a qualifier.
18563
+ if (!isBuiltinNotificationCapability(notificationCapability) && !Object.keys(notificationCapability.qualifier ?? {}).length) {
18564
+ throw Error(`[NotificationDefinitionError] Notification capability requires a qualifier [app=${app$3(notificationCapability)}, notification=${qualifier$3(notificationCapability)}]`);
18565
+ }
18566
+ // Assert capability to have properties.
18567
+ if (!notificationCapability.properties) {
18568
+ throw Error(`[NotificationDefinitionError] Notification capability requires properties [app=${app$3(notificationCapability)}, notification=${qualifier$3(notificationCapability)}]`);
18569
+ }
18570
+ // Assert the notification capability to have a path, unless provided by the host application.
18571
+ this.assertPath(notificationCapability);
18572
+ // Assert the notification capability to have a height, unless provided by the host application.
18573
+ this.assertSize(notificationCapability);
18574
+ // Assert host notification capabilities not to define the "showSplash" property.
18575
+ if (Microfrontends.isHostProvider(capability) && notificationCapability.properties.showSplash !== undefined) {
18576
+ throw Error(`[NotificationDefinitionError] Property "showSplash" not supported for notification capabilities of the host application [app=${app$3(notificationCapability)}, notification=${qualifier$3(notificationCapability)}]`);
18577
+ }
18578
+ return capability;
18011
18579
  }
18012
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18013
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider });
18580
+ assertPath(capability) {
18581
+ const path = capability.properties?.path;
18582
+ if (Microfrontends.isHostProvider(capability)) {
18583
+ if (path !== '') {
18584
+ throw Error(`[NotificationDefinitionError] Notification capabilities of the host application require an empty path. [app=${app$3(capability)}, notification=${qualifier$3(capability)}]. Change the path '${path}' to empty and add 'canMatchWorkbenchNotificationCapability(${JSON.stringify(capability.qualifier)})' guard to the route.\n\nExample:\nCapability: { type: 'notification', qualifier: ${JSON.stringify(capability.qualifier)}, properties: {path: ''} }\nRoute: { path: '', canMatch: [canMatchWorkbenchNotificationCapability(${JSON.stringify(capability.qualifier)})], component: NotificationComponent }`);
18585
+ }
18586
+ }
18587
+ else {
18588
+ if (path === null || path == undefined) {
18589
+ throw Error(`[NotificationDefinitionError] Notification capabilities require a path. [app=${app$3(capability)}, notification=${qualifier$3(capability)}]`);
18590
+ }
18591
+ }
18592
+ }
18593
+ assertSize(capability) {
18594
+ if (Microfrontends.isHostProvider(capability)) {
18595
+ return;
18596
+ }
18597
+ const size = capability.properties?.size;
18598
+ if (!size?.height) {
18599
+ throw Error(`[NotificationDefinitionError] Notification capability requires the 'size' property with a height [app=${app$3(capability)}, notification=${qualifier$3(capability)}]`);
18600
+ }
18601
+ }
18602
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationCapabilityValidator, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18603
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationCapabilityValidator });
18014
18604
  }
18015
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendTextNotificationCapabilityProvider, decorators: [{
18605
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MicrofrontendNotificationCapabilityValidator, decorators: [{
18016
18606
  type: Injectable
18017
18607
  }] });
18608
+ function isBuiltinNotificationCapability(capability) {
18609
+ return capability.properties?.[TEXT_NOTIFICATION_CAPABILITY_IDENTITY_PROPERTY] === TEXT_NOTIFICATION_CAPABILITY_IDENTITY;
18610
+ }
18018
18611
  /**
18019
- * Provides the built-in notification capability to display text.
18020
- *
18021
- * @see MicrofrontendNotificationIntentHandler
18612
+ * Returns the qualifier as string.
18022
18613
  */
18023
- function provideBuiltInTextNotificationCapability() {
18024
- return {
18025
- type: WorkbenchCapabilities.Notification,
18026
- qualifier: {},
18027
- params: [
18028
- {
18029
- name: eNOTIFICATION_MESSAGE_PARAM,
18030
- required: false,
18031
- description: 'Text to display in the notification.',
18032
- },
18033
- ],
18034
- private: false,
18035
- description: 'Displays a text notification.',
18036
- };
18614
+ function qualifier$3(capability) {
18615
+ return Objects.toMatrixNotation(capability.qualifier);
18616
+ }
18617
+ /**
18618
+ * Returns the app symbolic name.
18619
+ */
18620
+ function app$3(capability) {
18621
+ return capability.metadata.appSymbolicName;
18037
18622
  }
18038
18623
 
18039
18624
  /*
18040
- * Copyright (c) 2018-2025 Swiss Federal Railways
18625
+ * Copyright (c) 2018-2026 Swiss Federal Railways
18041
18626
  *
18042
18627
  * This program and the accompanying materials are made
18043
18628
  * available under the terms of the Eclipse Public License 2.0
@@ -18052,13 +18637,34 @@ function provideBuiltInTextNotificationCapability() {
18052
18637
  */
18053
18638
  function provideMicrofrontendNotification() {
18054
18639
  return makeEnvironmentProviders([
18640
+ MicrofrontendNotificationCapabilityValidator,
18055
18641
  MicrofrontendTextNotificationCapabilityProvider,
18056
- provideNotificationIntentHandler(),
18642
+ MicrofrontendNotificationIntentHandler,
18643
+ provideWorkbenchNotificationContext(),
18644
+ provideMicrofrontendTextNotificationRoute(),
18057
18645
  provideMicrofrontendPlatformInitializer(onPreStartup, { phase: MicrofrontendPlatformStartupPhase.PreStartup }),
18058
18646
  ]);
18059
18647
  function onPreStartup() {
18060
18648
  // Register built-in text notification capability in the host manifest.
18061
18649
  Beans.register(HostManifestInterceptor, { useValue: inject(MicrofrontendTextNotificationCapabilityProvider), multi: true });
18650
+ // Register notification capability validator.
18651
+ Beans.register(CapabilityInterceptor, { useValue: inject(MicrofrontendNotificationCapabilityValidator), multi: true });
18652
+ // Register notification intent handler.
18653
+ Beans.register(IntentInterceptor, { useValue: inject(MicrofrontendNotificationIntentHandler), multi: true });
18654
+ }
18655
+ /**
18656
+ * Provides beans of @scion/workbench-client available for DI if in the context of a workbench notification.
18657
+ */
18658
+ function provideWorkbenchNotificationContext() {
18659
+ return {
18660
+ provide: WORKBENCH_NOTIFICATION_CONTEXT,
18661
+ useFactory: () => [
18662
+ { provide: WorkbenchDialogService$1, useFactory: () => new _WorkbenchDialogService(inject(WorkbenchNotification).id) },
18663
+ { provide: WorkbenchMessageBoxService$1, useFactory: () => new _WorkbenchMessageBoxService(inject(WorkbenchNotification).id) },
18664
+ { provide: WorkbenchPopupService$1, useFactory: () => new _WorkbenchPopupService(inject(WorkbenchNotification).id) },
18665
+ ],
18666
+ multi: true,
18667
+ };
18062
18668
  }
18063
18669
  }
18064
18670
 
@@ -18707,15 +19313,19 @@ class TextMessageComponent {
18707
19313
  const { params, referrer } = inject(ActivatedMicrofrontend);
18708
19314
  const translatable = params().get(eMESSAGE_BOX_MESSAGE_PARAM);
18709
19315
  this.message = createRemoteTranslatable(translatable, { appSymbolicName: referrer() });
19316
+ // Limit the maximum messagebox width to break the message.
19317
+ inject(WorkbenchDialog).size.maxWidth = 'var(--sci-workbench-messagebox-max-width)';
18710
19318
  }
18711
19319
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: TextMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18712
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: TextMessageComponent, isStandalone: true, selector: "wb-text-message", ngImport: i0, template: "@if (message) {\n {{(message | wbText)()}}\n}\n", styles: [":host{overflow-wrap:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"], dependencies: [{ kind: "pipe", type: TextPipe, name: "wbText" }] });
19320
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.1", type: TextMessageComponent, isStandalone: true, selector: "wb-text-message", host: { properties: { "class.empty": "!message?.length" } }, ngImport: i0, template: "{{(message | wbText)()}}\n", styles: [":host{word-break:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}:host.empty{display:none}\n"], dependencies: [{ kind: "pipe", type: TextPipe, name: "wbText" }] });
18713
19321
  }
18714
19322
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: TextMessageComponent, decorators: [{
18715
19323
  type: Component,
18716
19324
  args: [{ selector: 'wb-text-message', imports: [
18717
19325
  TextPipe,
18718
- ], template: "@if (message) {\n {{(message | wbText)()}}\n}\n", styles: [":host{overflow-wrap:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}\n"] }]
19326
+ ], host: {
19327
+ '[class.empty]': '!message?.length',
19328
+ }, template: "{{(message | wbText)()}}\n", styles: [":host{word-break:break-word;white-space:pre-line;text-align:var(--sci-workbench-messagebox-text-align)}:host.empty{display:none}\n"] }]
18719
19329
  }], ctorParameters: () => [] });
18720
19330
  /**
18721
19331
  * Property to identify the built-in text message box capability.
@@ -19902,6 +20512,7 @@ function provideWorkbench(config) {
19902
20512
  provideWorkbenchViewContext(),
19903
20513
  provideWorkbenchDialogContext(),
19904
20514
  provideWorkbenchPopupContext(),
20515
+ provideWorkbenchNotificationContext(),
19905
20516
  provideWorkbenchMicrofrontendSupport(config),
19906
20517
  ]);
19907
20518
  }
@@ -19996,114 +20607,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
19996
20607
  }] });
19997
20608
 
19998
20609
  /*
19999
- * Copyright (c) 2018-2025 Swiss Federal Railways
20000
- *
20001
- * This program and the accompanying materials are made
20002
- * available under the terms of the Eclipse Public License 2.0
20003
- * which is available at https://www.eclipse.org/legal/epl-2.0/
20004
- *
20005
- * SPDX-License-Identifier: EPL-2.0
20006
- */
20007
- /**
20008
- * TODO [Angular 22] Remove with Angular 22. Used for backward compatiblity.
20009
- */
20010
- class RemoveLegacyInputPipe {
20011
- transform(inputs) {
20012
- const inputsCopy = { ...inputs ?? {} };
20013
- delete inputsCopy[LEGACY_NOTIFICATION_INPUT]; // eslint-disable-line @typescript-eslint/no-dynamic-delete
20014
- return inputsCopy;
20015
- }
20016
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: RemoveLegacyInputPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
20017
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.1", ngImport: i0, type: RemoveLegacyInputPipe, isStandalone: true, name: "wbRemoveLegacyInput" });
20018
- }
20019
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: RemoveLegacyInputPipe, decorators: [{
20020
- type: Pipe,
20021
- args: [{ name: 'wbRemoveLegacyInput' }]
20022
- }] });
20023
-
20024
- /*
20025
- * Copyright (c) 2018-2025 Swiss Federal Railways
20026
- *
20027
- * This program and the accompanying materials are made
20028
- * available under the terms of the Eclipse Public License 2.0
20029
- * which is available at https://www.eclipse.org/legal/epl-2.0/
20030
- *
20031
- * SPDX-License-Identifier: EPL-2.0
20032
- */
20033
- /**
20034
- * Renders the content of a workbench notification.
20035
- */
20036
- class WorkbenchNotificationComponent {
20037
- notification = input.required({ ...(ngDevMode ? { debugName: "notification" } : {}) });
20038
- hover = signal(false, { ...(ngDevMode ? { debugName: "hover" } : {}) });
20039
- constructor() {
20040
- this.installAutoCloseTimer();
20041
- }
20042
- onMousedown(event) {
20043
- if (event.buttons === AUXILARY_MOUSE_BUTTON) {
20044
- this.notification().close();
20045
- }
20046
- }
20047
- onClose() {
20048
- this.notification().close();
20049
- }
20050
- /**
20051
- * Installs a timer to close the notification.
20052
- */
20053
- installAutoCloseTimer() {
20054
- effect(onCleanup => {
20055
- const duration = this.notification().duration();
20056
- const hover = this.hover();
20057
- if (hover) {
20058
- return;
20059
- }
20060
- untracked(() => {
20061
- const subscription = fromDuration$(duration).subscribe(() => this.notification().close());
20062
- onCleanup(() => subscription.unsubscribe());
20063
- });
20064
- });
20065
- function fromDuration$(duration) {
20066
- switch (duration) {
20067
- case 'short':
20068
- return timer(7000);
20069
- case 'medium':
20070
- return timer(15000);
20071
- case 'long':
20072
- return timer(30000);
20073
- default:
20074
- if (typeof duration === 'number') {
20075
- return timer(duration);
20076
- }
20077
- return NEVER;
20078
- }
20079
- }
20080
- }
20081
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20082
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: WorkbenchNotificationComponent, isStandalone: true, selector: "wb-notification", inputs: { notification: { classPropertyName: "notification", publicName: "notification", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "mouseenter": "hover.set(true)", "mouseleave": "hover.set(false)", "mousedown": "onMousedown($event)" }, properties: { "attr.data-notificationid": "notification().id", "attr.data-severity": "notification().severity()", "class": "notification().cssClass()" } }, ngImport: i0, template: "<div class=\"outline\">\n <!-- Title -->\n @if (notification().title(); as title) {\n <header class=\"e2e-title\">{{(title | wbText)()}}</header>\n }\n\n <!-- Message -->\n @if (notification().slot.text) {\n <span class=\"e2e-message\">{{(notification().slot.text | wbText)()}}</span>\n } @else {\n <ng-container *ngComponentOutlet=\"notification().slot.component!; inputs: notification().inputs | wbRemoveLegacyInput; injector: notification().slot.injector\"/>\n }\n</div>\n<button (click)=\"onClose()\"\n [title]=\"('%workbench.close.tooltip' | wbText)()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n</button>\n", styles: [":host{display:grid;background-color:var(--sci-color-background-elevation);color:var(--sci-color-text);font-size:1rem;border-radius:var(--sci-corner);box-shadow:var(--sci-elevation) var(--sci-static-color-black);border-left:var(--sci-workbench-notification-severity-indicator-size) solid transparent;position:relative}:host[data-severity=info]{border-left-color:var(--sci-color-accent)}:host[data-severity=warn]{border-left-color:var(--sci-color-notice)}:host[data-severity=error]{border-left-color:var(--sci-color-negative)}:host>div.outline{border:1px solid var(--sci-color-border);border-left:none;padding:1em 1.5em;border-top-right-radius:var(--sci-corner);border-bottom-right-radius:var(--sci-corner);font-size:.9em;white-space:pre-line;overflow-wrap:break-word;overflow:hidden}:host>div.outline>header{font-weight:700;margin-bottom:.75em}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;top:.275em;right:.275em;padding:.125em;border-radius:var(--sci-corner-small)}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "wb-icon", inputs: ["icon"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "pipe", type: TextPipe, name: "wbText" }, { kind: "pipe", type: RemoveLegacyInputPipe, name: "wbRemoveLegacyInput" }] });
20083
- }
20084
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchNotificationComponent, decorators: [{
20085
- type: Component,
20086
- args: [{ selector: 'wb-notification', imports: [
20087
- TextPipe,
20088
- IconComponent,
20089
- NgComponentOutlet,
20090
- RemoveLegacyInputPipe,
20091
- ], host: {
20092
- '[attr.data-notificationid]': 'notification().id',
20093
- '[attr.data-severity]': 'notification().severity()',
20094
- '(mouseenter)': 'hover.set(true)',
20095
- '(mouseleave)': 'hover.set(false)',
20096
- '(mousedown)': 'onMousedown($event)',
20097
- '[class]': 'notification().cssClass()',
20098
- }, template: "<div class=\"outline\">\n <!-- Title -->\n @if (notification().title(); as title) {\n <header class=\"e2e-title\">{{(title | wbText)()}}</header>\n }\n\n <!-- Message -->\n @if (notification().slot.text) {\n <span class=\"e2e-message\">{{(notification().slot.text | wbText)()}}</span>\n } @else {\n <ng-container *ngComponentOutlet=\"notification().slot.component!; inputs: notification().inputs | wbRemoveLegacyInput; injector: notification().slot.injector\"/>\n }\n</div>\n<button (click)=\"onClose()\"\n [title]=\"('%workbench.close.tooltip' | wbText)()\"\n class=\"close e2e-close\">\n <wb-icon icon=\"workbench.close\"/>\n</button>\n", styles: [":host{display:grid;background-color:var(--sci-color-background-elevation);color:var(--sci-color-text);font-size:1rem;border-radius:var(--sci-corner);box-shadow:var(--sci-elevation) var(--sci-static-color-black);border-left:var(--sci-workbench-notification-severity-indicator-size) solid transparent;position:relative}:host[data-severity=info]{border-left-color:var(--sci-color-accent)}:host[data-severity=warn]{border-left-color:var(--sci-color-notice)}:host[data-severity=error]{border-left-color:var(--sci-color-negative)}:host>div.outline{border:1px solid var(--sci-color-border);border-left:none;padding:1em 1.5em;border-top-right-radius:var(--sci-corner);border-bottom-right-radius:var(--sci-corner);font-size:.9em;white-space:pre-line;overflow-wrap:break-word;overflow:hidden}:host>div.outline>header{font-weight:700;margin-bottom:.75em}:host>button.close:is(button,#sci-reset){all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-corner);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-workbench-button-cursor)}:host>button.close:is(button,#sci-reset):hover:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-hover)}:host>button.close:is(button,#sci-reset):active:where(:not(:disabled)){background-color:var(--sci-workbench-button-background-color-active)}:host>button.close:is(button,#sci-reset):focus:not(:focus-visible){outline:none}:host>button.close:is(button,#sci-reset):focus-visible{outline:var(--sci-workbench-button-outline-width-focus) solid var(--sci-color-accent)}:host>button.close:is(button,#sci-reset):disabled{color:var(--sci-color-gray-500)}:host>button.close:is(button,#sci-reset){position:absolute;top:.275em;right:.275em;padding:.125em;border-radius:var(--sci-corner-small)}\n"] }]
20099
- }], ctorParameters: () => [], propDecorators: { notification: [{ type: i0.Input, args: [{ isSignal: true, alias: "notification", required: true }] }] } });
20100
- /**
20101
- * Indicates that the auxilary mouse button is pressed (usually the mouse wheel button or middle button).
20102
- */
20103
- const AUXILARY_MOUSE_BUTTON = 4;
20104
-
20105
- /*
20106
- * Copyright (c) 2018-2022 Swiss Federal Railways
20610
+ * Copyright (c) 2018-2026 Swiss Federal Railways
20107
20611
  *
20108
20612
  * This program and the accompanying materials are made
20109
20613
  * available under the terms of the Eclipse Public License 2.0
@@ -20117,13 +20621,13 @@ const AUXILARY_MOUSE_BUTTON = 4;
20117
20621
  class NotificationListComponent {
20118
20622
  notifications = inject(WorkbenchNotificationRegistry).elements;
20119
20623
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: NotificationListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20120
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: NotificationListComponent, isStandalone: true, selector: "wb-notification-list", ngImport: i0, template: "@for (notification of notifications(); track notification.group || notification.id) {\n <wb-notification [notification]=\"notification\"/>\n}\n", styles: [":host{display:flex;flex-flow:column wrap-reverse;gap:.5em;max-height:100%;align-items:flex-start;align-content:flex-start;pointer-events:none;margin:.5em}:host>wb-notification{pointer-events:auto;width:var(--sci-workbench-notification-width);max-width:100%;position:relative;animation:slide-in .3s ease-out}@keyframes slide-in{0%{opacity:0;left:100%}to{opacity:1;left:0}}\n"], dependencies: [{ kind: "component", type: WorkbenchNotificationComponent, selector: "wb-notification", inputs: ["notification"] }] });
20624
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: NotificationListComponent, isStandalone: true, selector: "wb-notification-list", ngImport: i0, template: "@for (notification of notifications(); track notification.group || notification.id) {\n <div class=\"notification\">\n <ng-container *wbPortalOutlet=\"notification.portal; destroyOnDetach: true\"/>\n </div>\n}\n", styles: [":host{display:flex;flex-flow:column wrap-reverse;gap:.5em;max-height:100%;align-items:flex-start;align-content:flex-start;pointer-events:none;margin:.5em}:host>div.notification{pointer-events:auto;width:var(--sci-workbench-notification-width);max-width:100%;position:relative;animation:slide-in .3s ease-out}@keyframes slide-in{0%{opacity:0;left:100%}to{opacity:1;left:0}}\n"], dependencies: [{ kind: "directive", type: WorkbenchPortalOutletDirective, selector: "ng-template[wbPortalOutlet]", inputs: ["wbPortalOutlet", "wbPortalOutletDestroyOnDetach"] }] });
20121
20625
  }
20122
20626
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: NotificationListComponent, decorators: [{
20123
20627
  type: Component,
20124
20628
  args: [{ selector: 'wb-notification-list', imports: [
20125
- WorkbenchNotificationComponent,
20126
- ], template: "@for (notification of notifications(); track notification.group || notification.id) {\n <wb-notification [notification]=\"notification\"/>\n}\n", styles: [":host{display:flex;flex-flow:column wrap-reverse;gap:.5em;max-height:100%;align-items:flex-start;align-content:flex-start;pointer-events:none;margin:.5em}:host>wb-notification{pointer-events:auto;width:var(--sci-workbench-notification-width);max-width:100%;position:relative;animation:slide-in .3s ease-out}@keyframes slide-in{0%{opacity:0;left:100%}to{opacity:1;left:0}}\n"] }]
20629
+ WorkbenchPortalOutletDirective,
20630
+ ], template: "@for (notification of notifications(); track notification.group || notification.id) {\n <div class=\"notification\">\n <ng-container *wbPortalOutlet=\"notification.portal; destroyOnDetach: true\"/>\n </div>\n}\n", styles: [":host{display:flex;flex-flow:column wrap-reverse;gap:.5em;max-height:100%;align-items:flex-start;align-content:flex-start;pointer-events:none;margin:.5em}:host>div.notification{pointer-events:auto;width:var(--sci-workbench-notification-width);max-width:100%;position:relative;animation:slide-in .3s ease-out}@keyframes slide-in{0%{opacity:0;left:100%}to{opacity:1;left:0}}\n"] }]
20127
20631
  }] });
20128
20632
 
20129
20633
  /*
@@ -20295,7 +20799,7 @@ class ActivityPanelComponent {
20295
20799
  sash1: `${1 / (1 - ratio)}`,
20296
20800
  sash2: `${1 / ratio}`,
20297
20801
  };
20298
- }, { equal: Objects$1.isEqual });
20802
+ }, { equal: Objects.isEqual });
20299
20803
  }
20300
20804
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ActivityPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20301
20805
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: ActivityPanelComponent, isStandalone: true, selector: "wb-activity-panel", inputs: { panel: { classPropertyName: "panel", publicName: "panel", isSignal: true, isRequired: true, transformFunction: null }, activityId1: { classPropertyName: "activityId1", publicName: "activityId1", isSignal: true, isRequired: true, transformFunction: null }, activityId2: { classPropertyName: "activityId2", publicName: "activityId2", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-panel": "panel()" } }, ngImport: i0, template: "@if (activityId1() && activityId2()) {\n <sci-sashbox [direction]=\"direction()\" (sashStart)=\"onSashStart()\" (sashEnd)=\"onSashEnd($event)\">\n <ng-template sciSash [size]=\"sashSizes().sash1\" key=\"sash1\">\n <ng-container *ngTemplateOutlet=\"activity_template; context: {$implicit: activityId1()}\"/>\n </ng-template>\n\n <ng-template sciSash [size]=\"sashSizes().sash2\" key=\"sash2\">\n <ng-container *ngTemplateOutlet=\"activity_template; context: {$implicit: activityId2()}\"/>\n </ng-template>\n </sci-sashbox>\n} @else if (activityId1()) {\n <ng-container *ngTemplateOutlet=\"activity_template; context: {$implicit: activityId1()}\"/>\n} @else if (activityId2()) {\n <ng-container *ngTemplateOutlet=\"activity_template; context: {$implicit: activityId2()}\"/>\n}\n\n<ng-template #activity_template let-activityId>\n <wb-grid [grid]=\"layout().grids[activityId]!\"\n [attr.data-grid]=\"activityId\"\n [gridDropZone]=\"{\n dropRegionSize: .15,\n dropPlaceholderSize: .15,\n dropZoneAttributes: {'data-grid': activityId},\n }\"/>\n</ng-template>\n", styles: [":host{display:grid}:host>sci-sashbox{z-index:auto}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: SciSashboxComponent, selector: "sci-sashbox", inputs: ["direction"], outputs: ["sashStart", "sashEnd"] }, { kind: "directive", type: SciSashDirective, selector: "ng-template[sciSash]", inputs: ["size", "minSize", "key", "animate"], exportAs: ["sciSash"] }, { kind: "component", type: GridComponent, selector: "wb-grid", inputs: ["grid", "gridDropZone"] }] });
@@ -20534,7 +21038,7 @@ function configureWorkbenchGlassPane() {
20534
21038
  },
20535
21039
  {
20536
21040
  provide: GLASS_PANE_OPTIONS,
20537
- useValue: { cssClass: 'e2e-workbench' },
21041
+ useFactory: () => ({ cssClass: 'e2e-workbench' }),
20538
21042
  },
20539
21043
  ];
20540
21044
  }
@@ -21074,8 +21578,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
21074
21578
  /**
21075
21579
  * Shows a notification.
21076
21580
  *
21077
- * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered.
21078
- * It informs about system events, task completion or errors. The severity indicates importance or urgency.
21581
+ * A notification is a closable message displayed in the upper-right corner that disappears after a few seconds unless hovered or focused.
21582
+ * It informs about system events, task completion, or errors. Severity indicates importance or urgency.
21079
21583
  *
21080
21584
  * Notifications can be grouped. Only the most recent notification within a group is displayed.
21081
21585
  *
@@ -21143,7 +21647,7 @@ function migrateGroupInputReduceFn(config) {
21143
21647
  */
21144
21648
 
21145
21649
  /*
21146
- * Copyright (c) 2018-2022 Swiss Federal Railways
21650
+ * Copyright (c) 2018-2026 Swiss Federal Railways
21147
21651
  *
21148
21652
  * This program and the accompanying materials are made
21149
21653
  * available under the terms of the Eclipse Public License 2.0
@@ -21205,7 +21709,7 @@ class PopupConfig {
21205
21709
  */
21206
21710
 
21207
21711
  /*
21208
- * Copyright (c) 2018-2025 Swiss Federal Railways
21712
+ * Copyright (c) 2018-2026 Swiss Federal Railways
21209
21713
  *
21210
21714
  * This program and the accompanying materials are made
21211
21715
  * available under the terms of the Eclipse Public License 2.0
@@ -21285,7 +21789,7 @@ class PopupConfig {
21285
21789
  */
21286
21790
 
21287
21791
  /*
21288
- * Copyright (c) 2018-2023 Swiss Federal Railways
21792
+ * Copyright (c) 2018-2026 Swiss Federal Railways
21289
21793
  *
21290
21794
  * This program and the accompanying materials are made
21291
21795
  * available under the terms of the Eclipse Public License 2.0
@@ -21311,5 +21815,5 @@ class PopupConfig {
21311
21815
  * Generated bundle index. Do not edit.
21312
21816
  */
21313
21817
 
21314
- export { ActivatedMicrofrontend, IconComponent, LogAppender, LogLevel, Logger, LoggerName, MAIN_AREA, MAIN_AREA_INITIAL_PART_ID, MicrofrontendPlatformConfigLoader, MicrofrontendPlatformStartupPhase, Notification, NotificationService, Popup, PopupConfig, PopupService, TextPipe, VIEW_TAB_RENDERING_CONTEXT, WORKBENCH_DIALOG_CONTEXT, WORKBENCH_ID, WORKBENCH_PART_CONTEXT, WORKBENCH_POPUP_CONTEXT, WORKBENCH_POPUP_REFERRER, WORKBENCH_VIEW_CONTEXT, WorkbenchComponent, WorkbenchConfig, WorkbenchDesktopDirective, WorkbenchDialog, WorkbenchDialogActionDirective, WorkbenchDialogFooterDirective, WorkbenchDialogHeaderDirective, WorkbenchDialogService, WorkbenchLauncher, WorkbenchLayoutFactory, WorkbenchMessageBoxService, WorkbenchNotification, WorkbenchNotificationService, WorkbenchPart, WorkbenchPartActionDirective, WorkbenchPerspectiveData, WorkbenchPopup, WorkbenchPopupService, WorkbenchRouteData, WorkbenchRouter, WorkbenchRouterLinkDirective, WorkbenchService, WorkbenchStartup, WorkbenchStartupPhase, WorkbenchStorage, WorkbenchView, WorkbenchViewMenuItemDirective, canMatchWorkbenchDialogCapability, canMatchWorkbenchMessageBoxCapability, canMatchWorkbenchOutlet, canMatchWorkbenchPart, canMatchWorkbenchPartCapability, canMatchWorkbenchPerspective, canMatchWorkbenchPopupCapability, canMatchWorkbenchView, canMatchWorkbenchViewCapability, provideMicrofrontendPlatformInitializer, provideWorkbench, provideWorkbenchInitializer, text };
21818
+ export { ActivatedMicrofrontend, IconComponent, LogAppender, LogLevel, Logger, LoggerName, MAIN_AREA, MAIN_AREA_INITIAL_PART_ID, MicrofrontendPlatformConfigLoader, MicrofrontendPlatformStartupPhase, Notification, NotificationService, Popup, PopupConfig, PopupService, TextPipe, VIEW_TAB_RENDERING_CONTEXT, WORKBENCH_DIALOG_CONTEXT, WORKBENCH_ID, WORKBENCH_PART_CONTEXT, WORKBENCH_POPUP_CONTEXT, WORKBENCH_POPUP_REFERRER, WORKBENCH_VIEW_CONTEXT, WorkbenchComponent, WorkbenchConfig, WorkbenchDesktopDirective, WorkbenchDialog, WorkbenchDialogActionDirective, WorkbenchDialogFooterDirective, WorkbenchDialogHeaderDirective, WorkbenchDialogService, WorkbenchLauncher, WorkbenchLayoutFactory, WorkbenchMessageBoxService, WorkbenchNotification, WorkbenchNotificationService, WorkbenchPart, WorkbenchPartActionDirective, WorkbenchPerspectiveData, WorkbenchPopup, WorkbenchPopupService, WorkbenchRouteData, WorkbenchRouter, WorkbenchRouterLinkDirective, WorkbenchService, WorkbenchStartup, WorkbenchStartupPhase, WorkbenchStorage, WorkbenchView, WorkbenchViewMenuItemDirective, canMatchWorkbenchDialogCapability, canMatchWorkbenchMessageBoxCapability, canMatchWorkbenchNotificationCapability, canMatchWorkbenchOutlet, canMatchWorkbenchPart, canMatchWorkbenchPartCapability, canMatchWorkbenchPerspective, canMatchWorkbenchPopupCapability, canMatchWorkbenchView, canMatchWorkbenchViewCapability, provideMicrofrontendPlatformInitializer, provideWorkbench, provideWorkbenchInitializer, text };
21315
21819
  //# sourceMappingURL=scion-workbench.mjs.map