Skip to content

Instantly share code, notes, and snippets.

@souporserious
Created March 30, 2016 05:16
Show Gist options
  • Save souporserious/6a23e9c031565a5da99cf00fa9629cf8 to your computer and use it in GitHub Desktop.
Save souporserious/6a23e9c031565a5da99cf00fa9629cf8 to your computer and use it in GitHub Desktop.
import React, { Component, PropTypes, Children, cloneElement } from 'react'
import { findDOMNode } from 'react-dom'
import { Motion, spring, presets } from 'react-motion'
import Measure from '../src/react-measure'
class Slideable extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
show: PropTypes.bool.isRequired,
springConfig: React.PropTypes.objectOf(React.PropTypes.number)
}
static defaultProps = {
springConfig: presets.noWobble
}
state = {
height: -1
}
_instant = false
_measureComponent = null
_isAnimating = null
componentWillReceiveProps(nextProps) {
// if we're toggling "show", measure and set state
// so we can animate from an accurate measurement
if (this.props.show !== nextProps.show) {
const { height } = this._measureComponent.getDimensions()
this.setState({ height })
}
}
componentDidUpdate() {
if (this._instant) {
this._instant = false
this.forceUpdate()
}
}
_onMeasure = ({ height }, mutations, data) => {
this._instant = !this._isAnimating
this.setState({ height })
}
// stops a Slideable if in the middle of animating and moves to its value instantly
// good way to prime the slideable height without animating to it
stop() {
this._instant = true
this.forceUpdate()
}
render() {
const { show, rmConfig, children } = this.props
const child = Children.only(children)
const childStyles = child.props.style
const rmHeight = show ? this.state.height : 0
return (
<Measure
ref={c => this._measureComponent = c}
config={{
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['data-slideable', 'data-sliding']
}}
accurate
whitelist={['height']}
onMeasure={this._onMeasure}
>
<Motion
defaultStyle={{ height: 0 }}
style={{
height: this._instant ? rmHeight : spring(rmHeight, rmConfig)
}}
>
{({ height }) => {
const destHeight = parseFloat(this.state.height).toFixed(2)
const currHeight = parseFloat(height).toFixed(2)
let rmStyles = {}
// only animate when necessary
// don't always apply style values so height works responsively
if (destHeight !== currHeight && !this._instant) {
rmStyles = {
height,
overflow: 'hidden'
}
}
this._isAnimating = !(destHeight === currHeight || height === 0)
return(
cloneElement(
child,
{
ref: c => this._node = findDOMNode(c),
style: {
...rmStyles,
...childStyles
},
'data-sliding': this._isAnimating,
'data-slideable': true
}
)
)
}}
</Motion>
</Measure>
)
}
}
export default Slideable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment