/ common / components / AddressBookTableRow.tsx
AddressBookTableRow.tsx
  1  import React from 'react';
  2  import noop from 'lodash/noop';
  3  
  4  import translate, { translateRaw } from 'translations';
  5  import { Input, Identicon } from 'components/ui';
  6  
  7  interface Props {
  8    index: number;
  9    address: string;
 10    label: string;
 11    temporaryLabel: string;
 12    labelError?: string;
 13    isEditing: boolean;
 14    onChange(label: string): void;
 15    onSave(): void;
 16    onLabelInputBlur(): void;
 17    onEditClick(): void;
 18    onRemoveClick(): void;
 19  }
 20  
 21  interface State {
 22    labelInputTouched: boolean;
 23  }
 24  
 25  class AddressBookTableRow extends React.Component<Props> {
 26    public state: State = {
 27      labelInputTouched: false
 28    };
 29  
 30    private labelInput: HTMLInputElement | null = null;
 31  
 32    public componentWillReceiveProps(nextProps: Props) {
 33      this.setState({ label: nextProps.label, mostRecentValidLabel: nextProps.label });
 34    }
 35  
 36    public render() {
 37      const {
 38        address,
 39        temporaryLabel,
 40        labelError,
 41        isEditing,
 42        onEditClick,
 43        onRemoveClick
 44      } = this.props;
 45      const { labelInputTouched } = this.state;
 46      const trOnClick = isEditing ? noop : onEditClick;
 47      const hashName = `${address}-hash`;
 48      const labelName = `${address}-label`;
 49  
 50      return (
 51        <React.Fragment>
 52          <div className="AddressBookTable-row" onClick={trOnClick}>
 53            <div className="AddressBookTable-row-input">
 54              <div className="AddressBookTable-row-input-wrapper">
 55                <label htmlFor={hashName} className="AddressBookTable-row-input-wrapper-label">
 56                  {translate('ADDRESS')}
 57                </label>
 58                <Input
 59                  name={hashName}
 60                  title={address}
 61                  value={address}
 62                  readOnly={true}
 63                  isValid={true}
 64                />
 65              </div>
 66              <div className="AddressBookTable-row-identicon AddressBookTable-row-identicon-non-mobile">
 67                <Identicon address={address} />
 68              </div>
 69              <div className="AddressBookTable-row-identicon AddressBookTable-row-identicon-mobile">
 70                <Identicon address={address} size="3rem" />
 71              </div>
 72            </div>
 73            <div className="AddressBookTable-row-input">
 74              <div className="AddressBookTable-row-input-wrapper">
 75                <label htmlFor={labelName} className="AddressBookTable-row-input-wrapper-label">
 76                  {translate('LABEL')}
 77                </label>
 78                <Input
 79                  name={labelName}
 80                  title={translateRaw('EDIT_LABEL_FOR', {
 81                    $address: address
 82                  })}
 83                  value={temporaryLabel}
 84                  onChange={this.handleLabelChange}
 85                  onKeyDown={this.handleKeyDown}
 86                  onFocus={this.setLabelTouched}
 87                  onBlur={this.handleBlur}
 88                  showInvalidBeforeBlur={true}
 89                  setInnerRef={this.setLabelInputRef}
 90                  isValid={!(labelInputTouched && labelError)}
 91                />
 92              </div>
 93              <button
 94                title={translateRaw('REMOVE_LABEL')}
 95                className="btn btn-sm btn-danger"
 96                onClick={onRemoveClick}
 97              >
 98                <i className="fa fa-close" />
 99              </button>
100            </div>
101            {labelError && (
102              <div className="AddressBookTable-row AddressBookTable-row-error AddressBookTable-row-error--mobile">
103                <label className="AddressBookTable-row-input-wrapper-error">{labelError}</label>
104              </div>
105            )}
106          </div>
107          {labelError && (
108            <div className="AddressBookTable-row AddressBookTable-row-error AddressBookTable-row-error--non-mobile">
109              <label className="AddressBookTable-row-input-wrapper-error">{labelError}</label>
110            </div>
111          )}
112        </React.Fragment>
113      );
114    }
115  
116    private handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
117      const { labelInputTouched } = this.state;
118  
119      e.stopPropagation();
120  
121      if (e.key === 'Enter' && this.labelInput) {
122        this.labelInput.blur();
123      } else if (!labelInputTouched) {
124        this.setLabelTouched();
125      }
126    };
127  
128    private handleBlur = () => {
129      this.clearLabelTouched();
130      this.props.onSave();
131      this.props.onLabelInputBlur();
132    };
133  
134    private setLabelInputRef = (node: HTMLInputElement) => (this.labelInput = node);
135  
136    private setLabelTouched = () =>
137      !this.state.labelInputTouched && this.setState({ labelInputTouched: true });
138  
139    private clearLabelTouched = () => this.setState({ labelInputTouched: false });
140  
141    private handleLabelChange = (e: React.ChangeEvent<HTMLInputElement>) => {
142      const label = e.target.value;
143  
144      this.props.onChange(label);
145  
146      this.setState(
147        { labelInputTouched: true },
148        () => label.length === 0 && this.clearLabelTouched()
149      );
150    };
151  }
152  
153  export default AddressBookTableRow;