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

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.
@@ -9917,7 +9917,7 @@ function configureDialogGlassPane() {
9917
9917
  }
9918
9918
 
9919
9919
  /*
9920
- * Copyright (c) 2018-2023 Swiss Federal Railways
9920
+ * Copyright (c) 2018-2026 Swiss Federal Railways
9921
9921
  *
9922
9922
  * This program and the accompanying materials are made
9923
9923
  * available under the terms of the Eclipse Public License 2.0
@@ -9926,96 +9926,17 @@ function configureDialogGlassPane() {
9926
9926
  * SPDX-License-Identifier: EPL-2.0
9927
9927
  */
9928
9928
  /**
9929
- * Enables the display of a component in a dialog.
9930
- *
9931
- * A dialog is a visual element for focused interaction with the user, such as prompting the user for input or confirming actions.
9932
- * The user can move and resize a dialog.
9933
- *
9934
- * Displayed on top of other content, a modal dialog blocks interaction with other parts of the application.
9935
- *
9936
- * ## Modality
9937
- * A dialog can be context-modal or application-modal. Context-modal blocks a specific part of the application, as specified by the context;
9938
- * application-modal blocks the workbench or browser viewport, based on {@link WorkbenchConfig.dialog.modalityScope}.
9939
- *
9940
- * ## Context
9941
- * A dialog can be bound to a context (e.g., part or view), defaulting to the calling context.
9942
- * The dialog is displayed only if the context is visible and closes when the context is disposed.
9943
- *
9944
- * ## Positioning
9945
- * A dialog is opened in the center of its context, if any, unless opened from the peripheral area.
9946
- *
9947
- * ## Stacking
9948
- * Dialogs are stacked per modality, with only the topmost dialog in each stack being interactive.
9949
- *
9950
- * ## Dialog Component
9951
- * The dialog component can inject the {@link WorkbenchDialog} handle to interact with the dialog, such as setting the title or closing the dialog.
9952
- * Inputs passed to the dialog are available as input properties in the dialog component.
9953
- *
9954
- * ## Dialog Header
9955
- * By default, the dialog displays the title and a close button in the header. Alternatively, the dialog supports the use of a custom header.
9956
- * To provide a custom header, add an Angular template to the HTML of the dialog component and decorate it with the `wbDialogHeader` directive.
9957
- *
9958
- * ```html
9959
- * <ng-template wbDialogHeader>
9960
- * <app-dialog-header/>
9961
- * </ng-template>
9962
- * ```
9963
- *
9964
- * ## Dialog Footer
9965
- * A dialog has a default footer that displays actions defined in the HTML of the dialog component. An action is an Angular template decorated with
9966
- * the `wbDialogAction` directive. Multiple actions are supported, rendered in modeling order, and can be left- or right-aligned.
9967
- *
9968
- * ```html
9969
- * <!-- Checkbox -->
9970
- * <ng-template wbDialogAction align="start">
9971
- * <label>
9972
- * <input type="checkbox"/>
9973
- * Do not ask me again
9974
- * </label>
9975
- * </ng-template>
9976
- *
9977
- * <!-- OK Button -->
9978
- * <ng-template wbDialogAction align="end">
9979
- * <button (click)="...">OK</button>
9980
- * </ng-template>
9981
- *
9982
- * <!-- Cancel Button -->
9983
- * <ng-template wbDialogAction align="end">
9984
- * <button (click)="...">Cancel</button>
9985
- * </ng-template>
9986
- * ```
9987
- *
9988
- * Alternatively, the dialog supports the use of a custom footer. To provide a custom footer, add an Angular template to the HTML of the dialog component and
9989
- * decorate it with the `wbDialogFooter` directive.
9990
- *
9991
- * ```html
9992
- * <ng-template wbDialogFooter>
9993
- * <app-dialog-footer/>
9994
- * </ng-template>
9995
- * ```
9996
- *
9997
- * ## Styling
9998
- * The following CSS variables can be set to customize the default look of a dialog.
9999
- *
10000
- * - `--sci-workbench-dialog-padding`
10001
- * - `--sci-workbench-dialog-header-height`
10002
- * - `--sci-workbench-dialog-header-background-color`
10003
- * - `--sci-workbench-dialog-title-font-family`
10004
- * - `--sci-workbench-dialog-title-font-weight`
10005
- * - `--sci-workbench-dialog-title-font-size`
10006
- * - `--sci-workbench-dialog-title-align`
9929
+ * Provides {@link WorkbenchDialogService} for dependency injection.
10007
9930
  */
10008
- class WorkbenchDialogService {
10009
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10010
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogService, providedIn: 'root', useExisting: ɵWorkbenchDialogService });
9931
+ function provideWorkbenchDialogService() {
9932
+ return [
9933
+ ɵWorkbenchDialogService,
9934
+ { provide: WorkbenchDialogService, useExisting: ɵWorkbenchDialogService },
9935
+ ];
10011
9936
  }
10012
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogService, decorators: [{
10013
- type: Injectable,
10014
- args: [{ providedIn: 'root', useExisting: ɵWorkbenchDialogService }]
10015
- }] });
10016
9937
 
10017
9938
  /*
10018
- * Copyright (c) 2018-2023 Swiss Federal Railways
9939
+ * Copyright (c) 2018-2022 Swiss Federal Railways
10019
9940
  *
10020
9941
  * This program and the accompanying materials are made
10021
9942
  * available under the terms of the Eclipse Public License 2.0
@@ -10023,51 +9944,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
10023
9944
  *
10024
9945
  * SPDX-License-Identifier: EPL-2.0
10025
9946
  */
10026
- class MessageBoxFooterComponent {
10027
- actions = input.required({ ...(ngDevMode ? { debugName: "actions" } : {}) });
10028
- severity = input.required({ ...(ngDevMode ? { debugName: "severity" } : {}) });
10029
- action = output();
10030
- preferredSizeChange = output();
10031
- _actionButtons = viewChildren('action_button', { ...(ngDevMode ? { debugName: "_actionButtons" } : {}) });
9947
+ /**
9948
+ * Renders the content of a workbench popup.
9949
+ */
9950
+ class WorkbenchPopupComponent {
9951
+ _host = inject(ElementRef).nativeElement;
9952
+ _cdkTrapFocus = viewChild.required('focus_trap', { read: CdkTrapFocus });
9953
+ popup = inject(ɵWorkbenchPopup);
10032
9954
  constructor() {
10033
- void this.emitPreferredSize();
10034
- }
10035
- insertionSortOrderFn = () => 0;
10036
- onAction(key) {
10037
- this.action.emit(key);
10038
- }
10039
- onArrowKey(index, direction) {
10040
- const actionButtonCount = this._actionButtons().length;
10041
- const newIndex = (direction === 'left' ? index - 1 : index + 1);
10042
- this._actionButtons()[(newIndex + actionButtonCount) % actionButtonCount].nativeElement.focus();
9955
+ trackFocus(this._host, this.popup);
9956
+ this.focusInitialElement();
10043
9957
  }
10044
- async emitPreferredSize() {
10045
- const host = inject(ElementRef).nativeElement;
10046
- host.classList.add('calculating-min-width');
10047
- try {
10048
- // Wait for the CSS class to take effect, then wait an animation frame to avoid the error: "ResizeObserver loop completed with undelivered notifications".
10049
- await firstValueFrom(fromResize$(host).pipe(observeOn(animationFrameScheduler)));
10050
- this.preferredSizeChange.emit(host.offsetWidth);
10051
- }
10052
- finally {
10053
- host.classList.remove('calculating-min-width');
10054
- }
9958
+ focusInitialElement() {
9959
+ const effectRef = effect(() => {
9960
+ // [Angular 14] The initial focus must not be requested via `cdkTrapFocusAutoCapture` as this would restore
9961
+ // focus to the previously focused element when the `FocusTrap` is destroyed. This behavior is unwanted if the
9962
+ // popup is closed by losing focus. Otherwise, the newly focused element that caused the loss of focus and thus
9963
+ // the closing of the popup would immediately become unfocused again. This behavior could only be observed when
9964
+ // the popup loses focus by clicking on an element in a microfrontend.
9965
+ void this._cdkTrapFocus().focusTrap.focusInitialElementWhenReady();
9966
+ effectRef.destroy();
9967
+ }, { ...(ngDevMode ? { debugName: "effectRef" } : {}) });
10055
9968
  }
10056
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10057
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: MessageBoxFooterComponent, isStandalone: true, selector: "wb-message-box-footer", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: true, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { action: "action", preferredSizeChange: "preferredSizeChange" }, host: { properties: { "attr.data-severity": "severity()" } }, viewQueries: [{ propertyName: "_actionButtons", predicate: ["action_button"], descendants: true, isSignal: true }], ngImport: i0, template: "@for (action of actions() | keyvalue:insertionSortOrderFn; track action.key) {\n <button #action_button\n (click)=\"onAction(action.key)\"\n (keydown.arrowLeft)=\"onArrowKey($index, 'left')\"\n (keydown.arrowRight)=\"onArrowKey($index, 'right')\"\n [attr.data-action]=\"action.key\"\n class=\"action e2e-action\">\n {{(action.value | wbText)()}}\n </button>\n\n @if (!$last) {\n <span class=\"divider\"></span>\n }\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:flex;height:3em;background-color:var(--sci-color-background-secondary);color:var(--sci-color-text)}: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.calculating-min-width{position:absolute}:host>button.action:is(button,#sci-reset){all:unset;flex:1;margin:2px;border:1px solid transparent;border-radius:var(--sci-corner-small);transition:border-color ease-in-out .15s;cursor:var(--sci-workbench-messagebox-action-cursor);-webkit-user-select:none;user-select:none;text-align:center;min-width:7.5em;padding-inline:.5em;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}:host>button.action:is(button,#sci-reset):focus,:host>button.action:is(button,#sci-reset):active{outline:none;color:var(--\\275message-box-severity-color);border-color:var(--\\275message-box-severity-color)}:host>button.action:is(button,#sci-reset):hover{background-color:var(--sci-workbench-messagebox-action-background-color-hover)}:host>span.divider{width:1px;background-color:var(--sci-color-border)}\n"], dependencies: [{ kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: TextPipe, name: "wbText" }] });
9969
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchPopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9970
+ 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: [
9971
+ configurePopupGlassPane(),
9972
+ ], 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"] }] });
10058
9973
  }
10059
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxFooterComponent, decorators: [{
9974
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchPopupComponent, decorators: [{
10060
9975
  type: Component,
10061
- args: [{ selector: 'wb-message-box-footer', imports: [
10062
- KeyValuePipe,
10063
- TextPipe,
9976
+ args: [{ selector: 'wb-popup', imports: [
9977
+ CdkTrapFocus,
9978
+ SciViewportComponent,
9979
+ NgComponentOutlet,
9980
+ ], hostDirectives: [
9981
+ GlassPaneDirective,
9982
+ ], providers: [
9983
+ configurePopupGlassPane(),
10064
9984
  ], host: {
10065
- '[attr.data-severity]': 'severity()',
10066
- }, template: "@for (action of actions() | keyvalue:insertionSortOrderFn; track action.key) {\n <button #action_button\n (click)=\"onAction(action.key)\"\n (keydown.arrowLeft)=\"onArrowKey($index, 'left')\"\n (keydown.arrowRight)=\"onArrowKey($index, 'right')\"\n [attr.data-action]=\"action.key\"\n class=\"action e2e-action\">\n {{(action.value | wbText)()}}\n </button>\n\n @if (!$last) {\n <span class=\"divider\"></span>\n }\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:flex;height:3em;background-color:var(--sci-color-background-secondary);color:var(--sci-color-text)}: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.calculating-min-width{position:absolute}:host>button.action:is(button,#sci-reset){all:unset;flex:1;margin:2px;border:1px solid transparent;border-radius:var(--sci-corner-small);transition:border-color ease-in-out .15s;cursor:var(--sci-workbench-messagebox-action-cursor);-webkit-user-select:none;user-select:none;text-align:center;min-width:7.5em;padding-inline:.5em;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}:host>button.action:is(button,#sci-reset):focus,:host>button.action:is(button,#sci-reset):active{outline:none;color:var(--\\275message-box-severity-color);border-color:var(--\\275message-box-severity-color)}:host>button.action:is(button,#sci-reset):hover{background-color:var(--sci-workbench-messagebox-action-background-color-hover)}:host>span.divider{width:1px;background-color:var(--sci-color-border)}\n"] }]
10067
- }], ctorParameters: () => [], propDecorators: { actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: true }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: true }] }], action: [{ type: i0.Output, args: ["action"] }], preferredSizeChange: [{ type: i0.Output, args: ["preferredSizeChange"] }], _actionButtons: [{ type: i0.ViewChildren, args: ['action_button', { isSignal: true }] }] } });
9985
+ '[attr.data-popupid]': 'popup.id',
9986
+ '[style.width]': 'popup.size.width()',
9987
+ '[style.min-width]': 'popup.size.minWidth()',
9988
+ '[style.max-width]': 'popup.size.maxWidth()',
9989
+ '[style.height]': 'popup.size.height()',
9990
+ '[style.min-height]': 'popup.size.minHeight()',
9991
+ '[style.max-height]': 'popup.size.maxHeight()',
9992
+ '[class]': 'popup.cssClass()',
9993
+ }, 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"] }]
9994
+ }], ctorParameters: () => [], propDecorators: { _cdkTrapFocus: [{ type: i0.ViewChild, args: ['focus_trap', { ...{ read: CdkTrapFocus }, isSignal: true }] }] } });
9995
+ /**
9996
+ * Blocks this popup when dialog(s) overlay it.
9997
+ */
9998
+ function configurePopupGlassPane() {
9999
+ return [
10000
+ {
10001
+ provide: GLASS_PANE_BLOCKABLE,
10002
+ useFactory: () => inject(ɵWorkbenchPopup),
10003
+ },
10004
+ {
10005
+ provide: GLASS_PANE_OPTIONS,
10006
+ useFactory: () => ({ attributes: { 'data-popupid': inject(ɵWorkbenchPopup).id } }),
10007
+ },
10008
+ ];
10009
+ }
10010
+
10011
+ /**
10012
+ * A popup is a visual workbench element for displaying content above other content. The popup is positioned relative
10013
+ * to an anchor based on its preferred alignment. The anchor can be an element or a coordinate.
10014
+ *
10015
+ * The popup component can inject this handle to interact with the popup.
10016
+ *
10017
+ * Popup inputs are available as input properties in the popup component.
10018
+ *
10019
+ * @see WorkbenchPopupService
10020
+ *
10021
+ * @deprecated since version 21.0.0-beta.1. Replaced by `WorkbenchPopup`. Marked for removal in version 22.
10022
+ */
10023
+ class Popup extends WorkbenchPopup {
10024
+ }
10068
10025
 
10069
10026
  /*
10070
- * Copyright (c) 2018-2023 Swiss Federal Railways
10027
+ * Copyright (c) 2018-2025 Swiss Federal Railways
10071
10028
  *
10072
10029
  * This program and the accompanying materials are made
10073
10030
  * available under the terms of the Eclipse Public License 2.0
@@ -10076,421 +10033,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
10076
10033
  * SPDX-License-Identifier: EPL-2.0
10077
10034
  */
10078
10035
  /**
10079
- * Use this directive to replace the default dialog footer that renders actions contributed via the {@link WorkbenchDialogActionDirective} directive.
10080
- *
10081
- * The host element of this modeling directive must be a <ng-template>. The footer shares the lifecycle of the host element.
10082
- *
10083
- * **Example:**
10084
- * ```html
10085
- * <ng-template wbDialogFooter>
10086
- * <app-dialog-footer/>
10087
- * </ng-template>
10088
- * ```
10036
+ * DI token to register providers available for DI if in the context of a workbench popup.
10089
10037
  */
10090
- class WorkbenchDialogFooterDirective {
10091
- /**
10092
- * Specifies if to display a visual separator between the dialog content and this footer.
10093
- * Defaults to `true`.
10094
- */
10095
- divider = input(undefined, { ...(ngDevMode ? { debugName: "divider" } : {}), transform: booleanAttribute });
10096
- template = inject(TemplateRef);
10097
- _footer;
10098
- constructor() {
10099
- const dialog = inject(ɵWorkbenchDialog);
10100
- // Defer registering footer to avoid `ExpressionChangedAfterItHasBeenCheckedError`.
10101
- asapScheduler.schedule(() => this._footer = dialog.registerFooter(this));
10102
- // Defer disposing footer to avoid `ExpressionChangedAfterItHasBeenCheckedError`.
10103
- inject(DestroyRef).onDestroy(() => asapScheduler.schedule(() => this._footer?.dispose()));
10104
- }
10105
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
10106
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.1", type: WorkbenchDialogFooterDirective, isStandalone: true, selector: "ng-template[wbDialogFooter]", inputs: { divider: { classPropertyName: "divider", publicName: "divider", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
10038
+ const WORKBENCH_POPUP_CONTEXT = new InjectionToken('WORKBENCH_POPUP_CONTEXT');
10039
+ /**
10040
+ * Provides providers available for DI if in the context of a workbench popup.
10041
+ */
10042
+ function provideWorkbenchPopupContext() {
10043
+ return {
10044
+ provide: WORKBENCH_POPUP_CONTEXT,
10045
+ useFactory: () => [
10046
+ provideWorkbenchDialogService(),
10047
+ provideWorkbenchMessageBoxService(),
10048
+ provideWorkbenchPopupService(),
10049
+ ],
10050
+ multi: true,
10051
+ };
10107
10052
  }
10108
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogFooterDirective, decorators: [{
10109
- type: Directive,
10110
- args: [{ selector: 'ng-template[wbDialogFooter]' }]
10111
- }], ctorParameters: () => [], propDecorators: { divider: [{ type: i0.Input, args: [{ isSignal: true, alias: "divider", required: false }] }] } });
10112
10053
 
10113
10054
  /*
10114
- * Copyright (c) 2018-2023 Swiss Federal Railways
10115
- *
10116
- * This program and the accompanying materials are made
10117
- * available under the terms of the Eclipse Public License 2.0
10118
- * which is available at https://www.eclipse.org/legal/epl-2.0/
10119
- *
10120
- * SPDX-License-Identifier: EPL-2.0
10121
- */
10122
- /**
10123
- * Use this directive to replace the default dialog header that displays the title and a close button.
10124
- *
10125
- * The host element of this modeling directive must be a <ng-template>. The header shares the lifecycle of the host element.
10126
- *
10127
- * **Example:**
10128
- * ```html
10129
- * <ng-template wbDialogHeader>
10130
- * <app-dialog-header/>
10131
- * </ng-template>
10132
- * ```
10133
- */
10134
- class WorkbenchDialogHeaderDirective {
10135
- /**
10136
- * Specifies if to display a visual separator between this header and the dialog content.
10137
- * Defaults to `true`.
10138
- */
10139
- divider = input(undefined, { ...(ngDevMode ? { debugName: "divider" } : {}), transform: booleanAttribute });
10140
- template = inject(TemplateRef);
10141
- _header;
10142
- constructor() {
10143
- const dialog = inject(ɵWorkbenchDialog);
10144
- // Defer registering header to avoid `ExpressionChangedAfterItHasBeenCheckedError`.
10145
- asapScheduler.schedule(() => this._header = dialog.registerHeader(this));
10146
- // Defer disposing header to avoid `ExpressionChangedAfterItHasBeenCheckedError`.
10147
- inject(DestroyRef).onDestroy(() => asapScheduler.schedule(() => this._header?.dispose()));
10148
- }
10149
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
10150
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.1", type: WorkbenchDialogHeaderDirective, isStandalone: true, selector: "ng-template[wbDialogHeader]", inputs: { divider: { classPropertyName: "divider", publicName: "divider", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
10151
- }
10152
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogHeaderDirective, decorators: [{
10153
- type: Directive,
10154
- args: [{ selector: 'ng-template[wbDialogHeader]' }]
10155
- }], ctorParameters: () => [], propDecorators: { divider: [{ type: i0.Input, args: [{ isSignal: true, alias: "divider", required: false }] }] } });
10156
-
10157
- /*
10158
- * Copyright (c) 2018-2023 Swiss Federal Railways
10159
- *
10160
- * This program and the accompanying materials are made
10161
- * available under the terms of the Eclipse Public License 2.0
10162
- * which is available at https://www.eclipse.org/legal/epl-2.0/
10163
- *
10164
- * SPDX-License-Identifier: EPL-2.0
10165
- */
10166
- class MessageBoxHeaderComponent {
10167
- title = input(undefined, { ...(ngDevMode ? { debugName: "title" } : {}) });
10168
- severity = input.required({ ...(ngDevMode ? { debugName: "severity" } : {}) });
10169
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
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" }] });
10171
- }
10172
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxHeaderComponent, decorators: [{
10173
- type: Component,
10174
- args: [{ selector: 'wb-message-box-header', imports: [
10175
- TextPipe,
10176
- ], host: {
10177
- '[attr.data-severity]': 'severity()',
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"] }]
10179
- }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: true }] }] } });
10180
-
10181
- /*
10182
- * Copyright (c) 2018-2024 Swiss Federal Railways
10183
- *
10184
- * This program and the accompanying materials are made
10185
- * available under the terms of the Eclipse Public License 2.0
10186
- * which is available at https://www.eclipse.org/legal/epl-2.0/
10187
- *
10188
- * SPDX-License-Identifier: EPL-2.0
10189
- */
10190
- /**
10191
- * Tests if the object is of the specified type.
10192
- */
10193
- class TypeofPipe {
10194
- transform(object, type) {
10195
- return typeof object === type;
10196
- }
10197
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: TypeofPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
10198
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.1", ngImport: i0, type: TypeofPipe, isStandalone: true, name: "wbTypeof" });
10199
- }
10200
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: TypeofPipe, decorators: [{
10201
- type: Pipe,
10202
- args: [{ name: 'wbTypeof' }]
10203
- }] });
10204
-
10205
- /*
10206
- * Copyright (c) 2018-2023 Swiss Federal Railways
10207
- *
10208
- * This program and the accompanying materials are made
10209
- * available under the terms of the Eclipse Public License 2.0
10210
- * which is available at https://www.eclipse.org/legal/epl-2.0/
10211
- *
10212
- * SPDX-License-Identifier: EPL-2.0
10213
- */
10214
- /**
10215
- * Renders the workbench message box.
10216
- *
10217
- * This component is designed to be opened in a workbench dialog.
10218
- */
10219
- class WorkbenchMessageBoxComponent {
10220
- message = input.required({ ...(ngDevMode ? { debugName: "message" } : {}), transform: nullIfEmptyMessage });
10221
- options = input(undefined, { ...(ngDevMode ? { debugName: "options" } : {}) });
10222
- _dialog = inject(ɵWorkbenchDialog);
10223
- empty = signal(false, { ...(ngDevMode ? { debugName: "empty" } : {}) });
10224
- constructor() {
10225
- this._dialog.closable = false;
10226
- this._dialog.resizable = false;
10227
- this._dialog.padding = false;
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
- });
10234
- }
10235
- onAction(action) {
10236
- this._dialog.close(action);
10237
- }
10238
- onEscape() {
10239
- if ('cancel' in (this.options()?.actions ?? {})) {
10240
- this._dialog.close('cancel');
10241
- }
10242
- }
10243
- onFooterPreferredSizeChange(preferredSize) {
10244
- this._dialog.size.minWidth = `${preferredSize}px`;
10245
- }
10246
- onContentDimensionChange(dimension) {
10247
- this.empty.set(!dimension.offsetHeight);
10248
- }
10249
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
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" }] });
10251
- }
10252
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxComponent, decorators: [{
10253
- type: Component,
10254
- args: [{ selector: 'wb-message-box', imports: [
10255
- NgComponentOutlet,
10256
- SciDimensionDirective,
10257
- WorkbenchDialogHeaderDirective,
10258
- WorkbenchDialogFooterDirective,
10259
- MessageBoxHeaderComponent,
10260
- MessageBoxFooterComponent,
10261
- TypeofPipe,
10262
- TextPipe,
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 }] }] } });
10272
- function nullIfEmptyMessage(message) {
10273
- return message !== '' ? message : null;
10274
- }
10275
-
10276
- /**
10277
- * Provides a standardized dialog for presenting a message to the user, such as an info, warning or alert,
10278
- * or for prompting the user for confirmation. The message can be plain text or a component, allowing for
10279
- * structured content or input prompts.
10280
- *
10281
- * Displayed on top of other content, a modal message box blocks interaction with other parts of the application.
10282
- *
10283
- * ## Modality
10284
- * A message box can be context-modal or application-modal. Context-modal blocks a specific part of the application, as specified by the context;
10285
- * application-modal blocks the workbench or browser viewport, based on {@link WorkbenchConfig.dialog.modalityScope}.
10286
- *
10287
- * ## Context
10288
- * A message box can be bound to a context (e.g., part or view), defaulting to the calling context.
10289
- * The message box is displayed only if the context is visible and closes when the context is disposed.
10290
- *
10291
- * ## Positioning
10292
- * A message box is opened in the center of its context, if any, unless opened from the peripheral area.
10293
- *
10294
- * ## Stacking
10295
- * Message boxes are stacked per modality, with only the topmost message box in each stack being interactive.
10296
- *
10297
- * ## Styling
10298
- * The following CSS variables can be set to customize the default look of a message box.
10299
- *
10300
- * - `--sci-workbench-messagebox-max-width`
10301
- * - `--sci-workbench-messagebox-severity-indicator-size`
10302
- * - `--sci-workbench-messagebox-padding`
10303
- * - `--sci-workbench-messagebox-text-align`
10304
- * - `--sci-workbench-messagebox-title-align`
10305
- * - `--sci-workbench-messagebox-title-font-family`
10306
- * - `--sci-workbench-messagebox-title-font-weight`
10307
- * - `--sci-workbench-messagebox-title-font-size`
10308
- */
10309
- class WorkbenchMessageBoxService {
10310
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10311
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxService, providedIn: 'root', useExisting: ɵWorkbenchMessageBoxService });
10312
- }
10313
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxService, decorators: [{
10314
- type: Injectable,
10315
- args: [{ providedIn: 'root', useExisting: ɵWorkbenchMessageBoxService }]
10316
- }] });
10317
-
10318
- /*
10319
- * Copyright (c) 2018-2023 Swiss Federal Railways
10320
- *
10321
- * This program and the accompanying materials are made
10322
- * available under the terms of the Eclipse Public License 2.0
10323
- * which is available at https://www.eclipse.org/legal/epl-2.0/
10324
- *
10325
- * SPDX-License-Identifier: EPL-2.0
10326
- */
10327
- /** @inheritDoc */
10328
- class ɵWorkbenchMessageBoxService {
10329
- _workbenchDialogService = inject(WorkbenchDialogService);
10330
- _zone = inject(NgZone);
10331
- constructor() {
10332
- this.installServiceLifecycleLogger();
10333
- }
10334
- /**
10335
- * @inheritDoc
10336
- */
10337
- async open(message, options) {
10338
- assertNotInReactiveContext(this.open, 'Call WorkbenchMessageBoxService.open() in a non-reactive (non-tracking) context, such as within the untracked() function.');
10339
- // Ensure to run in Angular zone to display the message box even if called from outside the Angular zone, e.g. from an error handler.
10340
- if (!NgZone.isInAngularZone()) {
10341
- return this._zone.run(() => this.open(message, options));
10342
- }
10343
- return (await this._workbenchDialogService.open(WorkbenchMessageBoxComponent, {
10344
- inputs: { message, options },
10345
- modality: options?.modality,
10346
- injector: options?.injector,
10347
- providers: options?.providers,
10348
- cssClass: options?.cssClass,
10349
- context: options?.context,
10350
- animate: true,
10351
- }));
10352
- }
10353
- installServiceLifecycleLogger() {
10354
- const logger = inject(Logger);
10355
- const workbenchElement = inject(WORKBENCH_ELEMENT, { optional: true });
10356
- logger.debug(() => `Constructing WorkbenchMessageBoxService [context=${workbenchElement?.id}]`, LoggerNames.LIFECYCLE);
10357
- inject(DestroyRef).onDestroy(() => logger.debug(() => `Destroying WorkbenchMessageBoxService [context=${workbenchElement?.id}]'`, LoggerNames.LIFECYCLE));
10358
- }
10359
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchMessageBoxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10360
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchMessageBoxService, providedIn: 'root' });
10361
- }
10362
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchMessageBoxService, decorators: [{
10363
- type: Injectable,
10364
- args: [{ providedIn: 'root' }]
10365
- }], ctorParameters: () => [] });
10366
- /**
10367
- * Provides {@link WorkbenchDialogService} for dependency injection.
10368
- */
10369
- function provideWorkbenchMessageBoxService() {
10370
- return [
10371
- ɵWorkbenchMessageBoxService,
10372
- { provide: WorkbenchMessageBoxService, useExisting: ɵWorkbenchMessageBoxService },
10373
- ];
10374
- }
10375
-
10376
- /*
10377
- * Copyright (c) 2018-2022 Swiss Federal Railways
10378
- *
10379
- * This program and the accompanying materials are made
10380
- * available under the terms of the Eclipse Public License 2.0
10381
- * which is available at https://www.eclipse.org/legal/epl-2.0/
10382
- *
10383
- * SPDX-License-Identifier: EPL-2.0
10384
- */
10385
- /**
10386
- * Renders the content of a workbench popup.
10387
- */
10388
- class WorkbenchPopupComponent {
10389
- _host = inject(ElementRef).nativeElement;
10390
- _cdkTrapFocus = viewChild.required('focus_trap', { read: CdkTrapFocus });
10391
- popup = inject(ɵWorkbenchPopup);
10392
- constructor() {
10393
- trackFocus(this._host, this.popup);
10394
- this.focusInitialElement();
10395
- }
10396
- focusInitialElement() {
10397
- const effectRef = effect(() => {
10398
- // [Angular 14] The initial focus must not be requested via `cdkTrapFocusAutoCapture` as this would restore
10399
- // focus to the previously focused element when the `FocusTrap` is destroyed. This behavior is unwanted if the
10400
- // popup is closed by losing focus. Otherwise, the newly focused element that caused the loss of focus and thus
10401
- // the closing of the popup would immediately become unfocused again. This behavior could only be observed when
10402
- // the popup loses focus by clicking on an element in a microfrontend.
10403
- void this._cdkTrapFocus().focusTrap.focusInitialElementWhenReady();
10404
- effectRef.destroy();
10405
- }, { ...(ngDevMode ? { debugName: "effectRef" } : {}) });
10406
- }
10407
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchPopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
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: [
10409
- configurePopupGlassPane(),
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"] }] });
10411
- }
10412
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchPopupComponent, decorators: [{
10413
- type: Component,
10414
- args: [{ selector: 'wb-popup', imports: [
10415
- CdkTrapFocus,
10416
- SciViewportComponent,
10417
- NgComponentOutlet,
10418
- ], hostDirectives: [
10419
- GlassPaneDirective,
10420
- ], providers: [
10421
- configurePopupGlassPane(),
10422
- ], host: {
10423
- '[attr.data-popupid]': 'popup.id',
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()',
10430
- '[class]': 'popup.cssClass()',
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"] }]
10432
- }], ctorParameters: () => [], propDecorators: { _cdkTrapFocus: [{ type: i0.ViewChild, args: ['focus_trap', { ...{ read: CdkTrapFocus }, isSignal: true }] }] } });
10433
- /**
10434
- * Blocks this popup when dialog(s) overlay it.
10435
- */
10436
- function configurePopupGlassPane() {
10437
- return [
10438
- {
10439
- provide: GLASS_PANE_BLOCKABLE,
10440
- useFactory: () => inject(ɵWorkbenchPopup),
10441
- },
10442
- {
10443
- provide: GLASS_PANE_OPTIONS,
10444
- useFactory: () => ({ attributes: { 'data-popupid': inject(ɵWorkbenchPopup).id } }),
10445
- },
10446
- ];
10447
- }
10448
-
10449
- /**
10450
- * A popup is a visual workbench element for displaying content above other content. The popup is positioned relative
10451
- * to an anchor based on its preferred alignment. The anchor can be an element or a coordinate.
10452
- *
10453
- * The popup component can inject this handle to interact with the popup.
10454
- *
10455
- * Popup inputs are available as input properties in the popup component.
10456
- *
10457
- * @see WorkbenchPopupService
10458
- *
10459
- * @deprecated since version 21.0.0-beta.1. Replaced by `WorkbenchPopup`. Marked for removal in version 22.
10460
- */
10461
- class Popup extends WorkbenchPopup {
10462
- }
10463
-
10464
- /*
10465
- * Copyright (c) 2018-2025 Swiss Federal Railways
10466
- *
10467
- * This program and the accompanying materials are made
10468
- * available under the terms of the Eclipse Public License 2.0
10469
- * which is available at https://www.eclipse.org/legal/epl-2.0/
10470
- *
10471
- * SPDX-License-Identifier: EPL-2.0
10472
- */
10473
- /**
10474
- * DI token to register providers available for DI if in the context of a workbench popup.
10475
- */
10476
- const WORKBENCH_POPUP_CONTEXT = new InjectionToken('WORKBENCH_POPUP_CONTEXT');
10477
- /**
10478
- * Provides providers available for DI if in the context of a workbench popup.
10479
- */
10480
- function provideWorkbenchPopupContext() {
10481
- return {
10482
- provide: WORKBENCH_POPUP_CONTEXT,
10483
- useFactory: () => [
10484
- provideWorkbenchDialogService(),
10485
- provideWorkbenchMessageBoxService(),
10486
- provideWorkbenchPopupService(),
10487
- ],
10488
- multi: true,
10489
- };
10490
- }
10491
-
10492
- /*
10493
- * Copyright (c) 2018-2025 Swiss Federal Railways
10055
+ * Copyright (c) 2018-2025 Swiss Federal Railways
10494
10056
  *
10495
10057
  * This program and the accompanying materials are made
10496
10058
  * available under the terms of the Eclipse Public License 2.0
@@ -11024,6 +10586,65 @@ function createInvocationContext(elementId, options) {
11024
10586
  return null;
11025
10587
  }
11026
10588
 
10589
+ /*
10590
+ * Copyright (c) 2018-2022 Swiss Federal Railways
10591
+ *
10592
+ * This program and the accompanying materials are made
10593
+ * available under the terms of the Eclipse Public License 2.0
10594
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
10595
+ *
10596
+ * SPDX-License-Identifier: EPL-2.0
10597
+ */
10598
+ /** @inheritDoc */
10599
+ class ɵWorkbenchPopupService {
10600
+ _injector = inject(Injector);
10601
+ _rootInjector = inject(ApplicationRef).injector;
10602
+ _popupRegistry = inject(WorkbenchPopupRegistry);
10603
+ _zone = inject(NgZone);
10604
+ /** @inheritDoc */
10605
+ async open(component, options) {
10606
+ assertNotInReactiveContext(this.open, 'Call WorkbenchPopupService.open() in a non-reactive (non-tracking) context, such as within the untracked() function.');
10607
+ // Ensure to run in Angular zone to display the popup even when called from outside the Angular zone.
10608
+ if (!NgZone.isInAngularZone()) {
10609
+ return this._zone.run(() => this.open(component, options));
10610
+ }
10611
+ const popup = this.createPopup(component, options);
10612
+ this._popupRegistry.register(popup.id, popup);
10613
+ try {
10614
+ return await popup.waitForClose();
10615
+ }
10616
+ finally {
10617
+ this._popupRegistry.unregister(popup.id);
10618
+ }
10619
+ }
10620
+ /**
10621
+ * Creates the popup handle.
10622
+ */
10623
+ createPopup(component, options) {
10624
+ // Construct the handle in an injection context that shares the popup's lifecycle, allowing for automatic cleanup of effects and RxJS interop functions.
10625
+ const popupId = options.id ?? computePopupId();
10626
+ const popupInjector = Injector.create({
10627
+ parent: this._rootInjector, // use root injector to be independent of service construction context
10628
+ providers: [],
10629
+ name: `Workbench Popup ${popupId}`,
10630
+ });
10631
+ const invocationContext = createPopupInvocationContext(options, this._injector);
10632
+ return runInInjectionContext(popupInjector, () => new ɵWorkbenchPopup(popupId, component, invocationContext, options));
10633
+ }
10634
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchPopupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10635
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchPopupService, providedIn: 'root' });
10636
+ }
10637
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchPopupService, decorators: [{
10638
+ type: Injectable,
10639
+ args: [{ providedIn: 'root' }]
10640
+ }] });
10641
+ /**
10642
+ * Computes the popup's invocation context based on passsed options and injection context.
10643
+ */
10644
+ function createPopupInvocationContext(options, injector) {
10645
+ return createInvocationContext(options.context && (typeof options.context === 'object' ? options.context.viewId : options.context), { injector });
10646
+ }
10647
+
11027
10648
  /*
11028
10649
  * Copyright (c) 2018-2022 Swiss Federal Railways
11029
10650
  *
@@ -11080,103 +10701,54 @@ class ɵPopupService {
11080
10701
  { provide: LEGACY_POPUP_INPUT, useValue: config.input },
11081
10702
  ],
11082
10703
  });
11083
- }
11084
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵPopupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11085
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵPopupService, providedIn: 'root' });
11086
- }
11087
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵPopupService, decorators: [{
11088
- type: Injectable,
11089
- args: [{ providedIn: 'root' }]
11090
- }] });
11091
-
11092
- /*
11093
- * Copyright (c) 2018-2022 Swiss Federal Railways
11094
- *
11095
- * This program and the accompanying materials are made
11096
- * available under the terms of the Eclipse Public License 2.0
11097
- * which is available at https://www.eclipse.org/legal/epl-2.0/
11098
- *
11099
- * SPDX-License-Identifier: EPL-2.0
11100
- */
11101
- /**
11102
- * Enables the display of a component in a popup.
11103
- *
11104
- * A popup is a visual workbench element for displaying content above other content. It is positioned relative to an anchor,
11105
- * which can be an element or a coordinate. The popup moves with the anchor. By default, the popup closes on focus loss or
11106
- * when pressing the escape key.
11107
- *
11108
- * A popup can be bound to a context (e.g., part or view), displaying the popup only if the context is visible and closing
11109
- * it when the context is disposed. Defaults to the calling context.
11110
- *
11111
- * @deprecated since version 21.0.0-beta.1. Use `WorkbenchPopupService` to open popups. Marked for removal in version 22.
11112
- */
11113
- class PopupService {
11114
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: PopupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11115
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: PopupService, providedIn: 'root', useExisting: ɵPopupService });
11116
- }
11117
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: PopupService, decorators: [{
11118
- type: Injectable,
11119
- args: [{ providedIn: 'root', useExisting: ɵPopupService }]
11120
- }] });
11121
-
11122
- /*
11123
- * Copyright (c) 2018-2022 Swiss Federal Railways
11124
- *
11125
- * This program and the accompanying materials are made
11126
- * available under the terms of the Eclipse Public License 2.0
11127
- * which is available at https://www.eclipse.org/legal/epl-2.0/
11128
- *
11129
- * SPDX-License-Identifier: EPL-2.0
11130
- */
11131
- /** @inheritDoc */
11132
- class ɵWorkbenchPopupService {
11133
- _injector = inject(Injector);
11134
- _rootInjector = inject(ApplicationRef).injector;
11135
- _popupRegistry = inject(WorkbenchPopupRegistry);
11136
- _zone = inject(NgZone);
11137
- /** @inheritDoc */
11138
- async open(component, options) {
11139
- assertNotInReactiveContext(this.open, 'Call WorkbenchPopupService.open() in a non-reactive (non-tracking) context, such as within the untracked() function.');
11140
- // Ensure to run in Angular zone to display the popup even when called from outside the Angular zone.
11141
- if (!NgZone.isInAngularZone()) {
11142
- return this._zone.run(() => this.open(component, options));
11143
- }
11144
- const popup = this.createPopup(component, options);
11145
- this._popupRegistry.register(popup.id, popup);
11146
- try {
11147
- return await popup.waitForClose();
11148
- }
11149
- finally {
11150
- this._popupRegistry.unregister(popup.id);
11151
- }
11152
- }
11153
- /**
11154
- * Creates the popup handle.
11155
- */
11156
- createPopup(component, options) {
11157
- // Construct the handle in an injection context that shares the popup's lifecycle, allowing for automatic cleanup of effects and RxJS interop functions.
11158
- const popupId = options.id ?? computePopupId();
11159
- const popupInjector = Injector.create({
11160
- parent: this._rootInjector, // use root injector to be independent of service construction context
11161
- providers: [],
11162
- name: `Workbench Popup ${popupId}`,
11163
- });
11164
- const invocationContext = createPopupInvocationContext(options, this._injector);
11165
- return runInInjectionContext(popupInjector, () => new ɵWorkbenchPopup(popupId, component, invocationContext, options));
11166
- }
11167
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchPopupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11168
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchPopupService, providedIn: 'root' });
10704
+ }
10705
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵPopupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10706
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵPopupService, providedIn: 'root' });
11169
10707
  }
11170
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchPopupService, decorators: [{
10708
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵPopupService, decorators: [{
11171
10709
  type: Injectable,
11172
10710
  args: [{ providedIn: 'root' }]
11173
10711
  }] });
10712
+
10713
+ /*
10714
+ * Copyright (c) 2018-2022 Swiss Federal Railways
10715
+ *
10716
+ * This program and the accompanying materials are made
10717
+ * available under the terms of the Eclipse Public License 2.0
10718
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
10719
+ *
10720
+ * SPDX-License-Identifier: EPL-2.0
10721
+ */
11174
10722
  /**
11175
- * Computes the popup's invocation context based on passsed options and injection context.
10723
+ * Enables the display of a component in a popup.
10724
+ *
10725
+ * A popup is a visual workbench element for displaying content above other content. It is positioned relative to an anchor,
10726
+ * which can be an element or a coordinate. The popup moves with the anchor. By default, the popup closes on focus loss or
10727
+ * when pressing the escape key.
10728
+ *
10729
+ * A popup can be bound to a context (e.g., part or view), displaying the popup only if the context is visible and closing
10730
+ * it when the context is disposed. Defaults to the calling context.
10731
+ *
10732
+ * @deprecated since version 21.0.0-beta.1. Use `WorkbenchPopupService` to open popups. Marked for removal in version 22.
11176
10733
  */
11177
- function createPopupInvocationContext(options, injector) {
11178
- return createInvocationContext(options.context && (typeof options.context === 'object' ? options.context.viewId : options.context), { injector });
10734
+ class PopupService {
10735
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: PopupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10736
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: PopupService, providedIn: 'root', useExisting: ɵPopupService });
11179
10737
  }
10738
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: PopupService, decorators: [{
10739
+ type: Injectable,
10740
+ args: [{ providedIn: 'root', useExisting: ɵPopupService }]
10741
+ }] });
10742
+
10743
+ /*
10744
+ * Copyright (c) 2018-2026 Swiss Federal Railways
10745
+ *
10746
+ * This program and the accompanying materials are made
10747
+ * available under the terms of the Eclipse Public License 2.0
10748
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
10749
+ *
10750
+ * SPDX-License-Identifier: EPL-2.0
10751
+ */
11180
10752
  /**
11181
10753
  * Provides {@link WorkbenchPopupService} for dependency injection.
11182
10754
  */
@@ -11388,230 +10960,680 @@ class ɵWorkbenchDialog {
11388
10960
  return this._padding;
11389
10961
  }
11390
10962
  /** @inheritDoc */
11391
- set padding(padding) {
11392
- untracked(() => this._padding.set(padding));
10963
+ set padding(padding) {
10964
+ untracked(() => this._padding.set(padding));
10965
+ }
10966
+ /** @inheritDoc */
10967
+ get cssClass() {
10968
+ return this._cssClass;
10969
+ }
10970
+ /** @inheritDoc */
10971
+ set cssClass(cssClass) {
10972
+ untracked(() => this._cssClass.set(new Array().concat(this._options.cssClass ?? []).concat(cssClass)));
10973
+ }
10974
+ /**
10975
+ * Returns the position of the dialog in the dialog stack.
10976
+ */
10977
+ getPositionInDialogStack() {
10978
+ return this._workbenchDialogRegistry.indexOf(this);
10979
+ }
10980
+ /** @inheritDoc */
10981
+ blink() {
10982
+ this._blink$.next();
10983
+ }
10984
+ /**
10985
+ * Creates a portal to render {@link WorkbenchDialogComponent} in the dialog's injection context.
10986
+ */
10987
+ createPortal() {
10988
+ const injector = Injector.create({
10989
+ parent: this._options.injector ?? inject(Injector),
10990
+ providers: [
10991
+ { provide: ɵWorkbenchDialog, useValue: this },
10992
+ { provide: WorkbenchDialog, useExisting: ɵWorkbenchDialog },
10993
+ { provide: WORKBENCH_ELEMENT, useExisting: ɵWorkbenchDialog },
10994
+ inject(WORKBENCH_DIALOG_CONTEXT, { optional: true }) ?? [],
10995
+ this._options.providers ?? [],
10996
+ ],
10997
+ });
10998
+ inject(DestroyRef).onDestroy(() => injector.destroy());
10999
+ return new ComponentPortal(WorkbenchDialogComponent, null, injector);
11000
+ }
11001
+ /**
11002
+ * Creates a dedicated overlay per dialog to place it on top of previously created overlays, such as dialogs, popups, dropdowns, etc.
11003
+ */
11004
+ createOverlay() {
11005
+ const overlay = inject(Overlay);
11006
+ return overlay.create({
11007
+ disposeOnNavigation: true, // dispose dialog on browser back/forward navigation
11008
+ panelClass: ['wb-dialog-modality-context'],
11009
+ positionStrategy: overlay.position().global(),
11010
+ scrollStrategy: overlay.scrollStrategies.noop(),
11011
+ });
11012
+ }
11013
+ /**
11014
+ * Restores focus when re-attaching this dialog.
11015
+ */
11016
+ restoreFocusOnAttach() {
11017
+ effect(() => {
11018
+ const attached = this.attached();
11019
+ untracked(() => attached && this.focus());
11020
+ });
11021
+ }
11022
+ /**
11023
+ * Restores focus when unblocking this dialog.
11024
+ */
11025
+ restoreFocusOnUnblock() {
11026
+ effect(() => {
11027
+ const blocked = this.blockedBy();
11028
+ untracked(() => !blocked && this.focus());
11029
+ });
11030
+ }
11031
+ /**
11032
+ * Monitors attachment of the host element.
11033
+ */
11034
+ monitorHostElementAttached() {
11035
+ if (this.invocationContext) {
11036
+ return this.invocationContext.attached;
11037
+ }
11038
+ if (this._workbenchConfig.dialog?.modalityScope === 'viewport') {
11039
+ return computed(() => true);
11040
+ }
11041
+ const workbenchComponentRef = inject(WORKBENCH_COMPONENT_REF);
11042
+ return computed(() => !!workbenchComponentRef());
11043
+ }
11044
+ /**
11045
+ * Binds this dialog to its workbench host element, displaying it only when the host element is attached.
11046
+ *
11047
+ * Dialogs opened in non-peripheral area are displayed in the center of the host.
11048
+ */
11049
+ bindToHostElement() {
11050
+ if (!this.invocationContext && this._workbenchConfig.dialog?.modalityScope === 'viewport') {
11051
+ setStyle(this._overlayRef.hostElement, { inset: '0' });
11052
+ }
11053
+ else {
11054
+ const viewDragService = inject(ViewDragService);
11055
+ const workbenchComponentBounds = inject(WORKBENCH_COMPONENT_BOUNDS);
11056
+ const document = inject(DOCUMENT);
11057
+ effect(() => {
11058
+ const visible = this.attached() && !viewDragService.dragging();
11059
+ // Maintain position and size when hidden to prevent flickering when visible again and to support for virtual scrolling in dialog content.
11060
+ if (!visible) {
11061
+ setStyle(this._overlayRef.overlayElement, { visibility: 'hidden' }); // Hide via `visibility` instead of `display` property to retain the size.
11062
+ return;
11063
+ }
11064
+ // IMPORTANT: Track host bounds only if visible to prevent flickering.
11065
+ // Align dialog relative to contextual element if opened in non-peripheral area.
11066
+ const hostBounds = this.invocationContext?.peripheral() === false ? this.invocationContext.bounds() : workbenchComponentBounds();
11067
+ if (!hostBounds) {
11068
+ setStyle(this._overlayRef.overlayElement, { visibility: 'hidden' }); // Hide via `visibility` instead of `display` property to retain the size.
11069
+ return;
11070
+ }
11071
+ setStyle(this._overlayRef.overlayElement, { visibility: null });
11072
+ // Center the dialog horizontally within the host bounds.
11073
+ // Shift the overlay instead of fitting it to the host bounds, so the dialog can grow beyond host bounds
11074
+ // if not specifying dialog size via a dialog handle.
11075
+ const { left, top, width } = hostBounds;
11076
+ const viewportCenter = document.documentElement.offsetWidth / 2;
11077
+ const dialogCenter = left + width / 2;
11078
+ const xDelta = -1 * (viewportCenter - dialogCenter);
11079
+ setStyle(this._overlayRef.hostElement, {
11080
+ transform: `translateX(${Math.round(xDelta)}px) translateY(${Math.round(top)}px)`, // round offset to avoid blurry dialog
11081
+ });
11082
+ });
11083
+ }
11084
+ }
11085
+ /**
11086
+ * Computes if this dialog is blocked by another dialog.
11087
+ */
11088
+ computeBlocked() {
11089
+ const dialogRegistry = inject(WorkbenchDialogRegistry);
11090
+ const topInThisContext = dialogRegistry.top(this.id);
11091
+ const topInInvocationContext = dialogRegistry.top(this.invocationContext?.elementId);
11092
+ return computed(() => {
11093
+ // Get the top dialog in the context spawned by this dialog.
11094
+ if (topInThisContext()) {
11095
+ return topInThisContext();
11096
+ }
11097
+ // Get the top dialog in the context this dialog was opened in.
11098
+ if (topInInvocationContext() !== this) {
11099
+ return topInInvocationContext();
11100
+ }
11101
+ return null;
11102
+ });
11103
+ }
11104
+ blinkOnRequest() {
11105
+ this._blink$
11106
+ .pipe(switchMap(() => of(true).pipe(concatWith(of(false).pipe(delay(300))))), distinctUntilChanged(), takeUntilDestroyed())
11107
+ .subscribe(this.blinking$);
11108
+ }
11109
+ /**
11110
+ * Closes the dialog when the context element is destroyed.
11111
+ */
11112
+ closeOnHostDestroy() {
11113
+ if (this.invocationContext) {
11114
+ effect(() => {
11115
+ if (this.invocationContext.destroyed()) {
11116
+ untracked(() => this.close());
11117
+ }
11118
+ });
11119
+ }
11120
+ }
11121
+ /**
11122
+ * Destroys this dialog and associated resources.
11123
+ */
11124
+ destroy() {
11125
+ if (!this.destroyed()) {
11126
+ this.injector.destroy();
11127
+ this._overlayRef.dispose();
11128
+ }
11129
+ }
11130
+ }
11131
+ /** @inheritDoc */
11132
+ class ɵWorkbenchDialogSize {
11133
+ _height = signal(undefined, { ...(ngDevMode ? { debugName: "_height" } : {}) });
11134
+ _width = signal(undefined, { ...(ngDevMode ? { debugName: "_width" } : {}) });
11135
+ _minHeight = signal(undefined, { ...(ngDevMode ? { debugName: "_minHeight" } : {}) });
11136
+ _maxHeight = signal(undefined, { ...(ngDevMode ? { debugName: "_maxHeight" } : {}) });
11137
+ _minWidth = signal(undefined, { ...(ngDevMode ? { debugName: "_minWidth" } : {}) });
11138
+ _maxWidth = signal(undefined, { ...(ngDevMode ? { debugName: "_maxWidth" } : {}) });
11139
+ /** @inheritDoc */
11140
+ get height() {
11141
+ return this._height;
11142
+ }
11143
+ /** @inheritDoc */
11144
+ set height(height) {
11145
+ untracked(() => this._height.set(height));
11146
+ }
11147
+ /** @inheritDoc */
11148
+ get width() {
11149
+ return this._width;
11150
+ }
11151
+ /** @inheritDoc */
11152
+ set width(width) {
11153
+ untracked(() => this._width.set(width));
11154
+ }
11155
+ /** @inheritDoc */
11156
+ get minHeight() {
11157
+ return this._minHeight;
11158
+ }
11159
+ /** @inheritDoc */
11160
+ set minHeight(minHeight) {
11161
+ untracked(() => this._minHeight.set(minHeight));
11162
+ }
11163
+ /** @inheritDoc */
11164
+ get maxHeight() {
11165
+ return this._maxHeight;
11166
+ }
11167
+ /** @inheritDoc */
11168
+ set maxHeight(maxHeight) {
11169
+ untracked(() => this._maxHeight.set(maxHeight));
11170
+ }
11171
+ /** @inheritDoc */
11172
+ get minWidth() {
11173
+ return this._minWidth;
11174
+ }
11175
+ /** @inheritDoc */
11176
+ set minWidth(minWidth) {
11177
+ untracked(() => this._minWidth.set(minWidth));
11393
11178
  }
11394
11179
  /** @inheritDoc */
11395
- get cssClass() {
11396
- return this._cssClass;
11180
+ get maxWidth() {
11181
+ return this._maxWidth;
11397
11182
  }
11398
11183
  /** @inheritDoc */
11399
- set cssClass(cssClass) {
11400
- untracked(() => this._cssClass.set(new Array().concat(this._options.cssClass ?? []).concat(cssClass)));
11184
+ set maxWidth(maxWidth) {
11185
+ untracked(() => this._maxWidth.set(maxWidth));
11401
11186
  }
11402
- /**
11403
- * Returns the position of the dialog in the dialog stack.
11404
- */
11405
- getPositionInDialogStack() {
11406
- return this._workbenchDialogRegistry.indexOf(this);
11187
+ }
11188
+
11189
+ /*
11190
+ * Copyright (c) 2018-2023 Swiss Federal Railways
11191
+ *
11192
+ * This program and the accompanying materials are made
11193
+ * available under the terms of the Eclipse Public License 2.0
11194
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
11195
+ *
11196
+ * SPDX-License-Identifier: EPL-2.0
11197
+ */
11198
+ /** @inheritDoc */
11199
+ class ɵWorkbenchDialogService {
11200
+ _injector = inject(Injector);
11201
+ _rootInjector = inject(ApplicationRef).injector;
11202
+ _dialogRegistry = inject(WorkbenchDialogRegistry);
11203
+ _document = inject(DOCUMENT);
11204
+ _zone = inject(NgZone);
11205
+ constructor() {
11206
+ this.installServiceLifecycleLogger();
11407
11207
  }
11408
11208
  /** @inheritDoc */
11409
- blink() {
11410
- this._blink$.next();
11209
+ async open(component, options) {
11210
+ assertNotInReactiveContext(this.open, 'Call WorkbenchDialogService.open() in a non-reactive (non-tracking) context, such as within the untracked() function.');
11211
+ // Ensure to run in Angular zone to display the dialog even when called from outside the Angular zone.
11212
+ if (!NgZone.isInAngularZone()) {
11213
+ return this._zone.run(() => this.open(component, options));
11214
+ }
11215
+ // Delay the opening of a context-modal dialog until all application-modal dialogs are closed.
11216
+ // Otherwise, the context-modal dialog would overlap already opened application-modal dialogs.
11217
+ const invocationContext = createDialogInvocationContext(options ?? {}, this._injector);
11218
+ if (invocationContext) {
11219
+ await this.waitUntilApplicationModalDialogsClosed();
11220
+ }
11221
+ // Create the dialog.
11222
+ const dialog = this.createDialog(component, invocationContext, options ?? {});
11223
+ this._dialogRegistry.register(dialog.id, dialog);
11224
+ // Capture focused element to restore focus when closing the dialog.
11225
+ const previouslyFocusedElement = this._document.activeElement instanceof HTMLElement ? this._document.activeElement : undefined;
11226
+ try {
11227
+ return await dialog.waitForClose();
11228
+ }
11229
+ finally {
11230
+ this._dialogRegistry.unregister(dialog.id);
11231
+ // Restore focus to previously focused element when closing the last dialog in the current context.
11232
+ if (previouslyFocusedElement && !this._dialogRegistry.top(invocationContext?.elementId)()) {
11233
+ previouslyFocusedElement.focus();
11234
+ }
11235
+ }
11411
11236
  }
11412
11237
  /**
11413
- * Creates a portal to render {@link WorkbenchDialogComponent} in the dialog's injection context.
11238
+ * Creates the dialog handle.
11414
11239
  */
11415
- createPortal() {
11416
- const injector = Injector.create({
11417
- parent: this._options.injector ?? inject(Injector),
11418
- providers: [
11419
- { provide: ɵWorkbenchDialog, useValue: this },
11420
- { provide: WorkbenchDialog, useExisting: ɵWorkbenchDialog },
11421
- { provide: WORKBENCH_ELEMENT, useExisting: ɵWorkbenchDialog },
11422
- inject(WORKBENCH_DIALOG_CONTEXT, { optional: true }) ?? [],
11423
- this._options.providers ?? [],
11424
- ],
11240
+ createDialog(component, invocationContext, options) {
11241
+ // Construct the handle in an injection context that shares the dialog's lifecycle, allowing for automatic cleanup of effects and RxJS interop functions.
11242
+ const dialogId = computeDialogId();
11243
+ const dialogInjector = Injector.create({
11244
+ parent: this._rootInjector, // use root injector to be independent of service construction context
11245
+ providers: [],
11246
+ name: `Workbench Dialog ${dialogId}`,
11425
11247
  });
11426
- inject(DestroyRef).onDestroy(() => injector.destroy());
11427
- return new ComponentPortal(WorkbenchDialogComponent, null, injector);
11248
+ return runInInjectionContext(dialogInjector, () => new ɵWorkbenchDialog(dialogId, component, invocationContext, options));
11428
11249
  }
11429
11250
  /**
11430
- * Creates a dedicated overlay per dialog to place it on top of previously created overlays, such as dialogs, popups, dropdowns, etc.
11251
+ * Returns a Promise that resolves when all application modal-dialogs are closed. If none are opened, the Promise resolves immediately.
11431
11252
  */
11432
- createOverlay() {
11433
- const overlay = inject(Overlay);
11434
- return overlay.create({
11435
- disposeOnNavigation: true, // dispose dialog on browser back/forward navigation
11436
- panelClass: ['wb-dialog-modality-context'],
11437
- positionStrategy: overlay.position().global(),
11438
- scrollStrategy: overlay.scrollStrategies.noop(),
11439
- });
11253
+ async waitUntilApplicationModalDialogsClosed() {
11254
+ // Use root injector to be independent of service construction context.
11255
+ const injector = Injector.create({ parent: this._rootInjector, providers: [] });
11256
+ await firstValueFrom(toObservable(this._dialogRegistry.top(), { injector }).pipe(filter(top => !top)));
11257
+ injector.destroy();
11440
11258
  }
11441
- /**
11442
- * Restores focus when re-attaching this dialog.
11443
- */
11444
- restoreFocusOnAttach() {
11445
- effect(() => {
11446
- const attached = this.attached();
11447
- untracked(() => attached && this.focus());
11448
- });
11259
+ installServiceLifecycleLogger() {
11260
+ const logger = inject(Logger);
11261
+ const workbenchElement = inject(WORKBENCH_ELEMENT, { optional: true });
11262
+ logger.debug(() => `Constructing WorkbenchDialogService [context=${workbenchElement?.id}]`, LoggerNames.LIFECYCLE);
11263
+ inject(DestroyRef).onDestroy(() => logger.debug(() => `Destroying WorkbenchDialogService [context=${workbenchElement?.id}]'`, LoggerNames.LIFECYCLE));
11449
11264
  }
11450
- /**
11451
- * Restores focus when unblocking this dialog.
11452
- */
11453
- restoreFocusOnUnblock() {
11454
- effect(() => {
11455
- const blocked = this.blockedBy();
11456
- untracked(() => !blocked && this.focus());
11457
- });
11265
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchDialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11266
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchDialogService, providedIn: 'root' });
11267
+ }
11268
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchDialogService, decorators: [{
11269
+ type: Injectable,
11270
+ args: [{ providedIn: 'root' }]
11271
+ }], ctorParameters: () => [] });
11272
+ /**
11273
+ * Computes the dialog's invocation context based on passsed options and injection context.
11274
+ */
11275
+ function createDialogInvocationContext(options, injector) {
11276
+ if (options.modality === 'application') {
11277
+ return null;
11278
+ }
11279
+ return createInvocationContext(options.context && (typeof options.context === 'object' ? options.context.viewId : options.context), { injector });
11280
+ }
11281
+
11282
+ /*
11283
+ * Copyright (c) 2018-2023 Swiss Federal Railways
11284
+ *
11285
+ * This program and the accompanying materials are made
11286
+ * available under the terms of the Eclipse Public License 2.0
11287
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
11288
+ *
11289
+ * SPDX-License-Identifier: EPL-2.0
11290
+ */
11291
+ /**
11292
+ * Enables the display of a component in a dialog.
11293
+ *
11294
+ * A dialog is a visual element for focused interaction with the user, such as prompting the user for input or confirming actions.
11295
+ * The user can move and resize a dialog.
11296
+ *
11297
+ * Displayed on top of other content, a modal dialog blocks interaction with other parts of the application.
11298
+ *
11299
+ * ## Modality
11300
+ * A dialog can be context-modal or application-modal. Context-modal blocks a specific part of the application, as specified by the context;
11301
+ * application-modal blocks the workbench or browser viewport, based on {@link WorkbenchConfig.dialog.modalityScope}.
11302
+ *
11303
+ * ## Context
11304
+ * A dialog can be bound to a context (e.g., part or view), defaulting to the calling context.
11305
+ * The dialog is displayed only if the context is visible and closes when the context is disposed.
11306
+ *
11307
+ * ## Positioning
11308
+ * A dialog is opened in the center of its context, if any, unless opened from the peripheral area.
11309
+ *
11310
+ * ## Stacking
11311
+ * Dialogs are stacked per modality, with only the topmost dialog in each stack being interactive.
11312
+ *
11313
+ * ## Dialog Component
11314
+ * The dialog component can inject the {@link WorkbenchDialog} handle to interact with the dialog, such as setting the title or closing the dialog.
11315
+ * Inputs passed to the dialog are available as input properties in the dialog component.
11316
+ *
11317
+ * ## Dialog Header
11318
+ * By default, the dialog displays the title and a close button in the header. Alternatively, the dialog supports the use of a custom header.
11319
+ * To provide a custom header, add an Angular template to the HTML of the dialog component and decorate it with the `wbDialogHeader` directive.
11320
+ *
11321
+ * ```html
11322
+ * <ng-template wbDialogHeader>
11323
+ * <app-dialog-header/>
11324
+ * </ng-template>
11325
+ * ```
11326
+ *
11327
+ * ## Dialog Footer
11328
+ * A dialog has a default footer that displays actions defined in the HTML of the dialog component. An action is an Angular template decorated with
11329
+ * the `wbDialogAction` directive. Multiple actions are supported, rendered in modeling order, and can be left- or right-aligned.
11330
+ *
11331
+ * ```html
11332
+ * <!-- Checkbox -->
11333
+ * <ng-template wbDialogAction align="start">
11334
+ * <label>
11335
+ * <input type="checkbox"/>
11336
+ * Do not ask me again
11337
+ * </label>
11338
+ * </ng-template>
11339
+ *
11340
+ * <!-- OK Button -->
11341
+ * <ng-template wbDialogAction align="end">
11342
+ * <button (click)="...">OK</button>
11343
+ * </ng-template>
11344
+ *
11345
+ * <!-- Cancel Button -->
11346
+ * <ng-template wbDialogAction align="end">
11347
+ * <button (click)="...">Cancel</button>
11348
+ * </ng-template>
11349
+ * ```
11350
+ *
11351
+ * Alternatively, the dialog supports the use of a custom footer. To provide a custom footer, add an Angular template to the HTML of the dialog component and
11352
+ * decorate it with the `wbDialogFooter` directive.
11353
+ *
11354
+ * ```html
11355
+ * <ng-template wbDialogFooter>
11356
+ * <app-dialog-footer/>
11357
+ * </ng-template>
11358
+ * ```
11359
+ *
11360
+ * ## Styling
11361
+ * The following CSS variables can be set to customize the default look of a dialog.
11362
+ *
11363
+ * - `--sci-workbench-dialog-padding`
11364
+ * - `--sci-workbench-dialog-header-height`
11365
+ * - `--sci-workbench-dialog-header-background-color`
11366
+ * - `--sci-workbench-dialog-title-font-family`
11367
+ * - `--sci-workbench-dialog-title-font-weight`
11368
+ * - `--sci-workbench-dialog-title-font-size`
11369
+ * - `--sci-workbench-dialog-title-align`
11370
+ */
11371
+ class WorkbenchDialogService {
11372
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11373
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogService, providedIn: 'root', useExisting: ɵWorkbenchDialogService });
11374
+ }
11375
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogService, decorators: [{
11376
+ type: Injectable,
11377
+ args: [{ providedIn: 'root', useExisting: ɵWorkbenchDialogService }]
11378
+ }] });
11379
+
11380
+ /*
11381
+ * Copyright (c) 2018-2023 Swiss Federal Railways
11382
+ *
11383
+ * This program and the accompanying materials are made
11384
+ * available under the terms of the Eclipse Public License 2.0
11385
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
11386
+ *
11387
+ * SPDX-License-Identifier: EPL-2.0
11388
+ */
11389
+ class MessageBoxFooterComponent {
11390
+ actions = input.required({ ...(ngDevMode ? { debugName: "actions" } : {}) });
11391
+ severity = input.required({ ...(ngDevMode ? { debugName: "severity" } : {}) });
11392
+ action = output();
11393
+ preferredSizeChange = output();
11394
+ _actionButtons = viewChildren('action_button', { ...(ngDevMode ? { debugName: "_actionButtons" } : {}) });
11395
+ constructor() {
11396
+ void this.emitPreferredSize();
11458
11397
  }
11459
- /**
11460
- * Monitors attachment of the host element.
11461
- */
11462
- monitorHostElementAttached() {
11463
- if (this.invocationContext) {
11464
- return this.invocationContext.attached;
11465
- }
11466
- if (this._workbenchConfig.dialog?.modalityScope === 'viewport') {
11467
- return computed(() => true);
11468
- }
11469
- const workbenchComponentRef = inject(WORKBENCH_COMPONENT_REF);
11470
- return computed(() => !!workbenchComponentRef());
11398
+ insertionSortOrderFn = () => 0;
11399
+ onAction(key) {
11400
+ this.action.emit(key);
11471
11401
  }
11472
- /**
11473
- * Binds this dialog to its workbench host element, displaying it only when the host element is attached.
11474
- *
11475
- * Dialogs opened in non-peripheral area are displayed in the center of the host.
11476
- */
11477
- bindToHostElement() {
11478
- if (!this.invocationContext && this._workbenchConfig.dialog?.modalityScope === 'viewport') {
11479
- setStyle(this._overlayRef.hostElement, { inset: '0' });
11402
+ onArrowKey(index, direction) {
11403
+ const actionButtonCount = this._actionButtons().length;
11404
+ const newIndex = (direction === 'left' ? index - 1 : index + 1);
11405
+ this._actionButtons()[(newIndex + actionButtonCount) % actionButtonCount].nativeElement.focus();
11406
+ }
11407
+ async emitPreferredSize() {
11408
+ const host = inject(ElementRef).nativeElement;
11409
+ host.classList.add('calculating-min-width');
11410
+ try {
11411
+ // Wait for the CSS class to take effect, then wait an animation frame to avoid the error: "ResizeObserver loop completed with undelivered notifications".
11412
+ await firstValueFrom(fromResize$(host).pipe(observeOn(animationFrameScheduler)));
11413
+ this.preferredSizeChange.emit(host.offsetWidth);
11480
11414
  }
11481
- else {
11482
- const viewDragService = inject(ViewDragService);
11483
- const workbenchComponentBounds = inject(WORKBENCH_COMPONENT_BOUNDS);
11484
- const document = inject(DOCUMENT);
11485
- effect(() => {
11486
- const visible = this.attached() && !viewDragService.dragging();
11487
- // Maintain position and size when hidden to prevent flickering when visible again and to support for virtual scrolling in dialog content.
11488
- if (!visible) {
11489
- setStyle(this._overlayRef.overlayElement, { visibility: 'hidden' }); // Hide via `visibility` instead of `display` property to retain the size.
11490
- return;
11491
- }
11492
- // IMPORTANT: Track host bounds only if visible to prevent flickering.
11493
- // Align dialog relative to contextual element if opened in non-peripheral area.
11494
- const hostBounds = this.invocationContext?.peripheral() === false ? this.invocationContext.bounds() : workbenchComponentBounds();
11495
- if (!hostBounds) {
11496
- setStyle(this._overlayRef.overlayElement, { visibility: 'hidden' }); // Hide via `visibility` instead of `display` property to retain the size.
11497
- return;
11498
- }
11499
- setStyle(this._overlayRef.overlayElement, { visibility: null });
11500
- // Center the dialog horizontally within the host bounds.
11501
- // Shift the overlay instead of fitting it to the host bounds, so the dialog can grow beyond host bounds
11502
- // if not specifying dialog size via a dialog handle.
11503
- const { left, top, width } = hostBounds;
11504
- const viewportCenter = document.documentElement.offsetWidth / 2;
11505
- const dialogCenter = left + width / 2;
11506
- const xDelta = -1 * (viewportCenter - dialogCenter);
11507
- setStyle(this._overlayRef.hostElement, {
11508
- transform: `translateX(${Math.round(xDelta)}px) translateY(${Math.round(top)}px)`, // round offset to avoid blurry dialog
11509
- });
11510
- });
11415
+ finally {
11416
+ host.classList.remove('calculating-min-width');
11511
11417
  }
11512
11418
  }
11419
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11420
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: MessageBoxFooterComponent, isStandalone: true, selector: "wb-message-box-footer", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: true, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { action: "action", preferredSizeChange: "preferredSizeChange" }, host: { properties: { "attr.data-severity": "severity()" } }, viewQueries: [{ propertyName: "_actionButtons", predicate: ["action_button"], descendants: true, isSignal: true }], ngImport: i0, template: "@for (action of actions() | keyvalue:insertionSortOrderFn; track action.key) {\n <button #action_button\n (click)=\"onAction(action.key)\"\n (keydown.arrowLeft)=\"onArrowKey($index, 'left')\"\n (keydown.arrowRight)=\"onArrowKey($index, 'right')\"\n [attr.data-action]=\"action.key\"\n class=\"action e2e-action\">\n {{(action.value | wbText)()}}\n </button>\n\n @if (!$last) {\n <span class=\"divider\"></span>\n }\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:flex;height:3em;background-color:var(--sci-color-background-secondary);color:var(--sci-color-text)}: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.calculating-min-width{position:absolute}:host>button.action:is(button,#sci-reset){all:unset;flex:1;margin:2px;border:1px solid transparent;border-radius:var(--sci-corner-small);transition:border-color ease-in-out .15s;cursor:var(--sci-workbench-messagebox-action-cursor);-webkit-user-select:none;user-select:none;text-align:center;min-width:7.5em;padding-inline:.5em;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}:host>button.action:is(button,#sci-reset):focus,:host>button.action:is(button,#sci-reset):active{outline:none;color:var(--\\275message-box-severity-color);border-color:var(--\\275message-box-severity-color)}:host>button.action:is(button,#sci-reset):hover{background-color:var(--sci-workbench-messagebox-action-background-color-hover)}:host>span.divider{width:1px;background-color:var(--sci-color-border)}\n"], dependencies: [{ kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: TextPipe, name: "wbText" }] });
11421
+ }
11422
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxFooterComponent, decorators: [{
11423
+ type: Component,
11424
+ args: [{ selector: 'wb-message-box-footer', imports: [
11425
+ KeyValuePipe,
11426
+ TextPipe,
11427
+ ], host: {
11428
+ '[attr.data-severity]': 'severity()',
11429
+ }, template: "@for (action of actions() | keyvalue:insertionSortOrderFn; track action.key) {\n <button #action_button\n (click)=\"onAction(action.key)\"\n (keydown.arrowLeft)=\"onArrowKey($index, 'left')\"\n (keydown.arrowRight)=\"onArrowKey($index, 'right')\"\n [attr.data-action]=\"action.key\"\n class=\"action e2e-action\">\n {{(action.value | wbText)()}}\n </button>\n\n @if (!$last) {\n <span class=\"divider\"></span>\n }\n}\n", styles: ["@charset \"UTF-8\";:host{--\\275message-box-severity-color: initial;display:flex;height:3em;background-color:var(--sci-color-background-secondary);color:var(--sci-color-text)}: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.calculating-min-width{position:absolute}:host>button.action:is(button,#sci-reset){all:unset;flex:1;margin:2px;border:1px solid transparent;border-radius:var(--sci-corner-small);transition:border-color ease-in-out .15s;cursor:var(--sci-workbench-messagebox-action-cursor);-webkit-user-select:none;user-select:none;text-align:center;min-width:7.5em;padding-inline:.5em;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}:host>button.action:is(button,#sci-reset):focus,:host>button.action:is(button,#sci-reset):active{outline:none;color:var(--\\275message-box-severity-color);border-color:var(--\\275message-box-severity-color)}:host>button.action:is(button,#sci-reset):hover{background-color:var(--sci-workbench-messagebox-action-background-color-hover)}:host>span.divider{width:1px;background-color:var(--sci-color-border)}\n"] }]
11430
+ }], ctorParameters: () => [], propDecorators: { actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: true }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: true }] }], action: [{ type: i0.Output, args: ["action"] }], preferredSizeChange: [{ type: i0.Output, args: ["preferredSizeChange"] }], _actionButtons: [{ type: i0.ViewChildren, args: ['action_button', { isSignal: true }] }] } });
11431
+
11432
+ /*
11433
+ * Copyright (c) 2018-2023 Swiss Federal Railways
11434
+ *
11435
+ * This program and the accompanying materials are made
11436
+ * available under the terms of the Eclipse Public License 2.0
11437
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
11438
+ *
11439
+ * SPDX-License-Identifier: EPL-2.0
11440
+ */
11441
+ /**
11442
+ * Use this directive to replace the default dialog footer that renders actions contributed via the {@link WorkbenchDialogActionDirective} directive.
11443
+ *
11444
+ * The host element of this modeling directive must be a <ng-template>. The footer shares the lifecycle of the host element.
11445
+ *
11446
+ * **Example:**
11447
+ * ```html
11448
+ * <ng-template wbDialogFooter>
11449
+ * <app-dialog-footer/>
11450
+ * </ng-template>
11451
+ * ```
11452
+ */
11453
+ class WorkbenchDialogFooterDirective {
11513
11454
  /**
11514
- * Computes if this dialog is blocked by another dialog.
11515
- */
11516
- computeBlocked() {
11517
- const dialogRegistry = inject(WorkbenchDialogRegistry);
11518
- const topInThisContext = dialogRegistry.top(this.id);
11519
- const topInInvocationContext = dialogRegistry.top(this.invocationContext?.elementId);
11520
- return computed(() => {
11521
- // Get the top dialog in the context spawned by this dialog.
11522
- if (topInThisContext()) {
11523
- return topInThisContext();
11524
- }
11525
- // Get the top dialog in the context this dialog was opened in.
11526
- if (topInInvocationContext() !== this) {
11527
- return topInInvocationContext();
11528
- }
11529
- return null;
11530
- });
11531
- }
11532
- blinkOnRequest() {
11533
- this._blink$
11534
- .pipe(switchMap(() => of(true).pipe(concatWith(of(false).pipe(delay(300))))), distinctUntilChanged(), takeUntilDestroyed())
11535
- .subscribe(this.blinking$);
11536
- }
11537
- /**
11538
- * Closes the dialog when the context element is destroyed.
11455
+ * Specifies if to display a visual separator between the dialog content and this footer.
11456
+ * Defaults to `true`.
11539
11457
  */
11540
- closeOnHostDestroy() {
11541
- if (this.invocationContext) {
11542
- effect(() => {
11543
- if (this.invocationContext.destroyed()) {
11544
- untracked(() => this.close());
11545
- }
11546
- });
11547
- }
11458
+ divider = input(undefined, { ...(ngDevMode ? { debugName: "divider" } : {}), transform: booleanAttribute });
11459
+ template = inject(TemplateRef);
11460
+ _footer;
11461
+ constructor() {
11462
+ const dialog = inject(ɵWorkbenchDialog);
11463
+ // Defer registering footer to avoid `ExpressionChangedAfterItHasBeenCheckedError`.
11464
+ asapScheduler.schedule(() => this._footer = dialog.registerFooter(this));
11465
+ // Defer disposing footer to avoid `ExpressionChangedAfterItHasBeenCheckedError`.
11466
+ inject(DestroyRef).onDestroy(() => asapScheduler.schedule(() => this._footer?.dispose()));
11548
11467
  }
11468
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
11469
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.1", type: WorkbenchDialogFooterDirective, isStandalone: true, selector: "ng-template[wbDialogFooter]", inputs: { divider: { classPropertyName: "divider", publicName: "divider", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
11470
+ }
11471
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogFooterDirective, decorators: [{
11472
+ type: Directive,
11473
+ args: [{ selector: 'ng-template[wbDialogFooter]' }]
11474
+ }], ctorParameters: () => [], propDecorators: { divider: [{ type: i0.Input, args: [{ isSignal: true, alias: "divider", required: false }] }] } });
11475
+
11476
+ /*
11477
+ * Copyright (c) 2018-2023 Swiss Federal Railways
11478
+ *
11479
+ * This program and the accompanying materials are made
11480
+ * available under the terms of the Eclipse Public License 2.0
11481
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
11482
+ *
11483
+ * SPDX-License-Identifier: EPL-2.0
11484
+ */
11485
+ /**
11486
+ * Use this directive to replace the default dialog header that displays the title and a close button.
11487
+ *
11488
+ * The host element of this modeling directive must be a <ng-template>. The header shares the lifecycle of the host element.
11489
+ *
11490
+ * **Example:**
11491
+ * ```html
11492
+ * <ng-template wbDialogHeader>
11493
+ * <app-dialog-header/>
11494
+ * </ng-template>
11495
+ * ```
11496
+ */
11497
+ class WorkbenchDialogHeaderDirective {
11549
11498
  /**
11550
- * Destroys this dialog and associated resources.
11499
+ * Specifies if to display a visual separator between this header and the dialog content.
11500
+ * Defaults to `true`.
11551
11501
  */
11552
- destroy() {
11553
- if (!this.destroyed()) {
11554
- this.injector.destroy();
11555
- this._overlayRef.dispose();
11556
- }
11502
+ divider = input(undefined, { ...(ngDevMode ? { debugName: "divider" } : {}), transform: booleanAttribute });
11503
+ template = inject(TemplateRef);
11504
+ _header;
11505
+ constructor() {
11506
+ const dialog = inject(ɵWorkbenchDialog);
11507
+ // Defer registering header to avoid `ExpressionChangedAfterItHasBeenCheckedError`.
11508
+ asapScheduler.schedule(() => this._header = dialog.registerHeader(this));
11509
+ // Defer disposing header to avoid `ExpressionChangedAfterItHasBeenCheckedError`.
11510
+ inject(DestroyRef).onDestroy(() => asapScheduler.schedule(() => this._header?.dispose()));
11557
11511
  }
11512
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
11513
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.1", type: WorkbenchDialogHeaderDirective, isStandalone: true, selector: "ng-template[wbDialogHeader]", inputs: { divider: { classPropertyName: "divider", publicName: "divider", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
11558
11514
  }
11559
- /** @inheritDoc */
11560
- class ɵWorkbenchDialogSize {
11561
- _height = signal(undefined, { ...(ngDevMode ? { debugName: "_height" } : {}) });
11562
- _width = signal(undefined, { ...(ngDevMode ? { debugName: "_width" } : {}) });
11563
- _minHeight = signal(undefined, { ...(ngDevMode ? { debugName: "_minHeight" } : {}) });
11564
- _maxHeight = signal(undefined, { ...(ngDevMode ? { debugName: "_maxHeight" } : {}) });
11565
- _minWidth = signal(undefined, { ...(ngDevMode ? { debugName: "_minWidth" } : {}) });
11566
- _maxWidth = signal(undefined, { ...(ngDevMode ? { debugName: "_maxWidth" } : {}) });
11567
- /** @inheritDoc */
11568
- get height() {
11569
- return this._height;
11570
- }
11571
- /** @inheritDoc */
11572
- set height(height) {
11573
- untracked(() => this._height.set(height));
11574
- }
11575
- /** @inheritDoc */
11576
- get width() {
11577
- return this._width;
11578
- }
11579
- /** @inheritDoc */
11580
- set width(width) {
11581
- untracked(() => this._width.set(width));
11582
- }
11583
- /** @inheritDoc */
11584
- get minHeight() {
11585
- return this._minHeight;
11586
- }
11587
- /** @inheritDoc */
11588
- set minHeight(minHeight) {
11589
- untracked(() => this._minHeight.set(minHeight));
11590
- }
11591
- /** @inheritDoc */
11592
- get maxHeight() {
11593
- return this._maxHeight;
11515
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchDialogHeaderDirective, decorators: [{
11516
+ type: Directive,
11517
+ args: [{ selector: 'ng-template[wbDialogHeader]' }]
11518
+ }], ctorParameters: () => [], propDecorators: { divider: [{ type: i0.Input, args: [{ isSignal: true, alias: "divider", required: false }] }] } });
11519
+
11520
+ /*
11521
+ * Copyright (c) 2018-2023 Swiss Federal Railways
11522
+ *
11523
+ * This program and the accompanying materials are made
11524
+ * available under the terms of the Eclipse Public License 2.0
11525
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
11526
+ *
11527
+ * SPDX-License-Identifier: EPL-2.0
11528
+ */
11529
+ class MessageBoxHeaderComponent {
11530
+ title = input(undefined, { ...(ngDevMode ? { debugName: "title" } : {}) });
11531
+ severity = input.required({ ...(ngDevMode ? { debugName: "severity" } : {}) });
11532
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11533
+ 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" }] });
11534
+ }
11535
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: MessageBoxHeaderComponent, decorators: [{
11536
+ type: Component,
11537
+ args: [{ selector: 'wb-message-box-header', imports: [
11538
+ TextPipe,
11539
+ ], host: {
11540
+ '[attr.data-severity]': 'severity()',
11541
+ }, 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"] }]
11542
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: true }] }] } });
11543
+
11544
+ /*
11545
+ * Copyright (c) 2018-2024 Swiss Federal Railways
11546
+ *
11547
+ * This program and the accompanying materials are made
11548
+ * available under the terms of the Eclipse Public License 2.0
11549
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
11550
+ *
11551
+ * SPDX-License-Identifier: EPL-2.0
11552
+ */
11553
+ /**
11554
+ * Tests if the object is of the specified type.
11555
+ */
11556
+ class TypeofPipe {
11557
+ transform(object, type) {
11558
+ return typeof object === type;
11594
11559
  }
11595
- /** @inheritDoc */
11596
- set maxHeight(maxHeight) {
11597
- untracked(() => this._maxHeight.set(maxHeight));
11560
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: TypeofPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
11561
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.1", ngImport: i0, type: TypeofPipe, isStandalone: true, name: "wbTypeof" });
11562
+ }
11563
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: TypeofPipe, decorators: [{
11564
+ type: Pipe,
11565
+ args: [{ name: 'wbTypeof' }]
11566
+ }] });
11567
+
11568
+ /*
11569
+ * Copyright (c) 2018-2023 Swiss Federal Railways
11570
+ *
11571
+ * This program and the accompanying materials are made
11572
+ * available under the terms of the Eclipse Public License 2.0
11573
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
11574
+ *
11575
+ * SPDX-License-Identifier: EPL-2.0
11576
+ */
11577
+ /**
11578
+ * Renders the workbench message box.
11579
+ *
11580
+ * This component is designed to be opened in a workbench dialog.
11581
+ */
11582
+ class WorkbenchMessageBoxComponent {
11583
+ message = input.required({ ...(ngDevMode ? { debugName: "message" } : {}), transform: nullIfEmptyMessage });
11584
+ options = input(undefined, { ...(ngDevMode ? { debugName: "options" } : {}) });
11585
+ _dialog = inject(ɵWorkbenchDialog);
11586
+ empty = signal(false, { ...(ngDevMode ? { debugName: "empty" } : {}) });
11587
+ constructor() {
11588
+ this._dialog.closable = false;
11589
+ this._dialog.resizable = false;
11590
+ this._dialog.padding = false;
11591
+ // Limit the maximum messagebox width if text message to break the message.
11592
+ effect(() => {
11593
+ if (typeof this.message() === 'string' || this.message() === null) {
11594
+ this._dialog.size.maxWidth = 'var(--sci-workbench-messagebox-max-width)';
11595
+ }
11596
+ });
11598
11597
  }
11599
- /** @inheritDoc */
11600
- get minWidth() {
11601
- return this._minWidth;
11598
+ onAction(action) {
11599
+ this._dialog.close(action);
11602
11600
  }
11603
- /** @inheritDoc */
11604
- set minWidth(minWidth) {
11605
- untracked(() => this._minWidth.set(minWidth));
11601
+ onEscape() {
11602
+ if ('cancel' in (this.options()?.actions ?? {})) {
11603
+ this._dialog.close('cancel');
11604
+ }
11606
11605
  }
11607
- /** @inheritDoc */
11608
- get maxWidth() {
11609
- return this._maxWidth;
11606
+ onFooterPreferredSizeChange(preferredSize) {
11607
+ this._dialog.size.minWidth = `${preferredSize}px`;
11610
11608
  }
11611
- /** @inheritDoc */
11612
- set maxWidth(maxWidth) {
11613
- untracked(() => this._maxWidth.set(maxWidth));
11609
+ onContentDimensionChange(dimension) {
11610
+ this.empty.set(!dimension.offsetHeight);
11614
11611
  }
11612
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11613
+ 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" }] });
11614
+ }
11615
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxComponent, decorators: [{
11616
+ type: Component,
11617
+ args: [{ selector: 'wb-message-box', imports: [
11618
+ NgComponentOutlet,
11619
+ SciDimensionDirective,
11620
+ WorkbenchDialogHeaderDirective,
11621
+ WorkbenchDialogFooterDirective,
11622
+ MessageBoxHeaderComponent,
11623
+ MessageBoxFooterComponent,
11624
+ TypeofPipe,
11625
+ TextPipe,
11626
+ ], host: {
11627
+ // Ensure host element to be focusable in order to close the message box on Escape keystroke.
11628
+ '[attr.tabindex]': '-1',
11629
+ '[class.empty]': 'empty()',
11630
+ '[class.content-selectable]': 'options()?.contentSelectable',
11631
+ '[class.has-title]': '!!this.options()?.title',
11632
+ '(keydown.escape)': 'onEscape()',
11633
+ }, 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"] }]
11634
+ }], ctorParameters: () => [], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }] } });
11635
+ function nullIfEmptyMessage(message) {
11636
+ return message !== '' ? message : null;
11615
11637
  }
11616
11638
 
11617
11639
  /*
@@ -11624,95 +11646,103 @@ class ɵWorkbenchDialogSize {
11624
11646
  * SPDX-License-Identifier: EPL-2.0
11625
11647
  */
11626
11648
  /** @inheritDoc */
11627
- class ɵWorkbenchDialogService {
11628
- _injector = inject(Injector);
11629
- _rootInjector = inject(ApplicationRef).injector;
11630
- _dialogRegistry = inject(WorkbenchDialogRegistry);
11631
- _document = inject(DOCUMENT);
11649
+ class ɵWorkbenchMessageBoxService {
11650
+ _workbenchDialogService = inject(WorkbenchDialogService);
11632
11651
  _zone = inject(NgZone);
11633
11652
  constructor() {
11634
11653
  this.installServiceLifecycleLogger();
11635
11654
  }
11636
- /** @inheritDoc */
11637
- async open(component, options) {
11638
- assertNotInReactiveContext(this.open, 'Call WorkbenchDialogService.open() in a non-reactive (non-tracking) context, such as within the untracked() function.');
11639
- // Ensure to run in Angular zone to display the dialog even when called from outside the Angular zone.
11640
- if (!NgZone.isInAngularZone()) {
11641
- return this._zone.run(() => this.open(component, options));
11642
- }
11643
- // Delay the opening of a context-modal dialog until all application-modal dialogs are closed.
11644
- // Otherwise, the context-modal dialog would overlap already opened application-modal dialogs.
11645
- const invocationContext = createDialogInvocationContext(options ?? {}, this._injector);
11646
- if (invocationContext) {
11647
- await this.waitUntilApplicationModalDialogsClosed();
11648
- }
11649
- // Create the dialog.
11650
- const dialog = this.createDialog(component, invocationContext, options ?? {});
11651
- this._dialogRegistry.register(dialog.id, dialog);
11652
- // Capture focused element to restore focus when closing the dialog.
11653
- const previouslyFocusedElement = this._document.activeElement instanceof HTMLElement ? this._document.activeElement : undefined;
11654
- try {
11655
- return await dialog.waitForClose();
11656
- }
11657
- finally {
11658
- this._dialogRegistry.unregister(dialog.id);
11659
- // Restore focus to previously focused element when closing the last dialog in the current context.
11660
- if (previouslyFocusedElement && !this._dialogRegistry.top(invocationContext?.elementId)()) {
11661
- previouslyFocusedElement.focus();
11662
- }
11663
- }
11664
- }
11665
- /**
11666
- * Creates the dialog handle.
11667
- */
11668
- createDialog(component, invocationContext, options) {
11669
- // Construct the handle in an injection context that shares the dialog's lifecycle, allowing for automatic cleanup of effects and RxJS interop functions.
11670
- const dialogId = computeDialogId();
11671
- const dialogInjector = Injector.create({
11672
- parent: this._rootInjector, // use root injector to be independent of service construction context
11673
- providers: [],
11674
- name: `Workbench Dialog ${dialogId}`,
11675
- });
11676
- return runInInjectionContext(dialogInjector, () => new ɵWorkbenchDialog(dialogId, component, invocationContext, options));
11677
- }
11678
11655
  /**
11679
- * Returns a Promise that resolves when all application modal-dialogs are closed. If none are opened, the Promise resolves immediately.
11656
+ * @inheritDoc
11680
11657
  */
11681
- async waitUntilApplicationModalDialogsClosed() {
11682
- // Use root injector to be independent of service construction context.
11683
- const injector = Injector.create({ parent: this._rootInjector, providers: [] });
11684
- await firstValueFrom(toObservable(this._dialogRegistry.top(), { injector }).pipe(filter(top => !top)));
11685
- injector.destroy();
11658
+ async open(message, options) {
11659
+ assertNotInReactiveContext(this.open, 'Call WorkbenchMessageBoxService.open() in a non-reactive (non-tracking) context, such as within the untracked() function.');
11660
+ // Ensure to run in Angular zone to display the message box even if called from outside the Angular zone, e.g. from an error handler.
11661
+ if (!NgZone.isInAngularZone()) {
11662
+ return this._zone.run(() => this.open(message, options));
11663
+ }
11664
+ return (await this._workbenchDialogService.open(WorkbenchMessageBoxComponent, {
11665
+ inputs: { message, options },
11666
+ modality: options?.modality,
11667
+ injector: options?.injector,
11668
+ providers: options?.providers,
11669
+ cssClass: options?.cssClass,
11670
+ context: options?.context,
11671
+ animate: true,
11672
+ }));
11686
11673
  }
11687
11674
  installServiceLifecycleLogger() {
11688
11675
  const logger = inject(Logger);
11689
11676
  const workbenchElement = inject(WORKBENCH_ELEMENT, { optional: true });
11690
- logger.debug(() => `Constructing WorkbenchDialogService [context=${workbenchElement?.id}]`, LoggerNames.LIFECYCLE);
11691
- inject(DestroyRef).onDestroy(() => logger.debug(() => `Destroying WorkbenchDialogService [context=${workbenchElement?.id}]'`, LoggerNames.LIFECYCLE));
11677
+ logger.debug(() => `Constructing WorkbenchMessageBoxService [context=${workbenchElement?.id}]`, LoggerNames.LIFECYCLE);
11678
+ inject(DestroyRef).onDestroy(() => logger.debug(() => `Destroying WorkbenchMessageBoxService [context=${workbenchElement?.id}]'`, LoggerNames.LIFECYCLE));
11692
11679
  }
11693
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchDialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11694
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchDialogService, providedIn: 'root' });
11680
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchMessageBoxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11681
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchMessageBoxService, providedIn: 'root' });
11695
11682
  }
11696
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchDialogService, decorators: [{
11683
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ɵWorkbenchMessageBoxService, decorators: [{
11697
11684
  type: Injectable,
11698
11685
  args: [{ providedIn: 'root' }]
11699
11686
  }], ctorParameters: () => [] });
11687
+
11700
11688
  /**
11701
- * Computes the dialog's invocation context based on passsed options and injection context.
11689
+ * Provides a standardized dialog for presenting a message to the user, such as an info, warning or alert,
11690
+ * or for prompting the user for confirmation. The message can be plain text or a component, allowing for
11691
+ * structured content or input prompts.
11692
+ *
11693
+ * Displayed on top of other content, a modal message box blocks interaction with other parts of the application.
11694
+ *
11695
+ * ## Modality
11696
+ * A message box can be context-modal or application-modal. Context-modal blocks a specific part of the application, as specified by the context;
11697
+ * application-modal blocks the workbench or browser viewport, based on {@link WorkbenchConfig.dialog.modalityScope}.
11698
+ *
11699
+ * ## Context
11700
+ * A message box can be bound to a context (e.g., part or view), defaulting to the calling context.
11701
+ * The message box is displayed only if the context is visible and closes when the context is disposed.
11702
+ *
11703
+ * ## Positioning
11704
+ * A message box is opened in the center of its context, if any, unless opened from the peripheral area.
11705
+ *
11706
+ * ## Stacking
11707
+ * Message boxes are stacked per modality, with only the topmost message box in each stack being interactive.
11708
+ *
11709
+ * ## Styling
11710
+ * The following CSS variables can be set to customize the default look of a message box.
11711
+ *
11712
+ * - `--sci-workbench-messagebox-max-width`
11713
+ * - `--sci-workbench-messagebox-severity-indicator-size`
11714
+ * - `--sci-workbench-messagebox-padding`
11715
+ * - `--sci-workbench-messagebox-text-align`
11716
+ * - `--sci-workbench-messagebox-title-align`
11717
+ * - `--sci-workbench-messagebox-title-font-family`
11718
+ * - `--sci-workbench-messagebox-title-font-weight`
11719
+ * - `--sci-workbench-messagebox-title-font-size`
11702
11720
  */
11703
- function createDialogInvocationContext(options, injector) {
11704
- if (options.modality === 'application') {
11705
- return null;
11706
- }
11707
- return createInvocationContext(options.context && (typeof options.context === 'object' ? options.context.viewId : options.context), { injector });
11721
+ class WorkbenchMessageBoxService {
11722
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11723
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxService, providedIn: 'root', useExisting: ɵWorkbenchMessageBoxService });
11708
11724
  }
11725
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: WorkbenchMessageBoxService, decorators: [{
11726
+ type: Injectable,
11727
+ args: [{ providedIn: 'root', useExisting: ɵWorkbenchMessageBoxService }]
11728
+ }] });
11729
+
11730
+ /*
11731
+ * Copyright (c) 2018-2026 Swiss Federal Railways
11732
+ *
11733
+ * This program and the accompanying materials are made
11734
+ * available under the terms of the Eclipse Public License 2.0
11735
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
11736
+ *
11737
+ * SPDX-License-Identifier: EPL-2.0
11738
+ */
11709
11739
  /**
11710
- * Provides {@link WorkbenchDialogService} for dependency injection.
11740
+ * Provides {@link WorkbenchMessageBoxService} for dependency injection.
11711
11741
  */
11712
- function provideWorkbenchDialogService() {
11742
+ function provideWorkbenchMessageBoxService() {
11713
11743
  return [
11714
- ɵWorkbenchDialogService,
11715
- { provide: WorkbenchDialogService, useExisting: ɵWorkbenchDialogService },
11744
+ ɵWorkbenchMessageBoxService,
11745
+ { provide: WorkbenchMessageBoxService, useExisting: ɵWorkbenchMessageBoxService },
11716
11746
  ];
11717
11747
  }
11718
11748
 
@@ -12694,6 +12724,11 @@ class ViewTabComponent {
12694
12724
  event.stopPropagation();
12695
12725
  event.preventDefault();
12696
12726
  }
12727
+ onMouseDown(event) {
12728
+ if (event.button === 1) { // primary aux button
12729
+ event.preventDefault(); // prevent middle-click scrolling; necessary for aux click to work
12730
+ }
12731
+ }
12697
12732
  onDragStart(event) {
12698
12733
  if (!event.dataTransfer) {
12699
12734
  return;
@@ -12758,7 +12793,7 @@ class ViewTabComponent {
12758
12793
  });
12759
12794
  }
12760
12795
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ViewTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
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" }] });
12796
+ 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)", "mousedown": "onMouseDown($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" }] });
12762
12797
  }
12763
12798
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ViewTabComponent, decorators: [{
12764
12799
  type: Component,
@@ -12779,6 +12814,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
12779
12814
  '(click)': 'onClick()',
12780
12815
  '(auxclick)': 'onAuxClick($event)',
12781
12816
  '(contextmenu)': 'onContextmenu($event)',
12817
+ '(mousedown)': 'onMouseDown($event)',
12782
12818
  '(dragstart)': 'onDragStart($event)',
12783
12819
  '(dragend)': 'onDragEnd()',
12784
12820
  }, 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"] }]
@@ -17911,6 +17947,11 @@ class WorkbenchNotificationComponent {
17911
17947
  this.notification.close();
17912
17948
  }
17913
17949
  }
17950
+ onMouseDown(event) {
17951
+ if (event.button === 1) { // primary aux button
17952
+ event.preventDefault(); // prevent middle-click scrolling; necessary for aux click to work
17953
+ }
17954
+ }
17914
17955
  /**
17915
17956
  * Closes this notification when pressing escape if it is the most recently displayed notification.
17916
17957
  */
@@ -17961,7 +18002,7 @@ class WorkbenchNotificationComponent {
17961
18002
  }
17962
18003
  }
17963
18004
  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: [
18005
+ 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)", "mousedown": "onMouseDown($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
18006
  configureNotificationGlassPane(),
17966
18007
  ], 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
18008
  }
@@ -17989,6 +18030,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImpor
17989
18030
  '(mouseenter)': 'hover.set(true)',
17990
18031
  '(mouseleave)': 'hover.set(false)',
17991
18032
  '(auxclick)': 'onAuxClick($event)',
18033
+ '(mousedown)': 'onMouseDown($event)',
17992
18034
  '(keydown.escape)': 'onEscape($event)',
17993
18035
  }, 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
18036
  }], ctorParameters: () => [], propDecorators: { notificationSlotBounds: [{ type: i0.ViewChild, args: ['slot_bounds', { ...{ read: (ElementRef) }, isSignal: true }] }] } });