Files
shopit-front/src/app/shared/components/modal-host/modal-host.component.ts

103 lines
3.0 KiB
TypeScript

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;
const previousPaddingRight = body.style.paddingRight;
const view = this.document.defaultView;
const scrollbarWidth = view
? Math.max(0, view.innerWidth - this.document.documentElement.clientWidth)
: 0;
if (scrollbarWidth > 0 && view) {
const currentPaddingRight =
Number.parseFloat(view.getComputedStyle(body).paddingRight) || 0;
body.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`;
}
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;
body.style.paddingRight = previousPaddingRight;
});
});
}
protected onBackdropClick(): void {
const modal = this.activeModal();
if (modal?.config.closeOnBackdrop) {
modal.ref.dismiss('backdrop');
}
}
protected onCloseRequested(): void {
this.activeModal()?.ref.dismiss('programmatic');
}
}