import { Component , Input , OnChanges , SimpleChanges , OnDestroy , Output , EventEmitter , OnInit , SimpleChange , ChangeDetectionStrategy , ViewEncapsulation , Inject , LOCALE_ID , ChangeDetectorRef , TemplateRef , NgZone , } from '@angular/core' ; import { CountdownConfig , CountdownStatus , CountdownEvent , CountdownEventAction , CountdownItem } from './interfaces' ; import { CountdownTimer } from './countdown.timer' ; import { CountdownGlobalConfig } from './countdown.config' ; @ Component ( { selector : 'countdown' , template : `
    <ng-container *ngIf="!render">
      <span [innerHTML]="i.text"></span>
    </ng-container>
    <ng-container *ngTemplateOutlet="render; context: { $implicit: i }"></ng-container>
  ` , host : { '[class.count-down]' : 'true' } , encapsulation : ViewEncapsulation . None , changeDetection : ChangeDetectionStrategy . OnPush , } ) export class CountdownComponent implements OnInit , OnChanges , OnDestroy { private frequency = 1000 ; private _notify : { [ key ] : boolean ; } = { } ; private status : CountdownStatus = CountdownStatus . ing ; private isDestroy = false ; private _config ! : CountdownConfig ; i : CountdownItem = { } ; left = 0 ; @ Input ( ) set config ( i ) { if ( i . notify != null && ! Array . isArray ( i . notify ) && i . notify > 0 ) { i . notify = [ i . notify ] ; } this . _config = i ; } get config ( ) : CountdownConfig { return this . _config ; } @ Input ( ) render ! : TemplateRef < void > ; @ Output ( ) readonly event = new EventEmitter < CountdownEvent > ( ) ; constructor ( @ Inject ( LOCALE_ID ) private locale , private timer , private defCog , private cdr , private ngZone ) { } /**
     * Start countdown, you must manually call when `demand: false`
     */ begin ( ) { this . status = CountdownStatus . ing ; this . callEvent ( 'start' ) ; } /**
     * Restart countdown
     */ restart ( ) { if ( this . status !== CountdownStatus . stop ) { this . destroy ( ) ; } this . init ( ) ; this . callEvent ( 'restart' ) ; } /**
     * Stop countdown, must call `restart` when stopped, it's different from pause, unable to recover
     */ stop ( ) { if ( this . status === CountdownStatus . stop ) { return ; } this . status = CountdownStatus . stop ; this . destroy ( ) ; this . callEvent ( 'stop' ) ; } /**
     * Pause countdown, you can use `resume` to recover again
     */ pause ( ) { if ( this . status === CountdownStatus . stop || this . status === CountdownStatus . pause ) { return ; } this . status = CountdownStatus . pause ; this . callEvent ( 'pause' ) ; } /**
     * Resume countdown
     */ resume ( ) { if ( this . status === CountdownStatus . stop || this . status !== CountdownStatus . pause ) { return ; } this . status = CountdownStatus . ing ; this . callEvent ( 'resume' ) ; } private callEvent ( action ) { this . event . emit ( { action , left : this . left , status : this . status , text : this . i . text ! } ) ; } private init ( ) { const { locale , defCog } = this ; const config = ( this . config = { ... new CountdownGlobalConfig ( locale ) , ... defCog , ... this . config , } ) ; const frq = ( this . frequency = ~ config . format ! . indexOf ( 'S' ) ? 100 : 1000 ) ; this . status = config . demand ? CountdownStatus . pause : CountdownStatus . ing ; this . getLeft ( ) ; const _reflow = this . reflow ; this . reflow = ( count = 0 , force = false ) => _reflow . apply ( this , [ count , force ] ) ; if ( Array . isArray ( config . notify ) ) { config . notify . forEach ( ( time ) => { if ( time < 1 ) { throw new Error ( `The notify config must be a positive integer.` ) ; } time = time * 1000 ; time = time - ( time % frq ) ; this . _notify [ time ] = true ; } ) ; } this . timer . add ( this . reflow , frq ) . start ( ) ; this . reflow ( 0 , true ) ; } private destroy ( ) { this . timer . remove ( this . reflow ) ; return this ; } /**
     * 更新时钟
     */ private reflow ( count = 0 , force = false ) { if ( this . isDestroy ) { return ; } const { status , config , _notify } = this ; if ( ! force && status !== CountdownStatus . ing ) { return ; } let value = ( this . left = this . left - this . frequency * count ) ; if ( value < 1 ) { value = 0 ; } this . i = { value , text : config . formatDate ! ( { date : value , formatStr : config . format ! , timezone : config . timezone } ) , } ; if ( typeof config . prettyText === 'function' ) { this . i . text = config . prettyText ( this . i . text ! ) ; } this . cdr . detectChanges ( ) ; if ( config . notify === 0 || _notify [ value ] ) { this . ngZone . run ( ( ) => { this . callEvent ( 'notify' ) ; } ) ; } if ( value === 0 ) { this . ngZone . run ( ( ) => { this . status = CountdownStatus . done ; this . destroy ( ) ; this . callEvent ( 'done' ) ; } ) ; } } /**
     * 获取倒计时剩余帧数
     */ private getLeft ( ) { const { config , frequency } = this ; let left = config . leftTime ! * 1000 ; const end = config . stopTime ; if ( ! left && end ) { left = end - new Date ( ) . getTime ( ) ; } this . left = left - ( left % frequency ) ; } ngOnInit ( ) { this . init ( ) ; if ( ! this . config . demand ) { this . begin ( ) ; } } ngOnDestroy ( ) { this . isDestroy = true ; this . destroy ( ) ; } ngOnChanges ( changes ) { if ( ! changes . config ! . firstChange ) { this . restart ( ) ; } } }