new InputAddressSelect component (#3071)
* basic address autocomplete * validate input, propagate changes * show IdentityIcon in menu * show IdentityIcon next to input * refactoring, better variable names, linting * show default IdentityIcon if search by name * port #3065 over * show accounts in the beginning * show accounts before contacts * filter deleted accounts * UX improvements - limit number of search results shown - hint text * only render identity icon if valid address * UX improvements - align IdentityIcon - better hint text * align label & error with other inputs This probably needs to be changed soon again. Therefore this ugly hack has been put in place. * Align component with coding style for app * Use standard/tested AddressAutocmplete (WIP) * Address selection & inputs operational * Update TODOs, remove unused CSS * only handle input changes when editing * Simplify * Cleanup unused modules * Add contracts to address search * Updates Address Selector to handle valid input address #3071 * Added Address Selector to contracts read queries
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { MenuItem } from 'material-ui';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import AutoComplete from '../AutoComplete';
|
||||
import IdentityIcon from '../../IdentityIcon';
|
||||
@@ -24,57 +25,82 @@ import IdentityName from '../../IdentityName';
|
||||
import styles from './addressSelect.css';
|
||||
|
||||
export default class AddressSelect extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
disabled: PropTypes.bool,
|
||||
accounts: PropTypes.object,
|
||||
contacts: PropTypes.object,
|
||||
contracts: PropTypes.object,
|
||||
label: PropTypes.string,
|
||||
hint: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
tokens: PropTypes.object,
|
||||
onChange: PropTypes.func.isRequired
|
||||
onChange: PropTypes.func.isRequired,
|
||||
allowInput: PropTypes.bool
|
||||
}
|
||||
|
||||
state = {
|
||||
entries: {},
|
||||
addresses: [],
|
||||
value: ''
|
||||
}
|
||||
|
||||
entriesFromProps (props = this.props) {
|
||||
const { accounts, contacts, contracts } = props;
|
||||
const entries = Object.assign({}, accounts || {}, contacts || {}, contracts || {});
|
||||
return entries;
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
const { accounts, contacts, value } = this.props;
|
||||
const entries = Object.assign({}, accounts || {}, contacts || {});
|
||||
this.setState({ entries, value });
|
||||
const { value } = this.props;
|
||||
const entries = this.entriesFromProps();
|
||||
const addresses = Object.keys(entries).sort();
|
||||
|
||||
this.setState({ entries, addresses, value });
|
||||
}
|
||||
|
||||
componentWillReceiveProps (newProps) {
|
||||
const { accounts, contacts } = newProps;
|
||||
const entries = Object.assign({}, accounts || {}, contacts || {});
|
||||
this.setState({ entries });
|
||||
const entries = this.entriesFromProps();
|
||||
const addresses = Object.keys(entries).sort();
|
||||
|
||||
if (!isEqual(addresses, this.state.addresses)) {
|
||||
this.setState({ entries, addresses });
|
||||
}
|
||||
|
||||
if (newProps.value !== this.props.value) {
|
||||
this.setState({ value: newProps.value });
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { disabled, error, hint, label } = this.props;
|
||||
const { entries } = this.state;
|
||||
const value = this.getSearchText();
|
||||
const { allowInput, disabled, error, hint, label } = this.props;
|
||||
const { entries, value } = this.state;
|
||||
|
||||
const searchText = this.getSearchText();
|
||||
const icon = this.renderIdentityIcon(value);
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<AutoComplete
|
||||
className={ (error || !value) ? '' : styles.paddedInput }
|
||||
className={ !icon ? '' : styles.paddedInput }
|
||||
disabled={ disabled }
|
||||
label={ label }
|
||||
hint={ hint ? `search for ${hint}` : 'search for an address' }
|
||||
error={ error }
|
||||
onChange={ this.onChange }
|
||||
value={ value }
|
||||
onBlur={ this.onBlur }
|
||||
onUpdateInput={ allowInput && this.onUpdateInput }
|
||||
value={ searchText }
|
||||
filter={ this.handleFilter }
|
||||
entries={ entries }
|
||||
entry={ this.getEntry() || {} }
|
||||
renderItem={ this.renderItem }
|
||||
/>
|
||||
|
||||
{ this.renderIdentityIcon(value) }
|
||||
{ icon }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -82,7 +108,7 @@ export default class AddressSelect extends Component {
|
||||
renderIdentityIcon (inputValue) {
|
||||
const { error, value } = this.props;
|
||||
|
||||
if (error || !inputValue) {
|
||||
if (error || !inputValue || value.length !== 42) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -96,8 +122,9 @@ export default class AddressSelect extends Component {
|
||||
|
||||
renderItem = (entry) => {
|
||||
return {
|
||||
text: entry.address,
|
||||
value: this.renderSelectEntry(entry)
|
||||
text: entry.name && entry.name.toUpperCase() || entry.address,
|
||||
value: this.renderSelectEntry(entry),
|
||||
address: entry.address
|
||||
};
|
||||
}
|
||||
|
||||
@@ -127,32 +154,48 @@ export default class AddressSelect extends Component {
|
||||
|
||||
getSearchText () {
|
||||
const entry = this.getEntry();
|
||||
if (!entry) return '';
|
||||
const { value } = this.state;
|
||||
|
||||
return entry.name ? entry.name.toUpperCase() : '';
|
||||
return entry && entry.name
|
||||
? entry.name.toUpperCase()
|
||||
: value;
|
||||
}
|
||||
|
||||
getEntry () {
|
||||
const { value } = this.props;
|
||||
if (!value) return '';
|
||||
|
||||
const { entries } = this.state;
|
||||
return entries[value];
|
||||
const { entries, value } = this.state;
|
||||
return value ? entries[value] : null;
|
||||
}
|
||||
|
||||
handleFilter = (searchText, address) => {
|
||||
handleFilter = (searchText, name, item) => {
|
||||
const { address } = item;
|
||||
const entry = this.state.entries[address];
|
||||
const lowCaseSearch = searchText.toLowerCase();
|
||||
|
||||
return [ entry.name, entry.address ]
|
||||
return [entry.name, entry.address]
|
||||
.some(text => text.toLowerCase().indexOf(lowCaseSearch) !== -1);
|
||||
}
|
||||
|
||||
onChange = (entry, empty) => {
|
||||
const { allowInput } = this.props;
|
||||
const { value } = this.state;
|
||||
|
||||
const address = entry && entry.address
|
||||
? entry.address
|
||||
: (empty ? '' : this.state.value);
|
||||
: ((empty && !allowInput) ? '' : value);
|
||||
|
||||
this.props.onChange(null, address);
|
||||
}
|
||||
|
||||
onUpdateInput = (query, choices) => {
|
||||
const { api } = this.context;
|
||||
|
||||
const address = query.trim();
|
||||
|
||||
if (!/^0x/.test(address) && api.util.isAddressValid(`0x${address}`)) {
|
||||
const checksumed = api.util.toChecksumAddress(`0x${address}`);
|
||||
return this.props.onChange(null, checksumed);
|
||||
}
|
||||
|
||||
this.props.onChange(null, address);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user