github.com/argoproj/argo-cd@v1.8.7/ui/src/app/shared/components/editable-panel/editable-panel.tsx (about) 1 import {ErrorNotification, NotificationType} from 'argo-ui'; 2 import * as classNames from 'classnames'; 3 import * as React from 'react'; 4 import {Form, FormApi} from 'react-form'; 5 6 import {Consumer} from '../../context'; 7 import {Spinner} from '../spinner'; 8 9 export interface EditablePanelItem { 10 title: string; 11 key?: string; 12 before?: React.ReactNode; 13 view: string | React.ReactNode; 14 edit?: (formApi: FormApi) => React.ReactNode; 15 titleEdit?: (formApi: FormApi) => React.ReactNode; 16 } 17 18 export interface EditablePanelProps<T> { 19 title?: string | React.ReactNode; 20 values: T; 21 validate?: (values: T) => any; 22 save?: (input: T) => Promise<any>; 23 items: EditablePanelItem[]; 24 onModeSwitch?: () => any; 25 noReadonlyMode?: boolean; 26 view?: string | React.ReactNode; 27 edit?: (formApi: FormApi) => React.ReactNode; 28 } 29 30 interface EditablePanelState { 31 edit: boolean; 32 saving: boolean; 33 } 34 35 require('./editable-panel.scss'); 36 37 export class EditablePanel<T = {}> extends React.Component<EditablePanelProps<T>, EditablePanelState> { 38 private formApi: FormApi; 39 40 constructor(props: EditablePanelProps<T>) { 41 super(props); 42 this.state = {edit: !!props.noReadonlyMode, saving: false}; 43 } 44 45 public UNSAFE_componentWillReceiveProps(nextProps: EditablePanelProps<T>) { 46 if (this.formApi && JSON.stringify(this.props.values) !== JSON.stringify(nextProps.values)) { 47 if (!!nextProps.noReadonlyMode) { 48 this.formApi.setAllValues(nextProps.values); 49 } 50 } 51 } 52 53 public render() { 54 return ( 55 <Consumer> 56 {ctx => ( 57 <div className={classNames('white-box editable-panel', {'editable-panel--disabled': this.state.saving})}> 58 <div className='white-box__details'> 59 {!this.props.noReadonlyMode && this.props.save && ( 60 <div className='editable-panel__buttons'> 61 {!this.state.edit && ( 62 <button 63 onClick={() => { 64 this.setState({edit: true}); 65 this.onModeSwitch(); 66 }} 67 className='argo-button argo-button--base'> 68 Edit 69 </button> 70 )} 71 {this.state.edit && ( 72 <React.Fragment> 73 <button 74 disabled={this.state.saving} 75 onClick={() => !this.state.saving && this.formApi.submitForm(null)} 76 className='argo-button argo-button--base'> 77 <Spinner show={this.state.saving} style={{marginRight: '5px'}} /> 78 Save 79 </button>{' '} 80 <button 81 onClick={() => { 82 this.setState({edit: false}); 83 this.onModeSwitch(); 84 }} 85 className='argo-button argo-button--base-o'> 86 Cancel 87 </button> 88 </React.Fragment> 89 )} 90 </div> 91 )} 92 {this.props.title && <p>{this.props.title}</p>} 93 {(!this.state.edit && ( 94 <React.Fragment> 95 {this.props.view} 96 {this.props.items.map(item => ( 97 <React.Fragment key={item.key || item.title}> 98 {item.before} 99 <div className='row white-box__details-row'> 100 <div className='columns small-3'>{item.title}</div> 101 <div className='columns small-9'>{item.view}</div> 102 </div> 103 </React.Fragment> 104 ))} 105 </React.Fragment> 106 )) || ( 107 <Form 108 getApi={api => (this.formApi = api)} 109 formDidUpdate={async form => { 110 if (this.props.noReadonlyMode && this.props.save) { 111 await this.props.save(form.values as any); 112 } 113 }} 114 onSubmit={async input => { 115 try { 116 this.setState({saving: true}); 117 await this.props.save(input as any); 118 this.setState({edit: false, saving: false}); 119 this.onModeSwitch(); 120 } catch (e) { 121 ctx.notifications.show({ 122 content: <ErrorNotification title='Unable to save changes' e={e} />, 123 type: NotificationType.Error 124 }); 125 } finally { 126 this.setState({saving: false}); 127 } 128 }} 129 defaultValues={this.props.values} 130 validateError={this.props.validate}> 131 {api => ( 132 <React.Fragment> 133 {this.props.edit && this.props.edit(api)} 134 {this.props.items.map(item => ( 135 <React.Fragment key={item.key || item.title}> 136 {item.before} 137 <div className='row white-box__details-row'> 138 <div className='columns small-3'>{(item.titleEdit && item.titleEdit(api)) || item.title}</div> 139 <div className='columns small-9'>{(item.edit && item.edit(api)) || item.view}</div> 140 </div> 141 </React.Fragment> 142 ))} 143 </React.Fragment> 144 )} 145 </Form> 146 )} 147 </div> 148 </div> 149 )} 150 </Consumer> 151 ); 152 } 153 154 private onModeSwitch() { 155 if (this.props.onModeSwitch) { 156 this.props.onModeSwitch(); 157 } 158 } 159 }