@@ -23,9 +23,11 @@ module.exports = {
23
23
bind : function ( ) {
24
24
// uid as a cache identifier
25
25
this . id = '__v_repeat_' + ( ++ uid )
26
- // setup anchor node
27
- this . anchor = _ . createAnchor ( 'v-repeat' )
28
- _ . replace ( this . el , this . anchor )
26
+ // setup anchor nodes
27
+ this . start = _ . createAnchor ( 'v-repeat-start' )
28
+ this . end = _ . createAnchor ( 'v-repeat' )
29
+ _ . replace ( this . el , this . end )
30
+ _ . before ( this . start , this . end )
29
31
// check if this is a block repeat
30
32
this . template = this . el . tagName === 'TEMPLATE'
31
33
? templateParser . parse ( this . el , true )
@@ -39,6 +41,10 @@ module.exports = {
39
41
this . idKey =
40
42
this . _checkParam ( 'track-by' ) ||
41
43
this . _checkParam ( 'trackby' ) // 0.11.0 compat
44
+ // check for transition stagger
45
+ var stagger = + this . _checkParam ( 'stagger' )
46
+ this . enterStagger = + this . _checkParam ( 'enter-stagger' ) || stagger
47
+ this . leaveStagger = + this . _checkParam ( 'leave-stagger' ) || stagger
42
48
this . cache = Object . create ( null )
43
49
} ,
44
50
@@ -239,7 +245,9 @@ module.exports = {
239
245
diff : function ( data , oldVms ) {
240
246
var idKey = this . idKey
241
247
var converted = this . converted
242
- var anchor = this . anchor
248
+ var start = this . start
249
+ var end = this . end
250
+ var inDoc = _ . inDoc ( start )
243
251
var alias = this . arg
244
252
var init = ! oldVms
245
253
var vms = new Array ( data . length )
@@ -275,7 +283,7 @@ module.exports = {
275
283
vms [ i ] = vm
276
284
// insert if this is first run
277
285
if ( init ) {
278
- vm . $before ( anchor )
286
+ vm . $before ( end )
279
287
}
280
288
}
281
289
// if this is the first run, we're done.
@@ -285,45 +293,38 @@ module.exports = {
285
293
// Second pass, go through the old vm instances and
286
294
// destroy those who are not reused (and remove them
287
295
// from cache)
296
+ var removalIndex = 0
297
+ var totalRemoved = oldVms . length - vms . length
288
298
for ( i = 0 , l = oldVms . length ; i < l ; i ++ ) {
289
299
vm = oldVms [ i ]
290
300
if ( ! vm . _reused ) {
291
301
this . uncacheVm ( vm )
292
- vm . $destroy ( true )
302
+ vm . $destroy ( false , true ) // defer cleanup until removal
303
+ this . remove ( vm , removalIndex ++ , totalRemoved , inDoc )
293
304
}
294
305
}
295
306
// final pass, move/insert new instances into the
296
- // right place. We're going in reverse here because
297
- // insertBefore relies on the next sibling to be
298
- // resolved.
299
- var targetNext , currentNext
300
- i = vms . length
301
- while ( i -- ) {
307
+ // right place.
308
+ var targetPrev , prevEl , currentPrev
309
+ var insertionIndex = 0
310
+ for ( i = 0 , l = vms . length ; i < l ; i ++ ) {
302
311
vm = vms [ i ]
303
- // this is the vm that we should be in front of
304
- targetNext = vms [ i + 1 ]
305
- if ( ! targetNext ) {
306
- // This is the last item. If it's reused then
307
- // everything else will eventually be in the right
308
- // place, so no need to touch it. Otherwise, insert
309
- // it.
310
- if ( ! vm . _reused ) {
311
- vm . $before ( anchor )
312
+ // this is the vm that we should be after
313
+ targetPrev = vms [ i - 1 ]
314
+ prevEl = targetPrev
315
+ ? targetPrev . _staggerCb
316
+ ? targetPrev . _staggerAnchor
317
+ : targetPrev . _blockEnd || targetPrev . $el
318
+ : start
319
+ if ( vm . _reused && ! vm . _staggerCb ) {
320
+ currentPrev = findPrevVm ( vm , start )
321
+ if ( currentPrev !== targetPrev ) {
322
+ this . move ( vm , prevEl )
312
323
}
313
324
} else {
314
- var nextEl = targetNext . $el
315
- if ( vm . _reused ) {
316
- // this is the vm we are actually in front of
317
- currentNext = findNextVm ( vm , anchor )
318
- // we only need to move if we are not in the right
319
- // place already.
320
- if ( currentNext !== targetNext ) {
321
- vm . $before ( nextEl , null , false )
322
- }
323
- } else {
324
- // new instance, insert to existing next
325
- vm . $before ( nextEl )
326
- }
325
+ // new instance, or still in stagger.
326
+ // insert with updated stagger index.
327
+ this . insert ( vm , insertionIndex ++ , prevEl , inDoc )
327
328
}
328
329
vm . _reused = false
329
330
}
@@ -559,12 +560,114 @@ module.exports = {
559
560
this . converted = true
560
561
return res
561
562
}
563
+ } ,
564
+
565
+ /**
566
+ * Insert an instance.
567
+ *
568
+ * @param {Vue } vm
569
+ * @param {Number } index
570
+ * @param {Node } prevEl
571
+ * @param {Boolean } inDoc
572
+ */
573
+
574
+ insert : function ( vm , index , prevEl , inDoc ) {
575
+ if ( vm . _staggerCb ) {
576
+ vm . _staggerCb . cancel ( )
577
+ vm . _staggerCb = null
578
+ }
579
+ var staggerAmount = this . getStagger ( vm , index , null , 'enter' )
580
+ if ( inDoc && staggerAmount ) {
581
+ // create an anchor and insert it synchronously,
582
+ // so that we can resolve the correct order without
583
+ // worrying about some elements not inserted yet
584
+ var anchor = vm . _staggerAnchor
585
+ if ( ! anchor ) {
586
+ anchor = vm . _staggerAnchor = _ . createAnchor ( 'stagger-anchor' )
587
+ anchor . __vue__ = vm
588
+ }
589
+ _ . after ( anchor , prevEl )
590
+ var op = vm . _staggerCb = _ . cancellable ( function ( ) {
591
+ vm . _staggerCb = null
592
+ vm . $before ( anchor )
593
+ _ . remove ( anchor )
594
+ } )
595
+ setTimeout ( op , staggerAmount )
596
+ } else {
597
+ vm . $after ( prevEl )
598
+ }
599
+ } ,
600
+
601
+ /**
602
+ * Move an already inserted instance.
603
+ *
604
+ * @param {Vue } vm
605
+ * @param {Node } prevEl
606
+ */
607
+
608
+ move : function ( vm , prevEl ) {
609
+ vm . $after ( prevEl , null , false )
610
+ } ,
611
+
612
+ /**
613
+ * Remove an instance.
614
+ *
615
+ * @param {Vue } vm
616
+ * @param {Number } index
617
+ * @param {Boolean } inDoc
618
+ */
619
+
620
+ remove : function ( vm , index , total , inDoc ) {
621
+ if ( vm . _staggerCb ) {
622
+ vm . _staggerCb . cancel ( )
623
+ vm . _staggerCb = null
624
+ // it's not possible for the same vm to be removed
625
+ // twice, so if we have a pending stagger callback,
626
+ // it means this vm is queued for enter but removed
627
+ // before its transition started. Since it is already
628
+ // destroyed, we can just leave it in detached state.
629
+ return
630
+ }
631
+ var staggerAmount = this . getStagger ( vm , index , total , 'leave' )
632
+ if ( inDoc && staggerAmount ) {
633
+ var op = vm . _staggerCb = _ . cancellable ( function ( ) {
634
+ vm . _staggerCb = null
635
+ remove ( )
636
+ } )
637
+ setTimeout ( op , staggerAmount )
638
+ } else {
639
+ remove ( )
640
+ }
641
+ function remove ( ) {
642
+ vm . $remove ( function ( ) {
643
+ vm . _cleanup ( )
644
+ } )
645
+ }
646
+ } ,
647
+
648
+ /**
649
+ * Get the stagger amount for an insertion/removal.
650
+ *
651
+ * @param {Vue } vm
652
+ * @param {Number } index
653
+ * @param {String } type
654
+ * @param {Number } total
655
+ */
656
+
657
+ getStagger : function ( vm , index , total , type ) {
658
+ type = type + 'Stagger'
659
+ var transition = vm . $el . __v_trans
660
+ var hooks = transition && transition . hooks
661
+ var hook = hooks && ( hooks [ type ] || hooks . stagger )
662
+ return hook
663
+ ? hook . call ( vm , index , total )
664
+ : index * this [ type ]
562
665
}
563
666
564
667
}
565
668
566
669
/**
567
- * Helper to find the next element that is an instance
670
+ * Helper to find the previous element that is an instance
568
671
* root node. This is necessary because a destroyed vm's
569
672
* element could still be lingering in the DOM before its
570
673
* leaving transition finishes, but its __vue__ reference
@@ -575,10 +678,10 @@ module.exports = {
575
678
* @return {Vue }
576
679
*/
577
680
578
- function findNextVm ( vm , anchor ) {
579
- var el = ( vm . _blockEnd || vm . $el ) . nextSibling
681
+ function findPrevVm ( vm , anchor ) {
682
+ var el = vm . $el . previousSibling
580
683
while ( ! el . __vue__ && el !== anchor ) {
581
- el = el . nextSibling
684
+ el = el . previousSibling
582
685
}
583
686
return el . __vue__
584
687
}
0 commit comments