KeyValueArray.tsx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. import React from "react";
  2. import { KeyValueArrayField, KeyValueArrayFieldState } from "../types";
  3. import sliders from "../../../assets/sliders.svg";
  4. import upload from "../../../assets/upload.svg";
  5. import styled from "styled-components";
  6. import useFormField from "../hooks/useFormField";
  7. interface Props extends KeyValueArrayField {
  8. id: string;
  9. }
  10. const KeyValueArray: React.FC<Props> = (props) => {
  11. const { state, setState, variables } = useFormField<KeyValueArrayFieldState>(
  12. props.id,
  13. {
  14. initState: {
  15. values: [],
  16. showEnvModal: false,
  17. showEditorModal: false,
  18. },
  19. }
  20. );
  21. console.log(state);
  22. if (state == undefined) return <></>;
  23. const renderDeleteButton = (i: number) => {
  24. if (!props.isReadOnly) {
  25. return (
  26. <DeleteButton
  27. onClick={() => {
  28. state.values.splice(i, 1);
  29. setState((prev) => {
  30. return {
  31. values: prev.values
  32. .slice(0, i + 1)
  33. .concat(prev.values.slice(i + 1, prev.values.length)),
  34. };
  35. });
  36. }}
  37. >
  38. <i className="material-icons">cancel</i>
  39. </DeleteButton>
  40. );
  41. }
  42. };
  43. const renderHiddenOption = (hidden: boolean, i: number) => {
  44. if (props.secretOption && hidden) {
  45. return (
  46. <HideButton>
  47. <i className="material-icons">lock</i>
  48. </HideButton>
  49. );
  50. }
  51. };
  52. const renderInputList = () => {
  53. return (
  54. <>
  55. {state.values.map((entry: any, i: number) => {
  56. // Preprocess non-string env values set via raw Helm values
  57. let { value } = entry;
  58. if (typeof value === "object") {
  59. value = JSON.stringify(value);
  60. } else if (typeof value === "number" || typeof value === "boolean") {
  61. value = value.toString();
  62. }
  63. return (
  64. <InputWrapper key={i}>
  65. <Input
  66. placeholder="ex: key"
  67. width="270px"
  68. value={entry.key}
  69. onChange={(e: any) => {
  70. e.persist();
  71. setState((prev) => {
  72. return {
  73. values: prev.values.map((t, j) => {
  74. if (j == i) {
  75. return {
  76. ...t,
  77. key: e.target.value,
  78. };
  79. }
  80. return t;
  81. }),
  82. };
  83. });
  84. }}
  85. disabled={props.isReadOnly || value.includes("PORTERSECRET")}
  86. spellCheck={false}
  87. />
  88. <Spacer />
  89. <Input
  90. placeholder="ex: value"
  91. width="270px"
  92. value={value}
  93. onChange={(e: any) => {
  94. e.persist();
  95. setState((prev) => {
  96. return {
  97. values: prev.values.map((t, j) => {
  98. if (j == i) {
  99. return {
  100. ...t,
  101. value: e.target.value,
  102. };
  103. }
  104. return t;
  105. }),
  106. };
  107. });
  108. }}
  109. disabled={props.isReadOnly || value.includes("PORTERSECRET")}
  110. type={value.includes("PORTERSECRET") ? "password" : "text"}
  111. spellCheck={false}
  112. />
  113. {renderDeleteButton(i)}
  114. {renderHiddenOption(value.includes("PORTERSECRET"), i)}
  115. </InputWrapper>
  116. );
  117. })}
  118. </>
  119. );
  120. };
  121. return (
  122. <>
  123. <StyledInputArray>
  124. <Label>{props.label}</Label>
  125. {state.values.length === 0 ? <></> : renderInputList()}
  126. {props.isReadOnly ? (
  127. <></>
  128. ) : (
  129. <InputWrapper>
  130. <AddRowButton
  131. onClick={() => {
  132. setState((prev) => {
  133. console.log(prev);
  134. return {
  135. values: [...prev.values, { key: "", value: "" }],
  136. };
  137. });
  138. }}
  139. >
  140. <i className="material-icons">add</i> Add Row
  141. </AddRowButton>
  142. <Spacer />
  143. {variables.namespace && props.envLoader && (
  144. <LoadButton
  145. onClick={() =>
  146. setState((prev) => {
  147. return {
  148. showEnvModal: !prev.showEnvModal,
  149. };
  150. })
  151. }
  152. >
  153. <img src={sliders} /> Load from Env Group
  154. </LoadButton>
  155. )}
  156. {props.fileUpload && (
  157. <UploadButton
  158. onClick={() => {
  159. setState((prev) => {
  160. return {
  161. showEditorModal: true,
  162. };
  163. });
  164. }}
  165. >
  166. <img src={upload} /> Copy from File
  167. </UploadButton>
  168. )}
  169. </InputWrapper>
  170. )}
  171. </StyledInputArray>
  172. {/*{renderEnvModal()}*/}
  173. {/*{renderEditorModal()}*/}
  174. </>
  175. );
  176. };
  177. export default KeyValueArray;
  178. const Spacer = styled.div`
  179. width: 10px;
  180. height: 20px;
  181. `;
  182. const AddRowButton = styled.div`
  183. display: flex;
  184. align-items: center;
  185. width: 270px;
  186. font-size: 13px;
  187. color: #aaaabb;
  188. height: 32px;
  189. border-radius: 3px;
  190. cursor: pointer;
  191. background: #ffffff11;
  192. :hover {
  193. background: #ffffff22;
  194. }
  195. > i {
  196. color: #ffffff44;
  197. font-size: 16px;
  198. margin-left: 8px;
  199. margin-right: 10px;
  200. display: flex;
  201. align-items: center;
  202. justify-content: center;
  203. }
  204. `;
  205. const LoadButton = styled(AddRowButton)`
  206. background: none;
  207. border: 1px solid #ffffff55;
  208. > i {
  209. color: #ffffff44;
  210. font-size: 16px;
  211. margin-left: 8px;
  212. margin-right: 10px;
  213. display: flex;
  214. align-items: center;
  215. justify-content: center;
  216. }
  217. > img {
  218. width: 14px;
  219. margin-left: 10px;
  220. margin-right: 12px;
  221. }
  222. `;
  223. const UploadButton = styled(AddRowButton)`
  224. background: none;
  225. position: relative;
  226. border: 1px solid #ffffff55;
  227. > i {
  228. color: #ffffff44;
  229. font-size: 16px;
  230. margin-left: 8px;
  231. margin-right: 10px;
  232. display: flex;
  233. align-items: center;
  234. justify-content: center;
  235. }
  236. > img {
  237. width: 14px;
  238. margin-left: 10px;
  239. margin-right: 12px;
  240. }
  241. `;
  242. const DeleteButton = styled.div`
  243. width: 15px;
  244. height: 15px;
  245. display: flex;
  246. align-items: center;
  247. margin-left: 8px;
  248. margin-top: -3px;
  249. justify-content: center;
  250. > i {
  251. font-size: 17px;
  252. color: #ffffff44;
  253. display: flex;
  254. align-items: center;
  255. justify-content: center;
  256. cursor: pointer;
  257. :hover {
  258. color: #ffffff88;
  259. }
  260. }
  261. `;
  262. const HideButton = styled(DeleteButton)`
  263. margin-top: -5px;
  264. > i {
  265. font-size: 19px;
  266. cursor: default;
  267. :hover {
  268. color: #ffffff44;
  269. }
  270. }
  271. `;
  272. const InputWrapper = styled.div`
  273. display: flex;
  274. align-items: center;
  275. margin-top: 5px;
  276. `;
  277. const Input = styled.input`
  278. outline: none;
  279. border: none;
  280. margin-bottom: 5px;
  281. font-size: 13px;
  282. background: #ffffff11;
  283. border: 1px solid #ffffff55;
  284. border-radius: 3px;
  285. width: ${(props: { disabled?: boolean; width: string }) =>
  286. props.width ? props.width : "270px"};
  287. color: ${(props: { disabled?: boolean; width: string }) =>
  288. props.disabled ? "#ffffff44" : "white"};
  289. padding: 5px 10px;
  290. height: 35px;
  291. `;
  292. const Label = styled.div`
  293. color: #ffffff;
  294. margin-bottom: 10px;
  295. `;
  296. const StyledInputArray = styled.div`
  297. margin-bottom: 15px;
  298. margin-top: 22px;
  299. `;