DropdownButton.jsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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. // @flow
  15. import React from 'react'
  16. import styled, { css } from 'styled-components'
  17. import arrowImage from './images/arrow.js'
  18. import Palette from '../../styleUtils/Palette'
  19. import StyleProps from '../../styleUtils/StyleProps'
  20. const getLabelColor = props => {
  21. if (props.disabled) {
  22. return Palette.grayscale[3]
  23. }
  24. if (props.primary || props.secondary) {
  25. return 'white'
  26. }
  27. return Palette.black
  28. }
  29. const Label = styled.div`
  30. color: ${props => getLabelColor(props)};
  31. margin: 0 32px 0 ${props => props.embedded ? 0 : 16}px;
  32. overflow: hidden;
  33. text-overflow: ellipsis;
  34. white-space: nowrap;
  35. flex-grow: 1;
  36. ${props => props.useBold ? `font-weight: ${StyleProps.fontWeights.medium};` : ''}
  37. ${props => props.centered ? 'text-align: center;' : ''}
  38. `
  39. const getBackgroundColor = props => {
  40. if (props.embedded) {
  41. return 'white'
  42. }
  43. if (props.disabled) {
  44. return Palette.grayscale[0]
  45. }
  46. if (props.secondary) {
  47. return Palette.secondaryLight
  48. }
  49. if (props.primary) {
  50. return Palette.primary
  51. }
  52. return 'white'
  53. }
  54. const getArrowColor = props => {
  55. if (props.disabled) {
  56. return Palette.grayscale[3]
  57. }
  58. if (props.primary || props.secondary) {
  59. return 'white'
  60. }
  61. return Palette.black
  62. }
  63. const getWidth = props => {
  64. if (props.large) {
  65. return StyleProps.inputSizes.large.width - 2
  66. }
  67. if (props.width) {
  68. return props.width - 2
  69. }
  70. return StyleProps.inputSizes.regular.width - 2
  71. }
  72. const borderColor = props => {
  73. if (props.highlight) {
  74. return Palette.alert
  75. }
  76. if (props.disabled) {
  77. return Palette.grayscale[0]
  78. }
  79. if (props.primary) {
  80. return Palette.primary
  81. }
  82. if (props.secondary) {
  83. return Palette.secondaryLight
  84. }
  85. return Palette.grayscale[3]
  86. }
  87. const backgroundHover = props => {
  88. if (props.disabled || props.embedded) {
  89. return ''
  90. }
  91. if (props.secondary) {
  92. return Palette.secondaryLight
  93. }
  94. return Palette.primary
  95. }
  96. const Wrapper = styled.div`
  97. display: flex;
  98. align-items: center;
  99. position: relative;
  100. width: ${props => getWidth(props)}px;
  101. height: ${props => props.large ? StyleProps.inputSizes.large.height - 2
  102. : StyleProps.inputSizes.regular.height - 2}px;
  103. border: 1px solid ${props => borderColor(props)};
  104. border-radius: ${StyleProps.borderRadius};
  105. cursor: ${props => props.disabled ? 'default' : 'pointer'};
  106. transition: all ${StyleProps.animations.swift};
  107. background: ${props => getBackgroundColor(props)};
  108. ${props => props.embedded ? css`
  109. border: 0;
  110. width: calc(100% + 8px);
  111. ` : ''}
  112. #dropdown-arrow-image {stroke: ${props => getArrowColor(props)};}
  113. &:hover {
  114. #dropdown-arrow-image {stroke: ${props => props.disabled ? '' : props.embedded ? '' : 'white'};}
  115. background: ${props => backgroundHover(props)};
  116. }
  117. &:hover ${Label} {
  118. color: ${props => props.disabled ? '' : props.embedded ? '' : 'white'};
  119. }
  120. ${props => props.disabledLoading ? StyleProps.animations.disabledLoading : ''}
  121. `
  122. const Arrow = styled.div`
  123. position: absolute;
  124. right: 8px;
  125. top: 8px;
  126. display: flex;
  127. width: 16px;
  128. height: 16px;
  129. justify-content: center;
  130. align-items: center;
  131. `
  132. type Props = {
  133. value: string,
  134. onClick?: (event: Event) => void,
  135. customRef?: (ref: HTMLElement) => void,
  136. innerRef?: (ref: HTMLElement) => void,
  137. arrowRef?: (ref: HTMLElement) => void,
  138. className?: string,
  139. disabled?: boolean,
  140. disabledLoading?: boolean,
  141. 'data-test-id'?: string,
  142. embedded?: boolean,
  143. highlight?: boolean,
  144. secondary?: boolean,
  145. centered?: boolean,
  146. }
  147. class DropdownButton extends React.Component<Props> {
  148. render() {
  149. let disabled = this.props.disabled || this.props.disabledLoading
  150. return (
  151. <Wrapper
  152. data-test-id={this.props['data-test-id'] || 'dropdownButton'}
  153. {...this.props}
  154. disabled={disabled}
  155. disabledLoading={this.props.disabledLoading}
  156. innerRef={e => {
  157. if (this.props.customRef) {
  158. this.props.customRef(e)
  159. } else if (this.props.innerRef) {
  160. this.props.innerRef(e)
  161. }
  162. }}
  163. onClick={e => {
  164. if (!disabled && this.props.onClick) this.props.onClick(e)
  165. }}
  166. >
  167. <Label
  168. {...this.props}
  169. onClick={() => { }}
  170. innerRef={() => { }}
  171. data-test-id="dropdownButton-value"
  172. disabled={disabled}
  173. >
  174. {this.props.value}
  175. </Label>
  176. <Arrow
  177. {...this.props}
  178. innerRef={ref => { if (this.props.arrowRef) this.props.arrowRef(ref) }}
  179. onClick={() => { }}
  180. data-test-id=""
  181. disabled={disabled}
  182. dangerouslySetInnerHTML={{ __html: arrowImage }}
  183. />
  184. </Wrapper>
  185. )
  186. }
  187. }
  188. export default DropdownButton