Skip to content

Commit 90e0c92

Browse files
author
Jan Fischer
committed
Add react-is, Timeline targets Map, able to set refs from target forwardRef component
1 parent 094ec24 commit 90e0c92

File tree

9 files changed

+121
-32
lines changed

9 files changed

+121
-32
lines changed

packages/playground/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"gsap": "^3.2.6",
1818
"react": "^16.13.1",
1919
"react-dom": "^16.13.1",
20-
"react-gsap": "2.0.6",
20+
"react-gsap": "2.2.0",
2121
"react-router-dom": "^5.1.2",
2222
"react-scripts": "3.4.1",
2323
"react-transition-group": "^4.3.0",

packages/playground/src/examples/Animation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const RubberBand = ({
3434
children,
3535
...rest
3636
}: {
37-
children: React.ReactNode;
37+
children: React.ReactElement;
3838
[key: string]: any;
3939
}) => (
4040
<Timeline target={children} {...rest}>

packages/playground/src/examples/Timeline.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { Fragment } from 'react';
1+
import React, { forwardRef, Fragment, useRef, useImperativeHandle, ReactElement } from 'react';
22
import styled from 'styled-components';
33
import { Tween, Timeline, SplitWords, SplitLetters, Controls, PlayState } from 'react-gsap';
44

@@ -114,4 +114,24 @@ const TimelineComponent = () => (
114114
</TimelineStyled>
115115
);
116116

117-
export default TimelineComponent;
117+
const TargetWithNames = forwardRef((props, targets: any) => (
118+
<div>
119+
<div ref={div => targets.set('div1', div)}>first</div>
120+
<div ref={div => targets.set('div2', div)} style={{ display: 'inline-block' }}>
121+
second
122+
</div>
123+
<div ref={div => targets.set('div3', div)}>third</div>
124+
</div>
125+
));
126+
127+
const TimelineTargets = () => {
128+
return (
129+
<Timeline target={<TargetWithNames />}>
130+
<Tween to={{ x: '200px' }} target="div3" position="0" />
131+
<Tween to={{ x: '200px' }} target="div1" position="0" />
132+
<Tween to={{ x: '200px', rotation: 0 }} from={{ rotation: 180 }} target="div2" position="0" />
133+
</Timeline>
134+
);
135+
};
136+
137+
export default TimelineTargets;

packages/react-gsap/package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-gsap",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"description": "React components for GSAP",
55
"author": "bitworking",
66
"license": "MIT",
@@ -25,8 +25,8 @@
2525
},
2626
"peerDependencies": {
2727
"gsap": ">=3",
28-
"react": ">=16",
29-
"react-dom": ">=16"
28+
"react": ">=16.6",
29+
"react-dom": ">=16.6"
3030
},
3131
"husky": {
3232
"hooks": {
@@ -44,12 +44,16 @@
4444
"devDependencies": {
4545
"@types/react": "^16.9.34",
4646
"@types/react-dom": "^16.9.7",
47+
"@types/react-is": "^16.7.1",
4748
"gsap": "^3.2.6",
4849
"husky": "^4.2.5",
4950
"react": "^16.13.1",
5051
"react-dom": "^16.13.1",
5152
"tsdx": "^0.13.2",
5253
"tslib": "^1.11.1",
5354
"typescript": "^3.8.3"
55+
},
56+
"dependencies": {
57+
"react-is": "^16.13.1"
5458
}
5559
}

packages/react-gsap/src/Timeline.tsx

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1-
import React, { Fragment } from 'react';
1+
import React, { Fragment, ReactNode, ReactElement, forwardRef } from 'react';
22
import { gsap } from 'gsap';
3+
import { isForwardRef, isFragment } from 'react-is';
34
import { PlayState } from './types';
45
import { getTweenFunction, setPlayState, refOrInnerRef, nullishCoalescing } from './helper';
56
import Provider, { Context } from './Provider';
7+
import { TweenProps } from './Tween';
68

79
type Label = {
810
label: string;
911
position: string | number;
1012
};
1113

14+
export type Targets = Map<string | number, ReactElement>;
15+
16+
export type Target = ReactElement | null;
17+
1218
export type TimelineProps = {
13-
children: React.ReactNode;
14-
wrapper?: React.ReactElement;
15-
target?: any;
19+
children: ReactNode;
20+
wrapper?: ReactElement;
21+
target?: Target;
1622
position?: string | number;
1723
labels?: Label[];
1824

@@ -29,7 +35,7 @@ class Timeline extends Provider<TimelineProps> {
2935
static contextType = Context;
3036

3137
timeline: any;
32-
targets: any[] = [];
38+
targets: Targets = new Map();
3339

3440
setPlayState(playState: PlayState) {
3541
const { playState: previousPlayState } = this.props;
@@ -45,7 +51,7 @@ class Timeline extends Provider<TimelineProps> {
4551
}
4652

4753
getSnapshotBeforeUpdate() {
48-
this.targets = [];
54+
this.targets = new Map();
4955
return null;
5056
}
5157

@@ -105,12 +111,21 @@ class Timeline extends Provider<TimelineProps> {
105111
// add tweens or nested timelines to timeline
106112
this.consumers.forEach(consumer => {
107113
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-
});
114+
const { position, target, stagger, ...vars } = consumer.props as TweenProps;
115+
116+
const tween = getTweenFunction(
117+
// @ts-ignore
118+
nullishCoalescing(
119+
target ? this.targets.get(target) : null,
120+
Array.from(this.targets.values())
121+
),
122+
{
123+
stagger,
124+
...vars,
125+
}
126+
);
113127
this.timeline.add(tween, nullishCoalescing(position, '+=0'));
128+
consumer.setGSAP(tween);
114129
} else {
115130
const { position } = consumer.props;
116131
this.timeline.add(consumer.getGSAP(), nullishCoalescing(position, '+=0'));
@@ -139,12 +154,21 @@ class Timeline extends Provider<TimelineProps> {
139154
}
140155

141156
addTarget(target: any) {
142-
// target is null at unmount
143157
if (target !== null) {
144-
this.targets.push(target);
158+
this.targets.set(this.targets.size, target);
159+
}
160+
}
161+
162+
setTarget(key: string, target: any) {
163+
if (target !== null) {
164+
this.targets.set(key, target);
145165
}
146166
}
147167

168+
setTargets(targets: Targets) {
169+
this.targets = targets;
170+
}
171+
148172
getTargets() {
149173
return this.targets;
150174
}
@@ -156,20 +180,40 @@ class Timeline extends Provider<TimelineProps> {
156180
});
157181
}
158182

159-
render() {
160-
let { target, children, wrapper } = this.props;
183+
renderTarget(target?: Target): ReactNode {
184+
if (!target) {
185+
return null;
186+
}
161187

162-
let output = (
188+
// if is forwardRef clone and pass targets as ref
189+
if (isForwardRef(target)) {
190+
return <target.type ref={this.targets} />;
191+
}
192+
193+
// else iterate the first level of children and set targets
194+
return (
163195
<Fragment>
164196
{/* First render the target */}
165-
{React.Children.map(target, child => {
166-
if (child.type.toString() === 'Symbol(react.fragment)') {
197+
{React.Children.map<ReactElement, ReactElement>(target, child => {
198+
if (isFragment(child)) {
167199
return React.Children.map(child.props.children, fragmentChild => {
168200
return this.cloneElement(fragmentChild);
169201
});
170202
}
171203
return this.cloneElement(child);
172204
})}
205+
</Fragment>
206+
);
207+
}
208+
209+
render() {
210+
let { target, children, wrapper } = this.props;
211+
212+
const renderedTarget = this.renderTarget(target);
213+
214+
let output = (
215+
<Fragment>
216+
{renderedTarget}
173217
{children}
174218
</Fragment>
175219
);

packages/react-gsap/src/Tween.tsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { PlayState } from './types';
44
import { getTweenFunction, setPlayState, isEqual, refOrInnerRef } from './helper';
55
import { Context } from './Provider';
66

7-
// import { CSSPlugin } from 'gsap/CSSPlugin';
7+
import { CSSPlugin } from 'gsap/CSSPlugin';
88
import SvgDrawPlugin from './plugins/PlugInSvgDraw';
99
import CountPlugin from './plugins/PlugInCount';
1010

11-
// gsap.registerPlugin(CSSPlugin);
11+
gsap.registerPlugin(CSSPlugin);
1212
gsap.registerPlugin(SvgDrawPlugin);
1313
gsap.registerPlugin(CountPlugin);
1414

@@ -35,7 +35,7 @@ export type TweenProps = {
3535
/** One or multiple "refable" components */
3636
children?: React.ReactNode;
3737
wrapper?: React.ReactElement;
38-
target?: number;
38+
target?: number | string;
3939
position?: string | number;
4040

4141
from?: any;
@@ -70,7 +70,9 @@ class Tween extends React.Component<TweenProps, {}> {
7070
}
7171

7272
componentWillUnmount() {
73-
this.tween.kill();
73+
if (this.tween) {
74+
this.tween.kill();
75+
}
7476
}
7577

7678
getSnapshotBeforeUpdate() {
@@ -164,14 +166,25 @@ class Tween extends React.Component<TweenProps, {}> {
164166
if (this.tween) {
165167
this.tween.kill();
166168
}
167-
this.tween = getTweenFunction(this.targets, this.props);
169+
170+
if (this.props.children) {
171+
this.tween = getTweenFunction(this.targets, this.props);
172+
} else {
173+
// why this is needed?
174+
this.tween = () => {};
175+
}
176+
168177
this.context.registerConsumer(this);
169178
}
170179

171180
getGSAP() {
172181
return this.tween;
173182
}
174183

184+
setGSAP(tween: any) {
185+
this.tween = tween;
186+
}
187+
175188
addTarget(target: any) {
176189
// target is null at unmount
177190
if (target !== null) {

packages/react-gsap/src/helper.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const getTweenFunction = (targets: any, props: any): gsap.core.Tween | gsap.core
6666
}
6767

6868
// if multiple tweens (stagger), wrap them in a timeline
69+
// TODO: if it's already an timeline add event handlers
6970
if (Array.isArray(tweenFunction)) {
7071
tweenFunction.forEach(t => {
7172
t.paused(false);

packages/react-gsap/src/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export { default as Tween } from './Tween';
22
export { default as Timeline } from './Timeline';
33
export { default as Reveal } from './tools/Reveal';
4-
// export { default as Scroller } from './tools/Scroller';
4+
export { default as Scroller } from './tools/Scroller';
55
export { SplitWords, SplitChars, SplitLetters } from './tools/SplitText';
66
export { default as Controls } from './tools/Controls';
77
export { PlayState } from './types';

yarn.lock

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1990,6 +1990,13 @@
19901990
dependencies:
19911991
"@types/react" "*"
19921992

1993+
"@types/react-is@^16.7.1":
1994+
version "16.7.1"
1995+
resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-16.7.1.tgz#d3f1c68c358c00ce116b55ef5410cf486dd08539"
1996+
integrity sha512-dMLFD2cCsxtDgMkTydQCM0PxDq8vwc6uN5M/jRktDfYvH3nQj6pjC9OrCXS2lKlYoYTNJorI/dI8x9dpLshexQ==
1997+
dependencies:
1998+
"@types/react" "*"
1999+
19932000
"@types/react-native@*":
19942001
version "0.62.5"
19952002
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.62.5.tgz#605c2d31485c515ee18b62e8b29ffdbba404a443"
@@ -13652,7 +13659,7 @@ react-hot-loader@^4.12.20:
1365213659
shallowequal "^1.1.0"
1365313660
source-map "^0.7.3"
1365413661

13655-
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
13662+
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
1365613663
version "16.13.1"
1365713664
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
1365813665
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==

0 commit comments

Comments
 (0)