Selector.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import React, { Component } from "react";
  2. import styled from "styled-components";
  3. type PropsType = {
  4. activeValue: string;
  5. options: { value: string; label: string }[];
  6. setActiveValue: (x: string) => void;
  7. width: string;
  8. height?: string;
  9. dropdownLabel?: string;
  10. dropdownWidth?: string;
  11. dropdownMaxHeight?: string;
  12. closeOverlay?: boolean;
  13. };
  14. type StateType = {};
  15. export default class Selector extends Component<PropsType, StateType> {
  16. state = {
  17. expanded: false
  18. };
  19. wrapperRef: any = React.createRef();
  20. parentRef: any = React.createRef();
  21. componentDidMount() {
  22. document.addEventListener("mousedown", this.handleClickOutside.bind(this));
  23. }
  24. componentWillUnmount() {
  25. document.removeEventListener(
  26. "mousedown",
  27. this.handleClickOutside.bind(this)
  28. );
  29. }
  30. handleClickOutside = (event: any) => {
  31. if (
  32. this.wrapperRef &&
  33. this.wrapperRef.current &&
  34. !this.wrapperRef.current.contains(event.target) &&
  35. this.parentRef &&
  36. this.parentRef.current &&
  37. !this.parentRef.current.contains(event.target)
  38. ) {
  39. this.setState({ expanded: false });
  40. }
  41. };
  42. handleOptionClick = (option: { value: string; label: string }) => {
  43. this.props.setActiveValue(option.value);
  44. this.props.closeOverlay ? null : this.setState({ expanded: false });
  45. };
  46. renderOptionList = () => {
  47. let { options, activeValue } = this.props;
  48. return options.map(
  49. (option: { value: string; label: string }, i: number) => {
  50. return (
  51. <Option
  52. key={i}
  53. selected={option.value === activeValue}
  54. onClick={() => this.handleOptionClick(option)}
  55. lastItem={i === options.length - 1}
  56. >
  57. {option.label}
  58. </Option>
  59. );
  60. }
  61. );
  62. };
  63. renderDropdownLabel = () => {
  64. if (this.props.dropdownLabel && this.props.dropdownLabel !== "") {
  65. return <DropdownLabel>{this.props.dropdownLabel}</DropdownLabel>;
  66. }
  67. };
  68. renderDropdown = () => {
  69. if (this.state.expanded) {
  70. return (
  71. <Dropdown
  72. ref={this.wrapperRef}
  73. dropdownWidth={
  74. this.props.dropdownWidth
  75. ? this.props.dropdownWidth
  76. : this.props.width
  77. }
  78. dropdownMaxHeight={this.props.dropdownMaxHeight}
  79. onClick={() => this.setState({ expanded: false })}
  80. >
  81. {this.renderDropdownLabel()}
  82. {this.renderOptionList()}
  83. </Dropdown>
  84. );
  85. }
  86. };
  87. getLabel = (value: string): any => {
  88. let tgt = this.props.options.find(
  89. (element: { value: string; label: string }) => element.value === value
  90. );
  91. if (tgt) {
  92. return tgt.label;
  93. }
  94. };
  95. render() {
  96. let { activeValue } = this.props;
  97. return (
  98. <StyledSelector width={this.props.width}>
  99. <MainSelector
  100. ref={this.parentRef}
  101. onClick={() => this.setState({ expanded: !this.state.expanded })}
  102. expanded={this.state.expanded}
  103. width={this.props.width}
  104. height={this.props.height}
  105. >
  106. <TextWrap>
  107. {activeValue === "" ? "All" : this.getLabel(activeValue)}
  108. </TextWrap>
  109. <i className="material-icons">arrow_drop_down</i>
  110. </MainSelector>
  111. {this.renderDropdown()}
  112. </StyledSelector>
  113. );
  114. }
  115. }
  116. const TextWrap = styled.div`
  117. white-space: nowrap;
  118. overflow: hidden;
  119. text-overflow: ellipsis;
  120. z-index: 0;
  121. `;
  122. const DropdownLabel = styled.div`
  123. font-size: 13px;
  124. color: #ffffff44;
  125. font-weight: 500;
  126. margin: 10px 13px;
  127. `;
  128. const Option = styled.div`
  129. width: 100%;
  130. border-top: 1px solid #00000000;
  131. border-bottom: 1px solid
  132. ${(props: { selected: boolean; lastItem: boolean }) =>
  133. props.lastItem ? "#ffffff00" : "#ffffff15"};
  134. height: 37px;
  135. font-size: 13px;
  136. padding-top: 9px;
  137. align-items: center;
  138. padding-left: 15px;
  139. cursor: pointer;
  140. padding-right: 10px;
  141. white-space: nowrap;
  142. overflow: hidden;
  143. text-overflow: ellipsis;
  144. background: ${(props: { selected: boolean; lastItem: boolean }) =>
  145. props.selected ? "#ffffff11" : ""};
  146. :hover {
  147. background: #ffffff22;
  148. }
  149. `;
  150. const CloseOverlay = styled.div`
  151. position: fixed;
  152. top: 0;
  153. left: 0;
  154. width: 100%;
  155. height: 100%;
  156. z-index: 999;
  157. `;
  158. const Dropdown = styled.div`
  159. position: absolute;
  160. right: 0;
  161. top: calc(100% + 5px);
  162. background: #26282f;
  163. width: ${(props: { dropdownWidth: string; dropdownMaxHeight: string }) =>
  164. props.dropdownWidth};
  165. max-height: ${(props: { dropdownWidth: string; dropdownMaxHeight: string }) =>
  166. props.dropdownMaxHeight || "300px"};
  167. border-radius: 3px;
  168. z-index: 999;
  169. overflow-y: auto;
  170. margin-bottom: 20px;
  171. box-shadow: 0 8px 20px 0px #00000088;
  172. `;
  173. const StyledSelector = styled.div<{ width: string }>`
  174. position: relative;
  175. width: ${props => props.width};
  176. `;
  177. const MainSelector = styled.div`
  178. width: ${(props: { expanded: boolean; width: string; height?: string }) =>
  179. props.width};
  180. height: ${(props: { expanded: boolean; width: string; height?: string }) =>
  181. props.height ? props.height : "35px"};
  182. border: 1px solid #ffffff55;
  183. font-size: 13px;
  184. padding: 5px 10px;
  185. padding-left: 12px;
  186. border-radius: 3px;
  187. display: flex;
  188. align-items: center;
  189. justify-content: space-between;
  190. cursor: pointer;
  191. background: ${(props: {
  192. expanded: boolean;
  193. width: string;
  194. height?: string;
  195. }) => (props.expanded ? "#ffffff33" : "#ffffff11")};
  196. :hover {
  197. background: ${(props: {
  198. expanded: boolean;
  199. width: string;
  200. height?: string;
  201. }) => (props.expanded ? "#ffffff33" : "#ffffff22")};
  202. }
  203. > i {
  204. font-size: 20px;
  205. transform: ${(props: {
  206. expanded: boolean;
  207. width: string;
  208. height?: string;
  209. }) => (props.expanded ? "rotate(180deg)" : "")};
  210. }
  211. `;