UNPKG

@scion/workbench-client

Version:

SCION Workbench Client provides core API for a web app to interact with SCION Workbench and other microfrontends. It is a pure TypeScript library based on the framework-agnostic `@scion/microfrontend-platform` library and can be used with any web stack.

1 lines 179 kB
{"version":3,"file":"scion-workbench-client.mjs","sources":["../../../../projects/scion/workbench-client/src/lib/view/workbench-view.ts","../../../../projects/scion/workbench-client/src/lib/ɵworkbench-commands.ts","../../../../projects/scion/workbench-client/src/lib/observable-decorator.ts","../../../../projects/scion/workbench-client/src/lib/workbench-capabilities.enum.ts","../../../../projects/scion/workbench-client/src/lib/routing/ɵworkbench-router.ts","../../../../projects/scion/workbench-client/src/lib/view/ɵworkbench-view.ts","../../../../projects/scion/workbench-client/src/lib/view/workbench-view-initializer.ts","../../../../projects/scion/workbench-client/src/lib/routing/workbench-router.ts","../../../../projects/scion/workbench-client/src/lib/popup/workbench-popup-service.ts","../../../../projects/scion/workbench-client/src/lib/popup/workbench-popup.ts","../../../../projects/scion/workbench-client/src/lib/popup/workbench-popup-context.ts","../../../../projects/scion/workbench-client/src/lib/popup/ɵworkbench-popup.ts","../../../../projects/scion/workbench-client/src/lib/popup/workbench-popup-initializer.ts","../../../../projects/scion/workbench-client/src/lib/message-box/workbench-message-box-service.ts","../../../../projects/scion/workbench-client/src/lib/notification/workbench-notification-service.ts","../../../../projects/scion/workbench-client/src/lib/theme/workbench-theme-monitor.ts","../../../../projects/scion/workbench-client/src/lib/theme/ɵworkbench-theme-monitor.ts","../../../../projects/scion/workbench-client/src/lib/dialog/ɵworkbench-dialog-context.ts","../../../../projects/scion/workbench-client/src/lib/dialog/workbench-dialog.ts","../../../../projects/scion/workbench-client/src/lib/dialog/ɵworkbench-dialog.ts","../../../../projects/scion/workbench-client/src/lib/dialog/workbench-dialog-initializer.ts","../../../../projects/scion/workbench-client/src/lib/dialog/workbench-dialog-service.ts","../../../../projects/scion/workbench-client/src/lib/dialog/ɵworkbench-dialog-service.ts","../../../../projects/scion/workbench-client/src/lib/message-box/ɵworkbench-message-box.ts","../../../../projects/scion/workbench-client/src/lib/message-box/workbench-message-box.ts","../../../../projects/scion/workbench-client/src/lib/message-box/ɵworkbench-message-box-context.ts","../../../../projects/scion/workbench-client/src/lib/message-box/workbench-message-box-initializer.ts","../../../../projects/scion/workbench-client/src/lib/message-box/workbench-message-box-capability.ts","../../../../projects/scion/workbench-client/src/lib/message-box/ɵworkbench-message-box-service.ts","../../../../projects/scion/workbench-client/src/lib/style-sheet-installer.ts","../../../../projects/scion/workbench-client/src/lib/text/workbench-text-provider.ts","../../../../projects/scion/workbench-client/src/lib/text/workbench-text-service.ts","../../../../projects/scion/workbench-client/src/lib/text/ɵworkbench-text-service.ts","../../../../projects/scion/workbench-client/src/lib/part/ɵworkbench-part.ts","../../../../projects/scion/workbench-client/src/lib/part/workbench-part.ts","../../../../projects/scion/workbench-client/src/lib/part/ɵworkbench-part-context.ts","../../../../projects/scion/workbench-client/src/lib/part/workbench-part-initializer.ts","../../../../projects/scion/workbench-client/src/lib/workbench.identifiers.ts","../../../../projects/scion/workbench-client/src/lib/popup/ɵworkbench-popup-service.ts","../../../../projects/scion/workbench-client/src/lib/notification/ɵworkbench-notification-service.ts","../../../../projects/scion/workbench-client/src/lib/workbench-client.ts","../../../../projects/scion/workbench-client/src/lib/common/public_api.ts","../../../../projects/scion/workbench-client/src/lib/dialog/public_api.ts","../../../../projects/scion/workbench-client/src/lib/message-box/public_api.ts","../../../../projects/scion/workbench-client/src/lib/notification/public_api.ts","../../../../projects/scion/workbench-client/src/lib/perspective/workbench-perspective-capability.ts","../../../../projects/scion/workbench-client/src/lib/perspective/public_api.ts","../../../../projects/scion/workbench-client/src/lib/popup/public_api.ts","../../../../projects/scion/workbench-client/src/lib/routing/public_api.ts","../../../../projects/scion/workbench-client/src/lib/text/public_api.ts","../../../../projects/scion/workbench-client/src/lib/theme/public_api.ts","../../../../projects/scion/workbench-client/src/lib/view/public_api.ts","../../../../projects/scion/workbench-client/src/lib/part/public_api.ts","../../../../projects/scion/workbench-client/src/lib/public_api.ts","../../../../projects/scion/workbench-client/src/public-api.ts","../../../../projects/scion/workbench-client/src/scion-workbench-client.ts"],"sourcesContent":["/*\n * Copyright (c) 2018-2022 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\nimport {Observable} from 'rxjs';\nimport {WorkbenchViewCapability} from './workbench-view-capability';\nimport {PartId, ViewId} from '../workbench.identifiers';\nimport {Translatable} from '../text/workbench-text-provider.model';\n\n/**\n * A view is a visual workbench element for displaying content stacked or side-by-side in the workbench layout.\n *\n * Users can drag views from one part to another, even across windows, or place them side-by-side, horizontally and vertically.\n *\n * The view microfrontend can inject this handle to interact with the view.\n *\n * @category View\n * @see WorkbenchViewCapability\n */\nexport abstract class WorkbenchView {\n\n /**\n * Unique identity of this view.\n */\n public abstract readonly id: ViewId;\n\n /**\n * Signals readiness, notifying the workbench that this view has completed initialization.\n *\n * If `showSplash` is set to `true` on the view capability, the workbench displays a splash until the view microfrontend signals readiness.\n *\n * @see WorkbenchViewCapability.properties.showSplash\n */\n public abstract signalReady(): void;\n\n /**\n * Capability of the microfrontend loaded into the view.\n *\n * Upon subscription, emits the microfrontend's capability, and then emits continuously when navigating to a different microfrontend\n * of the same application. It completes when navigating to a microfrontend of another application.\n */\n public abstract readonly capability$: Observable<WorkbenchViewCapability>;\n\n /**\n * Parameters of the microfrontend loaded into the view.\n *\n * Upon subscription, emits the microfrontend's parameters, and then emits continuously when the parameters change.\n * The Observable completes when navigating to a microfrontend of another application, but not when navigating to a different microfrontend\n * of the same application.\n */\n public abstract readonly params$: Observable<ReadonlyMap<string, any>>;\n\n /**\n * The current snapshot of this view.\n */\n public abstract readonly snapshot: ViewSnapshot;\n\n /**\n * Indicates whether this view is active.\n *\n * Upon subscription, emits the active state of this view, and then emits continuously when it changes.\n * The Observable completes when navigating to a microfrontend of another application, but not when navigating to a different microfrontend\n * of the same application.\n */\n public abstract readonly active$: Observable<boolean>;\n\n /**\n * Indicates whether this view has the focus.\n *\n * Upon subscription, emits the focused state of this view, and then emits continuously when it changes.\n * The Observable completes when navigating to a microfrontend of another application, but not when navigating to a different microfrontend\n * of the same application.\n */\n public abstract readonly focused$: Observable<boolean>;\n\n /**\n * Gets the identity of the part that contains this view.\n *\n * Upon subscription, emits the identity of this view's part, and then emits continuously when it changes.\n * The Observable completes when navigating to a microfrontend of another application, but not when navigating to a different microfrontend\n * of the same application.\n */\n public abstract readonly partId$: Observable<PartId>;\n\n /**\n * Sets the title to be displayed in the view tab.\n *\n * Can be text or a translation key. A translation key starts with the percent symbol (`%`) and may include parameters in matrix notation for text interpolation.\n */\n public abstract setTitle(title: Translatable): void;\n\n /**\n * Sets the title to be displayed in the view tab.\n *\n * @deprecated since version 1.0.0-beta.31. To migrate, pass a translatable and provide the text using a text provider registered in `WorkbenchClient.registerTextProvider`.\n */\n public abstract setTitle(title: Observable<Translatable>): void; // eslint-disable-line @typescript-eslint/unified-signatures\n\n /**\n * Sets the subtitle to be displayed in the view tab.\n *\n * Can be text or a translation key. A translation key starts with the percent symbol (`%`) and may include parameters in matrix notation for text interpolation.\n */\n public abstract setHeading(heading: Translatable): void;\n\n /**\n * Sets the subtitle to be displayed in the view tab.\n *\n * @deprecated since version 1.0.0-beta.31. To migrate, pass a translatable and provide the text using a text provider registered in `WorkbenchClient.registerTextProvider`.\n */\n public abstract setHeading(heading: Observable<Translatable>): void; // eslint-disable-line @typescript-eslint/unified-signatures\n\n /**\n * Sets whether this view is dirty or pristine. When navigating to another microfrontend, the view's dirty state is set to pristine.\n *\n * You can provide the dirty/pristine state either as a boolean or as Observable. If you pass an Observable, it will be unsubscribed when\n * navigating to another microfrontend, whether from the same app or a different one.\n *\n * If not passing an argument, the view is marked as dirty. To mark it as pristine, you need to pass `false`.\n */\n public abstract markDirty(dirty?: boolean | Observable<boolean>): void;\n\n /**\n * Controls whether the user should be allowed to close this workbench view.\n *\n * You can provide either a boolean or Observable. If you pass an Observable, it will be unsubscribed when navigating to another microfrontend,\n * whether from the same app or a different one.\n */\n public abstract setClosable(closable: boolean | Observable<boolean>): void;\n\n /**\n * Initiates the closing of this workbench view.\n */\n public abstract close(): void;\n\n /**\n * Registers a guard to confirm closing the view, replacing any previous guard.\n *\n * Example:\n * ```ts\n * Beans.get(WorkbenchView).canClose(async () => {\n * const action = await Beans.get(WorkbenchMessageBoxService).open('Do you want to save changes?', {\n * actions: {\n * yes: 'Yes',\n * no: 'No',\n * cancel: 'Cancel'\n * }\n * });\n *\n * switch (action) {\n * case 'yes':\n * // Store changes ...\n * return true;\n * case 'no':\n * return true;\n * default:\n * return false;\n * }\n * });\n * ```\n *\n * @param canClose - Callback to confirm closing the view.\n * @returns Reference to the `CanClose` guard, which can be used to unregister the guard.\n */\n public abstract canClose(canClose: CanCloseFn): CanCloseRef;\n}\n\n/**\n * The signature of a function to confirm closing a view., e.g., if dirty.\n */\nexport type CanCloseFn = () => Observable<boolean> | Promise<boolean> | boolean;\n\n/**\n * Reference to the `CanClose` guard registered on a view.\n */\nexport interface CanCloseRef {\n\n /**\n * Removes the `CanClose` guard from the view.\n *\n * Has no effect if another guard was registered in the meantime.\n */\n dispose(): void;\n}\n\n/**\n * Provides information about a view displayed at a particular moment in time.\n *\n * @category View\n */\nexport interface ViewSnapshot {\n /**\n * Capability of the microfrontend loaded into the view.\n */\n capability: WorkbenchViewCapability;\n /**\n * Parameters of the microfrontend loaded into the view.\n */\n params: ReadonlyMap<string, any>;\n /**\n * The identity of the part that contains the view.\n */\n partId: PartId;\n /**\n * Indicates whether this view is active.\n */\n active: boolean;\n /**\n * Indicates whether this view has the focus.\n */\n focused: boolean;\n}\n","/*\n * Copyright (c) 2018-2024 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\nimport {DialogId, PartId, PopupId, ViewId} from './workbench.identifiers';\n\n/**\n * Defines command endpoints for the communication between SCION Workbench and SCION Workbench Client.\n *\n * @docs-private Not public API. For internal use only.\n */\nexport const ɵWorkbenchCommands = {\n\n /**\n * Computes the topic via which the title of a workbench view tab can be set.\n */\n viewTitleTopic: (viewId: ViewId | ':viewId') => `ɵworkbench/views/${viewId}/title`,\n\n /**\n * Computes the topic via which the heading of a workbench view tab can be set.\n */\n viewHeadingTopic: (viewId: ViewId | ':viewId') => `ɵworkbench/views/${viewId}/heading`,\n\n /**\n * Computes the topic via which a view tab can be marked dirty or pristine.\n */\n viewDirtyTopic: (viewId: ViewId | ':viewId') => `ɵworkbench/views/${viewId}/dirty`,\n\n /**\n * Computes the topic via which a view tab can be made closable.\n */\n viewClosableTopic: (viewId: ViewId | ':viewId') => `ɵworkbench/views/${viewId}/closable`,\n\n /**\n * Computes the topic via which a view can be closed.\n */\n viewCloseTopic: (viewId: ViewId | ':viewId') => `ɵworkbench/views/${viewId}/close`,\n\n /**\n * Computes the topic to notify the active state of a view.\n *\n * The active state is published as a retained message.\n */\n viewActiveTopic: (viewId: ViewId) => `ɵworkbench/views/${viewId}/active`,\n\n /**\n * Computes the topic to notify the active state of a part.\n *\n * The active state is published as a retained message.\n */\n partActiveTopic: (partId: PartId) => `ɵworkbench/parts/${partId}/active`,\n\n /**\n * Computes the topic to notify the focused state of a view.\n *\n * The focused state is published as a retained message.\n */\n viewFocusedTopic: (viewId: ViewId) => `ɵworkbench/views/${viewId}/focused`,\n\n /**\n * Computes the topic to notify the focused state of a part.\n *\n * The focused state is published as a retained message.\n */\n partFocusedTopic: (partId: PartId) => `ɵworkbench/parts/${partId}/focused`,\n\n /**\n * Computes the topic to notify the part of a view.\n *\n * The part identity is published as a retained message.\n */\n viewPartIdTopic: (viewId: ViewId) => `ɵworkbench/views/${viewId}/part/id`,\n\n /**\n * Computes the topic to request closing confirmation of a view.\n *\n * When closing a view and if the microfrontend has subscribed to this topic, the workbench requests closing confirmation\n * via this topic. By sending a `true` reply, the workbench continues with closing the view, by sending a `false` reply,\n * closing is prevented.\n */\n canCloseTopic: (viewId: ViewId) => `ɵworkbench/views/${viewId}/canClose`,\n\n /**\n * Computes the topic for signaling that a microfrontend is about to be replaced by a microfrontend of another app.\n */\n viewUnloadingTopic: (viewId: ViewId) => `ɵworkbench/views/${viewId}/unloading`,\n\n /**\n * Computes the topic for updating params of a microfrontend view.\n */\n viewParamsUpdateTopic: (viewId: ViewId, viewCapabilityId: string) => `ɵworkbench/views/${viewId}/capabilities/${viewCapabilityId}/params/update`,\n\n /**\n * Computes the topic for providing params to a view microfrontend.\n *\n * Params include the {@link ɵMicrofrontendRouteParams#ɵVIEW_CAPABILITY_ID capability id} and params as passed in {@link WorkbenchNavigationExtras.params}.\n *\n * Params are published as a retained message.\n */\n viewParamsTopic: (viewId: ViewId) => `ɵworkbench/views/${viewId}/params`,\n\n /**\n * Computes the topic for observing the popup origin.\n */\n popupOriginTopic: (popupId: PopupId) => `ɵworkbench/popups/${popupId}/origin`,\n\n /**\n * Computes the topic to notify the focused state of a popup.\n *\n * The focused state is published as a retained message.\n */\n popupFocusedTopic: (popupId: PopupId) => `ɵworkbench/popups/${popupId}/focused`,\n\n /**\n * Computes the topic via which a popup can be closed.\n */\n popupCloseTopic: (popupId: PopupId) => `ɵworkbench/popups/${popupId}/close`,\n\n /**\n * Computes the topic via which to set a result\n */\n popupResultTopic: (popupId: PopupId) => `ɵworkbench/popups/${popupId}/result`,\n\n /**\n * Computes the topic via which the title of a dialog can be set.\n */\n dialogTitleTopic: (dialogId: DialogId) => `ɵworkbench/dialogs/${dialogId}/title`,\n\n /**\n * Computes the topic to notify the focused state of a dialog.\n *\n * The focused state is published as a retained message.\n */\n dialogFocusedTopic: (dialogId: DialogId) => `ɵworkbench/dialogs/${dialogId}/focused`,\n\n /**\n * Computes the topic via which a dialog can be closed.\n */\n dialogCloseTopic: (dialogId: DialogId) => `ɵworkbench/dialogs/${dialogId}/close`,\n} as const;\n","/*\n * Copyright (c) 2018-2022 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\nimport {MonoTypeOperatorFunction, Observable} from 'rxjs';\nimport {Beans} from '@scion/toolkit/bean-manager';\nimport {ObservableDecorator} from '@scion/microfrontend-platform';\n\n/**\n * Decorates the source with registered {@link ObservableDecorator}, if any.\n *\n * @internal\n */\nexport function decorateObservable<T>(): MonoTypeOperatorFunction<T> {\n return (source$: Observable<T>) => Beans.opt(ObservableDecorator)?.decorate$(source$) ?? source$;\n}\n","/*\n * Copyright (c) 2018-2024 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\n/**\n * Built-in workbench capabilities.\n */\nexport enum WorkbenchCapabilities {\n /**\n * Contributes a workbench part.\n *\n * A part is a visual element of the workbench layout. Parts can be docked to the side or\n * positioned relative to each other. A part can display content or stack views.\n */\n Part = 'part',\n /**\n * Contributes a workbench view.\n *\n * A view is a visual element of the workbench layout for displaying content stacked or side-by-side.\n */\n View = 'view',\n /**\n * Contributes a workbench perspective.\n *\n * A perspective defines an arrangement of parts and views. Users can switch between perspectives. Perspectives share the same main area, if any.\n */\n Perspective = 'perspective',\n /**\n * Contributes a microfrontend for display in workbench popup.\n *\n * A popup is a visual workbench component for displaying content above other content.\n */\n Popup = 'popup',\n /**\n * Contributes a microfrontend for display in workbench dialog.\n *\n * A dialog is a visual element for focused interaction with the user, such as prompting the user for input or confirming actions.\n */\n Dialog = 'dialog',\n /**\n * Contributes a message box in the host app.\n *\n * A message box is a standardized dialog for presenting a message to the user, such as an info, warning or alert,\n * or for prompting the user for confirmation.\n */\n MessageBox = 'messagebox',\n /**\n * Contributes a notification in the host app.\n *\n * A notification appears in the upper-right corner and disappears automatically after a few seconds.\n * It informs the user of a system event, e.g., that a task has been completed or an error has occurred.\n */\n Notification = 'notification',\n /**\n * Provides texts to the SCION Workbench and micro apps.\n */\n TextProvider = 'text-provider',\n}\n","/*\n * Copyright (c) 2018-2022 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\nimport {IntentClient, mapToBody, MessageClient, Qualifier, RequestError} from '@scion/microfrontend-platform';\nimport {Beans} from '@scion/toolkit/bean-manager';\nimport {WorkbenchView} from '../view/workbench-view';\nimport {WorkbenchCapabilities} from '../workbench-capabilities.enum';\nimport {Dictionaries, Dictionary, Maps} from '@scion/toolkit/util';\nimport {ɵWorkbenchCommands} from '../ɵworkbench-commands';\nimport {lastValueFrom} from 'rxjs';\nimport {Empty} from '../common/utility-types';\nimport {WorkbenchNavigationExtras, WorkbenchRouter} from './workbench-router';\n\n/**\n * @ignore\n * @docs-private Not public API. For internal use only.\n */\nexport class ɵWorkbenchRouter implements WorkbenchRouter {\n\n /** @inheritDoc */\n public async navigate(qualifier: Qualifier | Empty<Qualifier>, extras?: WorkbenchNavigationExtras): Promise<boolean> {\n if (this.isSelfNavigation(qualifier)) {\n return this.updateViewParams(extras);\n }\n else {\n return this.issueViewIntent(qualifier, extras);\n }\n }\n\n private async issueViewIntent(qualifier: Qualifier, extras?: WorkbenchNavigationExtras): Promise<boolean> {\n const navigationExtras: WorkbenchNavigationExtras = {\n ...extras,\n params: undefined, // included in the intent\n paramsHandling: undefined, // only applicable for self-navigation\n };\n const navigate$ = Beans.get(IntentClient).request$<boolean>({type: WorkbenchCapabilities.View, qualifier, params: Maps.coerce(extras?.params)}, navigationExtras);\n try {\n return await lastValueFrom(navigate$.pipe(mapToBody()));\n }\n catch (error) {\n throw (error instanceof RequestError ? error.message : error);\n }\n }\n\n private async updateViewParams(extras?: WorkbenchNavigationExtras): Promise<boolean> {\n const viewCapabilityId = Beans.get(WorkbenchView).snapshot.params.get(ɵMicrofrontendRouteParams.ɵVIEW_CAPABILITY_ID) as string | undefined;\n if (viewCapabilityId === undefined) {\n return false; // Params cannot be updated until the loading of the view is completed\n }\n\n const command: ɵViewParamsUpdateCommand = {\n params: Dictionaries.coerce(extras?.params),\n paramsHandling: extras?.paramsHandling,\n };\n const updateParams$ = Beans.get(MessageClient).request$<boolean>(ɵWorkbenchCommands.viewParamsUpdateTopic(Beans.get(WorkbenchView).id, viewCapabilityId), command);\n try {\n return await lastValueFrom(updateParams$.pipe(mapToBody()));\n }\n catch (error) {\n throw (error instanceof RequestError ? error.message : error);\n }\n }\n\n private isSelfNavigation(qualifier: Qualifier | Empty<Qualifier>): boolean {\n if (Object.keys(qualifier).length === 0) {\n if (!Beans.opt(WorkbenchView)) {\n throw Error('[NavigateError] Self-navigation is supported only if in the context of a view.');\n }\n return true;\n }\n return false;\n }\n}\n\n/**\n * Command object for instructing the Workbench Router to update view params in self-navigation.\n *\n * @docs-private Not public API. For internal use only.\n * @ignore\n */\nexport interface ɵViewParamsUpdateCommand {\n /**\n * @see WorkbenchNavigationExtras#params\n */\n params: Dictionary;\n /**\n * @see WorkbenchNavigationExtras#paramsHandling\n */\n paramsHandling?: 'merge' | 'replace';\n}\n\n/**\n * Named parameters used in microfrontend routes.\n *\n * @docs-private Not public API. For internal use only.\n * @ignore\n */\nexport enum ɵMicrofrontendRouteParams {\n /**\n * Named path segment in the microfrontend route representing the view capability for which to embed its microfrontend.\n */\n ɵVIEW_CAPABILITY_ID = 'ɵViewCapabilityId',\n}\n","/*\n * Copyright (c) 2018-2024 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\nimport {Beans, PreDestroy} from '@scion/toolkit/bean-manager';\nimport {combineLatest, firstValueFrom, merge, Observable, OperatorFunction, pipe, Subject, Subscription} from 'rxjs';\nimport {WorkbenchViewCapability} from './workbench-view-capability';\nimport {ManifestService, mapToBody, MessageClient, MicrofrontendPlatformClient} from '@scion/microfrontend-platform';\nimport {ɵWorkbenchCommands} from '../ɵworkbench-commands';\nimport {distinctUntilChanged, filter, map, mergeMap, shareReplay, skip, switchMap, takeUntil, tap} from 'rxjs/operators';\nimport {Observables} from '@scion/toolkit/util';\nimport {CanCloseFn, CanCloseRef, ViewSnapshot, WorkbenchView} from './workbench-view';\nimport {decorateObservable} from '../observable-decorator';\nimport {PartId, ViewId} from '../workbench.identifiers';\nimport {Translatable} from '../text/workbench-text-provider.model';\nimport {ɵMicrofrontendRouteParams} from '../routing/ɵworkbench-router';\n\nexport class ɵWorkbenchView implements WorkbenchView, PreDestroy {\n\n private _propertyChange$ = new Subject<'title' | 'heading' | 'dirty' | 'closable'>();\n private _destroy$ = new Subject<void>();\n /**\n * Observable that emits when the application loaded into the current view receives an unloading event,\n * i.e., is just about to be replaced by a microfrontend of another application.\n */\n private _beforeUnload$: Observable<void>;\n /**\n * Observable that emits before navigating to a different microfrontend of the same app.\n */\n private _beforeInAppNavigation$ = new Subject<void>();\n private _canCloseFn: CanCloseFn | undefined;\n private _canCloseSubscription: Subscription | undefined;\n\n public active$: Observable<boolean>;\n public focused$: Observable<boolean>;\n public partId$: Observable<PartId>;\n public params$: Observable<ReadonlyMap<string, any>>;\n public capability$: Observable<WorkbenchViewCapability>;\n public whenProperties: Promise<void>;\n public snapshot: ViewSnapshot = {\n params: new Map<string, any>(),\n partId: undefined!,\n active: false,\n focused: false,\n capability: undefined!,\n };\n\n constructor(public id: ViewId) {\n this._beforeUnload$ = Beans.get(MessageClient).observe$<void>(ɵWorkbenchCommands.viewUnloadingTopic(this.id))\n .pipe(map(() => undefined), shareReplay({refCount: false, bufferSize: 1}));\n\n this.params$ = Beans.get(MessageClient).observe$<Map<string, any>>(ɵWorkbenchCommands.viewParamsTopic(this.id))\n .pipe(\n mapToBody(),\n shareReplay({refCount: false, bufferSize: 1}),\n decorateObservable(),\n takeUntil(merge(this._beforeUnload$, this._destroy$)),\n );\n\n this.capability$ = this.params$\n .pipe(\n map(params => params.get(ɵMicrofrontendRouteParams.ɵVIEW_CAPABILITY_ID) as string),\n lookupViewCapabilityAndShareReplay(),\n decorateObservable(),\n takeUntil(this._beforeUnload$),\n );\n\n this.active$ = Beans.get(MessageClient).observe$<boolean>(ɵWorkbenchCommands.viewActiveTopic(this.id))\n .pipe(\n mapToBody(),\n shareReplay({refCount: false, bufferSize: 1}),\n decorateObservable(),\n takeUntil(this._beforeUnload$),\n );\n\n this.focused$ = Beans.get(MessageClient).observe$<boolean>(ɵWorkbenchCommands.viewFocusedTopic(this.id))\n .pipe(\n mapToBody(),\n shareReplay({refCount: false, bufferSize: 1}),\n decorateObservable(),\n takeUntil(this._beforeUnload$),\n );\n\n this.partId$ = Beans.get(MessageClient).observe$<PartId>(ɵWorkbenchCommands.viewPartIdTopic(this.id))\n .pipe(\n mapToBody(),\n shareReplay({refCount: false, bufferSize: 1}),\n decorateObservable(),\n takeUntil(this._beforeUnload$),\n );\n // Update part id snapshot when part changes.\n this.partId$\n .pipe(takeUntil(this._destroy$))\n .subscribe(partId => this.snapshot.partId = partId);\n // Update params snapshot when params change.\n this.params$\n .pipe(takeUntil(this._destroy$))\n .subscribe(params => this.snapshot.params = new Map(params));\n // Update active snapshot when the active state changes.\n this.active$\n .pipe(takeUntil(this._destroy$))\n .subscribe(active => this.snapshot.active = active);\n // Update active snapshot when the focus state changes.\n this.focused$\n .pipe(takeUntil(this._destroy$))\n .subscribe(focused => this.snapshot.focused = focused);\n // Update capability snapshot when the capability changes.\n this.capability$\n .pipe(takeUntil(this._destroy$))\n .subscribe(capability => this.snapshot.capability = capability);\n // Detect navigation to a different view capability of the same app.\n // Do NOT use `capability$` observable to detect capability change, as its lookup is asynchronous.\n this.params$\n .pipe(\n map(params => params.get(ɵMicrofrontendRouteParams.ɵVIEW_CAPABILITY_ID) as string),\n distinctUntilChanged(),\n skip(1), // skip the initial navigation\n takeUntil(merge(this._beforeUnload$, this._destroy$)),\n )\n .subscribe(() => {\n this._beforeInAppNavigation$.next();\n this._canCloseFn = undefined;\n this._canCloseSubscription?.unsubscribe();\n this._canCloseSubscription = undefined;\n });\n\n // Signal view properties available.\n this.whenProperties = firstValueFrom(combineLatest([this.partId$, this.params$, this.capability$])).then();\n }\n\n /** @inheritDoc */\n public signalReady(): void {\n MicrofrontendPlatformClient.signalReady();\n }\n\n /** @inheritDoc */\n public setTitle(title: Translatable | Observable<string>): void {\n this._propertyChange$.next('title');\n\n Observables.coerce(title)\n .pipe(\n mergeMap(it => Beans.get(MessageClient).publish(ɵWorkbenchCommands.viewTitleTopic(this.id), it)),\n takeUntil(merge(this._propertyChange$.pipe(filter(prop => prop === 'title')), this._beforeInAppNavigation$, this._beforeUnload$, this._destroy$)),\n )\n .subscribe();\n }\n\n /** @inheritDoc */\n public setHeading(heading: Translatable | Observable<Translatable>): void {\n this._propertyChange$.next('heading');\n\n Observables.coerce(heading)\n .pipe(\n mergeMap(it => Beans.get(MessageClient).publish(ɵWorkbenchCommands.viewHeadingTopic(this.id), it)),\n takeUntil(merge(this._propertyChange$.pipe(filter(prop => prop === 'heading')), this._beforeInAppNavigation$, this._beforeUnload$, this._destroy$)),\n )\n .subscribe();\n }\n\n /** @inheritDoc */\n public markDirty(dirty: undefined | boolean | Observable<boolean>): void {\n this._propertyChange$.next('dirty');\n\n Observables.coerce(dirty ?? true)\n .pipe(\n mergeMap(it => Beans.get(MessageClient).publish(ɵWorkbenchCommands.viewDirtyTopic(this.id), it)),\n takeUntil(merge(this._propertyChange$.pipe(filter(prop => prop === 'dirty')), this._beforeInAppNavigation$, this._beforeUnload$, this._destroy$)),\n )\n .subscribe();\n }\n\n /** @inheritDoc */\n public setClosable(closable: boolean | Observable<boolean>): void {\n this._propertyChange$.next('closable');\n\n Observables.coerce(closable)\n .pipe(\n mergeMap(it => Beans.get(MessageClient).publish(ɵWorkbenchCommands.viewClosableTopic(this.id), it)),\n takeUntil(merge(this._propertyChange$.pipe(filter(prop => prop === 'closable')), this._beforeInAppNavigation$, this._beforeUnload$, this._destroy$)),\n )\n .subscribe();\n }\n\n /** @inheritDoc */\n public close(): void {\n void Beans.get(MessageClient).publish(ɵWorkbenchCommands.viewCloseTopic(this.id));\n }\n\n /** @inheritDoc */\n public canClose(canClose: CanCloseFn): CanCloseRef {\n this._canCloseFn = canClose;\n this._canCloseSubscription ??= Beans.get(MessageClient).onMessage(ɵWorkbenchCommands.canCloseTopic(this.id), () => this._canCloseFn?.() ?? true);\n return {\n dispose: () => {\n if (canClose === this._canCloseFn) {\n this._canCloseSubscription!.unsubscribe();\n this._canCloseSubscription = undefined;\n this._canCloseFn = undefined;\n }\n },\n };\n }\n\n public preDestroy(): void {\n this._canCloseSubscription?.unsubscribe();\n this._canCloseSubscription = undefined;\n this._destroy$.next();\n }\n}\n\n/**\n * Context key to retrieve the view ID for microfrontends embedded in the context of a workbench view.\n *\n * @docs-private Not public API. For internal use only.\n * @ignore\n * @see {@link ContextService}\n */\nexport const ɵVIEW_ID_CONTEXT_KEY = 'ɵworkbench.view.id';\n\n/**\n * Looks up the corresponding view capability for each capability id emitted by the source Observable.\n *\n * For new subscribers, the most recently looked up capability is replayed. It is guaranteed that no stale capability\n * is replayed, that is, that the replayed capability always corresponds to the most recent emitted capability id of\n * the source Observable.\n */\nfunction lookupViewCapabilityAndShareReplay(): OperatorFunction<string, WorkbenchViewCapability> {\n let latestViewCapabilityId: string;\n\n return pipe(\n distinctUntilChanged(),\n tap(viewCapabilityId => latestViewCapabilityId = viewCapabilityId),\n switchMap(viewCapabilityId => Beans.get(ManifestService).lookupCapabilities$<WorkbenchViewCapability>({id: viewCapabilityId})), // async call; long-living\n map(viewCapabilities => viewCapabilities[0]!),\n // Replay the latest looked up capability for new subscribers.\n shareReplay({refCount: false, bufferSize: 1}),\n // Ensure not to replay a stale capability upon the subscription of new subscribers. For this reason, we install a filter to filter them out.\n // The 'shareReplay' operator would replay a stale capability if the source has emitted a new capability id, but the lookup for it did not complete yet.\n filter(viewCapability => latestViewCapabilityId === viewCapability.metadata!.id),\n );\n}\n","/*\n * Copyright (c) 2018-2022 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\nimport {Beans, Initializer} from '@scion/toolkit/bean-manager';\nimport {ContextService} from '@scion/microfrontend-platform';\nimport {WorkbenchView} from './workbench-view';\nimport {ɵVIEW_ID_CONTEXT_KEY, ɵWorkbenchView} from './ɵworkbench-view';\nimport {ViewId} from '../workbench.identifiers';\n\n/**\n * Registers {@link WorkbenchView} in the bean manager if in the context of a workbench view.\n *\n * @internal\n */\nexport class WorkbenchViewInitializer implements Initializer {\n\n public async init(): Promise<void> {\n const viewId = await Beans.get(ContextService).lookup<ViewId>(ɵVIEW_ID_CONTEXT_KEY);\n if (viewId !== null) {\n const workbenchView = new ɵWorkbenchView(viewId);\n Beans.register(WorkbenchView, {useValue: workbenchView});\n Beans.register(Symbol.for('WORKBENCH_ELEMENT'), {useExisting: WorkbenchView});\n // Wait until initialized the view, supporting synchronous access to view properties in microfrontend constructor.\n await workbenchView.whenProperties;\n }\n }\n}\n","/*\n * Copyright (c) 2018-2022 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\nimport {Qualifier} from '@scion/microfrontend-platform';\nimport {Dictionary} from '@scion/toolkit/util';\nimport {Empty} from '../common/utility-types';\nimport {PartId} from '../workbench.identifiers';\n\n/**\n * Enables navigation of workbench views.\n *\n * A view is a visual workbench element for displaying content side-by-side or stacked.\n *\n * A microfrontend provided as a view capability can be opened in a view. The qualifier differentiates between different\n * view capabilities. Declaring an intention allows for opening public view capabilities of other applications.\n *\n * @category Router\n * @category View\n */\nexport abstract class WorkbenchRouter {\n\n /**\n * Navigates to a microfrontend of a view capability based on the given qualifier and extras.\n *\n * By default, the router opens a new view if no view is found that matches the specified qualifier and required params. Optional parameters do not affect view resolution.\n * If one or more views match the qualifier and required params, they will be navigated instead of opening the microfrontend in a new view tab.\n * This behavior can be changed by setting an explicit navigation target in navigation extras.\n *\n * @param qualifier - Identifies the view capability that provides the microfrontend to display in a view.\n * Passing an empty qualifier (`{}`) allows the microfrontend to update its parameters, restoring updated parameters when the page reloads.\n * Parameter handling can be controlled using the {@link WorkbenchNavigationExtras#paramsHandling} option.\n * @param extras - Options to control navigation.\n * @return Promise that resolves to `true` on successful navigation, or `false` otherwise.\n */\n public abstract navigate(qualifier: Qualifier | Empty<Qualifier>, extras?: WorkbenchNavigationExtras): Promise<boolean>;\n}\n\n/**\n * Options to control the navigation.\n *\n * @category Router\n * @category View\n */\nexport interface WorkbenchNavigationExtras {\n /**\n * Passes data to the view.\n *\n * The view can declare mandatory and optional parameters. No additional parameters are allowed. Refer to the documentation of the capability for more information.\n */\n params?: Map<string, any> | Dictionary;\n /**\n * Instructs the workbench router how to handle params in self-navigation.\n *\n * Self-navigation allows the microfrontend to update its parameters, restoring updated parameters when the page reloads.\n * Setting a `paramsHandling` strategy has no effect on navigations other than self-navigation. A self-navigation is\n * initiated by passing an empty qualifier.\n *\n * One of:\n * * `replace`: Replaces current parameters (default).\n * * `merge`: Merges new parameters with current parameters, with new parameters of equal name overwriting existing parameters.\n * A parameter can be removed by passing `undefined` as its value.\n */\n paramsHandling?: 'merge' | 'replace';\n /**\n * Controls where to open the view. Defaults to `auto`.\n *\n * One of:\n * - `auto`: Navigates existing views that match the qualifier and required params, or opens a new view otherwise. Optional parameters do not affect view resolution.\n * - `blank`: Opens a new view and navigates it.\n * - `<viewId>`: Navigates the specified view. If already opened, replaces it, or opens a new view otherwise.\n */\n target?: string | 'blank' | 'auto';\n /**\n * Controls which part to navigate views in.\n *\n * If {@link target} is `blank`, opens the view in the specified part.\n * If {@link target} is `auto`, navigates matching views in the specified part, or opens a new view in that part otherwise.\n *\n * If the specified part is not in the layout, opens the view in the active part, with the active part of the main area, if any, taking precedence.\n */\n partId?: PartId | string;\n /**\n * Instructs the router to activate the view. Defaults to `true`.\n */\n activate?: boolean;\n /**\n * Closes views that match the specified qualifier and required parameters. Optional parameters do not affect view resolution.\n *\n * The parameters support the asterisk wildcard value (`*`) to match views with any value for a parameter.\n *\n * Only views for which the application has an intention can be closed.\n */\n close?: boolean;\n /**\n * Specifies where to insert the view into the tab bar. Has no effect if navigating an existing view. Defaults to after the active view.\n */\n position?: number | 'start' | 'end' | 'before-active-view' | 'after-active-view';\n /**\n * Specifies CSS class(es) to add to the view, e.g., to locate the view in tests.\n */\n cssClass?: string | string[];\n}\n","/*\n * Copyright (c) 2018-2022 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\nimport {Qualifier} from '@scion/microfrontend-platform';\nimport {WorkbenchPopupConfig} from './workbench-popup.config';\n\n/**\n * Displays a microfrontend in a popup.\n *\n * A popup is a visual workbench element for displaying content above other content. It is positioned relative to an anchor,\n * which can be an element or a coordinate. The popup moves with the anchor. By default, the popup closes on focus loss or\n * when pressing the escape key.\n *\n * A microfrontend provided as a `popup` capability can be opened in a popup. The qualifier differentiates between different\n * popup capabilities. Declaring an intention allows for opening public popup capabilities of other applications.\n *\n * A popup can be bound to a context (e.g., a part or view), displaying the popup only if the context is visible and closing\n * it when the context is disposed. Defaults to the calling context.\n *\n * @category Popup\n * @see WorkbenchPopupCapability\n */\nexport abstract class WorkbenchPopupService {\n\n /**\n * Opens the microfrontend of a `popup` capability in a workbench popup based on the given qualifier and options.\n *\n * An anchor is used to position the popup based on its preferred alignment. The anchor can be an element or a coordinate.\n *\n * By default, the popup closes on focus loss or when pressing the escape key.\n *\n * @param qualifier - Identifies the popup capability that provides the microfrontend to open in a popup.\n * @param config - Controls the appearance and behavior of the popup.\n * @returns Promise that resolves to the popup result, if any, or that rejects if the popup was closed with an error or couldn't be opened,\n * e.g., because of missing the intention or because no `popup` capability matching the qualifier and visible to the application\n * was found.\n */\n public abstract open<T>(qualifier: Qualifier, config: WorkbenchPopupConfig): Promise<T | undefined>;\n}\n","/*\n * Copyright (c) 2018-2024 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\nimport {WorkbenchPopupCapability} from './workbench-popup-capability';\nimport {WorkbenchPopupReferrer} from './workbench-popup-referrer';\nimport {Observable} from 'rxjs';\nimport {PopupId} from '../workbench.identifiers';\n\n/**\n * A popup is a visual workbench component for displaying content above other content.\n *\n * If a microfrontend lives in the context of a workbench popup, regardless of its embedding level, it can inject an instance\n * of this class to interact with the workbench popup, such as reading passed parameters or closing the popup.\n *\n * #### Preferred Size\n * You can report preferred popup size using {@link @scion/microfrontend-platform!PreferredSizeService}. Typically, you would\n * subscribe to size changes of the microfrontend's primary content and report it. As a convenience, {@link @scion/microfrontend-platform!PreferredSizeService}\n * provides API to pass an element for automatic dimension monitoring. If your content can grow and shrink, e.g., if using expandable\n * panels, consider positioning primary content out of the document flow, that is, setting its position to `absolute`. This way,\n * you give it infinite space so that it can always be rendered at its preferred size.\n *\n * ```typescript\n * Beans.get(PreferredSizeService).fromDimension(<HTMLElement>);\n * ```\n *\n * Note that the microfrontend may take some time to load, causing the popup to flicker when opened. Therefore, for fixed-sized\n * popups, consider declaring the popup size in the popup capability.\n *\n * @category Popup\n */\nexport abstract class WorkbenchPopup<R = unknown> {\n\n /**\n * Represents the identity of this popup.\n */\n public abstract readonly id: PopupId;\n\n /**\n * Capability that represents the microfrontend loaded into this workbench popup.\n */\n public abstract readonly capability: WorkbenchPopupCapability;\n\n /**\n * Indicates whether this popup has the focus.\n */\n public abstract readonly focused$: Observable<boolean>;\n\n /**\n * Signals readiness, notifying the workbench that this popup has completed initialization.\n *\n * If `showSplash` is set to `true` on the popup capability, the workbench displays a splash until the popup microfrontend signals readiness.\n *\n * @see WorkbenchPopupCapability.properties.showSplash\n */\n public abstract signalReady(): void;\n\n /**\n * Provides information about the context in which this popup was opened.\n *\n * @deprecated since version 1.0.0-beta.34. Marked for removal. No replacement. Instead, add a parameter to the popup capability for the popup opener to pass required referrer information.\n */\n public abstract readonly referrer: WorkbenchPopupReferrer;\n\n /**\n * Parameters including qualifier entries as passed for navigation by the popup opener.\n */\n public abstract readonly params: Map<string, any>;\n\n /**\n * Sets a result that will be passed to the popup opener when the popup is closed on focus loss {@link CloseStrategy#onFocusLost}.\n */\n public abstract setResult(result?: R): void;\n\n /**\n * Closes the popup. Optionally, pass a result or an error to the popup opener.\n */\n public abstract close(result?: R | Error): void;\n}\n","/*\n * Copyright (c) 2018-2022 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\nimport {WorkbenchPopupCapability} from './workbench-popup-capability';\nimport {WorkbenchPopupReferrer} from './workbench-popup-referrer';\nimport {PopupId} from '../workbench.identifiers';\n\n/**\n * Context when displaying a microfrontend in a popup.\n *\n * This object can be obtained from the {@link ContextService} using the name {@link ɵPOPUP_CONTEXT}.\n *\n * @docs-private Not public API. For internal use only.\n * @ignore\n */\nexport interface ɵPopupContext {\n popupId: PopupId;\n params: Map<string, any>;\n capability: WorkbenchPopupCapability;\n /** @deprecated since version 1.0.0-beta.34. No replacement. Marked for removal. */\n referrer: WorkbenchPopupReferrer;\n}\n\n/**\n * Key for obtaining the current popup context using {@link ContextService}.\n *\n * The popup context is only available to microfrontends loaded in a workbench popup.\n *\n * @docs-private Not public API. For internal use only.\n * @ignore\n * @see {@link ContextService}\n * @see {@link ɵPopupContext}\n */\nexport const ɵPOPUP_CONTEXT = 'ɵworkbench.popup';\n","/*\n * Copyright (c) 2018-2024 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n * SPDX-License-Identifier: EPL-2.0\n */\n\nimport {WorkbenchPopupCapability} from './workbench-popup-capability';\nimport {Beans} from '@scion/toolkit/bean-manager';\nimport {ContextService, mapToBody, MessageClient, MicrofrontendPlatformClient, OUTLET_CONTEXT, OutletContext} from '@scion/microfrontend-platform';\nimport {ɵWorkbenchCommands} from '../ɵworkbench-commands';\nimport {ɵPopupContext} from './workbench-popup-context';\nimport {WorkbenchPopupReferrer} from './workbench-popup-referrer';\nimport {Observable} from 'rxjs';\nimport {shareReplay} from 'rxjs/operators';\nimport {decorateObservable} from '../observable-decorator';\nimport {PopupId} from '../workbench.identifiers';\nimport {WorkbenchPopup} from './workbench-popup';\n\n/**\n * @ignore\n * @docs-private Not public API. For internal use only.\n */\nexport class ɵ