common-ui/src/lib/dialog/confirmation-dialog/confirmation-dialog.component.ts
2023-05-29 21:24:06 +03:00

159 lines
5.3 KiB
TypeScript

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 { 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 const TitleColors = {
DEFAULT: 'default',
WARN: 'warn',
} as const;
export type TitleColor = ValuesOf<typeof TitleColors>;
export const ConfirmOptions = {
CONFIRM: 1,
SECOND_CONFIRM: 2,
DISCARD_CHANGES: 3,
} as const;
export type ConfirmOption = ValuesOf<typeof ConfirmOptions>;
interface CheckBox {
value: boolean;
label: string;
extraContent?: TemplateRef<unknown>;
extraContentData?: Record<string, unknown>;
}
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 type IConfirmationDialogData = Partial<InternalConfirmationDialogData>;
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 {
readonly config = getConfig(inject(MAT_DIALOG_DATA));
inputValue = '';
showToast = false;
readonly inputLabel: string;
readonly confirmOptions = ConfirmOptions;
readonly iconButtonTypes = IconButtonTypes;
constructor(
private readonly _dialogRef: MatDialogRef<ConfirmationDialogComponent, ConfirmOption>,
private readonly _translateService: TranslateService,
) {
this.translate(this.config);
this.inputLabel = `${this._translateService.instant('confirmation-dialog.input-label')} '${this.config.confirmationText || ''}'`;
}
get uncheckedBoxes(): boolean {
return this.config.checkboxesValidation && this.config.checkboxes.some(c => !c.value);
}
get isDeleteAction(): boolean {
return this.config.titleColor === TitleColors.WARN;
}
get confirmOption(): ConfirmOption {
if (!this.config.checkboxesValidation && this.config.checkboxes[0]?.value) {
return ConfirmOptions.SECOND_CONFIRM;
}
return ConfirmOptions.CONFIRM;
}
@HostListener('window:keyup.enter')
onKeyupEnter(): void {
if (this.config.requireInput && !this.confirmationDoesNotMatch()) {
this.confirm(ConfirmOptions.CONFIRM);
}
}
confirmationDoesNotMatch(): boolean {
return this.inputValue.toLowerCase() !== this.config.confirmationText.toLowerCase();
}
deny() {
this._dialogRef.close();
}
confirm(option: ConfirmOption) {
if (this.uncheckedBoxes) {
this.showToast = true;
} else {
this._dialogRef.close(option);
}
}
translate(obj: InternalConfirmationDialogData) {
const translateKeys: (keyof InternalConfirmationDialogData)[] = [
'title',
'question',
'details',
'confirmationText',
'alternativeConfirmationText',
'discardChangesText',
'denyText',
];
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 });
});
}
}