github.com/argoproj/argo-cd@v1.8.7/ui/src/app/shared/components/array-input/array-input.tsx (about) 1 import * as React from 'react'; 2 import * as ReactForm from 'react-form'; 3 4 /* 5 This provide a way to may a form field to an array of items. It allows you to 6 7 * Add a new (maybe duplicate) item. 8 * Replace an item. 9 * Remove an item. 10 11 E.g. 12 env: 13 - name: FOO 14 value: bar 15 - name: BAZ 16 value: qux 17 # You can have dup items 18 - name: FOO 19 value: bar 20 21 It does not allow re-ordering of elements (maybe in a v2). 22 */ 23 24 export interface NameValue { 25 name: string; 26 value: string; 27 } 28 29 export const NameValueEditor = (item: NameValue, onChange: (item: NameValue) => any) => ( 30 <React.Fragment> 31 <input 32 // disable chrome autocomplete 33 autoComplete='fake' 34 className='argo-field' 35 style={{width: '40%'}} 36 placeholder='Name' 37 value={item.name || ''} 38 onChange={e => onChange({...item, name: e.target.value})} 39 title='Name' 40 /> 41 = 42 <input 43 // disable chrome autocomplete 44 autoComplete='fake' 45 className='argo-field' 46 style={{width: '40%'}} 47 placeholder='Value' 48 value={item.value || ''} 49 onChange={e => onChange({...item, value: e.target.value})} 50 title='Value' 51 /> 52 53 </React.Fragment> 54 ); 55 56 interface Props<T> { 57 items: T[]; 58 onChange: (items: T[]) => void; 59 editor: (item: T, onChange: (updated: T) => any) => React.ReactNode; 60 } 61 62 export function ArrayInput<T>(props: Props<T>) { 63 const addItem = (item: T) => { 64 props.onChange([...props.items, item]); 65 }; 66 67 const replaceItem = (item: T, i: number) => { 68 const items = props.items.slice(); 69 items[i] = item; 70 props.onChange(items); 71 }; 72 73 const removeItem = (i: number) => { 74 const items = props.items.slice(); 75 items.splice(i, 1); 76 props.onChange(items); 77 }; 78 79 return ( 80 <div className='argo-field' style={{border: 0, marginTop: '15px', zIndex: 1}}> 81 {props.items.map((item, i) => ( 82 <div key={`item-${i}`} style={{marginBottom: '5px'}}> 83 {props.editor(item, (updated: T) => replaceItem(updated, i))} 84 85 <button> 86 <i className='fa fa-times' style={{cursor: 'pointer'}} onClick={() => removeItem(i)} /> 87 </button>{' '} 88 </div> 89 ))} 90 {props.items.length === 0 && <label>No items</label>} 91 <div> 92 <button className='argo-button argo-button--base argo-button--short' onClick={() => addItem({} as T)}> 93 <i style={{cursor: 'pointer'}} className='fa fa-plus' /> 94 </button> 95 </div> 96 </div> 97 ); 98 } 99 100 export const ArrayInputField = ReactForm.FormField((props: {fieldApi: ReactForm.FieldApi}) => { 101 const { 102 fieldApi: {getValue, setValue} 103 } = props; 104 return <ArrayInput editor={NameValueEditor} items={getValue() || []} onChange={setValue} />; 105 }); 106 107 export const MapInputField = ReactForm.FormField((props: {fieldApi: ReactForm.FieldApi}) => { 108 const { 109 fieldApi: {getValue, setValue} 110 } = props; 111 const items = new Array<NameValue>(); 112 const map = getValue() || {}; 113 Object.keys(map).forEach(key => items.push({name: key, value: map[key]})); 114 return ( 115 <ArrayInput 116 editor={NameValueEditor} 117 items={items} 118 onChange={array => { 119 const newMap = {} as any; 120 array.forEach(item => (newMap[item.name || ''] = item.value || '')); 121 setValue(newMap); 122 }} 123 /> 124 ); 125 });