Skip to content

Commit 810f9f2

Browse files
authored
Merge pull request #13 from myarcane/animation-fixes
Support transitionEnterTimeout & transitionLeaveTimeout
2 parents 7bfbe75 + 663cd4e commit 810f9f2

File tree

4 files changed

+118
-12
lines changed

4 files changed

+118
-12
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
"mocha": "^2.4.5",
6868
"npm-run-all": "^2.3.0",
6969
"phantomjs-prebuilt": "^2.1.5",
70-
"preact": "^6.0.2",
70+
"preact": "^8.1.0",
7171
"pretty-bytes-cli": "^1.0.0",
7272
"rollup": "^0.34.1",
7373
"rollup-plugin-babel": "^2.4.0",

src/CSSTransitionGroup.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export class CSSTransitionGroup extends Component {
181181
}
182182

183183
renderChild = child => {
184-
let { transitionName, transitionEnter, transitionLeave } = this.props,
184+
let { transitionName, transitionEnter, transitionLeave, transitionEnterTimeout, transitionLeaveTimeout } = this.props,
185185
key = getKey(child);
186186
return (
187187
<CSSTransitionGroupChild
@@ -191,13 +191,15 @@ export class CSSTransitionGroup extends Component {
191191
}}
192192
name={transitionName}
193193
enter={transitionEnter}
194-
leave={transitionLeave}>
194+
leave={transitionLeave}
195+
enterTimeout={transitionEnterTimeout}
196+
leaveTimeout={transitionLeaveTimeout}>
195197
{child}
196198
</CSSTransitionGroupChild>
197199
);
198200
};
199201

200-
render({ component:Component, transitionName, transitionEnter, transitionLeave, children:c, ...props }, { children }) {
202+
render({ component:Component, transitionName, transitionEnter, transitionLeave, transitionEnterTimeout, transitionLeaveTimeout, children:c, ...props }, { children }) {
201203
return (
202204
<Component {...props}>
203205
{ filterNullChildren(children).map(this.renderChild) }

src/CSSTransitionGroupChild.js

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ import { addEndEventListener, removeEndEventListener } from './TransitionEvents'
1919
const TICK = 17;
2020

2121
export class CSSTransitionGroupChild extends Component {
22-
transition(animationType, finishCallback) {
23-
let node = getComponentBase(this),
24-
className = this.props.name[animationType] || this.props.name + '-' + animationType,
25-
activeClassName = this.props.name[animationType + 'Active'] || className + '-active';
22+
transition(animationType, finishCallback, timeout) {
23+
let node = getComponentBase(this);
24+
25+
let className = this.props.name[animationType] || this.props.name + '-' + animationType;
26+
let activeClassName = this.props.name[animationType + 'Active'] || className + '-active';
27+
let timer = null;
2628

2729
if (this.endListener) {
2830
this.endListener();
@@ -31,9 +33,9 @@ export class CSSTransitionGroupChild extends Component {
3133
this.endListener = (e) => {
3234
if (e && e.target!==node) return;
3335

36+
clearTimeout(timer);
3437
removeClass(node, className);
3538
removeClass(node, activeClassName);
36-
3739
removeEndEventListener(node, this.endListener);
3840
this.endListener = null;
3941

@@ -44,7 +46,12 @@ export class CSSTransitionGroupChild extends Component {
4446
}
4547
};
4648

47-
addEndEventListener(node, this.endListener);
49+
if (timeout) {
50+
timer = setTimeout(this.endListener, timeout);
51+
this.transitionTimeouts.push(timer);
52+
} else {
53+
addEndEventListener(node, this.endListener);
54+
}
4855

4956
addClass(node, className);
5057

@@ -81,17 +88,21 @@ export class CSSTransitionGroupChild extends Component {
8188

8289
componentWillMount() {
8390
this.classNameQueue = [];
91+
this.transitionTimeouts = [];
8492
}
8593

8694
componentWillUnmount() {
8795
if (this.timeout) {
8896
clearTimeout(this.timeout);
8997
}
98+
this.transitionTimeouts.forEach((timeout) => {
99+
clearTimeout(timeout);
100+
});
90101
}
91102

92103
componentWillEnter(done) {
93104
if (this.props.enter) {
94-
this.transition('enter', done);
105+
this.transition('enter', done, this.props.enterTimeout);
95106
}
96107
else {
97108
done();
@@ -100,7 +111,7 @@ export class CSSTransitionGroupChild extends Component {
100111

101112
componentWillLeave(done) {
102113
if (this.props.leave) {
103-
this.transition('leave', done);
114+
this.transition('leave', done, this.props.leaveTimeout);
104115
}
105116
else {
106117
done();

tests/index.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,38 @@ class TodoList extends Component {
5151
}
5252
}
5353

54+
class TodoListWithTimeout extends Component {
55+
state = {
56+
items: ['hello', 'world', 'click', 'me']
57+
};
58+
59+
handleAdd(item) {
60+
let { items } = this.state;
61+
items = items.concat(item);
62+
this.setState({ items });
63+
}
64+
65+
handleRemove(i) {
66+
let { items } = this.state;
67+
items.splice(i, 1);
68+
this.setState({ items });
69+
}
70+
71+
render(_, { items }) {
72+
return (
73+
<div>
74+
<CSSTransitionGroup transitionName="example" transitionEnterTimeout={500} transitionLeaveTimeout={200}>
75+
{ items.map( (item, i) => (
76+
<Todo key={item} onClick={this.handleRemove.bind(this, i)}>
77+
{item}
78+
</Todo>
79+
)) }
80+
</CSSTransitionGroup>
81+
</div>
82+
);
83+
}
84+
}
85+
5486
class SVGList extends Component {
5587
state = {
5688
items: ['hello', 'world', 'click', 'me']
@@ -182,6 +214,67 @@ describe('CSSTransitionGroup', () => {
182214
});
183215
});
184216

217+
describe('CSSTransitionGroup: timeout', () => {
218+
let container = document.createElement('div'),
219+
list, root;
220+
document.body.appendChild(container);
221+
222+
let $ = s => [].slice.call(container.querySelectorAll(s));
223+
224+
beforeEach( () => {
225+
root = render(<div><Nothing /></div>, container, root);
226+
root = render(<div><TodoListWithTimeout ref={c => list=c} /></div>, container, root);
227+
});
228+
229+
afterEach( () => {
230+
list = null;
231+
});
232+
233+
it('create works', () => {
234+
expect($('.item')).to.have.length(4);
235+
});
236+
237+
it('transitionLeave works with the transitionLeaveTimeout', done => {
238+
// this.timeout(5999);
239+
list.handleRemove(0);
240+
241+
// make sure -leave class was added
242+
setTimeout( () => {
243+
expect($('.item')).to.have.length(4);
244+
245+
expect($('.item')[0].className).to.contain('example-leave');
246+
expect($('.item')[0].className).to.contain('example-leave-active');
247+
}, 100);
248+
249+
// then make sure it's gone
250+
setTimeout( () => {
251+
expect($('.item')).to.have.length(3);
252+
done();
253+
}, 300);
254+
});
255+
256+
it('transitionEnter works with the transitionEnterTimeout', done => {
257+
// this.timeout(5999);
258+
list.handleAdd(Date.now());
259+
260+
setTimeout( () => {
261+
expect($('.item')).to.have.length(5);
262+
263+
expect($('.item')[4].className).to.contain('example-enter');
264+
expect($('.item')[4].className).to.contain('example-enter-active');
265+
}, 300);
266+
267+
setTimeout( () => {
268+
expect($('.item')).to.have.length(5);
269+
270+
expect($('.item')[4].className).not.to.contain('example-enter');
271+
expect($('.item')[4].className).not.to.contain('example-enter-active');
272+
273+
done();
274+
}, 600);
275+
});
276+
});
277+
185278
describe('CSSTransitionGroup: SVG', () => {
186279
let container = document.createElement('div'),
187280
list, root;

0 commit comments

Comments
 (0)