import {NgModule,Component,Input,Output,OnInit,AfterViewInit,AfterContentInit,OnDestroy,ElementRef,ViewChild,EventEmitter,ContentChildren,QueryList,TemplateRef,ChangeDetectionStrategy, NgZone, ChangeDetectorRef, ViewEncapsulation} from '@angular/core';
import {CommonModule} from '@angular/common';
import {Message} from 'mirra-ui/api';
import {DomHandler} from 'mirra-ui/dom';
import {PrimeTemplate,SharedModule} from 'mirra-ui/api';
import {MessageService} from 'mirra-ui/api';
import {RippleModule} from 'mirra-ui/ripple';
import {Subscription} from 'rxjs';
import {trigger,state,style,transition,animate,query,animateChild,AnimationEvent} from '@angular/animations';
@Component({
selector: 'p-toastItem',
template: `
{{message.summary}}
{{message.detail}}
`,
animations: [
trigger('messageState', [
state('visible', style({
transform: 'translateY(0)',
opacity: 1
})),
transition('void => *', [
style({transform: '{{showTransformParams}}', opacity: 0}),
animate('{{showTransitionParams}}')
]),
transition('* => void', [
animate(('{{hideTransitionParams}}'), style({
height: 0,
opacity: 0,
transform: '{{hideTransformParams}}'
}))
])
])
],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToastItem implements AfterViewInit, OnDestroy {
@Input() message: Message;
@Input() index: number;
@Input() template: TemplateRef;
@Input() showTransformOptions: string;
@Input() hideTransformOptions: string;
@Input() showTransitionOptions: string;
@Input() hideTransitionOptions: string;
@Output() onClose: EventEmitter = new EventEmitter();
@ViewChild('container') containerViewChild: ElementRef;
timeout: any;
constructor(private zone: NgZone) {}
ngAfterViewInit() {
this.initTimeout();
}
initTimeout() {
if (!this.message.sticky) {
this.zone.runOutsideAngular(() => {
this.timeout = setTimeout(() => {
this.onClose.emit({
index: this.index,
message: this.message
});
}, this.message.life || 3000);
});
}
}
clearTimeout() {
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
}
onMouseEnter() {
this.clearTimeout();
}
onMouseLeave() {
this.initTimeout();
}
onCloseIconClick(event) {
this.clearTimeout();
this.onClose.emit({
index: this.index,
message: this.message
});
event.preventDefault();
}
ngOnDestroy() {
this.clearTimeout();
}
}
@Component({
selector: 'p-toast',
template: `
`,
animations: [
trigger('toastAnimation', [
transition(':enter, :leave', [
query('@*', animateChild())
])
])
],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
styleUrls: ['./toast.scss']
})
export class Toast implements OnInit,AfterContentInit,OnDestroy {
@Input() key: string;
@Input() autoZIndex: boolean = true;
@Input() baseZIndex: number = 0;
@Input() style: any;
@Input() styleClass: string;
@Input() position: string = 'top-right';
@Input() preventOpenDuplicates: boolean = false;
@Input() preventDuplicates: boolean = false;
@Input() showTransformOptions: string = 'translateY(100%)';
@Input() hideTransformOptions: string = 'translateY(-100%)';
@Input() showTransitionOptions: string = '300ms ease-out';
@Input() hideTransitionOptions: string = '250ms ease-in';
@Output() onClose: EventEmitter = new EventEmitter();
@ViewChild('container') containerViewChild: ElementRef;
@ContentChildren(PrimeTemplate) templates: QueryList;
messageSubscription: Subscription;
clearSubscription: Subscription;
messages: Message[];
messagesArchieve: Message[];
template: TemplateRef;
constructor(public messageService: MessageService, private cd: ChangeDetectorRef) {}
ngOnInit() {
this.messageSubscription = this.messageService.messageObserver.subscribe(messages => {
if (messages) {
if (messages instanceof Array) {
const filteredMessages = messages.filter(m => this.canAdd(m));
this.add(filteredMessages);
}
else if (this.canAdd(messages)) {
this.add([messages]);
}
}
});
this.clearSubscription = this.messageService.clearObserver.subscribe(key => {
if (key) {
if (this.key === key) {
this.messages = null;
}
}
else {
this.messages = null;
}
this.cd.markForCheck();
});
}
add(messages: Message[]): void {
this.messages = this.messages ? [...this.messages, ...messages] : [...messages];
if (this.preventDuplicates) {
this.messagesArchieve = this.messagesArchieve ? [...this.messagesArchieve, ...messages] : [...messages];
}
this.cd.markForCheck();
}
canAdd(message: Message): boolean {
let allow = this.key === message.key;
if (allow && this.preventOpenDuplicates) {
allow = !this.containsMessage(this.messages, message);
}
if (allow && this.preventDuplicates) {
allow = !this.containsMessage(this.messagesArchieve, message);
}
return allow;
}
containsMessage(collection: Message[], message: Message): boolean {
if (!collection) {
return false;
}
return collection.find(m => {
return ((m.summary === message.summary) && (m.detail == message.detail) && (m.severity === message.severity));
}) != null;
}
ngAfterContentInit() {
this.templates.forEach((item) => {
switch(item.getType()) {
case 'message':
this.template = item.template;
break;
default:
this.template = item.template;
break;
}
});
}
onMessageClose(event) {
this.messages.splice(event.index, 1);
this.onClose.emit({
message: event.message
});
this.cd.detectChanges();
}
onAnimationStart(event: AnimationEvent) {
if (event.fromState === 'void' && this.autoZIndex) {
this.containerViewChild.nativeElement.style.zIndex = String(this.baseZIndex + (++DomHandler.zindex));
}
}
ngOnDestroy() {
if (this.messageSubscription) {
this.messageSubscription.unsubscribe();
}
if (this.clearSubscription) {
this.clearSubscription.unsubscribe();
}
}
}
@NgModule({
imports: [CommonModule,RippleModule],
exports: [Toast,SharedModule],
declarations: [Toast,ToastItem]
})
export class ToastModule { }