Skip to content
This repository was archived by the owner on Feb 18, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Simply wrap child components with this component and dynamically change them to

| Prop | PropType | Description |
| ---- | -------- | ----------- |
| duration | React.PropTypes.number | Duration of animation |
| springConfig | React.PropTypes.object | A {stiffness, damping} for react-motion ([details](https://github.com/chenglou/react-motion#--spring-val-number-config-springhelperconfig--opaqueconfig)) |
| fade | React.PropTypes.bool | Should children fade on enter/leave |
| scale | React.PropTypes.bool | Should children scale on enter/leave |
| intial | React.PropTypes.bool | Should scale/fade occur on first load |
Expand Down
319 changes: 96 additions & 223 deletions lib/shuffle.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,286 +19,159 @@ var _reactDom = require('react-dom');

var _reactDom2 = _interopRequireDefault(_reactDom);

var _objectAssign = require('object-assign');
var _reactMotion = require('react-motion');

var _objectAssign2 = _interopRequireDefault(_objectAssign);
var _reactMotionLibStripStyle = require('react-motion/lib/stripStyle');

var _reactTweenState = require('react-tween-state');

var _reactTweenState2 = _interopRequireDefault(_reactTweenState);

var _reactAddonsTransitionGroup = require('react-addons-transition-group');

var _reactAddonsTransitionGroup2 = _interopRequireDefault(_reactAddonsTransitionGroup);

var Clones = _react2['default'].createClass({
displayName: 'ShuffleClones',

_childrenWithPositions: function _childrenWithPositions() {
var _this = this;

var children = [];
_react2['default'].Children.forEach(this.props.children, function (child) {
var style = _this.props.positions[child.key];
var key = child.key;
children.push(_react2['default'].createElement(Clone, {
child: child,
style: style,
key: key,
initial: _this.props.initial,
fade: _this.props.fade,
scale: _this.props.scale,
duration: _this.props.duration }));
});
return children.sort(function (a, b) {
return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
});
},

render: function render() {
return _react2['default'].createElement(
'div',
{ className: 'ShuffleClones' },
_react2['default'].createElement(
_reactAddonsTransitionGroup2['default'],
null,
this._childrenWithPositions()
)
);
}
});

var Clone = _react2['default'].createClass({
mixins: [_reactTweenState2['default'].Mixin],
displayName: 'ShuffleClone',
getInitialState: function getInitialState() {
return {
top: this.props.style ? this.props.style.top : 0,
left: this.props.style ? this.props.style.left : 0,
opacity: 1,
transform: 1
};
},
componentWillAppear: function componentWillAppear(cb) {
this.tweenState('opacity', {
easing: _reactTweenState2['default'].easingTypes.easeOutSine,
duration: this.props.duration,
beginValue: this.props.initial ? 0 : 1,
endValue: 1,
onEnd: cb
});
},
componentWillEnter: function componentWillEnter(cb) {
this.tweenState('opacity', {
easing: _reactTweenState2['default'].easingTypes.easeOutSine,
duration: this.props.duration,
beginValue: 0,
endValue: 1,
onEnd: cb
});
},
componentWillLeave: function componentWillLeave(cb) {
this.tweenState('opacity', {
easing: _reactTweenState2['default'].easingTypes.easeOutSine,
duration: this.props.duration,
endValue: 0,
onEnd: function onEnd() {
try {
cb();
} catch (e) {
// This try catch handles component umounting jumping the gun
}
}
});
},
componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
this.tweenState('top', {
easing: _reactTweenState2['default'].easingTypes.easeOutSine,
duration: nextProps.duration,
endValue: nextProps.style.top
});
this.tweenState('left', {
easing: _reactTweenState2['default'].easingTypes.easeOutSine,
duration: nextProps.duration,
endValue: nextProps.style.left
});
},
render: function render() {
var style = {};
if (this.props.style) {
style = {
top: this.getTweeningValue('top'),
left: this.getTweeningValue('left'),
opacity: this.props.fade ? this.getTweeningValue('opacity') : 1,
transform: this.props.scale ? 'scale(' + this.getTweeningValue('opacity') + ')' : 0,
transformOrigin: 'center center',
width: this.props.style.width,
height: this.props.style.height,
position: this.props.style.position
};
}
var key = this.props.key;
return _react2['default'].cloneElement(this.props.child, { style: style, key: key });
}
});
var _reactMotionLibStripStyle2 = _interopRequireDefault(_reactMotionLibStripStyle);

var Shuffle = _react2['default'].createClass({

displayName: 'Shuffle',

propTypes: {
duration: _react2['default'].PropTypes.number,
springConfig: _react2['default'].PropTypes.shape({
stiffness: _react2['default'].PropTypes.number,
damping: _react2['default'].PropTypes.number
}),
scale: _react2['default'].PropTypes.bool,
fade: _react2['default'].PropTypes.bool,
initial: _react2['default'].PropTypes.bool
},

getDefaultProps: function getDefaultProps() {
return {
duration: 300,
springConfig: undefined, // uses react-motion default
scale: true,
fade: true,
initial: false
};
},

getInitialState: function getInitialState() {
return {
animating: false,
ready: false
};
},

componentDidMount: function componentDidMount() {
this._makePortal();
window.addEventListener('resize', this._renderClonesInitially);
window.addEventListener('resize', this._renderClones);
this._renderClones();
},

componentWillUnmount: function componentWillUnmount() {
_reactDom2['default'].findDOMNode(this.refs.container).removeChild(this._portalNode);
window.removeEventListener('resize', this._renderClonesInitially);
},

componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
this._startAnimation(nextProps);
if (this.container !== null) {
this.container.removeChild(this._portalNode);
}
window.removeEventListener('resize', this._renderClones);
},

componentDidUpdate: function componentDidUpdate(prevProps) {
var _this2 = this;

if (this.state.ready === false) {
this.setState({ ready: true }, function () {
_this2._renderClonesInitially();
});
} else {
this._renderClonesToNewPositions(this.props);
}
componentDidUpdate: function componentDidUpdate() {
this._renderClones();
},

_makePortal: function _makePortal() {
this._portalNode = document.createElement('div');
this._portalNode.style.left = '0px';
this._portalNode.style.top = '0px';
this._portalNode.style.position = 'absolute';
_reactDom2['default'].findDOMNode(this.refs.container).appendChild(this._portalNode);
},

_addTransitionEndEvent: function _addTransitionEndEvent() {
setTimeout(this._finishAnimation, this.props.duration);
},

_startAnimation: function _startAnimation(nextProps) {
var _this3 = this;

if (this.state.animating) {
return;
if (this.container !== null) {
this.container.appendChild(this._portalNode);
}

var cloneProps = (0, _objectAssign2['default'])({}, nextProps, {
positions: this._getPositions(),
initial: this.props.initial,
fade: this.props.fade,
scale: this.props.scale,
duration: this.props.duration
});
this._renderClones(cloneProps, function () {
_this3._addTransitionEndEvent();
_this3.setState({ animating: true });
});
},

_renderClonesToNewPositions: function _renderClonesToNewPositions(props) {
var cloneProps = (0, _objectAssign2['default'])({}, props, {
positions: this._getPositions(),
initial: this.props.initial,
fade: this.props.fade,
scale: this.props.scale,
duration: this.props.duration
});
this._renderClones(cloneProps);
},

_finishAnimation: function _finishAnimation() {
this.setState({ animating: false });
},

_getPositions: function _getPositions() {
var _this4 = this;
_renderClones: function _renderClones() {
var _this = this;

var positions = {};
var styles = [];
var defaultStyles = [];
_react2['default'].Children.forEach(this.props.children, function (child) {
var ref = child.key;
var node = _reactDom2['default'].findDOMNode(_this4.refs[ref]);
var node = _this._refs[ref];
var rect = node.getBoundingClientRect();
var computedStyle = getComputedStyle(node);
var marginTop = parseInt(computedStyle.marginTop, 10);
var marginLeft = parseInt(computedStyle.marginLeft, 10);
var position = {
top: node.offsetTop - marginTop,
left: node.offsetLeft - marginLeft,
width: rect.width,
height: rect.height,
position: 'absolute',
opacity: 1
};
positions[ref] = position;
});
return positions;
},

_renderClonesInitially: function _renderClonesInitially() {
var cloneProps = (0, _objectAssign2['default'])({}, this.props, {
positions: this._getPositions(),
initial: this.props.initial,
fade: this.props.fade,
scale: this.props.scale,
duration: this.props.duration
styles.push({
key: child.key,
style: {
width: (0, _reactMotion.spring)(rect.width, _this.props.springConfig),
height: (0, _reactMotion.spring)(rect.height, _this.props.springConfig),
left: (0, _reactMotion.spring)(node.offsetLeft - marginLeft, _this.props.springConfig),
top: (0, _reactMotion.spring)(node.offsetTop - marginTop, _this.props.springConfig),
scale: _this.props.scale ? (0, _reactMotion.spring)(1) : 1,
opacity: _this.props.fade ? (0, _reactMotion.spring)(1) : 1
},
data: child
});
defaultStyles.push({
key: child.key,
style: {
width: rect.width,
height: rect.height,
left: node.offsetLeft - marginLeft,
top: node.offsetTop - marginTop,
scale: _this.props.scale ? 0 : 1,
opacity: _this.props.fade ? 0 : 1
},
data: child
});
});
_reactDom2['default'].render(_react2['default'].createElement(Clones, cloneProps), this._portalNode);
this.setState({ ready: true });
},

_renderClones: function _renderClones(props, cb) {
_reactDom2['default'].render(_react2['default'].createElement(Clones, props), this._portalNode, cb);
_reactDom2['default'].unstable_renderSubtreeIntoContainer(this, _react2['default'].createElement(
_reactMotion.TransitionMotion,
{
willLeave: function (style) {
return _extends({}, style.style, {
opacity: (0, _reactMotion.spring)(0, _this.props.springConfig),
scale: (0, _reactMotion.spring)(0, _this.props.springConfig) });
},
willEnter: function (style) {
return _extends({}, (0, _reactMotionLibStripStyle2['default'])(style.style), {
opacity: 0,
scale: 0
});
},
defaultStyles: this.props.initial ? defaultStyles : null,
styles: styles },
function (interpolatedStyles) {
return _react2['default'].createElement(
'div',
null,
interpolatedStyles.map(function (config) {
return _react2['default'].cloneElement(config.data, {
key: config.key,
style: {
position: 'absolute',
width: config.style.width,
height: config.style.height,
left: config.style.left,
top: config.style.top,
opacity: config.style.opacity,
transform: 'scale(' + config.style.scale + ')'
}
});
})
);
}
), this._portalNode);
},

_childrenWithRefs: function _childrenWithRefs() {
var _this2 = this;

return _react2['default'].Children.map(this.props.children, function (child) {
return _react2['default'].cloneElement(child, { ref: child.key });
return _react2['default'].cloneElement(child, { ref: function ref(r) {
_this2._refs = _this2._refs || {};
_this2._refs[child.key] = r;
} });
});
},

render: function render() {
var showContainer = this.props.initial ? 0 : 1;
if (this.state.ready) {
showContainer = 0;
}
var _this3 = this;

return _react2['default'].createElement(
'div',
_extends({ ref: 'container', style: { position: 'relative' } }, this.props),
_extends({ ref: function (ref) {
return _this3.container = ref;
}, style: { position: 'relative' } }, this.props),
_react2['default'].createElement(
'div',
{ style: { opacity: showContainer } },
{ style: { opacity: 0 } },
this._childrenWithRefs()
)
);
Expand Down
Loading