@@ -13,12 +13,13 @@ import {AnimationTransitionInstruction} from '../dsl/animation_transition_instru
1313import { AnimationTrigger } from '../dsl/animation_trigger' ;
1414import { ElementInstructionMap } from '../dsl/element_instruction_map' ;
1515import { AnimationStyleNormalizer } from '../dsl/style_normalization/animation_style_normalizer' ;
16- import { ENTER_CLASSNAME , LEAVE_CLASSNAME , LEAVE_SELECTOR , NG_ANIMATING_CLASSNAME , NG_TRIGGER_CLASSNAME , NG_TRIGGER_SELECTOR , copyObj , eraseStyles , iteratorToArray , setStyles } from '../util' ;
16+ import { ENTER_CLASSNAME , LEAVE_CLASSNAME , LEAVE_SELECTOR , NG_ANIMATING_CLASSNAME , NG_TRIGGER_CLASSNAME , NG_TRIGGER_SELECTOR , copyObj , eraseStyles , setStyles } from '../util' ;
1717
1818import { AnimationDriver } from './animation_driver' ;
1919import { getOrSetAsInMap , listenOnPlayer , makeAnimationEvent , normalizeKeyframes , optimizeGroupPlayer } from './shared' ;
2020
2121const EMPTY_PLAYER_ARRAY : AnimationPlayer [ ] = [ ] ;
22+ const ANIMATE_EPOCH_ATTR = 'ng-animate-id' ;
2223
2324interface TriggerListener {
2425 name : string ;
@@ -255,7 +256,7 @@ export class AnimationTransitionNamespace {
255256 }
256257
257258 private _destroyInnerNodes ( rootElement : any , context : any , animate : boolean = false ) {
258- listToArray ( this . _engine . driver . query ( rootElement , NG_TRIGGER_SELECTOR , true ) ) . forEach ( elm => {
259+ this . _engine . driver . query ( rootElement , NG_TRIGGER_SELECTOR , true ) . forEach ( elm => {
259260 if ( animate && containsClass ( elm , this . _hostClassName ) ) {
260261 const innerNs = this . _engine . namespacesByHostElement . get ( elm ) ;
261262
@@ -273,9 +274,7 @@ export class AnimationTransitionNamespace {
273274
274275 removeNode ( element : any , context : any , doNotRecurse ?: boolean ) : void {
275276 const engine = this . _engine ;
276-
277- addClass ( element , LEAVE_CLASSNAME ) ;
278- engine . afterFlush ( ( ) => removeClass ( element , LEAVE_CLASSNAME ) ) ;
277+ engine . markElementAsRemoved ( element ) ;
279278
280279 if ( ! doNotRecurse && element . childElementCount ) {
281280 this . _destroyInnerNodes ( element , context , true ) ;
@@ -382,7 +381,7 @@ export class AnimationTransitionNamespace {
382381
383382 insertNode ( element : any , parent : any ) : void { addClass ( element , this . _hostClassName ) ; }
384383
385- drainQueuedTransitions ( countId : number ) : QueueInstruction [ ] {
384+ drainQueuedTransitions ( microtaskId : number ) : QueueInstruction [ ] {
386385 const instructions : QueueInstruction [ ] = [ ] ;
387386 this . _queue . forEach ( entry => {
388387 const player = entry . player ;
@@ -395,7 +394,7 @@ export class AnimationTransitionNamespace {
395394 if ( listener . name == entry . triggerName ) {
396395 const baseEvent = makeAnimationEvent (
397396 element , entry . triggerName , entry . fromState . value , entry . toState . value ) ;
398- ( baseEvent as any ) [ '_data' ] = countId ;
397+ ( baseEvent as any ) [ '_data' ] = microtaskId ;
399398 listenOnPlayer ( entry . player , listener . phase , baseEvent , listener . callback ) ;
400399 }
401400 } ) ;
@@ -449,13 +448,13 @@ export interface QueuedTransition {
449448export class TransitionAnimationEngine {
450449 public players : TransitionAnimationPlayer [ ] = [ ] ;
451450 public queuedRemovals = new Map < any , ( ) => any > ( ) ;
452- public newlyInserted = new Set < any > ( ) ;
453451 public newHostElements = new Map < any , AnimationTransitionNamespace > ( ) ;
454452 public playersByElement = new Map < any , TransitionAnimationPlayer [ ] > ( ) ;
455453 public playersByQueriedElement = new Map < any , TransitionAnimationPlayer [ ] > ( ) ;
456454 public statesByElement = new Map < any , { [ triggerName : string ] : StateValue } > ( ) ;
457455 public totalAnimations = 0 ;
458456 public totalQueuedPlayers = 0 ;
457+ public currentEpochId = 0 ;
459458
460459 private _namespaceLookup : { [ id : string ] : AnimationTransitionNamespace } = { } ;
461460 private _namespaceList : AnimationTransitionNamespace [ ] = [ ] ;
@@ -498,7 +497,7 @@ export class TransitionAnimationEngine {
498497 // animation renderer type. If this happens then we can still have
499498 // access to this item when we query for :enter nodes. If the parent
500499 // is a renderer then the set data-structure will normalize the entry
501- this . newlyInserted . add ( hostElement ) ;
500+ this . updateElementEpoch ( hostElement ) ;
502501 }
503502 return this . _namespaceLookup [ namespaceId ] = ns ;
504503 }
@@ -526,17 +525,24 @@ export class TransitionAnimationEngine {
526525 return ns ;
527526 }
528527
529- register ( namespaceId : string , hostElement : any , name : string , trigger : AnimationTrigger ) {
528+ register ( namespaceId : string , hostElement : any ) {
530529 let ns = this . _namespaceLookup [ namespaceId ] ;
531530 if ( ! ns ) {
532531 ns = this . createNamespace ( namespaceId , hostElement ) ;
533532 }
534- if ( ns . register ( name , trigger ) ) {
533+ return ns ;
534+ }
535+
536+ registerTrigger ( namespaceId : string , name : string , trigger : AnimationTrigger ) {
537+ let ns = this . _namespaceLookup [ namespaceId ] ;
538+ if ( ns && ns . register ( name , trigger ) ) {
535539 this . totalAnimations ++ ;
536540 }
537541 }
538542
539543 destroy ( namespaceId : string , context : any ) {
544+ if ( ! namespaceId ) return ;
545+
540546 const ns = this . _fetchNamespace ( namespaceId ) ;
541547
542548 this . afterFlush ( ( ) => {
@@ -564,20 +570,51 @@ export class TransitionAnimationEngine {
564570 insertNode ( namespaceId : string , element : any , parent : any , insertBefore : boolean ) : void {
565571 if ( ! isElementNode ( element ) ) return ;
566572
567- this . _fetchNamespace ( namespaceId ) . insertNode ( element , parent ) ;
573+ // special case for when an element is removed and reinserted (move operation)
574+ // when this occurs we do not want to use the element for deletion later
575+ if ( this . queuedRemovals . has ( element ) ) {
576+ this . markElementAsRemoved ( element , true ) ;
577+ this . queuedRemovals . delete ( element ) ;
578+ }
579+
580+ // in the event that the namespaceId is blank then the caller
581+ // code does not contain any animation code in it, but it is
582+ // just being called so that the node is marked as being inserted
583+ if ( namespaceId ) {
584+ this . _fetchNamespace ( namespaceId ) . insertNode ( element , parent ) ;
585+ }
568586
569587 // only *directives and host elements are inserted before
570588 if ( insertBefore ) {
571- this . newlyInserted . add ( element ) ;
589+ this . updateElementEpoch ( element ) ;
590+ }
591+ }
592+
593+ updateElementEpoch ( element : any , isRemoval ?: boolean ) {
594+ const epoch = ( isRemoval ? - 1 : 1 ) * this . currentEpochId ;
595+ setAttribute ( element , ANIMATE_EPOCH_ATTR , epoch ) ;
596+ }
597+
598+ markElementAsRemoved ( element : any , unmark ?: boolean ) {
599+ if ( unmark ) {
600+ removeClass ( element , LEAVE_CLASSNAME ) ;
601+ } else {
602+ addClass ( element , LEAVE_CLASSNAME ) ;
603+ this . afterFlush ( ( ) => removeClass ( element , LEAVE_CLASSNAME ) ) ;
572604 }
573605 }
574606
575607 removeNode ( namespaceId : string , element : any , context : any , doNotRecurse ?: boolean ) : void {
576- const ns = this . _fetchNamespace ( namespaceId ) ;
577- if ( ! isElementNode ( element ) || ! ns ) {
578- this . _onRemovalComplete ( element , context ) ;
608+ if ( namespaceId ) {
609+ const ns = this . _fetchNamespace ( namespaceId ) ;
610+ if ( ! isElementNode ( element ) || ! ns ) {
611+ this . _onRemovalComplete ( element , context ) ;
612+ } else {
613+ ns . removeNode ( element , context , doNotRecurse ) ;
614+ }
579615 } else {
580- ns . removeNode ( element , context , doNotRecurse ) ;
616+ this . markElementAsRemoved ( element ) ;
617+ this . queuedRemovals . set ( element , ( ) => this . _onRemovalComplete ( element , context ) ) ;
581618 }
582619 }
583620
@@ -597,7 +634,7 @@ export class TransitionAnimationEngine {
597634 }
598635
599636 destroyInnerAnimations ( containerElement : any ) {
600- listToArray ( this . driver . query ( containerElement , NG_TRIGGER_SELECTOR , true ) ) . forEach ( element => {
637+ this . driver . query ( containerElement , NG_TRIGGER_SELECTOR , true ) . forEach ( element => {
601638 const players = this . playersByElement . get ( element ) ;
602639 if ( players ) {
603640 players . forEach ( player => {
@@ -628,20 +665,21 @@ export class TransitionAnimationEngine {
628665 } ) ;
629666 }
630667
631- flush ( countId : number = - 1 ) {
668+ flush ( microtaskId : number = - 1 ) {
632669 let players : AnimationPlayer [ ] = [ ] ;
633670 if ( this . newHostElements . size ) {
634- this . newHostElements . forEach ( ( ns , element ) => { this . _balanceNamespaceList ( ns , element ) ; } ) ;
671+ this . newHostElements . forEach ( ( ns , element ) => this . _balanceNamespaceList ( ns , element ) ) ;
635672 this . newHostElements . clear ( ) ;
636673 }
637674
638675 if ( this . _namespaceList . length && ( this . totalQueuedPlayers || this . queuedRemovals . size ) ) {
639- players = this . _flushAnimations ( countId ) ;
676+ players = this . _flushAnimations ( microtaskId ) ;
677+ } else {
678+ this . queuedRemovals . forEach ( fn => fn ( ) ) ;
640679 }
641680
642681 this . totalQueuedPlayers = 0 ;
643682 this . queuedRemovals . clear ( ) ;
644- this . newlyInserted . clear ( ) ;
645683 this . _flushFns . forEach ( fn => fn ( ) ) ;
646684 this . _flushFns = [ ] ;
647685
@@ -658,9 +696,11 @@ export class TransitionAnimationEngine {
658696 quietFns . forEach ( fn => fn ( ) ) ;
659697 }
660698 }
699+
700+ this . currentEpochId ++ ;
661701 }
662702
663- private _flushAnimations ( countId : number ) : TransitionAnimationPlayer [ ] {
703+ private _flushAnimations ( microtaskId : number ) : TransitionAnimationPlayer [ ] {
664704 const subTimelines = new ElementInstructionMap ( ) ;
665705 const skippedPlayers : TransitionAnimationPlayer [ ] = [ ] ;
666706 const skippedPlayersMap = new Map < any , AnimationPlayer [ ] > ( ) ;
@@ -672,13 +712,15 @@ export class TransitionAnimationEngine {
672712 // this must occur before the instructions are built below such that
673713 // the :enter queries match the elements (since the timeline queries
674714 // are fired during instruction building).
675- const allEnterNodes = iteratorToArray ( this . newlyInserted . values ( ) ) ;
676- const enterNodes : any [ ] = collectEnterElements ( this . driver , allEnterNodes ) ;
677715 const bodyNode = getBodyNode ( ) ;
716+ const allEnterNodes : any [ ] =
717+ bodyNode ? this . driver . query ( bodyNode , makeEpochSelector ( this . currentEpochId ) , true ) : [ ] ;
718+ const enterNodes : any [ ] =
719+ allEnterNodes . length ? collectEnterElements ( this . driver , allEnterNodes ) : [ ] ;
678720
679721 for ( let i = this . _namespaceList . length - 1 ; i >= 0 ; i -- ) {
680722 const ns = this . _namespaceList [ i ] ;
681- ns . drainQueuedTransitions ( countId ) . forEach ( entry => {
723+ ns . drainQueuedTransitions ( microtaskId ) . forEach ( entry => {
682724 const player = entry . player ;
683725
684726 const element = entry . element ;
@@ -751,7 +793,7 @@ export class TransitionAnimationEngine {
751793 allPreviousPlayersMap . forEach ( players => players . forEach ( player => player . destroy ( ) ) ) ;
752794
753795 const leaveNodes : any [ ] = bodyNode && allPostStyleElements . size ?
754- listToArray ( this . driver . query ( bodyNode , LEAVE_SELECTOR , true ) ) :
796+ this . driver . query ( bodyNode , LEAVE_SELECTOR , true ) :
755797 [ ] ;
756798
757799 // PRE STAGE: fill the ! styles
@@ -866,7 +908,6 @@ export class TransitionAnimationEngine {
866908 elementContainsData ( namespaceId : string , element : any ) {
867909 let containsData = false ;
868910 if ( this . queuedRemovals . has ( element ) ) containsData = true ;
869- if ( this . newlyInserted . has ( element ) ) containsData = true ;
870911 if ( this . playersByElement . has ( element ) ) containsData = true ;
871912 if ( this . playersByQueriedElement . has ( element ) ) containsData = true ;
872913 if ( this . statesByElement . has ( element ) ) containsData = true ;
@@ -1217,12 +1258,6 @@ function cloakAndComputeStyles(
12171258 return valuesMap ;
12181259}
12191260
1220- function listToArray ( list : any ) : any [ ] {
1221- const arr : any [ ] = [ ] ;
1222- arr . push ( ...( list as any [ ] ) ) ;
1223- return arr ;
1224- }
1225-
12261261function collectEnterElements ( driver : AnimationDriver , allEnterNodes : any [ ] ) {
12271262 allEnterNodes . forEach ( element => addClass ( element , POTENTIAL_ENTER_CLASSNAME ) ) ;
12281263 const enterNodes = filterNodeClasses ( driver , getBodyNode ( ) , POTENTIAL_ENTER_SELECTOR ) ;
@@ -1264,9 +1299,22 @@ function removeClass(element: any, className: string) {
12641299 }
12651300}
12661301
1302+ function setAttribute ( element : any , attr : string , value : any ) {
1303+ if ( element . setAttribute ) {
1304+ element . setAttribute ( attr , value ) ;
1305+ } else {
1306+ element [ attr ] = value ;
1307+ }
1308+ }
1309+
12671310function getBodyNode ( ) : any | null {
12681311 if ( typeof document != 'undefined' ) {
12691312 return document . body ;
12701313 }
12711314 return null ;
12721315}
1316+
1317+ function makeEpochSelector ( epochId : number , isRemoval ?: boolean ) {
1318+ const value = ( isRemoval ? - 1 : 1 ) * epochId ;
1319+ return `[${ ANIMATE_EPOCH_ATTR } ="${ value } "]` ;
1320+ }
0 commit comments