From 2fca2726d0056267fb5aa5fb9650d3e832605896 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bastian=20M=C3=BCller?= <bastian@turbolent.com>
Date: Sun, 18 Feb 2018 16:02:58 -0800
Subject: [PATCH 1/2] notify delegate when elements are invalidated

---
 src/collection-view.ts | 47 ++++++++++++++++++++++++++++--------------
 1 file changed, 31 insertions(+), 16 deletions(-)

diff --git a/src/collection-view.ts b/src/collection-view.ts
index ff6b111..a5bb421 100644
--- a/src/collection-view.ts
+++ b/src/collection-view.ts
@@ -11,6 +11,7 @@ const TRANSITION_END_EVENT = 'transitionend'
 export interface CollectionViewDelegate {
   getCount(): number
   configureElement(element: HTMLElement, index: number): void
+  invalidateElement?(element: HTMLElement, index: number): void
   onScroll?(collectionView: CollectionView): void
 }
 
@@ -162,12 +163,8 @@ export default class CollectionView {
                                  elementHandler(element))
     }
 
-    this._elements.forEach((element) => {
-      const parent = element.parentElement
-      if (parent) {
-        parent.removeChild(element)
-      }
-    })
+    this._elements.forEach((element) =>
+                             this.removeElement(element))
   }
 
   private get currentContainerSize(): NumberTuple {
@@ -249,8 +246,9 @@ export default class CollectionView {
     this.updateIndices(this.currentIndices)
   }
 
+  // update elements when viewport changes (e.g. when scrolling)
   private updateIndices(newIndices: number[]): void {
-    // determine old elements
+    // determine old elements. save invalid elements so they can be reused
     const invalidElements: HTMLElement[] = []
 
     this._elements.forEach((element, index) => {
@@ -259,6 +257,10 @@ export default class CollectionView {
       }
 
       this._elements.delete(index)
+      if (this.delegate.invalidateElement) {
+        this.delegate.invalidateElement(element, index)
+      }
+
       invalidElements.push(element)
     })
 
@@ -266,6 +268,7 @@ export default class CollectionView {
     const currentIndices = this._visibleIndices
     newIndices.filter((index) => currentIndices.indexOf(index) < 0)
               .forEach((index) => {
+                // reuse one of the invalid/old elements, or create a new element
                 const element = invalidElements.pop()
                   || this.createAndAddElement()
                 this.configureElement(this._layout, element, index)
@@ -280,10 +283,7 @@ export default class CollectionView {
       if (element == null) {
         return
       }
-      const parent = element.parentElement
-      if (parent) {
-        parent.removeChild(element)
-      }
+      this.removeElement(element)
     })
   }
 
@@ -384,6 +384,8 @@ export default class CollectionView {
       }
 
       setTimeout(() => {
+        this.updateIndices(futureIndices)
+
         if (completion) {
           completion()
         }
@@ -420,6 +422,15 @@ export default class CollectionView {
     requestAnimationFrame(scroll)
   }
 
+  private removeElement(element: HTMLElement) {
+    const parent = element.parentElement
+    if (!parent) {
+      return;
+    }
+
+    parent.removeChild(element)
+  }
+
   public changeIndices(removedIndices: number[], addedIndices: number[], movedIndexMap: Map<number, number>): void {
 
     // handle legacy Object
@@ -489,12 +500,16 @@ export default class CollectionView {
 
       element.classList.add(this.disappearingClassName)
       element.style.zIndex = '0'
+
+      // NOTE: notify delegate about invalidation after element was removed
+      // (animation finished), not immediately when stopping to keep track of it
       setTimeout(() => {
-          const parent = element.parentElement
-          if (parent) {
-            parent.removeChild(element)
-          }
-        }, this.animationDuration)
+                   this.removeElement(element)
+                   if (this.delegate.invalidateElement) {
+                     this.delegate.invalidateElement(element, index)
+                   }
+                 },
+                 this.animationDuration)
       this._elements.delete(index)
     })
 

From 5a7830f5de052b49444f87a04ac7ccc426b178fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bastian=20M=C3=BCller?= <bastian@turbolent.com>
Date: Sun, 18 Feb 2018 16:03:16 -0800
Subject: [PATCH 2/2] fix layout creation

---
 examples/change-size/src/index.js | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/examples/change-size/src/index.js b/examples/change-size/src/index.js
index 3b8ff68..5d19434 100644
--- a/examples/change-size/src/index.js
+++ b/examples/change-size/src/index.js
@@ -20,9 +20,8 @@ class Delegate {
 }
 
 function newLayout(large) {
-  const layout = new GridLayout()
-  layout.itemSize = large ? [260, 260] : [180, 180]
-  return layout
+  const itemSize = large ? [260, 260] : [180, 180]
+  return new GridLayout({itemSize})
 }
 
 window.onload = function () {