Skip to content
This repository was archived by the owner on Apr 11, 2018. It is now read-only.

Commit d616457

Browse files
committed
feat(push-animation): add a Push animation component
1 parent 5bc2ef9 commit d616457

File tree

5 files changed

+268
-1
lines changed

5 files changed

+268
-1
lines changed

examples/push.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<style>
5+
.container {
6+
border: 1px solid;
7+
}
8+
9+
.content {
10+
width: 100px;
11+
padding: 10px;
12+
color: #787878;
13+
background-color: #fcf7f8;
14+
font-size: 13px;
15+
font-family: Helvetica, Arial, sans-serif;
16+
letter-spacing: 1px;
17+
text-align: center;
18+
border: 1px solid rgba(0,0,0,.05);
19+
}
20+
</style>
21+
</head>
22+
<body>
23+
<div id="app"></div>
24+
<script src="push.js"></script>
25+
</body>
26+
</html>

examples/push.jsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import * as React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import { Push } from '@telerik/kendo-react-animation';
4+
5+
class App extends React.Component {
6+
constructor(props) {
7+
super(props);
8+
9+
this.state = { direction: "left", index: 1 };
10+
}
11+
12+
onClick = () => {
13+
this.setState({
14+
index: this.state.index + 1
15+
});
16+
}
17+
18+
onChange = (e) => {
19+
this.setState({
20+
direction: e.target.value
21+
});
22+
}
23+
24+
render() {
25+
const { direction, index } = this.state;
26+
27+
return (
28+
<div>
29+
<dl>
30+
<dt>
31+
Direction:
32+
</dt>
33+
<dd>
34+
<select onChange={this.onChange} value={direction}>
35+
<option value="up">Up</option>
36+
<option value="right">Right</option>
37+
<option value="down">Down</option>
38+
<option value="left">Left</option>
39+
</select>
40+
</dd>
41+
</dl>
42+
<dl>
43+
<dt>
44+
Push:
45+
</dt>
46+
<dd>
47+
<button onClick={this.onClick}>Animate</button>
48+
</dd>
49+
</dl>
50+
51+
<Push className="container" direction={direction}>
52+
<div className="content" key={index}>{index}</div>
53+
</Push>
54+
</div>
55+
);
56+
}
57+
}
58+
59+
ReactDOM.render(
60+
<App />,
61+
document.getElementById('app')
62+
);

src/Push.jsx

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import AnimationGroup from './AnimationGroup';
4+
5+
import assign from 'object-assign';
6+
import styles from '@telerik/kendo-theme-default/styles/animation/main';
7+
import util from './util';
8+
9+
/*eslint react/forbid-prop-types:0*/
10+
11+
export default class Push extends React.Component {
12+
static propTypes = {
13+
animateOnPushIn: React.PropTypes.bool,
14+
animateOnPushOut: React.PropTypes.bool,
15+
children: React.PropTypes.oneOfType([
16+
React.PropTypes.element,
17+
React.PropTypes.node
18+
]),
19+
className: React.PropTypes.string,
20+
componentChildClassName: React.PropTypes.string,
21+
componentDidPushIn: React.PropTypes.func,
22+
componentWillPushIn: React.PropTypes.func,
23+
direction: React.PropTypes.oneOf([ 'up', 'down', 'left', 'right' ]),
24+
fixedContainer: React.PropTypes.bool,
25+
pushInDuration: React.PropTypes.number,
26+
pushOutDuration: React.PropTypes.number,
27+
style: React.PropTypes.object,
28+
transitionName: React.PropTypes.oneOfType([
29+
React.PropTypes.string,
30+
React.PropTypes.shape({
31+
pushIn: React.PropTypes.string,
32+
pushInActive: React.PropTypes.string,
33+
pushOut: React.PropTypes.string,
34+
pushOutActive: React.PropTypes.string
35+
})
36+
])
37+
}
38+
39+
static defaultProps = {
40+
animateOnPushIn: true,
41+
animateOnPushOut: true,
42+
pushInDuration: 300,
43+
pushOutDuration: 300,
44+
direction: "left",
45+
fixedContainer: true
46+
}
47+
48+
constructor(props) {
49+
super(props);
50+
51+
this.state = { style: null };
52+
}
53+
54+
componentDidMount() {
55+
this.updateContainerDimensions();
56+
}
57+
58+
componentWillReceiveProps() {
59+
this.updateContainerDimensions();
60+
}
61+
62+
getDefaultTransitionName() {
63+
const { direction } = this.props;
64+
65+
return {
66+
pushIn: styles[`${direction}-enter`],
67+
pushInActive: styles[`${direction}-enter-active`],
68+
pushOut: styles[`${direction}-leave`],
69+
pushOutActive: styles[`${direction}-leave-active`]
70+
};
71+
}
72+
73+
updateContainerDimensions() {
74+
const { height, width } = this.state;
75+
const content = ReactDOM.findDOMNode(this).firstChild.firstChild;
76+
77+
let style = null;
78+
79+
if (this.props.fixedContainer && content) {
80+
const newHeight = util.outerHeight(content);
81+
const newWidth = util.outerWidth(content);
82+
83+
if (height !== newHeight || width !== newWidth) {
84+
style = {
85+
height: newHeight,
86+
width: newWidth
87+
};
88+
}
89+
}
90+
91+
this.setState({ style: style });
92+
}
93+
94+
componentDidEnter = () => {
95+
const { componentDidPushIn } = this.props;
96+
97+
if (componentDidPushIn) {
98+
componentDidPushIn();
99+
}
100+
101+
this.updateContainerDimensions();
102+
}
103+
104+
getChildProps() {
105+
const {
106+
animateOnPushIn,
107+
animateOnPushOut,
108+
componentChildClassName,
109+
componentWillPushIn,
110+
pushInDuration,
111+
pushOutDuration,
112+
transitionName = this.getDefaultTransitionName()
113+
} = this.props;
114+
115+
let mappedTransitionName = util.mapTransitionClasses(
116+
transitionName,
117+
[ 'pushIn', 'pushOut' ],
118+
[ 'enter', 'leave' ]
119+
);
120+
121+
return {
122+
componentChildClassName: componentChildClassName,
123+
componentDidEnter: this.componentDidEnter,
124+
componentWillEnter: componentWillPushIn,
125+
transitionAppear: false,
126+
transitionEnter: animateOnPushIn,
127+
transitionEnterTimeout: pushInDuration,
128+
transitionLeave: animateOnPushOut,
129+
transitionLeaveTimeout: pushOutDuration,
130+
transitionName: mappedTransitionName
131+
};
132+
}
133+
134+
render() {
135+
const { children, className, style } = this.props;
136+
137+
const animationProps = {
138+
childFactory: (child) => React.cloneElement(child, this.getChildProps()),
139+
className: className,
140+
style: assign({}, style, this.state.style),
141+
transitionName: ""
142+
};
143+
144+
return (
145+
<AnimationGroup {...animationProps}>
146+
{children}
147+
</AnimationGroup>
148+
);
149+
}
150+
}

src/main.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export Animation from './AnimationGroup';
22
export Fade from './Fade';
33
export Expand from './Expand';
4+
export Push from './Push';
45
export Slide from './Slide';

src/util.jsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,32 @@ const mapTransitionClasses = (name, inputNames, outputNames) => {
6464
return assign({}, ...mappedClasses);
6565
};
6666

67+
const outerHeight = (element) => {
68+
if (!element) {
69+
return 0;
70+
}
71+
72+
const wnd = element.ownerDocument.defaultView;
73+
const computedStyles = wnd.getComputedStyle(element);
74+
const marginTop = parseFloat(computedStyles.marginTop, 10);
75+
const marginBottom = parseFloat(computedStyles.marginBottom, 10);
76+
77+
return element.offsetHeight + marginTop + marginBottom;
78+
};
79+
80+
const outerWidth = (element) => {
81+
if (!element) {
82+
return 0;
83+
}
84+
85+
const wnd = element.ownerDocument.defaultView;
86+
const computedStyles = wnd.getComputedStyle(element);
87+
const marginLeft = parseFloat(computedStyles.marginLeft, 10);
88+
const marginRight = parseFloat(computedStyles.marginRight, 10);
89+
90+
return element.offsetWidth + marginLeft + marginRight;
91+
};
92+
6793
const noop = function() {};
6894

6995
export default {
@@ -74,5 +100,7 @@ export default {
74100
getTimeoutField: getTimeoutField,
75101
guid: guid,
76102
mapTransitionClasses: mapTransitionClasses,
77-
noop: noop
103+
noop: noop,
104+
outerHeight: outerHeight,
105+
outerWidth: outerWidth
78106
};

0 commit comments

Comments
 (0)