see: https://jsfiddle.net/zLnx5ub7/
preview screenshot:
see: https://jsfiddle.net/zLnx5ub7/
preview screenshot:
| <html> | |
| <body> | |
| <div id="app"></div> | |
| <script type="react"> | |
| class Accordion extends React.Component { | |
| static defaultProps = { | |
| expandedPercent: 50, // % | |
| itemsGap: 1, // px | |
| itemsHeight: 100, // px... | |
| } | |
| constructor(props){ | |
| super(props) | |
| this.state = { | |
| expandedIndex: 0, | |
| } | |
| } | |
| render(){ | |
| const {state, props} = this | |
| const expendedItemStyle = { | |
| width: props.expandedPercent + '%', | |
| marginLeft: props.itemsGap, | |
| height: props.itemsHeight, | |
| } | |
| const unexpanedItemStyle = { | |
| width: `calc(${(100 - props.expandedPercent) / (props.items.length - 1)}% - ${props.itemsGap}px)`, | |
| marginLeft: props.itemsGap, | |
| height: props.itemsHeight, | |
| } | |
| const expandedIndex = state.expandedIndex >= props.items.length ? props.items.length - 1 : state.expandedIndex | |
| return ( | |
| <div className="accordion" style={{height: props.itemsHeight}} > | |
| {props.items.map((item, i) => ( | |
| <AccordionItem {...item} key={i} | |
| expanded={i === expandedIndex} | |
| style={{ | |
| ...(i === expandedIndex ? expendedItemStyle : unexpanedItemStyle), | |
| ...(i === 0 ? {marginLeft: 0} : {}) | |
| }} | |
| onClick={() => this.expandTo(i)} > | |
| </AccordionItem> | |
| ))} | |
| </div> | |
| ) | |
| } | |
| expandTo(i){ | |
| i = +i | |
| i = i < 0 ? 0 : i | |
| i = i >= this.props.items.length ? this.props.items.length - 1 : i | |
| this.setState({expandedIndex: i}) | |
| } | |
| prev(){ | |
| this.expandTo(this.state.expandedIndex - 1) | |
| } | |
| next(){ | |
| this.expandTo(this.state.expandedIndex + 1) | |
| } | |
| } | |
| class AccordionItem extends React.Component { | |
| render(){ | |
| const {state, props} = this | |
| console.log("item: ", props) | |
| return ( | |
| <div className={'accordion-item ' + (props.expanded && 'expanded ')} | |
| style={{background: props.bkg, ...props.style}} | |
| onClick={props.onClick} > | |
| <div className="accordion-item-text">{props.expanded ? props.text : props.short}</div> | |
| </div> | |
| ) | |
| } | |
| } | |
| class App extends React.Component { | |
| constructor(props) { | |
| super(props) | |
| this.state = { | |
| itemsKeys: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", | |
| itemsBkgs: ['#f00', '#ff0', '#f0f', '#0ff', '#00f'], | |
| items: [ | |
| { text: "AAA", short: "e", bkg: '#f00' }, | |
| { text: "BBB", short: "d", bkg: '#ff0' }, | |
| { text: "CCC", short: "1", bkg: '#f0f' }, | |
| { text: "DDD", short: "b", bkg: '#0ff' }, | |
| { text: "EEE", short: "a", bkg: '#00f' }, | |
| ] | |
| } | |
| } | |
| render() { | |
| const {state} = this | |
| return ( | |
| <div> | |
| <button type="button" onClick={this.addItem}>Add Item</button> | |
| <button type="button" onClick={this.removeItem}>Remove Item</button> | |
| <button type="button" onClick={this.prev}>Prev</button> | |
| <button type="button" onClick={this.next}>Next</button> | |
| <Accordion items={state.items} ref={(x) => this.accordion = x} > | |
| </Accordion> | |
| </div> | |
| ) | |
| } | |
| removeItem = () => { | |
| if (this.state.items.length <= 2){ | |
| alert("Cannot remove: please keep at least 2 items.") | |
| } else { | |
| this.setState({ | |
| items: this.state.items.slice(0, this.state.items.length - 1) | |
| }) | |
| } | |
| } | |
| addItem = () => { | |
| const newItemKey = this.state.itemsKeys[this.state.items.length % this.state.itemsKeys.length] | |
| const newItemText = [newItemKey, newItemKey, newItemKey].join('') | |
| const newItemBkg = this.state.itemsBkgs[this.state.items.length % this.state.itemsBkgs.length] | |
| this.setState({ | |
| items: this.state.items.concat([{ | |
| text: newItemText, | |
| short: newItemKey, | |
| bkg: newItemBkg, | |
| }]) | |
| }) | |
| } | |
| prev = () => { | |
| this.accordion.prev() | |
| } | |
| next = () => { | |
| this.accordion.next() | |
| } | |
| } | |
| ReactDOM.render(<App />, document.querySelector("#app")) | |
| </script> | |
| <style lang="css"> | |
| body { | |
| background: #20262E; | |
| padding: 20px; | |
| font-family: Helvetica; | |
| } | |
| #app { | |
| background: #fff; | |
| border-radius: 4px; | |
| padding: 20px; | |
| transition: all 0.2s; | |
| } | |
| .accordion{ | |
| overflow:hidden; | |
| } | |
| .accordion-item{ | |
| position: relative; | |
| float: left; | |
| transition: width 0.3s; | |
| } | |
| .accordion-item-text{ | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| width: 100%; | |
| height: 1.5em; | |
| line-height: 1.5em; | |
| color: #fff; | |
| background: rgba(0,0,0,0.5); | |
| text-align: center; | |
| } | |
| </style> | |
| </body> | |
| </html> |