1- import React , { Fragment } from 'react' ;
1+ import React , { Fragment , ReactNode , ReactElement , forwardRef } from 'react' ;
22import { gsap } from 'gsap' ;
3+ import { isForwardRef , isFragment } from 'react-is' ;
34import { PlayState } from './types' ;
45import { getTweenFunction , setPlayState , refOrInnerRef , nullishCoalescing } from './helper' ;
56import Provider , { Context } from './Provider' ;
7+ import { TweenProps } from './Tween' ;
68
79type Label = {
810 label : string ;
911 position : string | number ;
1012} ;
1113
14+ export type Targets = Map < string | number , ReactElement | ReactElement [ ] > ;
15+ export type TargetsRef = {
16+ set : ( key : string , target : any ) => void ;
17+ } ;
18+
19+ export type Target = ReactElement | null ;
20+
1221export type TimelineProps = {
13- children : React . ReactNode ;
14- wrapper ?: React . ReactElement ;
15- target ?: any ;
22+ children : ReactNode ;
23+ wrapper ?: ReactElement ;
24+ target ?: Target ;
1625 position ?: string | number ;
1726 labels ?: Label [ ] ;
1827
@@ -29,7 +38,13 @@ class Timeline extends Provider<TimelineProps> {
2938 static contextType = Context ;
3039
3140 timeline : any ;
32- targets : any [ ] = [ ] ;
41+ targets : Targets = new Map ( ) ;
42+
43+ constructor ( props : TimelineProps ) {
44+ super ( props ) ;
45+
46+ this . setTarget = this . setTarget . bind ( this ) ;
47+ }
3348
3449 setPlayState ( playState : PlayState ) {
3550 const { playState : previousPlayState } = this . props ;
@@ -45,7 +60,7 @@ class Timeline extends Provider<TimelineProps> {
4560 }
4661
4762 getSnapshotBeforeUpdate ( ) {
48- this . targets = [ ] ;
63+ this . targets = new Map ( ) ;
4964 return null ;
5065 }
5166
@@ -105,12 +120,24 @@ class Timeline extends Provider<TimelineProps> {
105120 // add tweens or nested timelines to timeline
106121 this . consumers . forEach ( consumer => {
107122 if ( consumer . tween && ! consumer . props . children ) {
108- const { position, target, stagger, ...vars } = consumer . props ;
109- const tween = getTweenFunction ( nullishCoalescing ( this . targets [ target ] , this . targets ) , {
110- stagger,
111- ...vars ,
112- } ) ;
123+ const { position, target, stagger, ...vars } = consumer . props as TweenProps ;
124+
125+ // get target if not nullish
126+ let targets = null ;
127+ if ( target !== null && typeof target !== 'undefined' ) {
128+ targets = this . targets . get ( target ) ;
129+ }
130+
131+ const tween = getTweenFunction (
132+ // @ts -ignore
133+ nullishCoalescing ( targets , Array . from ( this . targets . values ( ) ) ) ,
134+ {
135+ stagger,
136+ ...vars ,
137+ }
138+ ) ;
113139 this . timeline . add ( tween , nullishCoalescing ( position , '+=0' ) ) ;
140+ consumer . setGSAP ( tween ) ;
114141 } else {
115142 const { position } = consumer . props ;
116143 this . timeline . add ( consumer . getGSAP ( ) , nullishCoalescing ( position , '+=0' ) ) ;
@@ -139,12 +166,28 @@ class Timeline extends Provider<TimelineProps> {
139166 }
140167
141168 addTarget ( target : any ) {
142- // target is null at unmount
143169 if ( target !== null ) {
144- this . targets . push ( target ) ;
170+ this . targets . set ( this . targets . size , target ) ;
145171 }
146172 }
147173
174+ setTarget ( key : string , target : any ) {
175+ if ( target !== null ) {
176+ if ( this . targets . has ( key ) ) {
177+ const targets = this . targets . get ( key ) ;
178+ if ( Array . isArray ( targets ) ) {
179+ this . targets . set ( key , [ ...targets , ...target ] ) ;
180+ return ;
181+ }
182+ }
183+ this . targets . set ( key , target ) ;
184+ }
185+ }
186+
187+ setTargets ( targets : Targets ) {
188+ this . targets = targets ;
189+ }
190+
148191 getTargets ( ) {
149192 return this . targets ;
150193 }
@@ -156,20 +199,40 @@ class Timeline extends Provider<TimelineProps> {
156199 } ) ;
157200 }
158201
159- render ( ) {
160- let { target, children, wrapper } = this . props ;
202+ renderTarget ( target ?: Target ) : ReactNode {
203+ if ( ! target ) {
204+ return null ;
205+ }
161206
162- let output = (
207+ // if is forwardRef clone and pass targets as ref
208+ if ( isForwardRef ( target ) ) {
209+ return < target . type ref = { { set : this . setTarget } } /> ;
210+ }
211+
212+ // else iterate the first level of children and set targets
213+ return (
163214 < Fragment >
164215 { /* First render the target */ }
165- { React . Children . map ( target , child => {
166- if ( child . type . toString ( ) === 'Symbol(react.fragment)' ) {
216+ { React . Children . map < ReactElement , ReactElement > ( target , child => {
217+ if ( isFragment ( child ) ) {
167218 return React . Children . map ( child . props . children , fragmentChild => {
168219 return this . cloneElement ( fragmentChild ) ;
169220 } ) ;
170221 }
171222 return this . cloneElement ( child ) ;
172223 } ) }
224+ </ Fragment >
225+ ) ;
226+ }
227+
228+ render ( ) {
229+ let { target, children, wrapper } = this . props ;
230+
231+ const renderedTarget = this . renderTarget ( target ) ;
232+
233+ let output = (
234+ < Fragment >
235+ { renderedTarget }
173236 { children }
174237 </ Fragment >
175238 ) ;
0 commit comments