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;