Pull request #6: VM/RED-2614
Merge in SL/common-ui from VM/RED-2614 to master * commit '2e36f3cac77411c4cef0ec5b51165aa62a9d075c': added general disabled implementation in base dialog updated save method definition to accept more optional params moved 'base-dialog component' and 'confirmation-dialog service' in common added 'disableConfirm' property to confirmation dialog component updated confirmation dialog component to include 'dischard changes' action
This commit is contained in:
commit
95644f59e4
@ -1,5 +1,15 @@
|
|||||||
import { Directive, HostListener } from '@angular/core';
|
import { Directive, HostListener, Injector, OnInit } from '@angular/core';
|
||||||
import { IqserEventTarget } from '../utils';
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { FormGroup } from '@angular/forms';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
import { AutoUnsubscribe, IqserEventTarget } from '../utils';
|
||||||
|
import { ConfirmOptions } from '../misc';
|
||||||
|
import { ConfirmationDialogService } from './confirmation-dialog.service';
|
||||||
|
|
||||||
|
export interface SaveOptions {
|
||||||
|
closeAfterSave?: boolean;
|
||||||
|
addMembers?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
/**
|
/**
|
||||||
@ -12,12 +22,87 @@ import { IqserEventTarget } from '../utils';
|
|||||||
* Make sure to remove the (submit)="save()" property from the form and to set type="button" on the save button
|
* Make sure to remove the (submit)="save()" property from the form and to set type="button" on the save button
|
||||||
* (otherwise the save request will be triggered twice).
|
* (otherwise the save request will be triggered twice).
|
||||||
* */
|
* */
|
||||||
export abstract class BaseDialogComponent {
|
export abstract class BaseDialogComponent extends AutoUnsubscribe implements OnInit {
|
||||||
abstract changed: boolean;
|
|
||||||
abstract valid: boolean;
|
|
||||||
abstract disabled: boolean;
|
|
||||||
|
|
||||||
abstract save(): void;
|
protected readonly _dialogService: ConfirmationDialogService = this._injector.get(ConfirmationDialogService);
|
||||||
|
protected _waitingForConfirmation = false;
|
||||||
|
|
||||||
|
form!: FormGroup;
|
||||||
|
initialFormValue;
|
||||||
|
|
||||||
|
constructor(protected readonly _injector: Injector, protected readonly _dialogRef: MatDialogRef<BaseDialogComponent>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract save(options?: SaveOptions): void;
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.addSubscription = this._dialogRef.backdropClick().subscribe(() => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get valid(): boolean {
|
||||||
|
return this.form.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
get changed(): boolean {
|
||||||
|
for (const key of Object.keys(this.form.getRawValue())) {
|
||||||
|
const initialValue = this.initialFormValue[key];
|
||||||
|
const updatedValue = this.form.get(key).value;
|
||||||
|
|
||||||
|
if (initialValue == null && updatedValue != null) {
|
||||||
|
const updatedValueType = typeof updatedValue;
|
||||||
|
if (updatedValueType !== 'string' && updatedValueType !== 'boolean') {
|
||||||
|
return true;
|
||||||
|
} else if (updatedValueType === 'string' && updatedValue.length > 0) {
|
||||||
|
return true;
|
||||||
|
} else if (updatedValueType === 'boolean' && updatedValue === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (initialValue !== updatedValue) {
|
||||||
|
if (Array.isArray(updatedValue)) {
|
||||||
|
if (JSON.stringify(initialValue) !== JSON.stringify(updatedValue)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (updatedValue instanceof moment) {
|
||||||
|
if (!moment(updatedValue).isSame(moment(initialValue))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabled(): boolean {
|
||||||
|
return !this.valid || !this.changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
if (this.changed) {
|
||||||
|
this._openConfirmDialog().then(result => {
|
||||||
|
if (result in ConfirmOptions) {
|
||||||
|
if (result === ConfirmOptions.CONFIRM) {
|
||||||
|
this.save({ closeAfterSave: true });
|
||||||
|
} else {
|
||||||
|
this._dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._waitingForConfirmation = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _openConfirmDialog() {
|
||||||
|
this._waitingForConfirmation = true;
|
||||||
|
const dialogRef = this._dialogService.openDialog({ disableConfirm: !this.valid });
|
||||||
|
return dialogRef.afterClosed().toPromise();
|
||||||
|
}
|
||||||
|
|
||||||
@HostListener('window:keydown.Enter', ['$event'])
|
@HostListener('window:keydown.Enter', ['$event'])
|
||||||
onEnter(event: KeyboardEvent): void {
|
onEnter(event: KeyboardEvent): void {
|
||||||
@ -26,4 +111,11 @@ export abstract class BaseDialogComponent {
|
|||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('window:keydown.Escape', ['$event'])
|
||||||
|
onEscape(): void {
|
||||||
|
if (!this._waitingForConfirmation) {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/lib/dialog/confirmation-dialog.service.ts
Normal file
39
src/lib/dialog/confirmation-dialog.service.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { ConfirmationDialogComponent, ConfirmationDialogInput, DialogConfig, TitleColors } from '@iqser/common-ui';
|
||||||
|
import { DialogService } from '../services/dialog.service';
|
||||||
|
|
||||||
|
type DialogType = 'confirm';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class ConfirmationDialogService extends DialogService<DialogType> {
|
||||||
|
protected readonly _config: DialogConfig<DialogType> = {
|
||||||
|
confirm: {
|
||||||
|
component: ConfirmationDialogComponent,
|
||||||
|
dialogConfig: { disableClose: false },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(protected readonly _dialog: MatDialog) {
|
||||||
|
super(_dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
openDialog(data?): MatDialogRef<unknown> {
|
||||||
|
return super.openDialog(
|
||||||
|
'confirm',
|
||||||
|
null,
|
||||||
|
new ConfirmationDialogInput({
|
||||||
|
title: _('confirmation-dialog.unsaved-changes.title'),
|
||||||
|
question: _('confirmation-dialog.unsaved-changes.question'),
|
||||||
|
details: _('confirmation-dialog.unsaved-changes.details'),
|
||||||
|
confirmationText: _('confirmation-dialog.unsaved-changes.confirmation-text'),
|
||||||
|
discardChangesText: _('confirmation-dialog.unsaved-changes.discard-changes-text'),
|
||||||
|
disableConfirm: data.disableConfirm,
|
||||||
|
titleColor: TitleColors.WARN,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,8 +15,8 @@
|
|||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button
|
<button
|
||||||
(click)="confirm(1)"
|
(click)="confirm(ConfirmOptions.CONFIRM)"
|
||||||
[disabled]="config.requireInput && confirmationDoesNotMatch()"
|
[disabled]="(config.requireInput && confirmationDoesNotMatch()) || config.disableConfirm"
|
||||||
color="primary"
|
color="primary"
|
||||||
mat-flat-button
|
mat-flat-button
|
||||||
>
|
>
|
||||||
@ -24,16 +24,22 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
(click)="confirm(2)"
|
(click)="confirm(ConfirmOptions.SECOND_CONFIRM)"
|
||||||
*ngIf="config.alternativeConfirmationText"
|
*ngIf="config.alternativeConfirmationText"
|
||||||
[disabled]="config.requireInput && confirmationDoesNotMatch()"
|
[disabled]="config.requireInput && confirmationDoesNotMatch()"
|
||||||
|
[ngClass]="{'all-caps-label cancel': true}"
|
||||||
color="primary"
|
color="primary"
|
||||||
mat-flat-button
|
mat-flat-button
|
||||||
|
|
||||||
>
|
>
|
||||||
{{ config.alternativeConfirmationText }}
|
{{ config.alternativeConfirmationText }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div (click)="deny()" class="all-caps-label cancel">
|
<div *ngIf="config.discardChangesText" (click)="confirm(ConfirmOptions.DISCARD_CHANGES)" class="all-caps-label cancel">
|
||||||
|
{{ config.discardChangesText }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="!config.discardChangesText" (click)="deny()" class="all-caps-label cancel">
|
||||||
{{ config.denyText }}
|
{{ config.denyText }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -10,6 +10,12 @@ export enum TitleColors {
|
|||||||
WARN = 'warn',
|
WARN = 'warn',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ConfirmOptions {
|
||||||
|
CONFIRM = 1,
|
||||||
|
SECOND_CONFIRM = 2,
|
||||||
|
DISCARD_CHANGES = 3,
|
||||||
|
}
|
||||||
|
|
||||||
export class ConfirmationDialogInput {
|
export class ConfirmationDialogInput {
|
||||||
title?: string;
|
title?: string;
|
||||||
titleColor?: TitleColor;
|
titleColor?: TitleColor;
|
||||||
@ -17,7 +23,9 @@ export class ConfirmationDialogInput {
|
|||||||
details?: string;
|
details?: string;
|
||||||
confirmationText?: string;
|
confirmationText?: string;
|
||||||
alternativeConfirmationText?: string;
|
alternativeConfirmationText?: string;
|
||||||
|
discardChangesText?: string;
|
||||||
requireInput?: boolean;
|
requireInput?: boolean;
|
||||||
|
disableConfirm?: boolean;
|
||||||
denyText?: string;
|
denyText?: string;
|
||||||
translateParams?: Record<string, unknown>;
|
translateParams?: Record<string, unknown>;
|
||||||
|
|
||||||
@ -28,7 +36,9 @@ export class ConfirmationDialogInput {
|
|||||||
this.details = options?.details || '';
|
this.details = options?.details || '';
|
||||||
this.confirmationText = options?.confirmationText || _('common.confirmation-dialog.confirm');
|
this.confirmationText = options?.confirmationText || _('common.confirmation-dialog.confirm');
|
||||||
this.alternativeConfirmationText = options?.alternativeConfirmationText;
|
this.alternativeConfirmationText = options?.alternativeConfirmationText;
|
||||||
|
this.discardChangesText = options?.discardChangesText;
|
||||||
this.requireInput = options?.requireInput || false;
|
this.requireInput = options?.requireInput || false;
|
||||||
|
this.disableConfirm = options?.disableConfirm || false;
|
||||||
this.denyText = options?.denyText || _('common.confirmation-dialog.deny');
|
this.denyText = options?.denyText || _('common.confirmation-dialog.deny');
|
||||||
this.translateParams = options?.translateParams || {};
|
this.translateParams = options?.translateParams || {};
|
||||||
}
|
}
|
||||||
@ -43,6 +53,7 @@ export class ConfirmationDialogComponent {
|
|||||||
config: ConfirmationDialogInput;
|
config: ConfirmationDialogInput;
|
||||||
inputValue = '';
|
inputValue = '';
|
||||||
readonly inputLabel: string;
|
readonly inputLabel: string;
|
||||||
|
readonly ConfirmOptions = ConfirmOptions;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _dialogRef: MatDialogRef<ConfirmationDialogComponent>,
|
private readonly _dialogRef: MatDialogRef<ConfirmationDialogComponent>,
|
||||||
@ -75,7 +86,7 @@ export class ConfirmationDialogComponent {
|
|||||||
this._dialogRef.close();
|
this._dialogRef.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm(option: number): void {
|
confirm(option: ConfirmOptions): void {
|
||||||
this._dialogRef.close(option);
|
this._dialogRef.close(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +97,7 @@ export class ConfirmationDialogComponent {
|
|||||||
'details',
|
'details',
|
||||||
'confirmationText',
|
'confirmationText',
|
||||||
'alternativeConfirmationText',
|
'alternativeConfirmationText',
|
||||||
|
'discardChangesText',
|
||||||
'denyText',
|
'denyText',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ export const largeDialogConfig: MatDialogConfig = {
|
|||||||
maxWidth: '90vw',
|
maxWidth: '90vw',
|
||||||
maxHeight: '90vh',
|
maxHeight: '90vh',
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
|
disableClose: true,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const defaultDialogConfig: MatDialogConfig = {
|
export const defaultDialogConfig: MatDialogConfig = {
|
||||||
@ -16,6 +17,7 @@ export const defaultDialogConfig: MatDialogConfig = {
|
|||||||
maxWidth: '90vw',
|
maxWidth: '90vw',
|
||||||
maxHeight: '90vh',
|
maxHeight: '90vh',
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
|
disableClose: true,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type DialogConfig<T extends string> = {
|
export type DialogConfig<T extends string> = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user