Selector.tsx 6.7 KB

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