diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html index cdf6728fca..98919b355c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html @@ -1,9 +1,9 @@ - @if (abpHeader) { + @if (abpHeader()) { } - @if (abpBody) { + @if (abpBody()) { } - @if (abpFooter) { + @if (abpFooter()) { } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts index d7c24c7601..de9a951ea5 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts @@ -1,20 +1,22 @@ import { SubscriptionService, uuid } from '@abp/ng.core'; import { Component, - ContentChild, - EventEmitter, - Inject, - Input, + DestroyRef, OnDestroy, OnInit, - Optional, - Output, TemplateRef, - ViewChild, + contentChild, + effect, + inject, + input, + model, + output, + viewChild, } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { NgbModal, NgbModalOptions, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { fromEvent, Subject } from 'rxjs'; -import { debounceTime, distinctUntilChanged, filter, takeUntil } from 'rxjs/operators'; +import { fromEvent } from 'rxjs'; +import { debounceTime, filter } from 'rxjs/operators'; import { Confirmation } from '../../models/confirmation'; import { ConfirmationService } from '../../services/confirmation.service'; import { SUPPRESS_UNSAVED_CHANGES_WARNING } from '../../tokens/suppress-unsaved-changes-warning.token'; @@ -30,66 +32,51 @@ export type ModalSize = 'sm' | 'md' | 'lg' | 'xl'; providers: [SubscriptionService], }) export class ModalComponent implements OnInit, OnDestroy, DismissableModal { - @Input() - get visible(): boolean { - return this._visible; - } - set visible(value: boolean) { - if (typeof value !== 'boolean') return; - this.toggle$.next(value); - } - - @Input() - get busy(): boolean { - return this._busy; - } - set busy(value: boolean) { - if (this.abpSubmit && this.abpSubmit instanceof ButtonComponent) { - this.abpSubmit.loading = value; - } - - this._busy = value; - } - - @Input() options: NgbModalOptions = { - keyboard: true, - }; + private confirmationService = inject(ConfirmationService); + private modal = inject(NgbModal); + private modalRefService = inject(ModalRefService); + private suppressUnsavedChangesWarningToken = inject(SUPPRESS_UNSAVED_CHANGES_WARNING, { + optional: true, + }); + private destroyRef = inject(DestroyRef); - @Input() suppressUnsavedChangesWarning = this.suppressUnsavedChangesWarningToken; + visible = model(false); - @ViewChild('modalContent') modalContent?: TemplateRef; + busy = input(false, { + transform: (value: boolean) => { + if (this.abpSubmit() && this.abpSubmit() instanceof ButtonComponent) { + this.abpSubmit().loading = value; + } + return value; + }, + }); - @ContentChild('abpHeader', { static: false }) abpHeader?: TemplateRef; + options = input({ keyboard: true }); - @ContentChild('abpBody', { static: false }) abpBody?: TemplateRef; + suppressUnsavedChangesWarning = input(this.suppressUnsavedChangesWarningToken); - @ContentChild('abpFooter', { static: false }) abpFooter?: TemplateRef; + modalContent = viewChild>('modalContent'); - @ContentChild(ButtonComponent, { static: false, read: ButtonComponent }) - abpSubmit?: ButtonComponent; + abpHeader = contentChild>('abpHeader'); - @Output() readonly visibleChange = new EventEmitter(); + abpBody = contentChild>('abpBody'); - @Output() readonly init = new EventEmitter(); + abpFooter = contentChild>('abpFooter'); - @Output() readonly appear = new EventEmitter(); + abpSubmit = contentChild(ButtonComponent, { read: ButtonComponent }); - @Output() readonly disappear = new EventEmitter(); + readonly init = output(); - _visible = false; + readonly appear = output(); - _busy = false; + readonly disappear = output(); modalRef!: NgbModalRef; isConfirmationOpen = false; - destroy$ = new Subject(); - modalIdentifier = `modal-${uuid()}`; - private toggle$ = new Subject(); - get modalWindowRef() { return document.querySelector(`ngb-modal-window.${this.modalIdentifier}`); } @@ -98,17 +85,12 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal { return Boolean(this.modalWindowRef?.querySelector('.ng-dirty')); } - constructor( - private confirmationService: ConfirmationService, - private subscription: SubscriptionService, - @Optional() - @Inject(SUPPRESS_UNSAVED_CHANGES_WARNING) - private suppressUnsavedChangesWarningToken: boolean, - private modal: NgbModal, - private modalRefService: ModalRefService, - ) { - this.initToggleStream(); + constructor() { + effect(() => { + this.toggle(this.visible()); + }); } + ngOnInit(): void { this.modalRefService.register(this); } @@ -116,7 +98,7 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal { dismiss(mode: ModalDismissMode) { switch (mode) { case 'hard': - this.visible = false; + this.visible.set(false); break; case 'soft': this.close(); @@ -126,37 +108,29 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal { } } - private initToggleStream() { - this.subscription.addOne(this.toggle$.pipe(debounceTime(0), distinctUntilChanged()), value => - this.toggle(value), - ); - } - private toggle(value: boolean) { - this._visible = value; - this.visibleChange.emit(value); + this.visible.set(value); if (!value) { this.modalRef?.dismiss(); this.disappear.emit(); - this.destroy$.next(); return; } setTimeout(() => this.listen(), 0); - this.modalRef = this.modal.open(this.modalContent, { + this.modalRef = this.modal.open(this.modalContent(), { size: 'md', centered: false, keyboard: false, scrollable: true, beforeDismiss: () => { - if (!this.visible) return true; + if (!this.visible()) return true; this.close(); - return !this.visible; + return !this.visible(); }, - ...this.options, - windowClass: `${this.options.windowClass || ''} ${this.modalIdentifier}`, + ...this.options(), + windowClass: `${this.options().windowClass || ''} ${this.modalIdentifier}`, }); this.appear.emit(); @@ -165,30 +139,27 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal { ngOnDestroy(): void { this.modalRefService.unregister(this); this.toggle(false); - this.destroy$.next(); } close() { - if (this.busy) return; + if (this.busy()) return; - if (this.isFormDirty && !this.suppressUnsavedChangesWarning) { + if (this.isFormDirty && !this.suppressUnsavedChangesWarning()) { if (this.isConfirmationOpen) return; this.isConfirmationOpen = true; this.confirmationService - .warn( - 'AbpUi::AreYouSureYouWantToCancelEditingWarningMessage', - 'AbpUi::AreYouSure', - { dismissible: false }, - ) + .warn('AbpUi::AreYouSureYouWantToCancelEditingWarningMessage', 'AbpUi::AreYouSure', { + dismissible: false, + }) .subscribe((status: Confirmation.Status) => { this.isConfirmationOpen = false; if (status === Confirmation.Status.confirm) { - this.visible = false; + this.visible.set(false); } }); } else { - this.visible = false; + this.visible.set(false); } } @@ -196,17 +167,17 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal { if (this.modalWindowRef) { fromEvent(this.modalWindowRef, 'keyup') .pipe( - takeUntil(this.destroy$), + takeUntilDestroyed(this.destroyRef), debounceTime(150), - filter((key: KeyboardEvent) => key && key.key === 'Escape' && this.options.keyboard), + filter((key: KeyboardEvent) => key && key.key === 'Escape' && this.options().keyboard), ) .subscribe(() => this.close()); } fromEvent(window, 'beforeunload') - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(event => { - if (this.isFormDirty && !this.suppressUnsavedChangesWarning) { + if (this.isFormDirty && !this.suppressUnsavedChangesWarning()) { event.preventDefault(); } });