import React from 'react';

import promisifyEventListeners from './utils/promisifyEventListeners.js';

class LayoutTransitionGroup extends React.Component {
    state = {
        _transitionPending: false,
        _transitionRef: undefined,
        _transitionInitialDimens: undefined,
    }

    beginTransition = (ref, stateUpdateFn) => {
        // Traverse top layers for inital positions
        const childNodes = Array.from(ref.childNodes);
        this.intitialDimens = childNodes.map((child) => child.getBoundingClientRect());

        // Update state
        this.setState((prevState) => ({
            ...stateUpdateFn(prevState),
            _transitionPending: true,
            _transitionRef: ref,
        }));
    };

    componentDidUpdate(prevProps, prevState) {
        if (!this.state._transitionPending) return;

        // Traverse top layer for final positions
        const childNodes = Array.from(this.state._transitionRef.childNodes);
        this.finalDimens = childNodes.map((child) => child.getBoundingClientRect());

        // Fix existing nodes in same place
        childNodes.forEach((child, i) => {
            if (!this.intitialDimens[i]) {
                child.style.opacity = 0;
                child.style.pointerEvents = 'none';
                return;
            };
            const x = this.intitialDimens[i].left - this.finalDimens[i].left;
            const y = this.intitialDimens[i].top - this.finalDimens[i].top;
            const sx = this.intitialDimens[i].width / this.finalDimens[i].width;
            const sy = this.intitialDimens[i].height / this.finalDimens[i].height;
            child.style.transition = '';
            child.style.transform = `translate(${x}px, ${y}px) scale(${sx}, ${sy})`;
            child.style.transformOrigin = '0 0';
        });

        // Transition
            // Animate into new position
        requestAnimationFrame(() => {
            requestAnimationFrame(() => {
                const initialNodes = [];
                const newNodes = [];
                childNodes.forEach((child, i) => {
                    if (!this.intitialDimens[i]) {
                        newNodes.push(child);
                        return;
                    }
                    child.style.transition = 'transform 200ms ease-in-out';
                    child.style.transform = '';
                    initialNodes.push(child);
                });
                const promisifiedTransitionEnd = promisifyEventListeners(initialNodes, 'transitionend');
                Promise.all(promisifiedTransitionEnd)
                .then(() => {
                    initialNodes.forEach((child) => {
                        child.style.transition = '';
                    });
                    newNodes.forEach((child) => {
                        child.style.transition = 'opacity 200ms ease-in-out';
                        child.style.opacity = 1;
                        child.style.pointerEvents = '';
                    });
                });
            });
        });

        this.setState((state) => ({
            _transitionPending: false,
            _transitionRef: undefined,
            _transitionInitialDimens: undefined,
        }));
    }

    render() {
        return (
            <div>
                {this.state.children}
            </div>
        );
    }
}

export default LayoutTransitionGroup;

@@ -150,16 +150,77 @@
 onent %7B%0A
+    intitialDimens = new Map();%0A    finalDimens = new Map();%0A
     stat
@@ -225,16 +225,16 @@
 ate = %7B%0A
-
         
@@ -299,53 +299,8 @@
 ed,%0A
-        _transitionInitialDimens: undefined,%0A
     
@@ -469,55 +469,244 @@
     
-this.int
+childNodes.forEach((child, index) =%3E %7B%0A            // mark in
 itial
-Di
+ ele
 men
+t
 s 
-= childNodes.map((child) =%3E
+with unique keys to track%0A            const key = %60.$%7Bindex%7D%60;%0A            child.setAttribute('data-layout-key', key);%0A%0A            this.intitialDimens.set(key,
  chi
@@ -725,32 +725,44 @@
 ngClientRect());
+%0A        %7D);
 %0A%0A        // Upd
@@ -1183,52 +1183,147 @@
     
-this.finalDimens = childNodes.map((child) =%3E
+childNodes.forEach((child) =%3E %7B%0A            if (!child.dataset.layoutKey) return;%0A            this.finalDimens.set(child.dataset.layoutKey,
  chi
@@ -1350,16 +1350,28 @@
 Rect());
+%0A        %7D);
 %0A%0A      
@@ -1467,35 +1467,65 @@
     
-if (!this.intitialDimens%5Bi%5D
+const key = child.dataset.layoutKey;%0A            if (!key
 ) %7B%0A
@@ -1674,17 +1674,28 @@
   const 
-x
+initialDimen
  = this.
@@ -1712,38 +1712,129 @@
 mens
-%5Bi%5D.left - this.finalDimens%5Bi%5D
+.get(key);%0A            const finalDimen = this.finalDimens.get(key);%0A            const x = initialDimen.left - finalDimen
 .lef
@@ -1854,32 +1854,26 @@
   const y = 
-this.
 in
-t
 itialDimens%5B
@@ -1874,24 +1874,15 @@
 imen
-s%5Bi%5D
 .top - 
-this.
 fina
@@ -1887,20 +1887,16 @@
 nalDimen
-s%5Bi%5D
 .top;%0A  
@@ -1916,24 +1916,18 @@
 st sx = 
-this.
 in
-t
 itialDim
@@ -1924,28 +1924,24 @@
 initialDimen
-s%5Bi%5D
 .width / thi
@@ -1937,21 +1937,16 @@
 width / 
-this.
 finalDim
@@ -1947,20 +1947,16 @@
 nalDimen
-s%5Bi%5D
 .width;%0A
@@ -1978,24 +1978,18 @@
 st sy = 
-this.
 in
-t
 itialDim
@@ -1986,28 +1986,24 @@
 initialDimen
-s%5Bi%5D
 .height / th
@@ -2000,21 +2000,16 @@
 eight / 
-this.
 finalDim
@@ -2010,20 +2010,16 @@
 nalDimen
-s%5Bi%5D
 .height;
@@ -2508,30 +2508,31 @@
 f (!
-this.intitialDimens%5Bi%5D
+child.dataset.layoutKey
 ) %7B%0A
@@ -3056,32 +3056,32 @@
 ch((child) =%3E %7B%0A
-
                 
@@ -3109,32 +3109,98 @@
 ransition = '';%0A
+                        child.removeAttribute('data-layout-key');%0A
                 

Tag transitioning elements