11import * as React from 'react' ;
2- import {
3- unstable_renderSubtreeIntoContainer as renderSubtreeIntoContainer ,
4- unmountComponentAtNode ,
5- } from 'react-dom' ;
2+ import { createPortal } from 'react-dom' ;
63import Collector , {
74 SupplyDataHandler ,
85 SupplyRenderChildrenHandler ,
@@ -14,6 +11,7 @@ import Collector, {
1411 InlineStyles ,
1512 TargetPropsFunc ,
1613 AnimationData ,
14+ AnimationCallback ,
1715} from '../Collector' ;
1816import { getElementBoundingBox } from '../lib/dom' ;
1917import defer from '../lib/defer' ;
@@ -40,6 +38,7 @@ export interface ChildProps {
4038
4139export interface State {
4240 childProps : ChildProps ;
41+ animationsMarkup : React . ReactPortal [ ] ;
4342}
4443
4544export interface BabaProps extends CollectorChildrenProps , InjectedProps {
@@ -86,6 +85,7 @@ export default class Baba extends React.PureComponent<BabaProps, State> {
8685 } ;
8786
8887 state : State = {
88+ animationsMarkup : [ ] ,
8989 childProps : { } ,
9090 } ;
9191
@@ -253,121 +253,97 @@ If it's an image, try and have the image loaded before mounting, or set a static
253253 } ;
254254
255255 // Loads each action up in an easy-to-execute format.
256- const actions = collectorData . map ( targetData => {
257- if ( targetData . action === CollectorActions . animation ) {
258- // Element will be lazily instantiated if we need to add something to the DOM.
259- let elementToMountChildren : HTMLElement | null = null ;
260-
261- const mount = ( jsx : React . ReactNode ) => {
262- if ( ! elementToMountChildren ) {
263- elementToMountChildren = document . createElement ( 'div' ) ;
264- // We insert the new element at the beginning of the body to ensure correct
265- // stacking context.
266- container . insertBefore ( elementToMountChildren , container . firstChild ) ;
267- }
256+ const actions = collectorData . map ( ( targetData , index ) => {
257+ if ( targetData . action !== CollectorActions . animation ) {
258+ return targetData ;
259+ }
260+
261+ // Element will be lazily instantiated if we need to add something to the DOM.
262+ let elementToMountChildren : HTMLElement | null = null ;
263+
264+ const mount = ( jsx : React . ReactNode ) => {
265+ if ( ! elementToMountChildren ) {
266+ elementToMountChildren = document . createElement ( 'div' ) ;
267+ // We insert the new element at the beginning of the body to ensure correct stacking context.
268+ container . insertBefore ( elementToMountChildren , container . firstChild ) ;
269+ }
268270
269- // This ensures that if there was an update to the jsx that is animating,
270- // it changes next frame. Resulting in the transition _actually_ happening.
271- requestAnimationFrame (
272- ( ) =>
273- elementToMountChildren &&
274- renderSubtreeIntoContainer (
275- this ,
276- jsx as React . ReactElement < { } > ,
277- elementToMountChildren
278- )
279- ) ;
280- } ;
281-
282- const unmount = ( ) => {
271+ // This ensures that if there was an update to the jsx that is animating it changes next frame.
272+ // Resulting in the transition _actually_ happening.
273+ requestAnimationFrame ( ( ) => {
283274 if ( elementToMountChildren ) {
284- unmountComponentAtNode ( elementToMountChildren ) ;
285- container . removeChild ( elementToMountChildren ) ;
286- elementToMountChildren = null ;
287- }
288- } ;
289-
290- const setChildProps = ( props : TargetPropsFunc | null ) => {
291- if ( props ) {
292- this . setState ( prevState => ( {
293- childProps : {
294- style : props . style
295- ? props . style ( prevState . childProps . style || { } )
296- : prevState . childProps . style ,
297- className : props . className
298- ? props . className ( prevState . childProps . className )
299- : prevState . childProps . className ,
300- } ,
301- } ) ) ;
302- } else {
303- this . setState ( {
304- childProps : { } ,
275+ this . setState ( prevState => {
276+ const newAnimationsMarkup = prevState . animationsMarkup . concat ( ) ;
277+ newAnimationsMarkup [ index ] = createPortal ( jsx , elementToMountChildren ! ) ;
278+ return {
279+ animationsMarkup : newAnimationsMarkup ,
280+ } ;
305281 } ) ;
306282 }
307- } ;
308-
309- return {
310- action : CollectorActions . animation ,
311- payload : {
312- beforeAnimate : ( ) => {
313- if ( targetData . payload . beforeAnimate ) {
314- const deferred = defer ( ) ;
315- const jsx = targetData . payload . beforeAnimate (
316- animationData ,
317- deferred . resolve ,
318- setChildProps
319- ) ;
320-
321- if ( jsx ) {
322- mount ( jsx ) ;
323- }
324-
325- return deferred . promise ;
326- }
283+ } ) ;
284+ } ;
285+
286+ const setChildProps = ( props : TargetPropsFunc | null ) => {
287+ if ( this . unmounting ) {
288+ return ;
289+ }
327290
328- return Promise . resolve ( ) ;
291+ if ( props ) {
292+ this . setState ( prevState => ( {
293+ childProps : {
294+ style : props . style
295+ ? props . style ( prevState . childProps . style || { } )
296+ : prevState . childProps . style ,
297+ className : props . className
298+ ? props . className ( prevState . childProps . className )
299+ : prevState . childProps . className ,
329300 } ,
330- animate : ( ) => {
331- const deferred = defer ( ) ;
332- const jsx = targetData . payload . animate (
333- animationData ,
334- deferred . resolve ,
335- setChildProps
336- ) ;
301+ } ) ) ;
302+ } else {
303+ this . setState ( {
304+ childProps : { } ,
305+ } ) ;
306+ }
307+ } ;
337308
338- if ( jsx ) {
339- mount ( jsx ) ;
340- }
309+ const generatePhase = ( cb : AnimationCallback | undefined ) => ( ) => {
310+ if ( cb ) {
311+ const deferred = defer ( ) ;
312+ const jsx = cb ( animationData , deferred . resolve , setChildProps ) ;
341313
342- return deferred . promise ;
343- } ,
344- afterAnimate : ( ) => {
345- if ( targetData . payload . afterAnimate ) {
346- const deferred = defer ( ) ;
347- const jsx = targetData . payload . afterAnimate (
348- animationData ,
349- deferred . resolve ,
350- setChildProps
351- ) ;
352-
353- if ( jsx ) {
354- mount ( jsx ) ;
355- }
356-
357- return deferred . promise ;
358- }
314+ if ( jsx ) {
315+ mount ( jsx ) ;
316+ }
359317
360- return Promise . resolve ( ) ;
361- } ,
362- cleanup : ( ) => {
363- unmount ( ) ;
364- setChildProps ( null ) ;
365- } ,
366- } ,
367- } ;
368- }
318+ return deferred . promise ;
319+ }
369320
370- return targetData ;
321+ return Promise . resolve ( ) ;
322+ } ;
323+
324+ return {
325+ action : CollectorActions . animation ,
326+ payload : {
327+ beforeAnimate : generatePhase ( targetData . payload . beforeAnimate ) ,
328+ animate : generatePhase ( targetData . payload . animate ) ,
329+ afterAnimate : generatePhase ( targetData . payload . afterAnimate ) ,
330+ cleanup : ( ) => {
331+ if ( elementToMountChildren && container . contains ( elementToMountChildren ) ) {
332+ container . removeChild ( elementToMountChildren ) ;
333+ }
334+
335+ if ( this . unmounting ) {
336+ return ;
337+ }
338+
339+ this . setState ( {
340+ animationsMarkup : [ ] ,
341+ } ) ;
342+
343+ setChildProps ( null ) ;
344+ } ,
345+ } ,
346+ } ;
371347 } ) ;
372348
373349 const blocks = actions . reduce < AnimationBlock [ ] > (
@@ -476,21 +452,24 @@ If it's an image, try and have the image loaded before mounting, or set a static
476452 } ;
477453
478454 render ( ) {
479- const { childProps } = this . state ;
455+ const { childProps, animationsMarkup } = this . state ;
480456 const { children } = this . props ;
481457
482458 return (
483- < Collector
484- topMostCollector
485- receiveData = { this . setData }
486- receiveRenderChildren = { this . setReactNode }
487- receiveRef = { this . setRef }
488- receiveFocalTargetRef = { this . setTargetRef }
489- style = { childProps . style }
490- className = { childProps . className }
491- >
492- { typeof children === 'function' ? children : React . Children . only ( children ) }
493- </ Collector >
459+ < React . Fragment >
460+ { animationsMarkup }
461+ < Collector
462+ topMostCollector
463+ receiveData = { this . setData }
464+ receiveRenderChildren = { this . setReactNode }
465+ receiveRef = { this . setRef }
466+ receiveFocalTargetRef = { this . setTargetRef }
467+ style = { childProps . style }
468+ className = { childProps . className }
469+ >
470+ { typeof children === 'function' ? children : React . Children . only ( children ) }
471+ </ Collector >
472+ </ React . Fragment >
494473 ) ;
495474 }
496475}
0 commit comments