Skip to content

Commit

Permalink
Rewrite using a more React appropriate way
Browse files Browse the repository at this point in the history
  • Loading branch information
emilpalsson committed Mar 21, 2018
1 parent 27b9b9b commit 977d1ed
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 28 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-tiny-accordion",
"version": "1.1.2",
"version": "2.0.0",
"description": "Tiny, super simple accordion for React",
"main": "dist/index.js",
"files": [
Expand Down
90 changes: 63 additions & 27 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import React from 'react'
class Accordion extends React.Component {
constructor(props) {
super(props)
this.index = typeof props.selectedIndex !== 'undefined' ? props.selectedIndex : -1
this.nodes = []
this.state = {
index: typeof props.selectedIndex !== 'undefined' ? props.selectedIndex : -1
heights: React.Children.map(
this.props.children,
(child, index) => (index === this.props.selectedIndex ? 'auto' : 0)
)
}
this.nodes = []
}

componentWillReceiveProps(props) {
if (typeof props.selectedIndex !== 'undefined' && this.state.index !== props.selectedIndex) {
if (typeof props.selectedIndex !== 'undefined' && this.index !== props.selectedIndex) {
this.toggle(props.selectedIndex)
}
}
Expand All @@ -19,36 +23,62 @@ class Accordion extends React.Component {
clearTimeout(this.timeout)
}

close(index) {
setTimeout(() => this.setHeight(index, 0), 50)
}

setHeight(index, height, callback) {
const heights = this.state.heights.slice()
heights[index] = height
this.setState({ heights }, callback)
}

open(index) {
clearTimeout(this.timeout)
this.setHeight(index, this.nodes[index].children[1].children[0].offsetHeight, () => {
this.timeout = setTimeout(() => this.setHeight(index, 'auto'), this.props.transitionDuration)
})
}

setFixedHeightOnCurrentlyOpenedItem() {
return new Promise(resolve => {
if (this.index > -1) {
this.setHeight(
this.index,
this.nodes[this.index].children[1].children[0].offsetHeight,
() => resolve()
)
}
else {
resolve()
}
})
}

toggle(index, click) {
clearTimeout(this.timeout)

if (click) {
if (this.props.onChange) this.props.onChange(index, this.state.index !== index, this.state.index !== index ? index : -1)
if (this.props.onChange) {
this.props.onChange(index, this.index !== index, this.index !== index ? index : -1)
}
if (!this.props.changeOnClick) return
}

if (this.state.index > -1) {
const content = this.nodes[this.state.index].ref.children[1]
content.style.height = `${content.children[0].offsetHeight}px` // Set fixed height before collapse of current open item
}

if (this.state.index === index || index === -1) {
setTimeout(() => { this.setState({ index: -1 }) }, 50)
} else {
setTimeout(() => {
this.setState({ index })
this.timeout = setTimeout(() => {
this.nodes[index].ref.children[1].style.height = 'auto' // Set auto height after expand
}, this.props.transitionDuration)
}, 50)
}
}
// First, set a fixed height on the currently opened item, for collapse animation to work
this.setFixedHeightOnCurrentlyOpenedItem().then(() => {
if (this.index > -1) {
this.close(this.index)
}

getHeight(index) {
if (index === this.state.index) {
return this.nodes.length > index ? this.nodes[index].ref.children[1].children[0].offsetHeight : 'auto'
}
return 0
if (index > -1 && index !== this.index) {
this.index = index
this.open(index)
}
else {
this.index = -1
}
})
}

render() {
Expand All @@ -57,9 +87,15 @@ class Accordion extends React.Component {
transition: `height ${this.props.transitionDuration}ms ${this.props.transitionTimingFunction}`
}
const nodes = React.Children.map(this.props.children, (child, index) => (
<div key={index} ref={div => { this.nodes[index] = { ref: div } }} className={this.state.index === index ? this.props.openClassName : ''}>
<div
key={index}
ref={div => {
this.nodes[index] = div
}}
className={this.index === index ? this.props.openClassName : ''}
>
<div onClick={() => this.toggle(index, true)}>{child.props['data-header']}</div>
<div style={{ ...style, height: this.getHeight(index) }}>{child}</div>
<div style={{ ...style, height: this.state.heights[index] }}>{child}</div>
</div>
))
return <div className={this.props.className}>{nodes}</div>
Expand Down

0 comments on commit 977d1ed

Please sign in to comment.