feat: implement modal service and components for enhanced user interaction, including modal showcase in demo page
This commit is contained in:
93
src/app/shared/components/modal-host/modal-host.component.ts
Normal file
93
src/app/shared/components/modal-host/modal-host.component.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { DOCUMENT, NgComponentOutlet, isPlatformBrowser } from '@angular/common';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
Injector,
|
||||
PLATFORM_ID,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
viewChild
|
||||
} from '@angular/core';
|
||||
|
||||
import {
|
||||
MODAL_DATA,
|
||||
ModalRef,
|
||||
ModalService
|
||||
} from '../../../core/services/modal.service';
|
||||
import { ModalShellComponent } from '../modal-shell/modal-shell.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-modal-host',
|
||||
imports: [NgComponentOutlet, ModalShellComponent],
|
||||
templateUrl: './modal-host.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ModalHostComponent {
|
||||
private readonly modalService = inject(ModalService);
|
||||
private readonly injector = inject(Injector);
|
||||
private readonly document = inject(DOCUMENT);
|
||||
private readonly platformId = inject(PLATFORM_ID);
|
||||
private readonly modalShell = viewChild(ModalShellComponent);
|
||||
private readonly isBrowser = isPlatformBrowser(this.platformId);
|
||||
|
||||
readonly activeModal = this.modalService.activeModal;
|
||||
protected readonly contentInjector = computed<Injector | undefined>(() => {
|
||||
const modal = this.activeModal();
|
||||
|
||||
if (!modal) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Injector.create({
|
||||
providers: [
|
||||
{ provide: ModalRef, useValue: modal.ref },
|
||||
{ provide: MODAL_DATA, useValue: modal.config.data ?? null }
|
||||
],
|
||||
parent: this.injector
|
||||
});
|
||||
});
|
||||
|
||||
constructor() {
|
||||
effect((onCleanup) => {
|
||||
const modal = this.activeModal();
|
||||
|
||||
if (!this.isBrowser || !modal) {
|
||||
return;
|
||||
}
|
||||
|
||||
const body = this.document.body;
|
||||
const previousOverflow = body.style.overflow;
|
||||
body.style.overflow = 'hidden';
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key !== 'Escape' || !this.activeModal()?.config.closeOnEscape) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
this.activeModal()?.ref.dismiss('escape');
|
||||
};
|
||||
|
||||
this.document.addEventListener('keydown', onKeyDown);
|
||||
queueMicrotask(() => this.modalShell()?.focusInitialElement());
|
||||
|
||||
onCleanup(() => {
|
||||
this.document.removeEventListener('keydown', onKeyDown);
|
||||
body.style.overflow = previousOverflow;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected onBackdropClick(): void {
|
||||
const modal = this.activeModal();
|
||||
|
||||
if (modal?.config.closeOnBackdrop) {
|
||||
modal.ref.dismiss('backdrop');
|
||||
}
|
||||
}
|
||||
|
||||
protected onCloseRequested(): void {
|
||||
this.activeModal()?.ref.dismiss('programmatic');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user