sa-modals
Version:
Angular Modal Library: A versatile and accessible modal component for Angular applications, designed to enhance user experience with smooth animations and responsive design. This library leverages Angular's CDK to create dynamic overlays, ensuring seamles
238 lines (228 loc) • 16.1 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, Component, Input, EventEmitter, ViewEncapsulation, HostListener, Output, ContentChild, ViewChild, NgModule } from '@angular/core';
import { trigger, transition, style, animate } from '@angular/animations';
import { Subject } from 'rxjs';
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i1$1 from '@angular/cdk/overlay';
import { OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import * as i3 from '@angular/cdk/a11y';
class SaModalService {
constructor() {
this.config = {
saWidth: 600,
hasBackdrop: true,
staticBackdrop: false,
closeWithESC: true,
extraClass: '',
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: SaModalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: SaModalService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: SaModalService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class HeaderComponent {
constructor() {
this.custom = false;
this.hideCLoseBtn = false;
this.status = new Subject();
}
close() {
this.status.next(false);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: HeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: HeaderComponent, selector: "modal-header", inputs: { header: "header", custom: "custom", hideCLoseBtn: "hideCLoseBtn" }, ngImport: i0, template: "<div class=\"sa-modal-header\" *ngIf=\"!custom else customeHeader\">\r\n <h3>{{header}}</h3>\r\n <button *ngIf=\"!hideCLoseBtn\" role=\"button\" type=\"button\" (click)=\"close()\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" hight=\"24\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M6 18L18 6M6 6L18 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\" />\r\n </svg>\r\n </button>\r\n</div>\r\n<ng-template #customeHeader>\r\n <ng-content />\r\n</ng-template>", styles: [".sa-modal-header{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--modal-border-color);padding:16px;background-color:var(--modal-bg-color)}.sa-modal-header h3{font-size:20px;font-weight:700}.sa-modal-header button{padding:4px;border:1px solid var(--modal-border-color);border-radius:var(--radius)}.sa-modal-header button:hover{background-color:var(--neutral-50)}.sa-modal-header button:active{background-color:var(--neutral-100)}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: HeaderComponent, decorators: [{
type: Component,
args: [{ selector: 'modal-header', template: "<div class=\"sa-modal-header\" *ngIf=\"!custom else customeHeader\">\r\n <h3>{{header}}</h3>\r\n <button *ngIf=\"!hideCLoseBtn\" role=\"button\" type=\"button\" (click)=\"close()\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" hight=\"24\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M6 18L18 6M6 6L18 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\" />\r\n </svg>\r\n </button>\r\n</div>\r\n<ng-template #customeHeader>\r\n <ng-content />\r\n</ng-template>", styles: [".sa-modal-header{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--modal-border-color);padding:16px;background-color:var(--modal-bg-color)}.sa-modal-header h3{font-size:20px;font-weight:700}.sa-modal-header button{padding:4px;border:1px solid var(--modal-border-color);border-radius:var(--radius)}.sa-modal-header button:hover{background-color:var(--neutral-50)}.sa-modal-header button:active{background-color:var(--neutral-100)}\n"] }]
}], propDecorators: { header: [{
type: Input
}], custom: [{
type: Input
}], hideCLoseBtn: [{
type: Input
}] } });
const uReveal = trigger('uReveal', [
transition(':enter', [
style({ transform: 'translateY(-30%)', opacity: 0 }),
animate('300ms ease', style({ transform: 'translateY(0%)', opacity: 1 })),
]),
transition(':leave', [
style({ transform: 'translateY(0%)', opacity: 1 }),
animate('300ms ease', style({ transform: 'translateY(-30%)', opacity: 0 })),
]),
]);
class SaModalComponent {
onPopState(event) {
if (this.visible) {
event.preventDefault();
this.close();
}
}
constructor(overlay, viewContainerRef, cdr, service, focusTrapFactory) {
this.overlay = overlay;
this.viewContainerRef = viewContainerRef;
this.cdr = cdr;
this.service = service;
this.focusTrapFactory = focusTrapFactory;
this.center = false;
this.extraClass = this.service.config.extraClass || '';
this.saWidth = this.service.config.saWidth || 600;
this.hasBackdrop = this.service.config.hasBackdrop || true;
this.staticBackdrop = false;
this.closeWithESC = this.service.config.closeWithESC || true;
this.onOpen = new EventEmitter();
this.onClose = new EventEmitter();
this.backdropClicked = new EventEmitter();
this.visible = false;
this.subscriptions = [];
}
ngAfterViewInit() {
this.subscriptions.push(this.header.status.subscribe((status) => {
this.close();
}));
}
open() {
const conf = new OverlayConfig({
hasBackdrop: this.hasBackdrop,
backdropClass: 'sa-modal-backdrop',
panelClass: 'sa-modal' + this.extraClass,
scrollStrategy: this.overlay.scrollStrategies.block(),
positionStrategy: this.overlay.position().global().centerHorizontally(),
});
if (this.center)
conf.positionStrategy = this.overlay
.position()
.global()
.centerHorizontally()
.centerVertically();
this.overlayRef = this.overlay.create(conf);
const modalPortal = new TemplatePortal(this.overlayTemplate, this.viewContainerRef);
this.visible = true;
history.pushState({ modalOpen: true }, '');
this.overlayRef.attach(modalPortal);
this.onOpen.emit();
this.cdr.detectChanges();
this.focusTrap = this.focusTrapFactory.create(this.contentRef.nativeElement);
this.contentRef.nativeElement.focus();
this.subscriptions.push(this.overlayRef.backdropClick().subscribe(() => {
this.backdropClicked.emit();
if (!this.staticBackdrop) {
this.close();
}
}));
this.subscriptions.push(this.overlayRef.keydownEvents().subscribe((e) => {
if (e.key == 'Escape' && this.closeWithESC) {
this.close();
}
}));
document.body.classList.add('modal-opened');
}
close() {
this.visible = false;
this.overlayRef.backdropElement?.classList.remove('cdk-overlay-backdrop-showing');
setTimeout(() => {
this.overlayRef.dispose();
document.body.classList.remove('modal-opened');
this.onClose.emit();
}, 300);
}
ngOnDestroy() {
this.subscriptions.forEach((e) => {
e.unsubscribe();
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: SaModalComponent, deps: [{ token: i1$1.Overlay }, { token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }, { token: SaModalService }, { token: i3.FocusTrapFactory }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: SaModalComponent, selector: "sa-modal", inputs: { center: "center", extraClass: "extraClass", saWidth: "saWidth", hasBackdrop: "hasBackdrop", staticBackdrop: "staticBackdrop", closeWithESC: "closeWithESC" }, outputs: { onOpen: "onOpen", onClose: "onClose", backdropClicked: "backdropClicked" }, host: { listeners: { "window:popstate": "onPopState($event)" } }, queries: [{ propertyName: "header", first: true, predicate: HeaderComponent, descendants: true }], viewQueries: [{ propertyName: "contentRef", first: true, predicate: ["content"], descendants: true }, { propertyName: "overlayTemplate", first: true, predicate: ["overlayTemplate"], descendants: true }], ngImport: i0, template: "<ng-template #overlayTemplate>\r\n <div #content *ngIf=\"visible\" class=\"sa-modal-content\" [style]=\"'--sa-modal-width:'+saWidth+'px '\"\r\n [@uReveal]>\r\n <ng-content></ng-content>\r\n </div>\r\n</ng-template>", styles: [".sa-modal{--modal-bg-color: #fff;--modal-border-color: #e5e5e5;--neutral-50: #fafafa;--neutral-100: #f5f5f5;--modal-transition: .3s;--radius: 8px;height:fit-content;padding:0 8px}.sa-modal .sa-modal-content{background:var(--modal-bg-color);max-width:100%;width:var(--sa-modal-width);margin:16px auto;padding:0 16px;overflow:hidden;border-radius:var(--radius);overflow:auto}.sa-modal-backdrop{background-color:#00000040}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], animations: [uReveal], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: SaModalComponent, decorators: [{
type: Component,
args: [{ selector: 'sa-modal', encapsulation: ViewEncapsulation.None, animations: [uReveal], template: "<ng-template #overlayTemplate>\r\n <div #content *ngIf=\"visible\" class=\"sa-modal-content\" [style]=\"'--sa-modal-width:'+saWidth+'px '\"\r\n [@uReveal]>\r\n <ng-content></ng-content>\r\n </div>\r\n</ng-template>", styles: [".sa-modal{--modal-bg-color: #fff;--modal-border-color: #e5e5e5;--neutral-50: #fafafa;--neutral-100: #f5f5f5;--modal-transition: .3s;--radius: 8px;height:fit-content;padding:0 8px}.sa-modal .sa-modal-content{background:var(--modal-bg-color);max-width:100%;width:var(--sa-modal-width);margin:16px auto;padding:0 16px;overflow:hidden;border-radius:var(--radius);overflow:auto}.sa-modal-backdrop{background-color:#00000040}\n"] }]
}], ctorParameters: () => [{ type: i1$1.Overlay }, { type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }, { type: SaModalService }, { type: i3.FocusTrapFactory }], propDecorators: { onPopState: [{
type: HostListener,
args: ['window:popstate', ['$event']]
}], center: [{
type: Input
}], extraClass: [{
type: Input
}], saWidth: [{
type: Input
}], hasBackdrop: [{
type: Input
}], staticBackdrop: [{
type: Input
}], closeWithESC: [{
type: Input
}], onOpen: [{
type: Output
}], onClose: [{
type: Output
}], backdropClicked: [{
type: Output
}], header: [{
type: ContentChild,
args: [HeaderComponent]
}], contentRef: [{
type: ViewChild,
args: ['content', { static: false }]
}], overlayTemplate: [{
type: ViewChild,
args: ['overlayTemplate']
}] } });
class FooterComponent {
constructor() {
this.justify = 'space-between';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: FooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: FooterComponent, selector: "modal-footer", inputs: { justify: "justify" }, ngImport: i0, template: "<div class=\"sa-modal-footer\" [style]=\"'--justify:'+justify\">\r\n <ng-content />\r\n</div>", styles: [".sa-modal-footer{padding:16px;background-color:var(--modal-bg-color);border-top:1px solid var(--modal-border-color);display:flex;justify-content:var(--justify);align-items:center;flex-wrap:wrap;gap:8px}\n"] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: FooterComponent, decorators: [{
type: Component,
args: [{ selector: 'modal-footer', template: "<div class=\"sa-modal-footer\" [style]=\"'--justify:'+justify\">\r\n <ng-content />\r\n</div>", styles: [".sa-modal-footer{padding:16px;background-color:var(--modal-bg-color);border-top:1px solid var(--modal-border-color);display:flex;justify-content:var(--justify);align-items:center;flex-wrap:wrap;gap:8px}\n"] }]
}], propDecorators: { justify: [{
type: Input
}] } });
class BodyComponent {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: BodyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: BodyComponent, selector: "modal-body", ngImport: i0, template: "<div class=\"sa-modal-body\">\r\n <ng-content></ng-content>\r\n</div>", styles: [".sa-modal-body{padding:8px 24px}\n"] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: BodyComponent, decorators: [{
type: Component,
args: [{ selector: 'modal-body', template: "<div class=\"sa-modal-body\">\r\n <ng-content></ng-content>\r\n</div>", styles: [".sa-modal-body{padding:8px 24px}\n"] }]
}] });
class SaModalModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: SaModalModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.3", ngImport: i0, type: SaModalModule, declarations: [HeaderComponent,
FooterComponent,
BodyComponent,
SaModalComponent], imports: [CommonModule, OverlayModule], exports: [HeaderComponent, FooterComponent, BodyComponent, SaModalComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: SaModalModule, imports: [CommonModule, OverlayModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: SaModalModule, decorators: [{
type: NgModule,
args: [{
declarations: [
HeaderComponent,
FooterComponent,
BodyComponent,
SaModalComponent,
],
exports: [HeaderComponent, FooterComponent, BodyComponent, SaModalComponent],
imports: [CommonModule, OverlayModule],
}]
}] });
/*
* Public API Surface of sa-modal
*/
/**
* Generated bundle index. Do not edit.
*/
export { BodyComponent, FooterComponent, HeaderComponent, SaModalComponent, SaModalModule, SaModalService };
//# sourceMappingURL=sa-modal.mjs.map