2016-12-11 19:31:31 +01:00
|
|
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
2016-10-31 23:22:22 +01:00
|
|
|
// This file is part of Parity.
|
|
|
|
|
|
|
|
// Parity is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
|
|
|
// Parity is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
import React, { Component, PropTypes } from 'react';
|
|
|
|
import { Chip } from 'material-ui';
|
|
|
|
import ChipInput from 'material-ui-chip-input';
|
|
|
|
import { blue300 } from 'material-ui/styles/colors';
|
|
|
|
import { uniq } from 'lodash';
|
|
|
|
|
2016-12-27 11:02:53 +01:00
|
|
|
import { nodeOrStringProptype } from '~/util/proptypes';
|
|
|
|
|
2016-10-31 23:22:22 +01:00
|
|
|
import styles from './inputChip.css';
|
|
|
|
|
|
|
|
export default class InputChip extends Component {
|
|
|
|
static propTypes = {
|
2016-12-27 11:02:53 +01:00
|
|
|
addOnBlur: PropTypes.bool,
|
|
|
|
clearOnBlur: PropTypes.bool,
|
2016-10-31 23:22:22 +01:00
|
|
|
className: PropTypes.string,
|
2016-12-27 11:02:53 +01:00
|
|
|
hint: nodeOrStringProptype(),
|
|
|
|
label: nodeOrStringProptype(),
|
2016-10-31 23:22:22 +01:00
|
|
|
onTokensChange: PropTypes.func,
|
|
|
|
onInputChange: PropTypes.func,
|
|
|
|
onBlur: PropTypes.func,
|
2016-12-27 11:02:53 +01:00
|
|
|
tokens: PropTypes.oneOfType([
|
|
|
|
PropTypes.array,
|
|
|
|
PropTypes.object
|
|
|
|
]).isRequired
|
2016-10-31 23:22:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static defaultProps = {
|
2016-11-01 13:51:02 +01:00
|
|
|
clearOnBlur: false,
|
|
|
|
addOnBlur: false
|
|
|
|
}
|
|
|
|
|
|
|
|
state = {
|
|
|
|
focused: false
|
2016-10-31 23:22:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
|
|
|
const { clearOnBlur, className, hint, label, tokens } = this.props;
|
2016-11-01 13:51:02 +01:00
|
|
|
const { focused } = this.state;
|
|
|
|
|
2016-10-31 23:22:22 +01:00
|
|
|
const classes = `${className}`;
|
|
|
|
|
2016-11-01 13:51:02 +01:00
|
|
|
const textFieldStyle = {
|
|
|
|
height: 55
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!focused) {
|
|
|
|
textFieldStyle.width = 0;
|
|
|
|
}
|
|
|
|
|
2016-10-31 23:22:22 +01:00
|
|
|
return (
|
|
|
|
<ChipInput
|
|
|
|
className={ classes }
|
|
|
|
ref='chipInput'
|
|
|
|
|
|
|
|
value={ tokens }
|
|
|
|
clearOnBlur={ clearOnBlur }
|
|
|
|
floatingLabelText={ label }
|
|
|
|
hintText={ hint }
|
|
|
|
|
|
|
|
chipRenderer={ this.chipRenderer }
|
|
|
|
|
2016-11-01 13:51:02 +01:00
|
|
|
onFocus={ this.handleFocus }
|
2016-10-31 23:22:22 +01:00
|
|
|
onBlur={ this.handleBlur }
|
|
|
|
onRequestAdd={ this.handleTokenAdd }
|
|
|
|
onRequestDelete={ this.handleTokenDelete }
|
|
|
|
onUpdateInput={ this.handleInputChange }
|
|
|
|
|
|
|
|
floatingLabelFixed
|
|
|
|
fullWidth
|
|
|
|
|
|
|
|
hintStyle={ {
|
2016-11-01 13:51:02 +01:00
|
|
|
bottom: 13,
|
|
|
|
left: 0,
|
2016-10-31 23:22:22 +01:00
|
|
|
transition: 'none'
|
|
|
|
} }
|
|
|
|
inputStyle={ {
|
2016-11-01 13:51:02 +01:00
|
|
|
marginBottom: 18,
|
|
|
|
width: 'initial'
|
2016-10-31 23:22:22 +01:00
|
|
|
} }
|
2016-11-01 13:51:02 +01:00
|
|
|
textFieldStyle={ textFieldStyle }
|
|
|
|
underlineStyle={ {
|
|
|
|
borderWidth: 2
|
|
|
|
} }
|
|
|
|
/>
|
2016-10-31 23:22:22 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
chipRenderer = (state, key) => {
|
|
|
|
const { value, isFocused, isDisabled, handleClick, handleRequestDelete } = state;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Chip
|
|
|
|
key={ key }
|
|
|
|
className={ styles.chip }
|
|
|
|
style={ {
|
2016-11-01 13:51:02 +01:00
|
|
|
margin: '15px 8px 0 0',
|
2016-10-31 23:22:22 +01:00
|
|
|
float: 'left',
|
|
|
|
pointerEvents: isDisabled ? 'none' : undefined,
|
|
|
|
alignItems: 'center'
|
|
|
|
} }
|
|
|
|
labelStyle={ {
|
|
|
|
paddingRight: 6,
|
|
|
|
fontSize: '0.9rem',
|
|
|
|
lineHeight: 'initial'
|
|
|
|
} }
|
|
|
|
backgroundColor={ isFocused ? blue300 : 'rgba(50, 50, 50, 0.73)' }
|
|
|
|
onTouchTap={ handleClick }
|
|
|
|
onRequestDelete={ handleRequestDelete }
|
|
|
|
>
|
|
|
|
{ value }
|
|
|
|
</Chip>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-11-01 13:51:02 +01:00
|
|
|
handleFocus = () => {
|
|
|
|
this.setState({ focused: true });
|
|
|
|
}
|
|
|
|
|
2016-10-31 23:22:22 +01:00
|
|
|
handleBlur = () => {
|
2016-11-01 13:51:02 +01:00
|
|
|
const { onBlur, addOnBlur } = this.props;
|
|
|
|
|
|
|
|
this.setState({ focused: false });
|
|
|
|
|
|
|
|
if (addOnBlur) {
|
|
|
|
const { inputValue } = this.refs.chipInput.state;
|
|
|
|
this.handleTokenAdd(inputValue);
|
|
|
|
}
|
2016-10-31 23:22:22 +01:00
|
|
|
|
|
|
|
if (typeof onBlur === 'function') {
|
|
|
|
onBlur();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handleTokenAdd = (value) => {
|
|
|
|
const { tokens, onInputChange } = this.props;
|
|
|
|
|
|
|
|
const newTokens = uniq([].concat(tokens, value));
|
|
|
|
|
|
|
|
this.handleTokensChange(newTokens);
|
|
|
|
|
2016-11-01 13:51:02 +01:00
|
|
|
if (value === this.refs.chipInput.state.inputValue) {
|
|
|
|
if (typeof onInputChange === 'function') {
|
|
|
|
onInputChange('');
|
|
|
|
}
|
|
|
|
this.refs.chipInput.setState({ inputValue: '' });
|
2016-10-31 23:22:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handleTokenDelete = (value) => {
|
|
|
|
const { tokens } = this.props;
|
|
|
|
|
|
|
|
const newTokens = uniq([]
|
|
|
|
.concat(tokens)
|
|
|
|
.filter(v => v !== value));
|
|
|
|
|
|
|
|
this.handleTokensChange(newTokens);
|
|
|
|
this.refs.chipInput.focus();
|
|
|
|
}
|
|
|
|
|
|
|
|
handleInputChange = (value) => {
|
|
|
|
const { onInputChange } = this.props;
|
|
|
|
|
|
|
|
const splitTokens = value.split(/[\s,;]/);
|
|
|
|
|
|
|
|
const inputValue = (splitTokens.length <= 1)
|
|
|
|
? value
|
|
|
|
: splitTokens.slice(-1)[0].trim();
|
|
|
|
|
|
|
|
this.refs.chipInput.setState({ inputValue });
|
|
|
|
|
|
|
|
if (splitTokens.length > 1) {
|
|
|
|
const tokensToAdd = splitTokens.slice(0, -1);
|
|
|
|
tokensToAdd.forEach(token => this.handleTokenAdd(token));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof onInputChange === 'function') {
|
|
|
|
onInputChange(inputValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handleTokensChange = (tokens) => {
|
|
|
|
const { onTokensChange } = this.props;
|
|
|
|
|
|
|
|
onTokensChange(tokens.filter(token => token && token.length > 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|