DropdownButton.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. import React, { PropTypes } from 'react';
  15. import withStyles from 'isomorphic-style-loader/lib/withStyles';
  16. import s from './DropdownButton.scss';
  17. class DropdownButton extends React.Component {
  18. static propTypes = {
  19. className: PropTypes.string,
  20. options: PropTypes.array.isRequired,
  21. onChange: PropTypes.func,
  22. value: PropTypes.object.isRequired,
  23. onButtonClick: PropTypes.func,
  24. disabled: PropTypes.bool
  25. }
  26. constructor(props) {
  27. super(props)
  28. this.onPageClick = this.onPageClick.bind(this)
  29. this.state = {
  30. value: props.value,
  31. options: props.options,
  32. showMenu: false
  33. }
  34. }
  35. componentDidMount() {
  36. window.addEventListener('mousedown', this.onPageClick, false)
  37. }
  38. componentWillUnmount() {
  39. window.removeEventListener('mousedown', this.onPageClick, false)
  40. }
  41. onPageClick() {
  42. if (!this.itemMouseDown) {
  43. this.closeMenu()
  44. }
  45. }
  46. onMenuItemClick(option) {
  47. if (this.props.onChange) {
  48. this.props.onChange(option)
  49. }
  50. this.closeMenu()
  51. this.setState({
  52. value: option,
  53. firstItemHover: false
  54. })
  55. }
  56. onLabelClick() {
  57. if (this.props.onButtonClick && !this.isDisabled()) {
  58. this.props.onButtonClick()
  59. }
  60. }
  61. onMenuItemMouseEnter(index) {
  62. if (index === 0) {
  63. this.setState({ firstItemHover: true })
  64. }
  65. }
  66. onMenuItemMouseLeave(index) {
  67. if (index === 0) {
  68. this.setState({ firstItemHover: false })
  69. }
  70. }
  71. isDisabled() {
  72. return typeof this.props.disabled !== 'undefined' ? this.props.disabled : false
  73. }
  74. toggleMenu() {
  75. if (!this.isDisabled()) {
  76. this.setState({ showMenu: !this.state.showMenu })
  77. }
  78. }
  79. closeMenu() {
  80. this.setState({ showMenu: false })
  81. }
  82. renderMenu() {
  83. if (!this.state.showMenu || this.state.options.length === 0) {
  84. return null
  85. }
  86. return (
  87. <div className={s.menu + (this.state.firstItemHover ? ' ' + s.firstItemHover : '')}>
  88. {this.state.options.map((o, i) => (
  89. <div key={o.value}
  90. className={s.menuItem + (o.value === this.state.value.value ? ' ' + s.selected : '')}
  91. onMouseEnter={() => { this.onMenuItemMouseEnter(i) }}
  92. onMouseLeave={() => { this.onMenuItemMouseLeave(i) }}
  93. onMouseDown={() => { this.itemMouseDown = true }}
  94. onMouseUp={() => { this.itemMouseDown = false }}
  95. onClick={() => { this.onMenuItemClick(o) }}
  96. >{o.label}</div>
  97. ))}
  98. </div>
  99. )
  100. }
  101. render() {
  102. let className = this.props.className || ' '
  103. if (this.isDisabled()) {
  104. className += ' ' + s.disabled
  105. }
  106. return (
  107. <div className={s.root + ' ' + className}>
  108. <div className={s.label}
  109. onClick={this.onLabelClick.bind(this)}
  110. >{this.state && this.state.value.label}</div>
  111. <div className={s.arrow}
  112. onClick={this.toggleMenu.bind(this)}
  113. onMouseDown={() => { this.itemMouseDown = true }}
  114. onMouseUp={() => { this.itemMouseDown = false }}
  115. />
  116. {this.renderMenu()}
  117. </div>
  118. )
  119. }
  120. }
  121. export default withStyles(DropdownButton, s);