UNPKG

@scion/workbench

Version:

SCION Workbench enables the creation of Angular web applications that require a flexible layout to display content side-by-side or stacked, all personalizable by the user via drag & drop. This type of layout is ideal for applications with non-linear workf

1,190 lines (1,179 loc) 255 kB
import * as _angular_cdk_portal from '@angular/cdk/portal'; import { ComponentType } from '@angular/cdk/portal'; import * as i0 from '@angular/core'; import { InjectionToken, Signal, Injector, Type, EnvironmentProviders, TemplateRef, WritableSignal, ComponentRef, Provider, ViewContainerRef, OnDestroy, OnInit, DestroyableInjector, ElementRef, PipeTransform } from '@angular/core'; import { MicrofrontendPlatformConfig, Qualifier, Capability } from '@scion/microfrontend-platform'; import { CanMatchFn, UrlSegment, NavigationExtras, ActivatedRoute } from '@angular/router'; import { Observable, BehaviorSubject } from 'rxjs'; import * as _scion_workbench from '@scion/workbench'; /** * Set of log levels to control logging output. */ declare enum LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3 } /** * Provides information about a message to be logged. */ interface LogEvent { /** * Severity of the message. */ level: LogLevel; /** * Name of the logger used to log the message. */ logger: string; /** * Supplies the message. */ messageSupplier: () => string; /** * Arguments as passed to the logger. */ args: any[]; } /** * Delivers log events to a destination, e.g., writing logs to the console. */ declare abstract class LogAppender { /** * Each message to be logged is passed to this method in the form of a {@link LogEvent} object. */ abstract onLogMessage(event: LogEvent): void; static ɵfac: i0.ɵɵFactoryDeclaration<LogAppender, never>; static ɵprov: i0.ɵɵInjectableDeclaration<LogAppender>; } /** * Logger name to be used to log a message. * * Pass an instance of this class as the first argument to the logger when logging a message. */ declare class LoggerName { private readonly _value; constructor(_value: string); toString(): string; } /** * Logger used by the workbench to log messages. * * Log messages are passed to registered log appenders in the form of log events for delivery to a destination. * * When configuring the workbench, you can register one or more log appenders and specify the minimum severity * level a message must have in order to be logged. At runtime, you can change the minimum required log level * by setting the `loglevel` query parameter. For example, to log messages with debug severity or higher, set * the query parameters as follows: `loglevel=debug`. * * By default, if not registering a log appender, the workbench registers a console log appender. The default * minimal required log level is set to {@link LogLevel#INFO}. * * @see {@link LogAppender} */ declare abstract class Logger { /** * Log level of the workbench logger. */ abstract readonly logLevel: LogLevel; /** * Logs a message with debug severity. * * For messages that are expensive to compose, you can provide a message supplier function. * The logger will call this function only if at least `DEBUG` log level is enabled. * * Optionally, you can pass the logger name as the first argument in the `args` list. The log message * will then be prefixed with that logger name. Logger names must be instances of {@link LoggerName}. */ abstract debug(message: string | (() => string), ...args: any[]): void; abstract debug(message: string | (() => string), loggerName: LoggerName, ...args: any[]): void; /** * Logs a message with info severity. * * For messages that are expensive to compose, you can provide a message supplier function. * The logger will call this function only if at least `INFO` log level is enabled. * * Optionally, you can pass the logger name as the first argument in the `args` list. The log message * will then be prefixed with that logger name. Logger names must be instances of {@link LoggerName}. */ abstract info(message: string | (() => string), ...args: any[]): void; abstract info(message: string | (() => string), loggerName: LoggerName, ...args: any[]): void; /** * Logs a message with warn severity. * * For messages that are expensive to compose, you can provide a message supplier function. * The logger will call this function only if at least `WARN` log level is enabled. * * Optionally, you can pass the logger name as the first argument in the `args` list. The log message * will then be prefixed with that logger name. Logger names must be instances of {@link LoggerName}. */ abstract warn(message: string | (() => string), ...args: any[]): void; abstract warn(message: string | (() => string), loggerName: LoggerName, ...args: any[]): void; /** * Logs a message with error severity. * * For messages that are expensive to compose, you can provide a message supplier function. * The logger will call this function only if at least `ERROR` log level is enabled. * * Optionally, you can pass the logger name as the first argument in the `args` list. The log message * will then be prefixed with that logger name. Logger names must be instances of {@link LoggerName}. */ abstract error(message: string | (() => string), ...args: any[]): void; abstract error(message: string | (() => string), loggerName: LoggerName, ...args: any[]): void; static ɵfac: i0.ɵɵFactoryDeclaration<Logger, never>; static ɵprov: i0.ɵɵInjectableDeclaration<Logger>; } /** * Allows loading the configuration for the SCION Microfrontend Platform asynchronously, e.g., over the network or from a JSON file. */ declare abstract class MicrofrontendPlatformConfigLoader { /** * Loads the platform configuration asynchronously. */ abstract load(): Promise<MicrofrontendPlatformConfig>; static ɵfac: i0.ɵɵFactoryDeclaration<MicrofrontendPlatformConfigLoader, never>; static ɵprov: i0.ɵɵInjectableDeclaration<MicrofrontendPlatformConfigLoader>; } /** * Configures a route to only match workbench parts navigated to the specified part capability. * * Use this guard to differentiate microfrontend routes, which must all have an empty path. * * @example - Route matching a part capability with qualifier {part: 'navigator'} * ```ts * import {Routes} from '@angular/router'; * import {canMatchWorkbenchPartCapability} from '@scion/workbench'; * * const routes: Routes = [ * {path: '', canMatch: [canMatchWorkbenchPartCapability({part: 'navigator'})], component: NavigatorComponent}, * ]; * ``` * * The above route matches the following part capability: * * ```json * { * "type": "part", * "qualifier": { * "part": "navigator" * }, * "properties": { * "path": "" * } * } * ``` * * @param qualifier - Identifies the part capability. * @return guard matching the specified part capability. */ declare function canMatchWorkbenchPartCapability(qualifier: Qualifier): CanMatchFn; /** * Configures a route to only match workbench views navigated to the specified view capability. * * Use this guard to differentiate microfrontend routes, which must all have an empty path. * * @example - Route matching a view capability with qualifier {view: 'search'} * ```ts * import {Routes} from '@angular/router'; * import {canMatchWorkbenchViewCapability} from '@scion/workbench'; * * const routes: Routes = [ * {path: '', canMatch: [canMatchWorkbenchViewCapability({view: 'search'})], component: SearchComponent}, * ]; * ``` * * The above route matches the following view capability: * * ```json * { * "type": "view", * "qualifier": { * "view": "search" * }, * "properties": { * "path": "" * } * } * ``` * * @param qualifier - Identifies the view capability. * @return guard matching the specified view capability. */ declare function canMatchWorkbenchViewCapability(qualifier: Qualifier): CanMatchFn; /** * Configures a route to only match workbench dialogs navigated to the specified dialog capability. * * Use this guard to differentiate microfrontend routes, which must all have an empty path. * * @example - Route matching a dialog capability with qualifier {dialog: 'about'} * ```ts * import {Routes} from '@angular/router'; * import {canMatchWorkbenchDialogCapability} from '@scion/workbench'; * * const routes: Routes = [ * {path: '', canMatch: [canMatchWorkbenchDialogCapability({dialog: 'about'})], component: AboutComponent}, * ]; * ``` * * The above route matches the following dialog capability: * * ```json * { * "type": "dialog", * "qualifier": { * "dialog": "about" * }, * "properties": { * "path": "" * } * } * ``` * * @param qualifier - Identifies the dialog capability. * @return guard matching the specified dialog capability. */ declare function canMatchWorkbenchDialogCapability(qualifier: Qualifier): CanMatchFn; /** * Configures a route to only match workbench messageboxes navigated to the specified messagebox capability. * * Use this guard to differentiate microfrontend routes, which must all have an empty path. * * @example - Route matching a messagebox capability with qualifier {messagebox: 'alert'} * ```ts * import {Routes} from '@angular/router'; * import {canMatchWorkbenchMessageBoxCapability} from '@scion/workbench'; * * const routes: Routes = [ * {path: '', canMatch: [canMatchWorkbenchMessageBoxCapability({messagebox: 'alert'})], component: AlertComponent}, * ]; * ``` * * The above route matches the following messagebox capability: * * ```json * { * "type": "messagebox", * "qualifier": { * "messagebox": "alert" * }, * "properties": { * "path": "" * } * } * ``` * * @param qualifier - Identifies the messagebox capability. * @return guard matching the specified messagebox capability. */ declare function canMatchWorkbenchMessageBoxCapability(qualifier: Qualifier): CanMatchFn; /** * Configures a route to only match workbench popups navigated to the specified popup capability. * * Use this guard to differentiate microfrontend routes, which must all have an empty path. * * @example - Route matching a popup capability with qualifier {popup: 'info'} * ```ts * import {Routes} from '@angular/router'; * import {canMatchWorkbenchPopupCapability} from '@scion/workbench'; * * const routes: Routes = [ * {path: '', canMatch: [canMatchWorkbenchPopupCapability({popup: 'info'})], component: InfoComponent}, * ]; * ``` * * The above route matches the following popup capability: * * ```json * { * "type": "popup", * "qualifier": { * "popup": "info" * }, * "properties": { * "path": "" * } * } * ``` * * @param qualifier - Identifies the popup capability. * @return guard matching the specified popup capability. */ declare function canMatchWorkbenchPopupCapability(qualifier: Qualifier): CanMatchFn; /** * Configures a route to only match workbench notifications navigated to the specified notification capability. * * Use this guard to differentiate microfrontend routes, which must all have an empty path. * * @example - Route matching a notification capability with qualifier {notification: 'info'} * ```ts * import {Routes} from '@angular/router'; * import {canMatchWorkbenchNotificationCapability} from '@scion/workbench'; * * const routes: Routes = [ * {path: '', canMatch: [canMatchWorkbenchNotificationCapability({notification: 'info'})], component: InfoComponent}, * ]; * ``` * * The above route matches the following notification capability: * * ```json * { * "type": "notification", * "qualifier": { * "notification": "info" * }, * "properties": { * "path": "" * } * } * ``` * * @param qualifier - Identifies the notification capability. * @return guard matching the specified notification capability. */ declare function canMatchWorkbenchNotificationCapability(qualifier: Qualifier): CanMatchFn; /** * Format of a microfrontend capability id. */ type MicrofrontendCapabilityId = string; /** * Format of a microfrontend host outlet name. * * Microfrontend host outlets are used to render microfrontends provided by the host application. * * Format: `workbench.microfrontend.host.<capabilityId>.<workbenchElementType>.<workbenchElementId>` * Example: `workbench.microfrontend.host.39768ab.part.5485357a` */ type MicrofrontendHostOutlet = `workbench.microfrontend.host.${MicrofrontendCapabilityId}.${PartId | ViewId | DialogId | PopupId | NotificationId}`; /** * DI token to get a unique id of the workbench. * * The id is different each time the app is reloaded. Different workbench windows have different ids. */ declare const WORKBENCH_ID: InjectionToken<string>; /** * Format of a part identifier. * * Each part is assigned a unique identifier (e.g., `part.9fdf7ab4`, `part.c6485225`, etc.). * A part can also have an alternative id, a meaningful but not necessarily unique name. A part can * be identified either by its unique or alternative id. */ type PartId = `${typeof PART_ID_PREFIX}${string}`; /** * Format of a view identifier. * * Each view is assigned a unique identifier (e.g., `view.d4de99fb`, `view.cad347dd`, etc.). * A view can also have an alternative id, a meaningful but not necessarily unique name. A view can * be identified either by its unique or alternative id. */ type ViewId = `${typeof VIEW_ID_PREFIX}${string}`; /** * Format of a dialog identifier. */ type DialogId = `${typeof DIALOG_ID_PREFIX}${string}`; /** * Format of a popup identifier. */ type PopupId = `${typeof POPUP_ID_PREFIX}${string}`; /** * Format of a notification identifier. */ type NotificationId = `${typeof NOTIFICATION_ID_PREFIX}${string}`; /** * Format of an activity identifier. * * Each activity is assigned a unique identifier (e.g., `activity.9fdf7ab4`, `activity.c6485225`, etc.). * * @docs-private Not public API. For internal use only. */ type ActivityId = `${typeof ACTIVITY_ID_PREFIX}${string}`; /** * Format of a view outlet name. */ type ViewOutlet = ViewId; /** * Format of a part outlet name. */ type PartOutlet = PartId; /** * Union of workbench outlets. */ type WorkbenchOutlet = PartOutlet | ViewOutlet | MicrofrontendHostOutlet; /** * Represents the id prefix of views. * * @see ViewId */ declare const VIEW_ID_PREFIX = "view."; /** * Represents the id prefix of parts. * * @see PartId */ declare const PART_ID_PREFIX = "part."; /** * Represents the id prefix of activities. * * @see ActivityId */ declare const ACTIVITY_ID_PREFIX = "activity."; /** * Represents the id prefix of dialogs. * * @see DialogId */ declare const DIALOG_ID_PREFIX = "dialog."; /** * Represents the id prefix of notifications. * * @see NotificationId */ declare const NOTIFICATION_ID_PREFIX = "notification."; /** * Represents the id prefix of popups. * * @see PopupId */ declare const POPUP_ID_PREFIX = "popup."; /** * Options to control the navigation. */ interface WorkbenchNavigationExtras extends NavigationExtras { /** * Controls where to open the view. Defaults to `auto`. * * One of: * - `auto`: Navigates existing views that match the path, or opens a new view otherwise. Matrix params do not affect view resolution. * - `blank`: Opens a new view and navigates it. * - `<viewId>`: Navigates the specified view. If already opened, replaces it, or opens a new view otherwise. */ target?: ViewId | string | 'blank' | 'auto'; /** * Controls in which part to navigate views. * * If {@link target} is `blank`, opens the view in the specified part. * If {@link target} is `auto`, navigates matching views in the specified part, or opens a new view in that part otherwise. * * 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. */ partId?: PartId | string; /** * Allows differentiation between routes with identical paths. * * Multiple views can navigate to the same path but still resolve to different routes, e.g., the empty-path route to maintain a clean URL. * Like the path, a hint affects view resolution. If set, the router will only navigate views with an equivalent hint, or if not set, views without a hint. * * Example route config matching routes based on the hint passed to the navigation: * ```ts * import {canMatchWorkbenchView} from '@scion/workbench'; * * const routes = [ * { * path: '', * component: View1Component, * canMatch: [canMatchWorkbenchView('hint-1')], // matches navigations with hint 'hint-1' * }, * { * path: '', * component: View2Component, * canMatch: [canMatchWorkbenchView('hint-2')], // matches navigations with hint 'hint-2' * }, * ]; * ``` * @see canMatchWorkbenchView */ hint?: string; /** * Instructs the router to activate the view. Defaults to `true`. */ activate?: boolean; /** * Specifies where to insert the view into the tab bar. Has no effect if navigating an existing view. Defaults to after the active view. */ position?: number | 'start' | 'end' | 'before-active-view' | 'after-active-view'; /** * Associates data with the navigation. * * Unlike matrix parameters, navigation data is stored in the layout and not added to the URL, allowing data to be passed to an empty-path navigation. * * Data must be JSON serializable. Data can be read from {@link WorkbenchView.navigation.data}. */ data?: NavigationData; /** * Passes state to the navigation. * * State is not persistent, unlike {@link data}; however, state is added to the browser's session history to support back/forward browser navigation. * State can be read from {@link WorkbenchView.navigation.state} or from the browser's session history via `history.state`. */ state?: NavigationState; /** * Closes views that match the specified path and navigation hint. Matrix parameters do not affect view resolution. * The path supports the asterisk wildcard segment (`*`) to match views with any value in a segment. * To close a specific view, set a view target instead of a path. */ close?: boolean; /** * Specifies CSS class(es) to add to the view, e.g., to locate the view in tests. */ cssClass?: string | string[]; } /** * Represents an ordered list of path segments instructing the router which route to navigate to. * * A command can be a string or an object literal. A string represents a path segment, an object literal associates matrix parameters with the preceding segment. * Multiple segments can be combined into a single command, separated by a forward slash. * * The first path segment supports the usage of navigational symbols such as `/`, `./`, or `../`. * * Examples: * - Navigates to the path 'path/to/view', passing two parameters: * ['path', 'to', 'view', {param1: 'value1', param2: 'value2'}] * - Alternative syntax using a combined segment: * ['path/to/view', {param1: 'value1', param2: 'value2'}] */ type Commands = readonly unknown[]; /** * URL segments of workbench elements contained in the workbench layout. */ interface Outlets { [outlet: WorkbenchOutlet]: UrlSegment[]; } /** * State associated with a navigation. */ interface NavigationStates { [outlet: WorkbenchOutlet]: NavigationState; } /** * State passed to a navigation. * * State is not persistent, unlike {@link data}; however, state is added to the browser's session history to support back/forward browser navigation. */ interface NavigationState { [key: string]: unknown; } /** * Data associated with a navigation. Data must be JSON serializable. */ interface NavigationData { [key: string]: unknown; } /** * Signature of a function to modify the workbench layout. * * The router will invoke this function with the current workbench layout. The layout has methods for modifying it. * The layout is immutable; each modification creates a new instance. * * The function can call `inject` to get any required dependencies. * * ## Workbench Layout * The workbench layout is an arrangement of parts and views. Parts can be docked to the side or positioned relative to each other. * Views are stacked in parts and can be dragged to other parts. Content can be displayed in both parts and views. * * ## Example * The following example adds a part to the left of the main area, inserts a view and navigates it. * * ```ts * inject(WorkbenchRouter).navigate(layout => layout * .addPart('left', {relativeTo: MAIN_AREA, align: 'left'}) * .addView('navigator', {partId: 'left'}) * .navigateView('navigator', ['path/to/view']) * .activateView('navigator') * ); * ``` * * @param layout - Reference to the current workbench layout for modification. * @return Modified layout, or `null` to cancel the navigation. */ type NavigateFn = (layout: WorkbenchLayout) => Promise<WorkbenchLayout | null> | WorkbenchLayout | null; /** * Signature of a function to provide texts to the SCION Workbench. * * Texts starting with the percent symbol (`%`) are passed to the text provider for translation, with the percent symbol omitted. * * A text provider can be registered via configuration passed to the {@link provideWorkbench} function. * * The function: * - Can call `inject` to get any required dependencies. * - Can call `toSignal` to convert an Observable to a Signal. * * @param key - Translation key of the text. * @param params - Parameters used for text interpolation. * @return Text associated with the key, or `undefined` if not found. * Localized applications should return the text in the current language, and update the signal with the translated text each time when the language changes. */ type WorkbenchTextProviderFn = (key: string, params: { [name: string]: string; }) => Signal<string> | string | undefined; /** * Represents either a text or a key for translation. * * A translation key starts with the percent symbol (`%`) and may include parameters in matrix notation for text interpolation. * Key and parameters are passed to {@link WorkbenchConfig.textProvider} for translation. * * Semicolons in interpolation parameters must be escaped with two backslashes (`\\;`). * * Examples: * - `%key`: translation key * - `%key;param=value`: translation key with a single interpolation parameter * - `%key;param1=value1;param2=value2`: translation key with multiple interpolation parameters * - `text`: no translation key, text is returned as is */ type Translatable = string | `%${string}`; /** * Factory for creating a {@link WorkbenchLayout}. */ declare abstract class WorkbenchLayoutFactory { /** * Creates a layout with given part. * * @param id - The id of the part. Use {@link MAIN_AREA} to add the main area. * @param extras - Controls how to add the part to the layout. * @param extras.activate - Controls whether to activate the part. If not set, defaults to `false`. * @return layout with the part added. */ abstract addPart(id: string | MAIN_AREA, extras?: PartExtras): WorkbenchLayout; static ɵfac: i0.ɵɵFactoryDeclaration<WorkbenchLayoutFactory, never>; static ɵprov: i0.ɵɵInjectableDeclaration<WorkbenchLayoutFactory>; } /** * The workbench layout is an arrangement of parts and views. Parts can be docked to the side or positioned relative to each other. * Views are stacked in parts and can be dragged to other parts. Content can be displayed in both parts and views. * * A typical workbench application has a main area part and parts docked to the side, providing navigation and context-sensitive assistance to * support the user's workflow. * * Multiple layouts, called perspectives, can be created. Users can switch between perspectives. Perspectives share the same main area, if any. * * The layout has methods for modifying it. The layout is immutable; each modification creates a new instance. */ interface WorkbenchLayout { /** * Adds a part with the given id to this layout. Position and size are expressed relative to a reference part. * * A part can also be aligned relative to a docked part, enabling inline layouts within docked parts, such as splitting the docked parts into multiple sections. * * @param id - The id of the part to add. Use {@link MAIN_AREA} to add the main area. * @param relativeTo - Specifies the reference part to lay out the part. * @param extras - Controls how to add the part to the layout. * @return a copy of this layout with the part added. */ addPart(id: string | MAIN_AREA, relativeTo: ReferencePart, extras?: PartExtras): WorkbenchLayout; /** * Adds a part with the given id to this layout, docking it to the specified docking area. * * Docked parts can be minimized to create more space for the main content. Users cannot drag views into or out of docked parts. * * A part can be docked to the left, right, or bottom side of the workbench. * Each side has two docking areas: `left-top` and `left-bottom`, `right-top` and `right-bottom`, and `bottom-left` and `bottom-right`. * Parts added to the same area are stacked, with only one part active per stack. If there is an active part in both stacks of a side, * the two parts are split vertically or horizontally, depending on the side. * * A docked part may be navigated to display content, have views, or define a layout with multiple parts aligned relative to each other. * * @param id - The id of the part to add. * @param dockTo - Controls to which area to dock the part. * @param extras - Controls how to add the part to the layout. * @return a copy of this layout with the part added. */ addPart(id: string, dockTo: DockingArea, extras: DockedPartExtras): WorkbenchLayout; /** * Navigates the specified part based on the provided array of commands and extras. * * Navigating a part displays content when its view stack is empty. A navigated part can still have views but won't display * navigated content unless its view stack is empty. Views cannot be dragged into parts displaying navigated content, except * for the main area part. * * A command can be a string or an object literal. A string represents a path segment, an object literal associates matrix parameters with the preceding segment. * Multiple segments can be combined into a single command, separated by a forward slash. * * By default, navigation is absolute. Set `relativeTo` in extras for relative navigation. * * Usage: * ``` * layout.navigatePart(partId, ['path', 'to', 'part', {param 'value'}]); * layout.navigatePart(partId, ['path/to/part', {param: 'value'}]); * ``` * * @param id - Identifies the part for navigation. * @param commands - Instructs the router which route to navigate to. * @param extras - Controls navigation: * @param extras.hint - Allows differentiation between routes with identical paths. * Multiple parts can navigate to the same path but still resolve to different routes, e.g., the empty-path route to maintain a clean URL. * * Example: * ```ts * import {canMatchWorkbenchPart} from '@scion/workbench'; * * const routes = [ * { * path: '', * component: Part1Component, * canMatch: [canMatchWorkbenchPart('hint-1')], // matches navigations with hint 'hint-1' * }, * { * path: '', * component: Part2Component, * canMatch: [canMatchWorkbenchPart('hint-2')], // matches navigations with hint 'hint-1' * }, * ]; * ``` * @param extras.relativeTo - Specifies the route for relative navigation, supporting navigational symbols such as '/', './', or '../' in the commands. * @param extras.data - Associates data with the navigation. * Unlike matrix parameters, navigation data is stored in the layout and not added to the URL, allowing data to be passed to an empty-path navigation. * Data must be JSON serializable. Data can be read from {@link WorkbenchPart.navigation.data}. * @param extras.state - Passes state to the navigation. * State is not persistent, unlike {@link data}; however, state is added to the browser's session history to support back/forward browser navigation. * State can be read from {@link WorkbenchPart.navigation.state} or from the browser's session history via `history.state`. * @param extras.cssClass - Specifies CSS class(es) to add to the part, e.g., to locate the part in tests. * @return a copy of this layout with the part navigated. */ navigatePart(id: string, commands: Commands, extras?: { hint?: string; relativeTo?: ActivatedRoute; data?: NavigationData; state?: NavigationState; cssClass?: string | string[]; }): WorkbenchLayout; /** * Adds a view to the specified part. * * @param id - The id of the view to add. * @param extras - Controls how to add the view to the layout. * @param extras.partId - References the part to which to add the view. * @param extras.position - Specifies the position where to insert the view. The position is zero-based. Defaults to `end`. * @param extras.activateView - Controls whether to activate the view. Defaults to `false`. * @param extras.activatePart - Controls whether to activate the part that contains the view. Defaults to `false`. * @param extras.cssClass - Specifies CSS class(es) to add to the view, e.g., to locate the view in tests. * @return a copy of this layout with the view added. */ addView(id: string, extras: { partId: string; position?: number | 'start' | 'end' | 'before-active-view' | 'after-active-view'; activateView?: boolean; activatePart?: boolean; cssClass?: string | string[]; }): WorkbenchLayout; /** * Navigates the specified view based on the provided array of commands and extras. * * A command can be a string or an object literal. A string represents a path segment, an object literal associates matrix parameters with the preceding segment. * Multiple segments can be combined into a single command, separated by a forward slash. * * By default, navigation is absolute. Set `relativeTo` in extras for relative navigation. * * Usage: * ``` * layout.navigateView(viewId, ['path', 'to', 'view', {param 'value'}]); * layout.navigateView(viewId, ['path/to/view', {param: 'value'}]); * ``` * * @param id - Identifies the view for navigation. * @param commands - Instructs the router which route to navigate to. * @param extras - Controls navigation: * @param extras.hint - Allows differentiation between routes with identical paths. * Multiple views can navigate to the same path but still resolve to different routes, e.g., the empty-path route to maintain a clean URL. * Like the path, a hint affects view resolution. If set, the router will only navigate views with an equivalent hint, or if not set, views without a hint. * * Example route config matching routes based on the hint passed to the navigation: * ```ts * import {canMatchWorkbenchView} from '@scion/workbench'; * * const routes = [ * { * path: '', * component: View1Component, * canMatch: [canMatchWorkbenchView('hint-1')], // matches navigations with hint 'hint-1' * }, * { * path: '', * component: View2Component, * canMatch: [canMatchWorkbenchView('hint-2')], // matches navigations with hint 'hint-2' * }, * ]; * ``` * @param extras.relativeTo - Specifies the route for relative navigation, supporting navigational symbols such as '/', './', or '../' in the commands. * @param extras.data - Associates data with the navigation. * Unlike matrix parameters, navigation data is stored in the layout and not added to the URL, allowing data to be passed to an empty-path navigation. * Data must be JSON serializable. Data can be read from {@link WorkbenchView.navigation.data}. * @param extras.state - Passes state to the navigation. * State is not persistent, unlike {@link data}; however, state is added to the browser's session history to support back/forward browser navigation. * State can be read from {@link WorkbenchView.navigation.state} or from the browser's session history via `history.state`. * @param extras.cssClass - Specifies CSS class(es) to add to the view, e.g., to locate the view in tests. * @return a copy of this layout with the view navigated. */ navigateView(id: string, commands: Commands, extras?: { hint?: string; relativeTo?: ActivatedRoute; data?: NavigationData; state?: NavigationState; cssClass?: string | string[]; }): WorkbenchLayout; /** * Removes given view from the layout. * * - If the view is active, the last used view is activated. * - If the view is the last view in the part, the part is removed unless it is the last part in its grid or a structural part. * * @param id - Specifies the id of the view to remove. * @return a copy of this layout with the view removed. */ removeView(id: string): WorkbenchLayout; /** * Removes given part from the layout. * * If the part is active, the last used part is activated. * * @param id - The id of the part to remove. * @return a copy of this layout with the part removed. */ removePart(id: string): WorkbenchLayout; /** * Moves a view to a different part or moves it within a part. * * @param id - The id of the view to be moved. * @param targetPartId - The id of the part to which to move the view. * @param options - Controls moving of the view. * @param options.position - Specifies the position where to move the view in the target part. The position is zero-based. Defaults to `end` when moving the view to a different part. * @param options.activateView - Controls if to activate the view. Defaults to `false`. * @param options.activatePart - Controls if to activate the target part. Defaults to `false`. * @return a copy of this layout with the view moved. */ moveView(id: string, targetPartId: string, options?: { position?: number | 'start' | 'end' | 'before-active-view' | 'after-active-view'; activateView?: boolean; activatePart?: boolean; }): WorkbenchLayout; /** * Activates the given view. * * @param id - The id of the view which to activate. * @param options - Controls view activation. * @param options.activatePart - Controls whether to activate the part that contains the view. Defaults to `false`. * @return a copy of this layout with the view activated. */ activateView(id: string, options?: { activatePart?: boolean; }): WorkbenchLayout; /** * Activates the given part. * * @param id - The id of the part which to activate. * @return a copy of this layout with the part activated. */ activatePart(id: string): WorkbenchLayout; /** * Tests if given part is contained in the layout. */ hasPart(id: string): boolean; /** * Tests if given view is contained in the layout. */ hasView(id: string): boolean; /** * Applies a modification function to this layout, enabling conditional changes while maintaining method chaining. * * @param modifyFn - A function that takes the current layout and returns a modified layout. * @return The modified layout returned by the modify function. */ modify(modifyFn: (layout: WorkbenchLayout) => WorkbenchLayout): WorkbenchLayout; } /** * Describes how to lay out a part relative to another part. */ interface ReferencePart { /** * Specifies the part which to use as the reference part to lay out the part. * If not set, the part will be aligned relative to the root of the workbench layout. */ relativeTo?: string; /** * Specifies the side of the reference part where to add the part. */ align: 'left' | 'right' | 'top' | 'bottom'; /** * Specifies the proportional size of the part relative to the reference part. * The ratio is the closed interval [0,1]. If not set, defaults to `0.5`. */ ratio?: number; } /** * Controls the appearance and behavior of a part. */ interface PartExtras { /** * Title displayed in the part bar. * * 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. */ title?: Translatable; /** * Specifies CSS class(es) to add to the part, e.g., to locate the part in tests. */ cssClass?: string | string[]; /** * Controls whether to activate the part. Defaults to `false`. */ activate?: boolean; } /** * Controls the appearance of a docked part and its toggle button. * * A docked part is a part that is docked to the left, right, or bottom side of the workbench. * * Docked parts can be minimized to create more space for the main content. Users cannot drag * views into or out of docked parts. */ interface DockedPartExtras { /** * Icon (key) displayed in the toggle button. * * The actual icon is resolved through an {@link WorkbenchIconProviderFn} registered in {@link WorkbenchConfig.iconProvider}. * * If no icon provider is configured, the icon defaults to a Material Icon font ligature. The default icon provider requires * the application to include the Material icon font, for example in `styles.scss`, as follows: * * ```scss * @import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded'); * ``` * * The application can then reference icons from the Material Icons Font: https://fonts.google.com/icons */ icon: string; /** * Label displayed in the toggle button. * * 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. */ label: Translatable; /** * Tooltip displayed when hovering over the toggle button. * * 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. */ tooltip?: Translatable; /** * Title displayed in the part bar. * * If not provided, defaults to {@link DockedPartExtras.label}. Set to `false` to not display a title. * * 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. */ title?: Translatable | false; /** * CSS class(es) to add to the docked part and its toggle button, e.g., to locate the part in tests. */ cssClass?: string | string[]; /** * Controls whether to activate the docked part. Defaults to `false`. */ activate?: boolean; /** * Internal identifier for the docked part. * * @docs-private Not public API. For internal use only. */ ɵactivityId?: ActivityId; } /** * A part can be docked to the left, right, or bottom side of the workbench. * * Each side has two docking areas: `left-top` and `left-bottom`, `right-top` and `right-bottom`, and `bottom-left` and `bottom-right`. * Parts added to the same area are stacked, with only one part active per stack. If there is an active part in both stacks of a side, * the two parts are split vertically or horizontally, depending on the side. */ interface DockingArea { /** * Controls where to dock a part. * - `left-top`: Dock to the top on the left side. * - `left-bottom`: Dock to the bottom on the left side. * - `right-top`: Dock to the top on the right side. * - `right-bottom`: Dock to the bottom on the right side. * - `bottom-left`: Dock to the left on the bottom side. * - `bottom-right`: Dock to the right on the bottom side. */ dockTo: 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom' | 'bottom-left' | 'bottom-right'; } /** * Identifies the main area part in the workbench layout. * * Refer to this part to align parts relative to the main area. * * The main area is a special part that can be added to the layout. The main area is where the workbench opens views by default. * It is shared between perspectives and its layout is not reset when resetting perspectives. */ declare const MAIN_AREA: MAIN_AREA; /** * Represents the type of the {@link MAIN_AREA} constant. */ type MAIN_AREA = 'part.main-area'; /** * Signature of a function to provide a workbench layout. * * The workbench will invoke this function with a factory object to create the layout. The layout is immutable; each modification creates a new instance. * * The function can call `inject` to get any required dependencies. * * ## Workbench Layout * The workbench layout is an arrangement of parts and views. Parts can be docked to the side or positioned relative to each other. * Views are stacked in parts and can be dragged to other parts. Content can be displayed in both parts and views. * * The layout typically has a main area part and parts docked to the side, providing navigation and context-sensitive assistance to support * the user's workflow. Initially empty or displaying a welcome page, the main area is where the workbench opens views by default. * Unlike any other part, the main area is shared between perspectives, and its layout is not reset when resetting perspectives. * ## Steps to create the layout * Start by adding parts to the layout. Parts can be docked to a specific area (`left-top`, `left-bottom`, `right-top`, `right-bottom`, `bottom-left`, `bottom-right`) * or aligned relative to each other. Views can be added to any part except the main area part. To display content, navigate parts and views to registered routes. * * To maintain a clean URL, navigate parts and views in the layout to empty-path routes and use a navigation hint for differentiation. * - Use the `canMatchWorkbenchPart` guard to match a route only for parts navigated with a particular hint. * - Use the `canMatchWorkbenchView` guard to match a route only for views navigated with a particular hint. * * ## Example * The following example defines a layout with a main area and four parts docked to the side: * * ```plain * +------+-----------+------+ * | | | | * | | | | * | left | main area | right| * | | | | * | | | | * +------+-----------+------+ * | bottom | * +-------------------------+ * ``` * * ```ts * import {bootstrapApplication} from '@angular/platform-browser'; * import {MAIN_AREA, provideWorkbench, WorkbenchLayoutFactory} from '@scion/workbench'; * * bootstrapApplication(AppComponent, { * providers: [ * provideWorkbench({ * layout: (factory: WorkbenchLayoutFactory) => factory * // Add parts to dock areas on the side. * .addPart(MAIN_AREA) * .addPart('navigator', {dockTo: 'left-top'}, {label: 'Navigator', icon: 'folder'}) * .addPart('find', {dockTo: 'bottom-left'}, {label: 'Find', icon: 'search'}) * .addPart('inventory', {dockTo: 'right-top'}, {label: 'Inventory', icon: 'inventory'}) * .addPart('notifications', {dockTo: 'right-bottom'}, {label: 'Notifications', icon: 'notifications'}) * * // Add views to parts. * .addView('find1', {partId: 'find'}) * .addView('find2', {partId: 'find'}) * .addView('find3', {partId: 'find'}) * * // Navigate the main area to display a welcome page. * .navigatePart(MAIN_AREA, [], {hint: 'welcome'}) * * // Navigate parts to display content. * .navigatePart('navigator', [], {hint: 'navigator'}) * .navigatePart('inventory', ['path/to/inventory']) * .navigatePart('notifications', [], {hint: 'notifications'}) * * // Navigate views to display content. * .navigateView('find1', ['path/to/find']) * .navigateView('find2', ['path/to/find']) * .navigateView('find3', ['path/to/find']) * * // Activate parts to open docked parts. * .activatePart('navigator') * .activatePart('find') * .activatePart('inventory'), * }), * ], * }); * ``` * * The layout requires the following routes. * * ```ts * import {bootstrapApplication} from '@angular/platform-browser'; * import {provideRouter} from '@angular/router'; * import {canMatchWorkbenchView, canMatchWorkbenchPart} from '@scion/workbench'; * * bootstrapApplication(AppComponent, { * providers: [ * provideRouter([ * // Route for the "MAIN_AREA Part" * {path: '', canMatch: [canMatchWorkbenchPart('welcome')], loadComponent: () => import('./welcome/welcome.component')}, * // Route for the "Navigator Part" * {path: '', canMatch: [canMatchWorkbenchPart('navigator')], loadComponent: () => import('./navigator/navigator.component')}, * // Route for the "Find View" * {path: 'path/to/find', loadComponent: () => import('./find/find.component')}, * // Route for the "Inventory Part" * {path: 'path/to/inventory', loadComponent: () => import('./inventory/inventory.component')}, * // Route for the "Notifications Part" * {path: '', canMatch: [canMatchWorkbenchPart('notifications')], loadComponent: () => import('./notifications/notifications.component')}, * ]), * ], * }); * ``` */ type WorkbenchLayoutFn = (factory: WorkbenchLayoutFactory) => Promise<WorkbenchLayout> | WorkbenchLayout; /** * Represents a workbench perspective. * * A perspective is a named layout. Multiple perspectives can be created. Users can switch between perspectives. * Perspectives share the same main area, if any. */ interface WorkbenchPerspective { /** * Unique identifier of this perspective. */ readonly id: string; /** * Arbitrary data associated with this perspective. */ readonly data: { [key: string]: unknown; }; /** * Indicates whether this perspective is active. */ readonly active: Signal<boolean>; /** * Indicates whether this perspective is transient, with its layout only memoized, not persisted. */ readonly transient: boolean; } /** * Defines the workbench layouts of the application * * A perspective is a named layout. Multiple perspectives can be created. Users can switch between perspectives. * Perspectives share the same main area, if any. */ interface WorkbenchPerspectives { /** * Specifies perspectives to register in the workbench. */ perspectives?: WorkbenchPerspectiveDefinition[]; /** * Specifies which perspective to activate. Defaults to the first registered perspective. * * The workbench remembers the active perspective and only evaluates this property when starting the workbench * for the first time, after clearing storage, or if the active perspective no longer exists. * * Can be either the identity of a perspective or a function to resolve the perspective. */ initialPerspective?: string | WorkbenchPerspectiveSelectionFn; } /** * Describes a perspective to register in the workbench. */ interface WorkbenchPerspectiveDefinition { /** * Specifies the unique identity of the perspective. */ id: string; /** * Function to create the layout of the perspective. The function can call `inject` to get any required dependencies. * * A perspective defines an arrangement of parts and views. Parts can be docked to the side or positioned relative to each other. * Views are stacked in parts and can be dragged to other parts. Content can be displayed in both parts and views. * * A perspective typically has a main area part and parts docked to the side, providing navigation and context-sensitive assistance to support * the user's workflow. Initially empty or displaying a welcome page, the main area is where the workbench opens views by default. * Unlike any other part, the main area is shared between perspectives, and its layo