ImageSelector.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import React, { Component } from 'react';
  2. import styled from 'styled-components';
  3. import info from '../../assets/info.svg';
  4. import api from '../../shared/api';
  5. import { getRegistryIcon } from '../../shared/common';
  6. import { Context } from '../../shared/Context';
  7. import Loading from '../Loading';
  8. type PropsType = {
  9. forceExpanded?: boolean,
  10. selectedImageUrl: string | null,
  11. setSelectedImageUrl: (x: string) => void
  12. };
  13. type StateType = {
  14. isExpanded: boolean,
  15. loading: boolean,
  16. error: boolean,
  17. images: any[]
  18. };
  19. const dummyImages = [
  20. {
  21. kind: 'docker-hub',
  22. source: 'https://index.docker.io/jusrhee/image1',
  23. },
  24. {
  25. kind: 'docker-hub',
  26. source: 'https://index.docker.io/jusrhee/image2',
  27. },
  28. {
  29. kind: 'docker-hub',
  30. source: 'https://index.docker.io/jusrhee/image3',
  31. },
  32. {
  33. kind: 'gcr',
  34. source: 'https://gcr.io/some-registry/image1',
  35. },
  36. {
  37. kind: 'gcr',
  38. source: 'https://gcr.io/some-registry/image2',
  39. },
  40. {
  41. kind: 'ecr',
  42. source: 'https://aws_account_id.dkr.ecr.region.amazonaws.com/smth/1',
  43. },
  44. {
  45. kind: 'ecr',
  46. source: 'https://aws_account_id.dkr.ecr.region.amazonaws.com/smth/2',
  47. },
  48. ];
  49. export default class ImageSelector extends Component<PropsType, StateType> {
  50. state = {
  51. isExpanded: this.props.forceExpanded,
  52. loading: false,
  53. error: false,
  54. images: [] as any[]
  55. }
  56. componentDidMount() {
  57. this.setState({ images: dummyImages });
  58. }
  59. renderImageList = () => {
  60. let { images, loading, error } = this.state;
  61. if (loading) {
  62. return <LoadingWrapper><Loading /></LoadingWrapper>
  63. } else if (error || !images) {
  64. return <LoadingWrapper>Error loading repos</LoadingWrapper>
  65. }
  66. return images.map((image: any, i: number) => {
  67. let icon = getRegistryIcon(image.kind);
  68. return (
  69. <ImageItem
  70. key={i}
  71. isSelected={image.source === this.props.selectedImageUrl}
  72. lastItem={i === images.length - 1}
  73. onClick={() => this.props.setSelectedImageUrl(image.source)}
  74. >
  75. <img src={icon && icon} />{image.source}
  76. </ImageItem>
  77. );
  78. });
  79. }
  80. renderExpanded = () => {
  81. return (
  82. <ExpandedWrapper>
  83. {this.renderImageList()}
  84. </ExpandedWrapper>
  85. );
  86. }
  87. renderSelected = () => {
  88. let { selectedImageUrl, setSelectedImageUrl } = this.props;
  89. let icon = info;
  90. return (
  91. <Label>
  92. <img src={icon} />
  93. <Input
  94. onClick={(e: any) => e.stopPropagation()}
  95. value={selectedImageUrl}
  96. onChange={(e: any) => setSelectedImageUrl(e.value)}
  97. placeholder='Enter or select your container image URL'
  98. />
  99. </Label>
  100. );
  101. }
  102. handleClick = () => {
  103. if (!this.props.forceExpanded) {
  104. this.setState({ isExpanded: !this.state.isExpanded });
  105. }
  106. }
  107. render() {
  108. return (
  109. <div>
  110. <StyledImageSelector
  111. onClick={this.handleClick}
  112. isExpanded={this.state.isExpanded}
  113. forceExpanded={this.props.forceExpanded}
  114. >
  115. {this.renderSelected()}
  116. {this.props.forceExpanded ? null : <i className="material-icons">{this.state.isExpanded ? 'close' : 'build'}</i>}
  117. </StyledImageSelector>
  118. {this.state.isExpanded ? this.renderExpanded() : null}
  119. </div>
  120. );
  121. }
  122. }
  123. ImageSelector.contextType = Context;
  124. const Input = styled.input`
  125. outline: 0;
  126. background: none;
  127. border: 0;
  128. width: calc(100% - 60px);
  129. color: white;
  130. `;
  131. const ImageItem = styled.div`
  132. display: flex;
  133. width: 100%;
  134. font-size: 13px;
  135. border-bottom: 1px solid ${(props: { lastItem: boolean, isSelected: boolean }) => props.lastItem ? '#00000000' : '#606166'};
  136. color: #ffffff;
  137. user-select: none;
  138. align-items: center;
  139. padding: 10px 0px;
  140. cursor: pointer;
  141. background: ${(props: { isSelected: boolean, lastItem: boolean }) => props.isSelected ? '#ffffff22' : '#ffffff11'};
  142. :hover {
  143. background: #ffffff22;
  144. > i {
  145. background: #ffffff22;
  146. }
  147. }
  148. > img {
  149. width: 18px;
  150. height: 18px;
  151. margin-left: 12px;
  152. margin-right: 12px;
  153. filter: grayscale(100%);
  154. }
  155. `;
  156. const LoadingWrapper = styled.div`
  157. padding: 30px 0px;
  158. background: #ffffff11;
  159. display: flex;
  160. align-items: center;
  161. font-size: 13px;
  162. justify-content: center;
  163. color: #ffffff44;
  164. `;
  165. const ExpandedWrapper = styled.div`
  166. margin-top: 10px;
  167. width: 100%;
  168. border-radius: 3px;
  169. border: 1px solid #ffffff44;
  170. max-height: 275px;
  171. overflow-y: auto;
  172. `;
  173. const Label = styled.div`
  174. display: flex;
  175. align-items: center;
  176. flex: 1;
  177. > img {
  178. width: 18px;
  179. height: 18px;
  180. margin-left: 12px;
  181. margin-right: 12px;
  182. }
  183. `;
  184. const StyledImageSelector = styled.div`
  185. width: 100%;
  186. border: 1px solid #ffffff55;
  187. background: ${(props: { isExpanded: boolean, forceExpanded: boolean }) => props.isExpanded ? '#ffffff11' : ''};
  188. border-radius: 3px;
  189. user-select: none;
  190. height: 40px;
  191. font-size: 13px;
  192. color: #ffffff;
  193. display: flex;
  194. align-items: center;
  195. justify-content: space-between;
  196. cursor: ${(props: { isExpanded: boolean, forceExpanded: boolean }) => props.forceExpanded ? '' : 'pointer'};;
  197. :hover {
  198. background: #ffffff11;
  199. > i {
  200. background: #ffffff22;
  201. }
  202. }
  203. > i {
  204. font-size: 16px;
  205. color: #ffffff66;
  206. margin-right: 10px;
  207. display: flex;
  208. align-items: center;
  209. justify-content: center;
  210. border-radius: 20px;
  211. padding: 4px;
  212. }
  213. `;