argo-cd
332 строки · 11.6 Кб
1import * as React from 'react';
2import * as ReactForm from 'react-form';
3import {FormValue} from 'react-form';
4
5/*
6This provide a way to may a form field to an array of items. It allows you to
7
8* Add a new (maybe duplicate) item.
9* Replace an item.
10* Remove an item.
11
12E.g.
13env:
14- name: FOO
15value: bar
16- name: BAZ
17value: qux
18# You can have dup items
19- name: FOO
20value: bar
21
22It does not allow re-ordering of elements (maybe in a v2).
23*/
24
25export interface NameValue {
26name: string;
27value: string;
28}
29
30export const NameValueEditor = (item: NameValue, onChange?: (item: NameValue) => any) => {
31return (
32<React.Fragment>
33<input
34// disable chrome autocomplete
35autoComplete='fake'
36className='argo-field'
37style={{width: '40%', borderColor: !onChange ? '#eff3f5' : undefined}}
38placeholder='Name'
39value={item.name}
40onChange={e => onChange({...item, name: e.target.value})}
41// onBlur={e=>onChange({...item, name: e.target.value})}
42title='Name'
43readOnly={!onChange}
44/>
45 =
46<input
47// disable chrome autocomplete
48autoComplete='fake'
49className='argo-field'
50style={{width: '40%', borderColor: !onChange ? '#eff3f5' : undefined}}
51placeholder='Value'
52value={item.value || ''}
53onChange={e => onChange({...item, value: e.target.value})}
54title='Value'
55readOnly={!onChange}
56/>
57
58</React.Fragment>
59);
60};
61
62export const ValueEditor = (item: string, onChange: (item: string) => any) => {
63return (
64<input
65// disable chrome autocomplete
66autoComplete='fake'
67className='argo-field'
68style={{width: '40%', borderColor: !onChange ? '#eff3f5' : undefined}}
69placeholder='Value'
70value={item || ''}
71onChange={e => onChange(e.target.value)}
72title='Value'
73readOnly={!onChange}
74/>
75);
76};
77
78interface Props<T> {
79items: T[];
80onChange: (items: T[]) => void;
81editor: (item: T, onChange: (updated: T) => any) => React.ReactNode;
82}
83
84export function ArrayInput<T>(props: Props<T>) {
85const addItem = (item: T) => {
86props.onChange([...props.items, item]);
87};
88
89const replaceItem = (item: T, i: number) => {
90const items = props.items.slice();
91items[i] = item;
92props.onChange(items);
93};
94
95const removeItem = (i: number) => {
96const items = props.items.slice();
97items.splice(i, 1);
98props.onChange(items);
99};
100
101return (
102<div className='argo-field' style={{border: 0, marginTop: '15px', zIndex: 1}}>
103{props.items.map((item, i) => (
104<div key={`item-${i}`} style={{marginBottom: '5px'}}>
105{props.editor(item, (updated: T) => replaceItem(updated, i))}
106
107<button>
108<i className='fa fa-times' style={{cursor: 'pointer'}} onClick={() => removeItem(i)} />
109</button>{' '}
110</div>
111))}
112{props.items.length === 0 && <label>No items</label>}
113<div>
114<button className='argo-button argo-button--base argo-button--short' onClick={() => addItem({} as T)}>
115<i style={{cursor: 'pointer'}} className='fa fa-plus' />
116</button>
117</div>
118</div>
119);
120}
121
122export const ResetOrDeleteButton = (props: {
123isPluginPar: boolean;
124getValue: () => FormValue;
125name: string;
126index: number;
127setValue: (value: FormValue) => void;
128setAppParamsDeletedState: any;
129}) => {
130const handleDeleteChange = () => {
131if (props.index >= 0) {
132props.setAppParamsDeletedState((val: string[]) => val.concat(props.name));
133}
134};
135
136const handleResetChange = () => {
137if (props.index >= 0) {
138const items = [...props.getValue()];
139items.splice(props.index, 1);
140props.setValue(items);
141}
142};
143
144const disabled = props.index === -1;
145
146const content = props.isPluginPar ? 'Reset' : 'Delete';
147let tooltip = '';
148if (content === 'Reset' && !disabled) {
149tooltip = 'Resets the parameter to the value provided by the plugin. This removes the parameter override from the application manifest';
150} else if (content === 'Delete' && !disabled) {
151tooltip = 'Deletes this parameter values from the application manifest.';
152}
153
154return (
155<button
156className='argo-button argo-button--base'
157disabled={disabled}
158title={tooltip}
159style={{fontSize: '12px', display: 'flex', marginLeft: 'auto', marginTop: '8px'}}
160onClick={props.isPluginPar ? handleResetChange : handleDeleteChange}>
161{content}
162</button>
163);
164};
165
166export const ArrayInputField = ReactForm.FormField((props: {fieldApi: ReactForm.FieldApi}) => {
167const {
168fieldApi: {getValue, setValue}
169} = props;
170return <ArrayInput editor={NameValueEditor} items={getValue() || []} onChange={setValue} />;
171});
172
173export const ArrayValueField = ReactForm.FormField(
174(props: {fieldApi: ReactForm.FieldApi; name: string; defaultVal: string[]; isPluginPar: boolean; setAppParamsDeletedState: any}) => {
175const {
176fieldApi: {getValue, setValue}
177} = props;
178
179let liveParamArray;
180const liveParam = getValue()?.find((val: {name: string; array: object}) => val.name === props.name);
181if (liveParam) {
182liveParamArray = liveParam?.array ?? [];
183}
184const index = getValue()?.findIndex((val: {name: string; array: object}) => val.name === props.name) ?? -1;
185const values = liveParamArray ?? props.defaultVal ?? [];
186
187return (
188<React.Fragment>
189<ResetOrDeleteButton
190isPluginPar={props.isPluginPar}
191getValue={getValue}
192name={props.name}
193index={index}
194setValue={setValue}
195setAppParamsDeletedState={props.setAppParamsDeletedState}
196/>
197<ArrayInput
198editor={ValueEditor}
199items={values || []}
200onChange={change => {
201const update = change.map((val: string | object) => (typeof val !== 'string' ? '' : val));
202if (index >= 0) {
203getValue()[index].array = update;
204setValue([...getValue()]);
205} else {
206setValue([...(getValue() || []), {name: props.name, array: update}]);
207}
208}}
209/>
210</React.Fragment>
211);
212}
213);
214
215export const StringValueField = ReactForm.FormField(
216(props: {fieldApi: ReactForm.FieldApi; name: string; defaultVal: string; isPluginPar: boolean; setAppParamsDeletedState: any}) => {
217const {
218fieldApi: {getValue, setValue}
219} = props;
220let liveParamString;
221const liveParam = getValue()?.find((val: {name: string; string: string}) => val.name === props.name);
222if (liveParam) {
223liveParamString = liveParam?.string ? liveParam?.string : '';
224}
225const values = liveParamString ?? props.defaultVal ?? '';
226const index = getValue()?.findIndex((val: {name: string; string: string}) => val.name === props.name) ?? -1;
227
228return (
229<React.Fragment>
230<ResetOrDeleteButton
231isPluginPar={props.isPluginPar}
232getValue={getValue}
233name={props.name}
234index={index}
235setValue={setValue}
236setAppParamsDeletedState={props.setAppParamsDeletedState}
237/>
238<div>
239<input
240// disable chrome autocomplete
241autoComplete='fake'
242className='argo-field'
243style={{width: '40%', display: 'inline-block', marginTop: 25}}
244placeholder='Value'
245value={values || ''}
246onChange={e => {
247if (index >= 0) {
248getValue()[index].string = e.target.value;
249setValue([...getValue()]);
250} else {
251setValue([...(getValue() || []), {name: props.name, string: e.target.value}]);
252}
253}}
254title='Value'
255/>
256</div>
257</React.Fragment>
258);
259}
260);
261
262export const MapInputField = ReactForm.FormField((props: {fieldApi: ReactForm.FieldApi}) => {
263const {
264fieldApi: {getValue, setValue}
265} = props;
266const items = new Array<NameValue>();
267const map = getValue() || {};
268Object.keys(map).forEach(key => items.push({name: key, value: map[key]}));
269return (
270<ArrayInput
271editor={NameValueEditor}
272items={items}
273onChange={array => {
274const newMap = {} as any;
275array.forEach(item => (newMap[item.name || ''] = item.value || ''));
276setValue(newMap);
277}}
278/>
279);
280});
281
282export const MapValueField = ReactForm.FormField(
283(props: {fieldApi: ReactForm.FieldApi; name: string; defaultVal: Map<string, string>; isPluginPar: boolean; setAppParamsDeletedState: any}) => {
284const {
285fieldApi: {getValue, setValue}
286} = props;
287const items = new Array<NameValue>();
288const liveParam = getValue()?.find((val: {name: string; map: object}) => val.name === props.name);
289const index = getValue()?.findIndex((val: {name: string; map: object}) => val.name === props.name) ?? -1;
290if (liveParam) {
291liveParam.map = liveParam.map ? liveParam.map : new Map<string, string>();
292}
293if (liveParam?.array) {
294items.push(...liveParam.array);
295} else {
296const map = liveParam?.map ?? props.defaultVal ?? new Map<string, string>();
297Object.keys(map).forEach(item => items.push({name: item || '', value: map[item] || ''}));
298if (liveParam?.map) {
299getValue()[index].array = items;
300}
301}
302
303return (
304<React.Fragment>
305<ResetOrDeleteButton
306isPluginPar={props.isPluginPar}
307getValue={getValue}
308name={props.name}
309index={index}
310setValue={setValue}
311setAppParamsDeletedState={props.setAppParamsDeletedState}
312/>
313
314<ArrayInput
315editor={NameValueEditor}
316items={items || []}
317onChange={change => {
318if (index === -1) {
319getValue().push({
320name: props.name,
321array: change
322});
323} else {
324getValue()[index].array = change;
325}
326setValue([...getValue()]);
327}}
328/>
329</React.Fragment>
330);
331}
332);
333