File

projects/ng-snotify/src/lib/components/toast/toast.component.ts

Implements

OnInit OnDestroy AfterContentInit

Metadata

encapsulation ViewEncapsulation.None
selector ng-snotify-toast
templateUrl ./toast.component.html

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor(service: SnotifyService)
Parameters :
Name Type Optional
service SnotifyService No

Inputs

toast
Type : SnotifyToast

Get toast from notifications array

Outputs

stateChanged
Type : EventEmitter

Methods

initToast
initToast()

Initialize base toast config

Returns : void
ngAfterContentInit
ngAfterContentInit()
Returns : void
ngOnDestroy
ngOnDestroy()

Unsubscribe subscriptions

Returns : void
ngOnInit
ngOnInit()

Init base options. Subscribe to toast changed, toast deleted

Returns : void
onClick
onClick()

Trigger OnClick lifecycle

Returns : void
onExitTransitionEnd
onExitTransitionEnd()

Remove toast completely after animation

Returns : void
onMouseEnter
onMouseEnter()

Trigger onHoverEnter lifecycle

Returns : void
onMouseLeave
onMouseLeave()

Trigger onHoverLeave lifecycle

Returns : void
onRemove
onRemove()

Trigger beforeDestroy lifecycle. Removes toast

Returns : void
startTimeout
startTimeout(startTime: number)

Start progress bar

Parameters :
Name Type Optional Default value Description
startTime number No 0

number

Returns : void

Properties

animationFrame
Type : number

requestAnimationFrame id

state
Type : object
Default value : { paused: false, progress: 0, animation: '', isDestroying: false, promptType: SnotifyStyle.prompt }

Toast state

toastChangedSubscription
Type : Subscription
toastDeletedSubscription
Type : Subscription
import {
  AfterContentInit,
  Component,
  EventEmitter,
  Input,
  OnDestroy,
  OnInit,
  Output,
  ViewEncapsulation
} from '@angular/core';
import { SnotifyService } from '../../services/snotify.service';
import { SnotifyToast } from '../../models/snotify-toast.model';
import { Subscription } from 'rxjs';
import { SnotifyEvent } from '../../types/event.type';
import { SnotifyStyle } from '../../enums/snotify-style.enum';

@Component({
  selector: 'ng-snotify-toast',
  templateUrl: './toast.component.html',
  encapsulation: ViewEncapsulation.None
})
export class ToastComponent implements OnInit, OnDestroy, AfterContentInit {
  /**
   * Get toast from notifications array
   */
  @Input() toast: SnotifyToast;
  @Output() stateChanged = new EventEmitter<SnotifyEvent>();

  toastDeletedSubscription: Subscription;
  toastChangedSubscription: Subscription;

  /**
   * requestAnimationFrame id
   */
  animationFrame: number;

  /**
   * Toast state
   */
  state = {
    paused: false,
    progress: 0,
    animation: '',
    isDestroying: false,
    promptType: SnotifyStyle.prompt
  };

  constructor(private service: SnotifyService) {}

  // Lifecycles

  /**
   * Init base options. Subscribe to toast changed, toast deleted
   */
  ngOnInit() {
    this.toastChangedSubscription = this.service.toastChanged.subscribe((toast: SnotifyToast) => {
      if (this.toast.id === toast.id) {
        this.initToast();
      }
    });
    this.toastDeletedSubscription = this.service.toastDeleted.subscribe(id => {
      if (this.toast.id === id) {
        this.onRemove();
      }
    });
    if (!this.toast.config.timeout) {
      this.toast.config.showProgressBar = false;
    }
    this.toast.eventEmitter.next('mounted');
    this.state.animation = 'snotifyToast--in';
  }

  ngAfterContentInit() {
    setTimeout(() => {
      this.stateChanged.emit('beforeShow');
      this.toast.eventEmitter.next('beforeShow');
      this.state.animation = this.toast.config.animation.enter;
    }, this.service.config.toast.animation.time / 5); // time to show toast push animation (snotifyToast--in)
  }

  /**
   * Unsubscribe subscriptions
   */
  ngOnDestroy(): void {
    cancelAnimationFrame(this.animationFrame);
    this.toast.eventEmitter.next('destroyed');
    this.toastChangedSubscription.unsubscribe();
    this.toastDeletedSubscription.unsubscribe();
  }

  /*
  Event hooks
   */

  /**
   * Trigger OnClick lifecycle
   */
  onClick() {
    this.toast.eventEmitter.next('click');
    if (this.toast.config.closeOnClick) {
      this.service.remove(this.toast.id);
    }
  }

  /**
   * Trigger beforeDestroy lifecycle. Removes toast
   */
  onRemove() {
    this.state.isDestroying = true;
    this.toast.eventEmitter.next('beforeHide');
    this.stateChanged.emit('beforeHide');
    this.state.animation = this.toast.config.animation.exit;
    setTimeout(() => {
      this.stateChanged.emit('hidden');
      this.state.animation = 'snotifyToast--out';
      this.toast.eventEmitter.next('hidden');
      setTimeout(() => this.service.remove(this.toast.id, true), this.toast.config.animation.time / 2);
    }, this.toast.config.animation.time / 2);
  }

  /**
   * Trigger onHoverEnter lifecycle
   */
  onMouseEnter() {
    this.toast.eventEmitter.next('mouseenter');
    if (this.toast.config.pauseOnHover) {
      this.state.paused = true;
    }
  }

  /**
   * Trigger onHoverLeave lifecycle
   */
  onMouseLeave() {
    if (this.toast.config.pauseOnHover && this.toast.config.timeout) {
      this.state.paused = false;
      this.startTimeout(this.toast.config.timeout * this.state.progress);
    }
    this.toast.eventEmitter.next('mouseleave');
  }

  /**
   * Remove toast completely after animation
   */
  onExitTransitionEnd() {
    if (this.state.isDestroying) {
      return;
    }
    this.initToast();
    this.toast.eventEmitter.next('shown');
  }

  /*
   Common
   */

  /**
   * Initialize base toast config
   *
   */
  initToast(): void {
    if (this.toast.config.timeout > 0) {
      this.startTimeout(0);
    }
  }

  /**
   * Start progress bar
   * @param startTime number
   */
  startTimeout(startTime: number = 0) {
    const start = performance.now();
    const calculate = () => {
      this.animationFrame = requestAnimationFrame(timestamp => {
        const runtime = timestamp + startTime - start;
        const progress = Math.min(runtime / this.toast.config.timeout, 1);
        if (this.state.paused) {
          cancelAnimationFrame(this.animationFrame);
        } else if (runtime < this.toast.config.timeout) {
          this.state.progress = progress;
          calculate();
        } else {
          this.state.progress = 1;
          cancelAnimationFrame(this.animationFrame);
          this.service.remove(this.toast.id);
        }
      });
    };
    calculate();
  }
}
<div
  [attr.role]="toast.config.type === state.promptType ? 'dialog' : 'alert'"
  [attr.aria-labelledby]="'snotify_' + toast.id"
  [attr.aria-modal]="toast.config.type === state.promptType"
  [ngClass]="[
    'snotifyToast animated',
    'snotify-' + toast.config.type,
    state.animation,
    toast.valid === undefined ? '' : toast.valid ? 'snotifyToast--valid' : 'snotifyToast--invalid'
  ]"
  [ngStyle]="{
    '-webkit-transition': toast.config.animation.time + 'ms',
    transition: toast.config.animation.time + 'ms',
    '-webkit-animation-duration': toast.config.animation.time + 'ms',
    'animation-duration': toast.config.animation.time + 'ms'
  }"
  (animationend)="onExitTransitionEnd()"
  (click)="onClick()"
  (mouseenter)="onMouseEnter()"
  (mouseleave)="onMouseLeave()"
>
  <div class="snotifyToast__progressBar" *ngIf="toast.config.showProgressBar">
    <span class="snotifyToast__progressBar__percentage" [ngStyle]="{ width: state.progress * 100 + '%' }"></span>
  </div>
  <div class="snotifyToast__inner" *ngIf="!toast.config.html; else toastHTML">
    <div class="snotifyToast__title" [attr.id]="'snotify_' + toast.id" *ngIf="toast.title">
      {{ toast.title | truncate: toast.config.titleMaxLength }}
    </div>
    <div class="snotifyToast__body" *ngIf="toast.body">{{ toast.body | truncate: toast.config.bodyMaxLength }}</div>
    <ng-snotify-prompt *ngIf="toast.config.type === state.promptType" [toast]="toast"> </ng-snotify-prompt>
    <div
      *ngIf="!toast.config.icon; else elseBlock"
      [ngClass]="['snotify-icon', toast.config.iconClass || 'snotify-icon--' + toast.config.type]"
    ></div>
    <ng-template #elseBlock>
      <img class="snotify-icon" [src]="toast.config.icon" />
    </ng-template>
  </div>
  <ng-template #toastHTML>
    <div class="snotifyToast__inner" [innerHTML]="toast.config.html"></div>
  </ng-template>
  <ng-snotify-button *ngIf="toast.config.buttons" [toast]="toast"></ng-snotify-button>
</div>
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""