UNPKG

igniteui-angular-sovn

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

204 lines (183 loc) 8.28 kB
import { AnimationReferenceMetadata } from '@angular/animations'; import { Directive, ElementRef, EventEmitter, Inject, OnDestroy } from '@angular/core'; import { noop, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { growVerIn, growVerOut } from '../animations/grow'; import { IgxAngularAnimationService } from '../services/animation/angular-animation-service'; import { AnimationPlayer, AnimationService } from '../services/animation/animation'; /**@hidden @internal */ export interface ToggleAnimationSettings { openAnimation: AnimationReferenceMetadata; closeAnimation: AnimationReferenceMetadata; } export interface ToggleAnimationOwner { animationSettings: ToggleAnimationSettings; openAnimationStart: EventEmitter<void>; openAnimationDone: EventEmitter<void>; closeAnimationStart: EventEmitter<void>; closeAnimationDone: EventEmitter<void>; openAnimationPlayer: AnimationPlayer; closeAnimationPlayer: AnimationPlayer; playOpenAnimation(element: ElementRef, onDone: () => void): void; playCloseAnimation(element: ElementRef, onDone: () => void): void; } /** @hidden @internal */ export enum ANIMATION_TYPE { OPEN = 'open', CLOSE = 'close', } /**@hidden @internal */ @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix export abstract class ToggleAnimationPlayer implements ToggleAnimationOwner, OnDestroy { /** @hidden @internal */ public openAnimationDone: EventEmitter<void> = new EventEmitter(); /** @hidden @internal */ public closeAnimationDone: EventEmitter<void> = new EventEmitter(); /** @hidden @internal */ public openAnimationStart: EventEmitter<void> = new EventEmitter(); /** @hidden @internal */ public closeAnimationStart: EventEmitter<void> = new EventEmitter(); public get animationSettings(): ToggleAnimationSettings { return this._animationSettings; } public set animationSettings(value: ToggleAnimationSettings) { this._animationSettings = value; } /** @hidden @internal */ public openAnimationPlayer: AnimationPlayer = null; /** @hidden @internal */ public closeAnimationPlayer: AnimationPlayer = null; protected destroy$: Subject<void> = new Subject(); protected players: Map<string, AnimationPlayer> = new Map(); protected _animationSettings: ToggleAnimationSettings = { openAnimation: growVerIn, closeAnimation: growVerOut }; private closeInterrupted = false; private openInterrupted = false; private _defaultClosedCallback = noop; private _defaultOpenedCallback = noop; private onClosedCallback: () => any = this._defaultClosedCallback; private onOpenedCallback: () => any = this._defaultOpenedCallback; constructor(@Inject(IgxAngularAnimationService) protected animationService: AnimationService) { } /** @hidden @internal */ public playOpenAnimation(targetElement: ElementRef, onDone?: () => void): void { this.startPlayer(ANIMATION_TYPE.OPEN, targetElement, onDone || this._defaultOpenedCallback); } /** @hidden @internal */ public playCloseAnimation(targetElement: ElementRef, onDone?: () => void): void { this.startPlayer(ANIMATION_TYPE.CLOSE, targetElement, onDone || this._defaultClosedCallback); } /** @hidden @internal */ public ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } private startPlayer(type: ANIMATION_TYPE, targetElement: ElementRef, callback: () => void): void { if (!targetElement) { // if no element is passed, there is nothing to animate return; } let target = this.getPlayer(type); if (!target) { target = this.initializePlayer(type, targetElement, callback); } // V.S. Jun 28th, 2021 #9783: player will NOT be initialized w/ null settings // events will already be emitted if (!target || target.hasStarted()) { return; } const targetEmitter = type === ANIMATION_TYPE.OPEN ? this.openAnimationStart : this.closeAnimationStart; targetEmitter.emit(); if (target) { target.play(); } } private initializePlayer(type: ANIMATION_TYPE, targetElement: ElementRef, callback: () => void): AnimationPlayer { const oppositeType = type === ANIMATION_TYPE.OPEN ? ANIMATION_TYPE.CLOSE : ANIMATION_TYPE.OPEN; // V.S. Jun 28th, 2021 #9783: Treat falsy animation settings as disabled animations const targetAnimationSettings = this.animationSettings || { closeAnimation: null, openAnimation: null }; const animationSettings = type === ANIMATION_TYPE.OPEN ? targetAnimationSettings.openAnimation : targetAnimationSettings.closeAnimation; // V.S. Jun 28th, 2021 #9783: When no animation in target direction, emit start and done events and return if (!animationSettings) { this.setCallback(type, callback); const targetEmitter = type === ANIMATION_TYPE.OPEN ? this.openAnimationStart : this.closeAnimationStart; targetEmitter.emit(); this.onDoneHandler(type); return; } const opposite = this.getPlayer(oppositeType); let oppositePosition = 1; if (opposite) { oppositePosition = opposite.position; this.cleanUpPlayer(oppositeType); } if (type === ANIMATION_TYPE.OPEN) { this.openAnimationPlayer = this.animationService.buildAnimation(animationSettings, targetElement.nativeElement); } else if (type === ANIMATION_TYPE.CLOSE) { this.closeAnimationPlayer = this.animationService.buildAnimation(animationSettings, targetElement.nativeElement); } const target = this.getPlayer(type); target.init(); this.getPlayer(type).position = 1 - oppositePosition; this.setCallback(type, callback); target.animationEnd.pipe(takeUntil(this.destroy$)).subscribe(() => { this.onDoneHandler(type); }); return target; } private onDoneHandler(type) { const targetEmitter = type === ANIMATION_TYPE.OPEN ? this.openAnimationDone : this.closeAnimationDone; const targetCallback = type === ANIMATION_TYPE.OPEN ? this.onOpenedCallback : this.onClosedCallback; targetCallback(); if (!(type === ANIMATION_TYPE.OPEN ? this.openInterrupted : this.closeInterrupted)) { targetEmitter.emit(); } this.cleanUpPlayer(type); } private setCallback(type: ANIMATION_TYPE, callback: () => void) { if (type === ANIMATION_TYPE.OPEN) { this.onOpenedCallback = callback; this.openInterrupted = false; } else if (type === ANIMATION_TYPE.CLOSE) { this.onClosedCallback = callback; this.closeInterrupted = false; } } private cleanUpPlayer(target: ANIMATION_TYPE) { switch (target) { case ANIMATION_TYPE.CLOSE: if (this.closeAnimationPlayer != null) { this.closeAnimationPlayer.reset(); this.closeAnimationPlayer.destroy(); this.closeAnimationPlayer = null; } this.closeInterrupted = true; this.onClosedCallback = this._defaultClosedCallback; break; case ANIMATION_TYPE.OPEN: if (this.openAnimationPlayer != null) { this.openAnimationPlayer.reset(); this.openAnimationPlayer.destroy(); this.openAnimationPlayer = null; } this.openInterrupted = true; this.onOpenedCallback = this._defaultOpenedCallback; break; default: break; } } private getPlayer(type: ANIMATION_TYPE): AnimationPlayer { switch (type) { case ANIMATION_TYPE.OPEN: return this.openAnimationPlayer; case ANIMATION_TYPE.CLOSE: return this.closeAnimationPlayer; default: return null; } } }