github.com/argoproj/argo-cd/v3@v3.2.1/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  import {helpTip} from '../../../applications/components/utils';
     6  import {Consumer} from '../../context';
     7  import {Spinner} from '../spinner';
     8  
     9  export interface EditablePanelItem {
    10      title: string;
    11      customTitle?: string | React.ReactNode;
    12      key?: string;
    13      before?: React.ReactNode;
    14      view: string | React.ReactNode;
    15      edit?: (formApi: FormApi) => React.ReactNode;
    16      titleEdit?: (formApi: FormApi) => React.ReactNode;
    17  }
    18  
    19  export interface EditablePanelProps<T> {
    20      title?: string | React.ReactNode;
    21      titleCollapsed?: string | React.ReactNode;
    22      floatingTitle?: string | React.ReactNode;
    23      values: T;
    24      validate?: (values: T) => any;
    25      save?: (input: T, query: {validate?: boolean}) => Promise<any>;
    26      items: EditablePanelItem[];
    27      onModeSwitch?: () => any;
    28      noReadonlyMode?: boolean;
    29      view?: string | React.ReactNode;
    30      edit?: (formApi: FormApi) => React.ReactNode;
    31      hasMultipleSources?: boolean;
    32      collapsible?: boolean;
    33      collapsed?: boolean;
    34      collapsedDescription?: string;
    35  }
    36  
    37  interface EditablePanelState {
    38      edit: boolean;
    39      saving: boolean;
    40      collapsed: boolean;
    41  }
    42  
    43  require('./editable-panel.scss');
    44  
    45  export class EditablePanel<T = {}> extends React.Component<EditablePanelProps<T>, EditablePanelState> {
    46      private formApi: FormApi;
    47  
    48      constructor(props: EditablePanelProps<T>) {
    49          super(props);
    50          this.state = {edit: !!props.noReadonlyMode, saving: false, collapsed: this.props.collapsed};
    51      }
    52  
    53      public UNSAFE_componentWillReceiveProps(nextProps: EditablePanelProps<T>) {
    54          if (this.formApi && JSON.stringify(this.props.values) !== JSON.stringify(nextProps.values)) {
    55              if (nextProps.noReadonlyMode) {
    56                  this.formApi.setAllValues(nextProps.values);
    57              }
    58          }
    59      }
    60  
    61      public render() {
    62          return (
    63              <Consumer>
    64                  {ctx =>
    65                      this.props.collapsible && this.state.collapsed ? (
    66                          <div className='settings-overview__redirect-panel' style={{marginTop: 0}} onClick={() => this.setState({collapsed: !this.state.collapsed})}>
    67                              <div className='settings-overview__redirect-panel__content'>
    68                                  <div className='settings-overview__redirect-panel__title'>{this.props.titleCollapsed ? this.props.titleCollapsed : this.props.title}</div>
    69                                  <div className='settings-overview__redirect-panel__description'>{this.props.collapsedDescription}</div>
    70                              </div>
    71                              <div className='settings-overview__redirect-panel__arrow'>
    72                                  <i className='fa fa-angle-down' />
    73                              </div>
    74                          </div>
    75                      ) : (
    76                          <div className={classNames('white-box editable-panel', {'editable-panel--disabled': this.state.saving})}>
    77                              {this.props.floatingTitle && <div className='white-box--additional-top-space editable-panel__sticky-title'>{this.props.floatingTitle}</div>}
    78                              <div className='white-box__details'>
    79                                  {!this.props.noReadonlyMode && this.props.save && (
    80                                      <div className='editable-panel__buttons' style={{right: this.props.collapsible ? '5em' : '3em'}}>
    81                                          {!this.state.edit && (
    82                                              <button
    83                                                  onClick={() => {
    84                                                      this.setState({edit: true});
    85                                                      this.onModeSwitch();
    86                                                  }}
    87                                                  disabled={this.props.hasMultipleSources}
    88                                                  className='argo-button argo-button--base'>
    89                                                  {this.props.hasMultipleSources &&
    90                                                      helpTip('Sources are not editable for applications with multiple sources. You can edit them in the "Manifest" tab.')}{' '}
    91                                                  Edit
    92                                              </button>
    93                                          )}
    94                                          {this.state.edit && (
    95                                              <React.Fragment>
    96                                                  <button
    97                                                      disabled={this.state.saving}
    98                                                      onClick={() => !this.state.saving && this.formApi.submitForm(null)}
    99                                                      className='argo-button argo-button--base'>
   100                                                      <Spinner show={this.state.saving} style={{marginRight: '5px'}} />
   101                                                      Save
   102                                                  </button>{' '}
   103                                                  <button
   104                                                      onClick={() => {
   105                                                          this.setState({edit: false});
   106                                                          this.onModeSwitch();
   107                                                      }}
   108                                                      className='argo-button argo-button--base-o'>
   109                                                      Cancel
   110                                                  </button>
   111                                              </React.Fragment>
   112                                          )}
   113                                      </div>
   114                                  )}
   115                                  {this.props.collapsible && (
   116                                      <div className='editable-panel__collapsible-button'>
   117                                          <i
   118                                              className={`fa fa-angle-${this.state.collapsed ? 'down' : 'up'} filter__collapse`}
   119                                              onClick={() => {
   120                                                  this.setState({collapsed: !this.state.collapsed});
   121                                              }}
   122                                          />
   123                                      </div>
   124                                  )}
   125                                  {this.props.title && <p>{this.props.title}</p>}
   126                                  {(!this.state.edit && (
   127                                      <React.Fragment>
   128                                          {this.props.view}
   129                                          {this.props.items
   130                                              .filter(item => item.view)
   131                                              .map(item => (
   132                                                  <React.Fragment key={item.key || item.title}>
   133                                                      {item.before}
   134                                                      <div className='row white-box__details-row'>
   135                                                          <div className='columns small-3'>{item.customTitle || item.title}</div>
   136                                                          <div className='columns small-9'>{item.view}</div>
   137                                                      </div>
   138                                                  </React.Fragment>
   139                                              ))}
   140                                      </React.Fragment>
   141                                  )) || (
   142                                      <Form
   143                                          getApi={api => (this.formApi = api)}
   144                                          formDidUpdate={async form => {
   145                                              if (this.props.noReadonlyMode && this.props.save) {
   146                                                  await this.props.save(form.values as any, {});
   147                                              }
   148                                          }}
   149                                          onSubmit={async input => {
   150                                              try {
   151                                                  this.setState({saving: true});
   152                                                  await this.props.save(input as any, {});
   153                                                  this.setState({edit: false, saving: false});
   154                                                  this.onModeSwitch();
   155                                              } catch (e) {
   156                                                  ctx.notifications.show({
   157                                                      content: <ErrorNotification title='Unable to save changes' e={e} />,
   158                                                      type: NotificationType.Error
   159                                                  });
   160                                              } finally {
   161                                                  this.setState({saving: false});
   162                                              }
   163                                          }}
   164                                          defaultValues={this.props.values}
   165                                          validateError={this.props.validate}>
   166                                          {api => (
   167                                              <React.Fragment>
   168                                                  {this.props.edit && this.props.edit(api)}
   169                                                  {this.props.items.map(item => (
   170                                                      <React.Fragment key={item.key || item.title}>
   171                                                          {item.before}
   172                                                          <div className='row white-box__details-row'>
   173                                                              <div className='columns small-3'>{(item.titleEdit && item.titleEdit(api)) || item.customTitle || item.title}</div>
   174                                                              <div className='columns small-9'>{(item.edit && item.edit(api)) || item.view}</div>
   175                                                          </div>
   176                                                      </React.Fragment>
   177                                                  ))}
   178                                              </React.Fragment>
   179                                          )}
   180                                      </Form>
   181                                  )}
   182                              </div>
   183                          </div>
   184                      )
   185                  }
   186              </Consumer>
   187          );
   188      }
   189  
   190      private onModeSwitch() {
   191          if (this.props.onModeSwitch) {
   192              this.props.onModeSwitch();
   193          }
   194      }
   195  }