@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,258 lines (1,246 loc) • 227 kB
TypeScript
import * as _angular_cdk_portal from '@angular/cdk/portal';
import { ComponentType, ComponentPortal } from '@angular/cdk/portal';
import * as i0 from '@angular/core';
import { InjectionToken, Signal, TemplateRef, Injector, Type, EnvironmentProviders, WritableSignal, Provider, ViewContainerRef, ComponentRef, AbstractType, ElementRef, StaticProvider, PipeTransform } from '@angular/core';
import { MicrofrontendPlatformConfig } from '@scion/microfrontend-platform';
import * as _angular_router from '@angular/router';
import { UrlSegment, NavigationExtras, ActivatedRoute, CanMatchFn } 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>;
}
/**
* Represents the id prefix of parts.
*
* @see PartId
*/
declare const PART_ID_PREFIX = "part.";
/**
* Represents the id prefix of views.
*
* @see ViewId
*/
declare const VIEW_ID_PREFIX = "view.";
/**
* Represents the id prefix of activities.
*/
declare const ACTIVITY_ID_PREFIX = "activity.";
/**
* Represents the id prefix of popups.
*/
declare const POPUP_ID_PREFIX = "popup.";
/**
* Represents the id prefix of dialogs.
*/
declare const DIALOG_ID_PREFIX = "dialog.";
/**
* Represents the id prefix of message boxes.
*/
declare const MESSAGE_BOX_ID_PREFIX = "messagebox.";
/**
* Format of a part outlet name.
*/
type PartOutlet = `${typeof PART_ID_PREFIX}${string}`;
/**
* Format of a view outlet name.
*/
type ViewOutlet = `${typeof VIEW_ID_PREFIX}${number}`;
/**
* Format of a popup outlet name.
*/
type PopupOutlet = `${typeof POPUP_ID_PREFIX}${string}`;
/**
* Format of a dialog outlet name.
*/
type DialogOutlet = `${typeof DIALOG_ID_PREFIX}${string}`;
/**
* Format of a messagebox outlet name.
*/
type MessageBoxOutlet = `${typeof MESSAGE_BOX_ID_PREFIX}${string}`;
/**
* Union of workbench outlets.
*/
type WorkbenchOutlet = PartOutlet | ViewOutlet | PopupOutlet | DialogOutlet | MessageBoxOutlet;
/**
* Defines the context in which a viewtab is rendered.
*
* - `tab`: if rendered as view tab in the tabbar
* - `list-item`: if rendered as list item in the view list menu
* - `drag-image`: if rendered as drag image while dragging a view tab
*/
type ViewTabRenderingContext = 'tab' | 'list-item' | 'drag-image';
/**
* DI token to inject the context in which the view tab is rendered.
*/
declare const VIEW_TAB_RENDERING_CONTEXT: InjectionToken<ViewTabRenderingContext>;
/**
* A part is a visual workbench element to create the workbench layout. Parts can be docked to the side or
* positioned relative to each other. A part can be a stack of views or display content.
*
* The part component can inject this handle to interact with the part.
*
* @see WorkbenchView
*/
declare abstract class WorkbenchPart {
/**
* Unique identity of this part.
*/
abstract readonly id: PartId;
/**
* Alternative identity of this part.
*
* A part can have an alternative id, a meaningful but not necessarily unique name. A part can
* be identified either by its unique or alternative id.
*
* @see id
*/
abstract readonly alternativeId: string | undefined;
/**
* Title displayed in the part bar.
*
* Note that the title of the top-leftmost part of a docked part cannot be changed.
*/
abstract get title(): Signal<string | undefined>;
abstract set title(title: string | undefined);
/**
* Indicates whether this part is located in the workbench main area.
*/
abstract readonly isInMainArea: boolean;
/**
* Indicates whether this part is located in the workbench peripheral area.
*/
abstract readonly peripheral: Signal<boolean>;
/**
* Indicates whether this part is the top-leftmost part.
*/
abstract readonly topLeft: Signal<boolean>;
/**
* Indicates whether this part is the top-rightmost part.
*/
abstract readonly topRight: Signal<boolean>;
/**
* Indicates whether this part is active or inactive.
*/
abstract readonly active: Signal<boolean>;
/**
* Identifies the active view, or `null` if none.
*/
abstract readonly activeViewId: Signal<ViewId | null>;
/**
* Identifies views opened in this part.
*/
abstract readonly viewIds: Signal<ViewId[]>;
/**
* Actions associated with this part.
*/
abstract readonly actions: Signal<WorkbenchPartAction[]>;
/**
* Specifies CSS class(es) to add to the part, e.g., to locate the part in tests.
*/
abstract get cssClass(): Signal<string[]>;
abstract set cssClass(cssClass: string | string[]);
/**
* Provides navigation details of this part.
*
* A part can be navigated to display 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.
*
* A part can be navigated using {@link WorkbenchLayout#navigatePart}.
*/
abstract readonly navigation: Signal<WorkbenchPartNavigation | undefined>;
}
/**
* 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 = PartOutlet;
/**
* Provides navigation details of a workbench part.
*/
interface WorkbenchPartNavigation {
/**
* Path of this part.
*/
path: UrlSegment[];
/**
* Hint passed to the navigation.
*/
hint?: string;
/**
* Data passed to the navigation.
*/
data?: NavigationData;
/**
* State passed to the navigation.
*/
state?: NavigationState;
}
/**
* The signature of a function to confirm closing a view., e.g., if dirty.
*
* The function can call `inject` to get dependencies.
*/
type CanCloseFn = () => Observable<boolean> | Promise<boolean> | boolean;
/**
* Reference to a `CanClose` guard registered on a view.
*/
interface CanCloseRef {
/**
* Removes the `CanClose` guard from the view.
*
* Has no effect if another guard was registered in the meantime.
*/
dispose(): void;
}
/**
* Represents an action of a {@link WorkbenchPart}.
*
* Part actions are displayed in the part bar, enabling interaction with the part and its content. Actions can be aligned to the left or right.
*/
interface WorkbenchPartAction {
/**
* Specifies the content of the action.
*
* Use a {@link ComponentType} to render a component, or a {@link TemplateRef} to render a template.
*
* The component and template can inject the {@link WorkbenchPart}, either through dependency injection or default template-local variable (`let-part`).
*/
content: ComponentType<unknown> | TemplateRef<unknown>;
/**
* Optional data to pass to the component or template.
*
* If using a component, inputs are available as input properties.
*
* ```ts
* @Component({...})
* class ActionComponent {
* someInput = input.required<string>();
* }
* ```
*
* If using a template, inputs are available for binding via local template let declarations.
*
* ```html
* <ng-template let-input="someInput">
* ...
* </ng-template>
* ```
*/
inputs?: {
[name: string]: unknown;
};
/**
* Sets the injector for the instantiation of the component or template, giving control over the objects available for injection.
*
* ```ts
* Injector.create({
* parent: ...,
* providers: [
* {provide: <TOKEN>, useValue: <VALUE>}
* ],
* })
* ```
*/
injector?: Injector;
/**
* Controls where to place this action in the part bar. Defaults to `end`.
*/
align?: 'start' | 'end';
/**
* Specifies CSS class(es) to add to the action, e.g., to locate the action in tests.
*/
cssClass?: string | string[];
}
/**
* Represents a menu item contained in the context menu of a {@link WorkbenchView}.
*
* Right-clicking on a view tab opens a context menu to interact with the view and its content.
*/
interface WorkbenchMenuItem {
/**
* Specifies the content of the menu item.
*
* Use a {@link ComponentType} to render a component, or a {@link TemplateRef} to render a template.
*
* The component and template can inject the {@link WorkbenchView}, either through dependency injection or default template-local variable (`let-view`).
*/
content: ComponentType<unknown> | TemplateRef<unknown>;
/**
* Optional data to pass to the component or template.
*
* If using a component, inputs are available as input properties.
*
* ```ts
* @Component({...})
* class MenuItemComponent {
* someInput = input.required<string>();
* }
* ```
*
* If using a template, inputs are available for binding via local template let declarations.
*
* ```html
* <ng-template let-input="someInput">
* ...
* </ng-template>
* ```
*/
inputs?: {
[name: string]: unknown;
};
/**
* Sets the injector for the instantiation of the component or template, giving control over the objects available for injection.
*
* ```ts
* Injector.create({
* parent: ...,
* providers: [
* {provide: <TOKEN>, useValue: <VALUE>}
* ],
* })
* ```
*/
injector?: Injector;
/**
* Function invoked when the menu item is clicked.
*
* The function can call `inject` to get any required dependencies.
*/
onAction: () => void;
/**
* Binds keyboard accelerator(s) to the menu item, e.g., ['ctrl', 'alt', 1].
*
* Supported modifiers are 'ctrl', 'shift', 'alt' and 'meta'.
*/
accelerator?: string[];
/**
* Enables grouping of menu items.
*/
group?: string;
/**
* Controls if the menu item is disabled. Defaults to `false`.
*/
disabled?: boolean;
/**
* Specifies CSS class(es) to add to the menu item, e.g., to locate the menu item in tests.
*/
cssClass?: string | string[];
}
/**
* Signature of a function to contribute an action to a {@link WorkbenchPart}.
*
* The function:
* - Can return a component or template, or an object literal for more control.
* - Is called per part. Returning the action adds it to the part, returning `null` skips it.
* - Can call `inject` to get any required dependencies.
* - Runs in a reactive context and is called again when tracked signals change.
* Use Angular's `untracked` function to execute code outside this reactive context.
*/
type WorkbenchPartActionFn = (part: WorkbenchPart) => WorkbenchPartAction | ComponentType<unknown> | TemplateRef<unknown> | null;
/**
* Signature of a function to contribute a menu item to the context menu of a {@link WorkbenchView}.
*
* The function:
* - Is called per view. Returning the menu item adds it to the context menu of the view, returning `null` skips it.
* - Can call `inject` to get any required dependencies.
* - Runs in a reactive context and is called again when tracked signals change.
* Use Angular's `untracked` function to execute code outside this reactive context.
*/
type WorkbenchViewMenuItemFn = (view: WorkbenchView) => WorkbenchMenuItem | null;
/**
* Information about a workbench theme.
*
* @deprecated since version 19.0.0-beta.3. Read the theme from `WorkbenchService.settings.theme` signal, and the color scheme from 'getComputedStyle(inject(DOCUMENT).documentElement).colorScheme'. API will be removed in version 21.
*/
interface WorkbenchTheme {
/**
* The name of the theme.
*/
name: string;
/**
* The color scheme of the theme.
*/
colorScheme: 'light' | 'dark';
}
/**
* 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.
* Otherwise, the text is returned as is.
*
* 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 - Key for which to provide the text.
* @param params - Parameters associated with the translation key.
* @return The text associated with the provided key, or `undefined` if not found.
*/
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 can include parameters in matrix notation.
* Key and parameters are passed to {@link WorkbenchConfig.textProvider} for translation.
*
* Examples:
* - `text`: no translatable text
* - `%key`: translation key
* - `%key;param=value`: translation key with a single param
* - `%key;param1=value1;param2=value2`: translation key with multiple parameters
*/
type Translatable = string | `%${string}`;
/**
* A view is a visual workbench element for displaying content stacked or side-by-side in the workbench layout.
*
* Users can drag views from one part to another, even across windows, or place them side-by-side, horizontally and vertically.
*
* The view component can inject this handle to interact with the view.
*
* @see WorkbenchRouter
*/
declare abstract class WorkbenchView {
/**
* Unique identity of this view.
*
* @see alternativeId
*/
abstract readonly id: ViewId;
/**
* Alternative identity of this view.
*
* A view can have an alternative id, a meaningful but not necessarily unique name. A view can
* be identified either by its unique or alternative id.
*
* @see id
*/
abstract readonly alternativeId: string | undefined;
/**
* Provides navigation details of this view.
*
* A view can be navigated using {@link WorkbenchRouter#navigate} or {@link WorkbenchLayout#navigateView}.
*/
abstract readonly navigation: Signal<WorkbenchViewNavigation | undefined>;
/**
* Hint passed to the navigation.
*
* @deprecated since version 19.0.0-beta.2. Use {@link navigation.hint} instead. API will be removed in version 21.
*/
abstract readonly navigationHint: Signal<string | undefined>;
/**
* Data passed to the navigation.
*
* @deprecated since version 19.0.0-beta.2. Use {@link navigation.data} instead. API will be removed in version 21.
*/
abstract readonly navigationData: Signal<NavigationData>;
/**
* State passed to the navigation.
*
* @deprecated since version 19.0.0-beta.2. Use {@link navigation.state} instead. API will be removed in version 21.
*/
abstract readonly navigationState: Signal<NavigationState>;
/**
* Part which contains this view.
*
* Note: the part of a view can change, e.g., when the view is moved to another part.
*/
abstract readonly part: Signal<WorkbenchPart>;
/**
* Title displayed in the view tab.
*/
abstract get title(): Signal<Translatable | null>;
abstract set title(title: Translatable | null);
/**
* Subtitle displayed in the view tab.
*/
abstract get heading(): Signal<Translatable | null>;
abstract set heading(heading: Translatable | null);
/**
* Specifies CSS class(es) to add to the view, e.g., to locate the view in tests.
*/
abstract get cssClass(): Signal<string[]>;
abstract set cssClass(cssClass: string | string[]);
/**
* Indicates whether the view has unsaved changes.
* If marked as dirty, a visual indicator is displayed in the view tab.
*/
abstract get dirty(): Signal<boolean>;
abstract set dirty(dirty: boolean);
/**
* Controls whether the view can be closed. Defaults to `true`.
*/
abstract get closable(): Signal<boolean>;
abstract set closable(closable: boolean);
/**
* Indicates whether this view is active or inactive.
*/
abstract readonly active: Signal<boolean>;
/**
* The position of this view in the tabbar.
*/
abstract readonly position: Signal<number>;
/**
* `True` when this view is the first view in the tabbar.
*/
abstract readonly first: Signal<boolean>;
/**
* `True` when this view is the last view in the tabbar.
*/
abstract readonly last: Signal<boolean>;
/**
* Indicates whether the tab of this view is scrolled into the tabbar.
*/
abstract readonly scrolledIntoView: Signal<boolean>;
/**
* Menu items associated with this view.
*/
abstract readonly menuItems: Signal<WorkbenchMenuItem[]>;
/**
* Indicates whether this view is destroyed.
*/
abstract readonly destroyed: boolean;
/**
* URL associated with this view.
*
* @deprecated since version 19.0.0-beta.2. Use {@link navigation.path} instead. API will be removed in version 21.
*/
abstract readonly urlSegments: Signal<UrlSegment[]>;
/**
* Activates this view.
*
* Note: This instruction runs asynchronously via URL routing.
*/
abstract activate(): Promise<boolean>;
/**
* Destroys this view (or sibling views) and the associated routed component.
*
* Note: This instruction runs asynchronously via URL routing.
*
* @param target
* Allows to control which view(s) to close:
*
* - self: closes this view
* - all-views: closes all views of this part
* - other-views: closes the other views of this part
* - views-to-the-right: closes the views to the right of this view
* - views-to-the-left: closes the views to the left of this view
*
*/
abstract close(target?: 'self' | 'all-views' | 'other-views' | 'views-to-the-right' | 'views-to-the-left'): Promise<boolean>;
/**
* Moves this view to a new browser window.
*/
abstract move(target: 'new-window'): void;
/**
* Moves this view to a different or new part in the specified region.
*
* Specifying a target workbench identifier allows the view to be moved to a workbench in a different browser window.
* The target workbench ID is available via {@link WORKBENCH_ID} DI token in the target application.
*/
abstract move(partId: string, options?: {
region?: 'north' | 'south' | 'west' | 'east';
workbenchId?: string;
}): void;
/**
* Registers a guard to confirm closing the view, replacing any previous guard.
*
* The callback can call `inject` to get dependencies.
*
* Example:
* ```ts
* import {inject} from '@angular/core';
* import {WorkbenchMessageBoxService, WorkbenchView} from '@scion/workbench';
*
* inject(WorkbenchView).canClose(async () => {
* if (!inject(WorkbenchView).dirty()) {
* return true;
* }
* const action = await inject(WorkbenchMessageBoxService).open('Do you want to save changes?', {
* actions: {
* yes: 'Yes',
* no: 'No',
* cancel: 'Cancel'
* }
* });
*
* switch (action) {
* case 'yes':
* // Store changes ...
* return true;
* case 'no':
* return true;
* default:
* return false;
* }
* });
* ```
*
* @param canClose - Callback to confirm closing the view.
* @returns Reference to the `CanClose` guard, which can be used to unregister the guard.
*/
abstract canClose(canClose: CanCloseFn): CanCloseRef;
}
/**
* Format of a view identifier.
*
* Each view is assigned a unique identifier (e.g., `view.1`, `view.2`, 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 = ViewOutlet;
/**
* Provides navigation details of a workbench view.
*/
interface WorkbenchViewNavigation {
/**
* Path of this view.
*/
path: UrlSegment[];
/**
* Hint passed to the navigation.
*/
hint?: string;
/**
* Data passed to the navigation.
*/
data?: NavigationData;
/**
* State passed to the navigation.
*/
state?: NavigationState;
}
/**
* 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;
/**
* Format of an activity identifier.
*
* Each activity is assigned a unique identifier (e.g., `activity.9fdf7ab4`, `activity.c6485225`, etc.).
*
* @docs-private Not public API, intended for internal use only.
*/
type ActivityId = `${typeof ACTIVITY_ID_PREFIX}${string}`;
/**
* Defines the arrangement of docked parts (also referred to as activities) in the workbench.
*
* Docked parts can be minimized to create more space for the main content.
*
* 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.
*
* The M-prefix indicates this object is a model object that is serialized and stored, requiring migration on breaking change.
*/
interface MActivityLayout {
toolbars: {
leftTop: MActivityStack;
leftBottom: MActivityStack;
rightTop: MActivityStack;
rightBottom: MActivityStack;
bottomLeft: MActivityStack;
bottomRight: MActivityStack;
};
panels: {
left: {
width: number;
ratio: number;
};
right: {
width: number;
ratio: number;
};
bottom: {
height: number;
ratio: number;
};
};
}
/**
* Stack of activities in a docking area.
*
* The M-prefix indicates this object is a model object that is serialized and stored, requiring migration on breaking change.
*/
interface MActivityStack {
/**
* List of activities in the stack.
*/
activities: MActivity[];
/**
* Reference to the currently active activity in the stack.
*/
activeActivityId?: ActivityId;
/**
* Reference to the activity that was active prior to minimization, used to restore the active activity when exiting minimization mode.
*/
minimizedActivityId?: ActivityId;
}
/**
* Represents an activity docked to a side of the workbench.
*
* The M-prefix indicates this object is a model object that is serialized and stored, requiring migration on breaking change.
*/
interface MActivity {
/** @see DockedPartExtras#ɵactivityId */
id: ActivityId;
/**
* Refers to the docked part specified when creating the activity.
*
* Characteristics of the reference part:
* - Structural part, remaining even if its last view is removed.
* - Provides the title of the activity, independent of visibility and position in the layout.
* - Explicitly removing this part removes the activity.
*/
referencePartId: PartId;
/** @see DockedPartExtras#icon */
icon: string;
/** @see DockedPartExtras#label */
label: Translatable;
/** @see DockedPartExtras#tooltip */
tooltip?: Translatable;
/** @see DockedPartExtras#cssClass */
cssClass?: 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 other 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;
/**
* 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.
*/
relativeT