update confirmation dialog component
This commit is contained in:
parent
cb8a0ddcf9
commit
a1d11cd9eb
@ -4,13 +4,11 @@ import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
|
||||
import { CommonUiOptions, IqserAppConfig, ModuleOptions } from './utils';
|
||||
import { ToastComponent } from './shared';
|
||||
import { ConnectionStatusComponent, FullPageErrorComponent } from './error';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
|
||||
import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox';
|
||||
import { MatLegacyProgressBarModule as MatProgressBarModule } from '@angular/material/legacy-progress-bar';
|
||||
import { ConfirmationDialogComponent } from './dialog';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { ApiPathInterceptor, DefaultUserPreferenceService, IqserConfigService, IqserUserPreferenceService } from './services';
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
@ -30,7 +28,7 @@ const matModules = [
|
||||
MatTooltipModule,
|
||||
MatProgressBarModule,
|
||||
];
|
||||
const components = [ConnectionStatusComponent, FullPageErrorComponent, ConfirmationDialogComponent, ToastComponent];
|
||||
const components = [ConnectionStatusComponent, FullPageErrorComponent];
|
||||
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
|
||||
@ -61,20 +61,20 @@ export abstract class BaseDialogComponent implements OnInit, OnDestroy {
|
||||
this.#subscriptions.unsubscribe();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
if (this._isInEditMode && this.changed) {
|
||||
this._openConfirmDialog().then(result => {
|
||||
if (result in ConfirmOptions) {
|
||||
if (result === ConfirmOptions.CONFIRM) {
|
||||
this.save({ closeAfterSave: true });
|
||||
} else {
|
||||
this._dialogRef.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
close() {
|
||||
if (!this._isInEditMode || !this.changed) {
|
||||
this._dialogRef.close();
|
||||
}
|
||||
|
||||
this._openConfirmDialog().then(result => {
|
||||
if (result) {
|
||||
if (result === ConfirmOptions.CONFIRM) {
|
||||
this.save({ closeAfterSave: true });
|
||||
} else {
|
||||
this._dialogRef.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@HostListener('window:keydown.Enter', ['$event'])
|
||||
@ -94,7 +94,7 @@ export abstract class BaseDialogComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
protected _openConfirmDialog() {
|
||||
const dialogRef = this.#confirmationDialogService.openDialog({ disableConfirm: !this.valid });
|
||||
const dialogRef = this.#confirmationDialogService.open({ disableConfirm: !this.valid });
|
||||
return firstValueFrom(dialogRef.afterClosed());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,39 +1,29 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { DialogConfig, DialogService } from '../services';
|
||||
import { ConfirmationDialogComponent, ConfirmationDialogInput, TitleColors } from '.';
|
||||
|
||||
type DialogType = 'confirm';
|
||||
import { ConfirmationDialogComponent, ConfirmOption, defaultDialogConfig, IConfirmationDialogData, TitleColors } from '.';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ConfirmationDialogService extends DialogService<DialogType> {
|
||||
protected readonly _config: DialogConfig<DialogType> = {
|
||||
confirm: {
|
||||
component: ConfirmationDialogComponent,
|
||||
dialogConfig: { disableClose: false },
|
||||
},
|
||||
};
|
||||
export class ConfirmationDialogService {
|
||||
readonly #dialog = inject(MatDialog);
|
||||
|
||||
constructor(protected readonly _dialog: MatDialog) {
|
||||
super(_dialog);
|
||||
}
|
||||
open(data?: { disableConfirm: boolean }) {
|
||||
const dialogData: IConfirmationDialogData = {
|
||||
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,
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
openDialog(data?: { disableConfirm: boolean; [key: string]: unknown }): MatDialogRef<unknown> {
|
||||
return super.openDialog(
|
||||
'confirm',
|
||||
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,
|
||||
}),
|
||||
);
|
||||
return this.#dialog.open<ConfirmationDialogComponent, IConfirmationDialogData, ConfirmOption>(ConfirmationDialogComponent, {
|
||||
...defaultDialogConfig,
|
||||
disableClose: false,
|
||||
data: dialogData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,19 +34,17 @@
|
||||
[label]="config.confirmationText"
|
||||
[type]="iconButtonTypes.primary"
|
||||
buttonId="confirm"
|
||||
>
|
||||
</iqser-icon-button>
|
||||
></iqser-icon-button>
|
||||
|
||||
<iqser-icon-button
|
||||
(action)="confirm(ConfirmOptions.SECOND_CONFIRM)"
|
||||
(action)="confirm(confirmOptions.SECOND_CONFIRM)"
|
||||
*ngIf="config.alternativeConfirmationText"
|
||||
[disabled]="config.requireInput && confirmationDoesNotMatch()"
|
||||
[label]="config.alternativeConfirmationText"
|
||||
[type]="iconButtonTypes.primary"
|
||||
>
|
||||
</iqser-icon-button>
|
||||
></iqser-icon-button>
|
||||
|
||||
<div (click)="confirm(ConfirmOptions.DISCARD_CHANGES)" *ngIf="config.discardChangesText" class="all-caps-label cancel">
|
||||
<div (click)="confirm(confirmOptions.DISCARD_CHANGES)" *ngIf="config.discardChangesText" class="all-caps-label cancel">
|
||||
{{ config.discardChangesText }}
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,21 +1,28 @@
|
||||
import { ChangeDetectionStrategy, Component, HostListener, Inject, TemplateRef } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ChangeDetectionStrategy, Component, HostListener, inject, TemplateRef } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { IconButtonTypes } from '../../buttons';
|
||||
import { CircleButtonComponent, IconButtonComponent, IconButtonTypes } from '../../buttons';
|
||||
import { NgForOf, NgIf, NgTemplateOutlet } from '@angular/common';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { ValuesOf } from '../../utils';
|
||||
|
||||
export type TitleColor = 'default' | 'warn';
|
||||
export const TitleColors = {
|
||||
DEFAULT: 'default',
|
||||
WARN: 'warn',
|
||||
} as const;
|
||||
|
||||
export enum TitleColors {
|
||||
DEFAULT = 'default',
|
||||
WARN = 'warn',
|
||||
}
|
||||
export type TitleColor = ValuesOf<typeof TitleColors>;
|
||||
|
||||
export enum ConfirmOptions {
|
||||
CONFIRM = 1,
|
||||
SECOND_CONFIRM = 2,
|
||||
DISCARD_CHANGES = 3,
|
||||
}
|
||||
export const ConfirmOptions = {
|
||||
CONFIRM: 1,
|
||||
SECOND_CONFIRM: 2,
|
||||
DISCARD_CHANGES: 3,
|
||||
} as const;
|
||||
|
||||
export type ConfirmOption = ValuesOf<typeof ConfirmOptions>;
|
||||
|
||||
interface CheckBox {
|
||||
value: boolean;
|
||||
@ -24,76 +31,71 @@ interface CheckBox {
|
||||
extraContentData?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface IConfirmationDialogInput {
|
||||
title?: string;
|
||||
titleColor?: TitleColor;
|
||||
question?: string;
|
||||
details?: string;
|
||||
confirmationText?: string;
|
||||
alternativeConfirmationText?: string;
|
||||
discardChangesText?: string;
|
||||
requireInput?: boolean;
|
||||
disableConfirm?: boolean;
|
||||
denyText?: string;
|
||||
translateParams?: Record<string, unknown>;
|
||||
checkboxes?: CheckBox[];
|
||||
checkboxesValidation?: boolean;
|
||||
toastMessage?: string;
|
||||
interface InternalConfirmationDialogData {
|
||||
readonly title: string;
|
||||
readonly titleColor: TitleColor;
|
||||
readonly question: string;
|
||||
readonly details: string;
|
||||
readonly confirmationText: string;
|
||||
readonly alternativeConfirmationText?: string;
|
||||
readonly discardChangesText?: string;
|
||||
readonly requireInput: boolean;
|
||||
readonly disableConfirm: boolean;
|
||||
readonly denyText: string;
|
||||
readonly translateParams?: Record<string, unknown>;
|
||||
readonly checkboxes: CheckBox[];
|
||||
readonly checkboxesValidation: boolean;
|
||||
readonly toastMessage?: string;
|
||||
}
|
||||
|
||||
export class ConfirmationDialogInput implements IConfirmationDialogInput {
|
||||
title: string;
|
||||
titleColor: TitleColor;
|
||||
question: string;
|
||||
details: string;
|
||||
confirmationText: string;
|
||||
alternativeConfirmationText?: string;
|
||||
discardChangesText?: string;
|
||||
requireInput: boolean;
|
||||
disableConfirm: boolean;
|
||||
denyText: string;
|
||||
translateParams: Record<string, unknown>;
|
||||
checkboxes: CheckBox[];
|
||||
checkboxesValidation: boolean;
|
||||
toastMessage?: string;
|
||||
export type IConfirmationDialogData = Partial<InternalConfirmationDialogData>;
|
||||
|
||||
constructor(options?: IConfirmationDialogInput) {
|
||||
this.title = options?.title || _('common.confirmation-dialog.title');
|
||||
this.titleColor = options?.titleColor || TitleColors.DEFAULT;
|
||||
this.question = options?.question || _('common.confirmation-dialog.description');
|
||||
this.details = options?.details || '';
|
||||
this.confirmationText = options?.confirmationText || _('common.confirmation-dialog.confirm');
|
||||
this.alternativeConfirmationText = options?.alternativeConfirmationText;
|
||||
this.discardChangesText = options?.discardChangesText;
|
||||
this.requireInput = options?.requireInput || false;
|
||||
this.disableConfirm = options?.disableConfirm || false;
|
||||
this.denyText = options?.denyText || _('common.confirmation-dialog.deny');
|
||||
this.translateParams = options?.translateParams || {};
|
||||
this.checkboxes = options?.checkboxes || [];
|
||||
this.checkboxesValidation = typeof options?.checkboxesValidation === 'boolean' ? options.checkboxesValidation : true;
|
||||
this.toastMessage = options?.toastMessage;
|
||||
}
|
||||
function getConfig(options?: IConfirmationDialogData): InternalConfirmationDialogData {
|
||||
return {
|
||||
...options,
|
||||
title: options?.title ?? _('common.confirmation-dialog.title'),
|
||||
titleColor: options?.titleColor ?? TitleColors.DEFAULT,
|
||||
question: options?.question ?? _('common.confirmation-dialog.description'),
|
||||
details: options?.details ?? '',
|
||||
confirmationText: options?.confirmationText ?? _('common.confirmation-dialog.confirm'),
|
||||
requireInput: options?.requireInput ?? false,
|
||||
disableConfirm: options?.disableConfirm ?? false,
|
||||
denyText: options?.denyText ?? _('common.confirmation-dialog.deny'),
|
||||
checkboxes: options?.checkboxes ?? [],
|
||||
checkboxesValidation: typeof options?.checkboxesValidation === 'boolean' ? options.checkboxesValidation : true,
|
||||
};
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: './confirmation-dialog.component.html',
|
||||
styleUrls: ['./confirmation-dialog.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgIf,
|
||||
MatIconModule,
|
||||
FormsModule,
|
||||
NgForOf,
|
||||
MatCheckboxModule,
|
||||
TranslateModule,
|
||||
NgTemplateOutlet,
|
||||
IconButtonComponent,
|
||||
CircleButtonComponent,
|
||||
MatDialogModule,
|
||||
],
|
||||
})
|
||||
export class ConfirmationDialogComponent {
|
||||
config: ConfirmationDialogInput;
|
||||
readonly config = getConfig(inject(MAT_DIALOG_DATA));
|
||||
inputValue = '';
|
||||
showToast = false;
|
||||
readonly inputLabel: string;
|
||||
readonly ConfirmOptions = ConfirmOptions;
|
||||
readonly confirmOptions = ConfirmOptions;
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
|
||||
constructor(
|
||||
private readonly _dialogRef: MatDialogRef<ConfirmationDialogComponent>,
|
||||
private readonly _dialogRef: MatDialogRef<ConfirmationDialogComponent, ConfirmOption>,
|
||||
private readonly _translateService: TranslateService,
|
||||
@Inject(MAT_DIALOG_DATA) private readonly _confirmationDialogInput: ConfirmationDialogInput,
|
||||
) {
|
||||
this.config = _confirmationDialogInput ?? new ConfirmationDialogInput();
|
||||
this.translate(this.config);
|
||||
this.inputLabel = `${this._translateService.instant('confirmation-dialog.input-label')} '${this.config.confirmationText || ''}'`;
|
||||
}
|
||||
@ -103,10 +105,10 @@ export class ConfirmationDialogComponent {
|
||||
}
|
||||
|
||||
get isDeleteAction(): boolean {
|
||||
return this.config?.titleColor === TitleColors.WARN;
|
||||
return this.config.titleColor === TitleColors.WARN;
|
||||
}
|
||||
|
||||
get confirmOption(): ConfirmOptions {
|
||||
get confirmOption(): ConfirmOption {
|
||||
if (!this.config.checkboxesValidation && this.config.checkboxes[0]?.value) {
|
||||
return ConfirmOptions.SECOND_CONFIRM;
|
||||
}
|
||||
@ -116,19 +118,19 @@ export class ConfirmationDialogComponent {
|
||||
@HostListener('window:keyup.enter')
|
||||
onKeyupEnter(): void {
|
||||
if (this.config.requireInput && !this.confirmationDoesNotMatch()) {
|
||||
this.confirm(1);
|
||||
this.confirm(ConfirmOptions.CONFIRM);
|
||||
}
|
||||
}
|
||||
|
||||
confirmationDoesNotMatch(): boolean {
|
||||
return this.inputValue.toLowerCase() !== this.config.confirmationText?.toLowerCase();
|
||||
return this.inputValue.toLowerCase() !== this.config.confirmationText.toLowerCase();
|
||||
}
|
||||
|
||||
deny(): void {
|
||||
deny() {
|
||||
this._dialogRef.close();
|
||||
}
|
||||
|
||||
confirm(option: ConfirmOptions): void {
|
||||
confirm(option: ConfirmOption) {
|
||||
if (this.uncheckedBoxes) {
|
||||
this.showToast = true;
|
||||
} else {
|
||||
@ -136,8 +138,8 @@ export class ConfirmationDialogComponent {
|
||||
}
|
||||
}
|
||||
|
||||
translate(obj: ConfirmationDialogInput): void {
|
||||
const translateKeys: (keyof typeof obj)[] = [
|
||||
translate(obj: InternalConfirmationDialogData) {
|
||||
const translateKeys: (keyof InternalConfirmationDialogData)[] = [
|
||||
'title',
|
||||
'question',
|
||||
'details',
|
||||
@ -147,12 +149,10 @@ export class ConfirmationDialogComponent {
|
||||
'denyText',
|
||||
];
|
||||
|
||||
translateKeys
|
||||
.filter(key => !!obj[key])
|
||||
.forEach(key => {
|
||||
Object.assign(obj, {
|
||||
[key]: this._translateService.instant(obj[key] as string, this.config.translateParams) as string,
|
||||
});
|
||||
});
|
||||
const filtered = translateKeys.filter(key => !!obj[key]);
|
||||
filtered.forEach(key => {
|
||||
const value = this._translateService.instant(obj[key] as string, this.config.translateParams);
|
||||
Object.assign(obj, { [key]: value });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export * from './base-dialog.component';
|
||||
export * from './confirmation-dialog.service';
|
||||
export * from './confirmation-dialog/confirmation-dialog.component';
|
||||
export * from './dialog.service';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
export * from './dialog.service';
|
||||
export * from './toaster.service';
|
||||
export * from './error-message.service';
|
||||
export * from './generic.service';
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-right">
|
||||
<a (click)="remove()" *ngIf="options.closeButton" class="toast-close-button">
|
||||
<mat-icon svgIcon="iqser:close"></mat-icon>
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { Toast, ToastPackage, ToastrService } from 'ngx-toastr';
|
||||
import { Toast } from 'ngx-toastr';
|
||||
import { ToasterActions, ToasterOptions } from '../../services';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { NgForOf, NgIf } from '@angular/common';
|
||||
import { StopPropagationDirective } from '../../directives';
|
||||
|
||||
@Component({
|
||||
templateUrl: './toast.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [MatIconModule, NgIf, StopPropagationDirective, NgForOf],
|
||||
})
|
||||
export class ToastComponent extends Toast {
|
||||
constructor(protected readonly _toastrService: ToastrService, readonly toastPackage: ToastPackage) {
|
||||
super(_toastrService, toastPackage);
|
||||
}
|
||||
|
||||
get actions(): ToasterActions[] {
|
||||
return (this.options as ToasterOptions)?.actions || [];
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user