103 lines
3.0 KiB
TypeScript
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');
|
|
}
|
|
}
|