-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(workbench): provide workbench dialog
A dialog is a visual element for focused interaction with the user, such as prompting the user for input or confirming actions. The user can move or resize a dialog. Displayed on top of other content, a dialog blocks interaction with other parts of the application. A dialog can be view-modal or application-modal. Multiple dialogs are stacked, and only the topmost dialog in each modality stack can be interacted with.
- Loading branch information
1 parent
83e5070
commit 34e5acc
Showing
55 changed files
with
3,060 additions
and
301 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
apps/workbench-testing-app/src/app/dialog-opener-page/dialog-opener-page.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<form [formGroup]="form"> | ||
<section> | ||
<sci-form-field label="Component"> | ||
<select [formControl]="form.controls.component" class="e2e-component"> | ||
<option value="dialog-page">DialogPageComponent</option> | ||
<option value="focus-test-page">FocusTestPagePO</option> | ||
<option value="dialog-opener-page">DialogOpenerPageComponent</option> | ||
<option value="blank">BlankTestPageComponent</option> | ||
</select> | ||
</sci-form-field> | ||
</section> | ||
|
||
<section> | ||
<sci-form-field label="Input"> | ||
<sci-key-value-field [keyValueFormArray]="form.controls.options.controls.inputs" [addable]="true" [removable]="true" class="e2e-inputs"/> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Modality"> | ||
<select [formControl]="form.controls.options.controls.modality" class="e2e-modality"> | ||
<option value="application">application</option> | ||
<option value="view">view</option> | ||
<option value="">default</option> | ||
</select> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Contextual View ID"> | ||
<input [formControl]="form.controls.options.controls.contextualViewId" class="e2e-contextual-view-id"> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Animate"> | ||
<sci-checkbox [formControl]="form.controls.options.controls.animate"></sci-checkbox> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="CSS Class(es)"> | ||
<input [formControl]="form.controls.options.controls.cssClass" class="e2e-class" placeholder="Separate multiple CSS classes by space"> | ||
</sci-form-field> | ||
</section> | ||
|
||
<section> | ||
<sci-form-field label="View Context Active"> | ||
<sci-checkbox [formControl]="form.controls.viewContext" class="e2e-view-context"></sci-checkbox> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Count"> | ||
<input [formControl]="form.controls.count" class="e2e-count" | ||
placeholder="Number of dialogs to open (1 by default). For every dialog, an 'index-$1' CSS class is added, where $1 is the index of the dialog, starting with 0."> | ||
</sci-form-field> | ||
</section> | ||
</form> | ||
|
||
<button (click)="onDialogOpen()" class="e2e-open" [disabled]="form.invalid" sci-primary> | ||
Open dialog | ||
</button> | ||
|
||
<output class="return-value e2e-return-value" *ngIf="returnValue"> | ||
{{returnValue}} | ||
</output> | ||
|
||
<output class="dialog-error e2e-dialog-error" *ngIf="dialogError"> | ||
{{dialogError}} | ||
</output> |
39 changes: 39 additions & 0 deletions
39
apps/workbench-testing-app/src/app/dialog-opener-page/dialog-opener-page.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
:host { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 1em; | ||
padding: 1em; | ||
|
||
> form { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 1em; | ||
|
||
> section { | ||
display: flex; | ||
flex-direction: column; | ||
gap: .5em; | ||
border: 1px solid var(--sci-color-border); | ||
border-radius: var(--sci-corner); | ||
padding: 1em; | ||
} | ||
} | ||
|
||
> output.return-value { | ||
border: 1px solid var(--sci-color-positive); | ||
background-color: var(--sci-color-background-positive); | ||
color: var(--sci-color-positive); | ||
border-radius: var(--sci-corner); | ||
padding: 1em; | ||
font-family: monospace; | ||
} | ||
|
||
> output.dialog-error { | ||
border: 1px solid var(--sci-color-negative); | ||
background-color: var(--sci-color-background-negative); | ||
color: var(--sci-color-negative); | ||
border-radius: var(--sci-corner); | ||
padding: 1em; | ||
font-family: monospace; | ||
} | ||
} |
124 changes: 124 additions & 0 deletions
124
apps/workbench-testing-app/src/app/dialog-opener-page/dialog-opener-page.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/* | ||
* Copyright (c) 2018-2023 Swiss Federal Railways | ||
* | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*/ | ||
|
||
import {ApplicationRef, Component, Type} from '@angular/core'; | ||
import {FormGroup, NonNullableFormBuilder, ReactiveFormsModule, Validators} from '@angular/forms'; | ||
import {WorkbenchDialogService} from '@scion/workbench'; | ||
import {startWith} from 'rxjs/operators'; | ||
import {NgIf} from '@angular/common'; | ||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; | ||
import {stringifyError} from '../common/stringify-error.util'; | ||
import {KeyValueEntry, SciKeyValueFieldComponent} from '@scion/components.internal/key-value-field'; | ||
import {SciFormFieldComponent} from '@scion/components.internal/form-field'; | ||
import {SciCheckboxComponent} from '@scion/components.internal/checkbox'; | ||
import {DialogPageComponent} from '../dialog-page/dialog-page.component'; | ||
import BlankTestPageComponent from '../test-pages/blank-test-page/blank-test-page.component'; | ||
import {FocusTestPageComponent} from '../test-pages/focus-test-page/focus-test-page.component'; | ||
|
||
@Component({ | ||
selector: 'app-dialog-opener-page', | ||
templateUrl: './dialog-opener-page.component.html', | ||
styleUrls: ['./dialog-opener-page.component.scss'], | ||
standalone: true, | ||
imports: [ | ||
NgIf, | ||
ReactiveFormsModule, | ||
SciFormFieldComponent, | ||
SciKeyValueFieldComponent, | ||
SciCheckboxComponent, | ||
], | ||
}) | ||
export default class DialogOpenerPageComponent { | ||
|
||
public form = this._formBuilder.group({ | ||
component: this._formBuilder.control('dialog-page', Validators.required), | ||
options: this._formBuilder.group({ | ||
inputs: this._formBuilder.array<FormGroup<KeyValueEntry>>([]), | ||
modality: this._formBuilder.control<'application' | 'view' | ''>(''), | ||
contextualViewId: this._formBuilder.control(''), | ||
cssClass: this._formBuilder.control(''), | ||
animate: this._formBuilder.control(undefined), | ||
}), | ||
count: this._formBuilder.control(''), | ||
viewContext: this._formBuilder.control(true), | ||
}); | ||
public dialogError: string | undefined; | ||
public returnValue: string | undefined; | ||
|
||
constructor(private _formBuilder: NonNullableFormBuilder, | ||
private _dialogService: WorkbenchDialogService, | ||
private _appRef: ApplicationRef) { | ||
this.installContextualViewIdEnabler(); | ||
} | ||
|
||
public async onDialogOpen(): Promise<void> { | ||
this.dialogError = undefined; | ||
this.returnValue = undefined; | ||
|
||
const unsetViewContext = !this.form.controls.viewContext.value; | ||
const dialogService = unsetViewContext ? this._appRef.injector.get(WorkbenchDialogService) : this._dialogService; | ||
|
||
const dialogs = []; | ||
for (let i = 0; i < Number(this.form.controls.count.value || 1); i++) { | ||
dialogs.push(this.openDialog(dialogService, i)); | ||
} | ||
await Promise.all(dialogs); | ||
} | ||
|
||
private openDialog(dialogService: WorkbenchDialogService, index: number): Promise<string | undefined> { | ||
const component = this.parseComponentFromUI(); | ||
return dialogService.open<string | undefined>(component, { | ||
inputs: SciKeyValueFieldComponent.toDictionary(this.form.controls.options.controls.inputs) ?? undefined, | ||
modality: this.form.controls.options.controls.modality.value || undefined, | ||
cssClass: [`index-${index}`].concat(this.form.controls.options.controls.cssClass.value.split(/\s+/).filter(Boolean) || []), | ||
animate: this.form.controls.options.controls.animate.value, | ||
context: { | ||
viewId: this.form.controls.options.controls.contextualViewId.value || undefined, | ||
}, | ||
}) | ||
.then(result => this.returnValue = result) | ||
.catch(error => this.dialogError = stringifyError(error) || 'Workbench Dialog was closed with an error'); | ||
} | ||
|
||
private parseComponentFromUI(): Type<DialogPageComponent | BlankTestPageComponent | DialogOpenerPageComponent> { | ||
switch (this.form.controls.component.value) { | ||
case 'dialog-page': | ||
return DialogPageComponent; | ||
case 'dialog-opener-page': | ||
return DialogOpenerPageComponent; | ||
case 'focus-test-page': | ||
return FocusTestPageComponent; | ||
case 'blank': | ||
return BlankTestPageComponent; | ||
default: | ||
throw Error(`[IllegalDialogComponent] Dialog component not supported: ${this.form.controls.component.value}`); | ||
} | ||
} | ||
|
||
/** | ||
* Enables the field for setting a contextual view reference when choosing view modality. | ||
*/ | ||
private installContextualViewIdEnabler(): void { | ||
this.form.controls.options.controls.modality.valueChanges | ||
.pipe( | ||
startWith(this.form.controls.options.controls.modality.value), | ||
takeUntilDestroyed(), | ||
) | ||
.subscribe(modality => { | ||
if (modality === 'view') { | ||
this.form.controls.options.controls.contextualViewId.enable(); | ||
} | ||
else { | ||
this.form.controls.options.controls.contextualViewId.setValue(''); | ||
this.form.controls.options.controls.contextualViewId.disable(); | ||
} | ||
}); | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
apps/workbench-testing-app/src/app/dialog-page/dialog-page.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<form [formGroup]="form"> | ||
<sci-form-field label="Title" direction="column"> | ||
<input [formControl]="form.controls.title" class="e2e-title"> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Input" *ngIf="input"> | ||
<input class="e2e-input" [value]="input" disabled> | ||
</sci-form-field> | ||
|
||
<sci-accordion class="e2e-size" variant="solid"> | ||
<ng-template sciAccordionItem [panel]="panel_size"> | ||
<header>Size</header> | ||
</ng-template> | ||
<ng-template #panel_size> | ||
<sci-form-field label="Min Height"> | ||
<input [formControl]="form.controls.size.controls.minHeight"> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Height"> | ||
<input [formControl]="form.controls.size.controls.height"> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Max Height"> | ||
<input [formControl]="form.controls.size.controls.maxHeight"> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Min Width"> | ||
<input [formControl]="form.controls.size.controls.minWidth"> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Width"> | ||
<input [formControl]="form.controls.size.controls.width"> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Max Width"> | ||
<input [formControl]="form.controls.size.controls.maxWidth"> | ||
</sci-form-field> | ||
</ng-template> | ||
</sci-accordion> | ||
|
||
<sci-accordion class="e2e-miscellaneous" variant="solid"> | ||
<ng-template sciAccordionItem [panel]="panel_miscellaneous"> | ||
<header>Miscellaneous</header> | ||
</ng-template> | ||
<ng-template #panel_miscellaneous> | ||
<sci-form-field label="Instance ID" title="Unique identifier of this component instance"> | ||
<input class="e2e-component-instance-id" [value]="uuid" disabled> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Closable"> | ||
<sci-checkbox [formControl]="form.controls.miscellaneous.controls.closable" class="e2e-closable"></sci-checkbox> | ||
</sci-form-field> | ||
|
||
<sci-form-field label="Padding"> | ||
<input [formControl]="form.controls.miscellaneous.controls.padding"> | ||
</sci-form-field> | ||
</ng-template> | ||
</sci-accordion> | ||
|
||
<sci-accordion class="return-value e2e-return-value" variant="solid"> | ||
<ng-template sciAccordionItem [panel]="panel_return_value"> | ||
<header>Return Value</header> | ||
</ng-template> | ||
<ng-template #panel_return_value> | ||
<input class="e2e-return-value" [formControl]="form.controls.result" placeholder="Optional data to return to the dialog opener"> | ||
</ng-template> | ||
</sci-accordion> | ||
</form> | ||
|
||
<div class="buttons"> | ||
<button (click)="onClose()" class="e2e-close" sci-primary>Close</button> | ||
<button (click)="onCloseWithError()" class="e2e-close-with-error">Close (with error)</button> | ||
</div> |
31 changes: 31 additions & 0 deletions
31
apps/workbench-testing-app/src/app/dialog-page/dialog-page.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
@use '@scion/components.internal/design' as sci-design; | ||
|
||
:host { | ||
display: flex; | ||
flex-direction: column; | ||
|
||
> form { | ||
flex: auto; | ||
display: flex; | ||
flex-direction: column; | ||
gap: 1em; | ||
|
||
> sci-accordion { | ||
header { | ||
font-weight: bold; | ||
} | ||
|
||
&.return-value input { | ||
@include sci-design.style-input-field(); | ||
} | ||
} | ||
} | ||
|
||
> div.buttons { | ||
flex: none; | ||
display: grid; | ||
grid-template-columns: 1fr 1fr; | ||
column-gap: .25em; | ||
margin-top: 1em; | ||
} | ||
} |
Oops, something went wrong.