Align tag inputs with other input boxes (#2965)
* Wrap tag input component * Postcss nested selectors * Chips has same size as in ui * Input matches with sizes/paddings of others * Adjust colours, move hint text * Added ChipInput from search in wrapper * Using InputChip Wrapper in search (#2965)
This commit is contained in:
		
							parent
							
								
									04432b2766
								
							
						
					
					
						commit
						381af547fa
					
				| @ -92,6 +92,7 @@ | ||||
|     "nock": "^8.0.0", | ||||
|     "postcss-import": "^8.1.2", | ||||
|     "postcss-loader": "^0.8.1", | ||||
|     "postcss-nested": "^1.0.0", | ||||
|     "postcss-simple-vars": "^3.0.0", | ||||
|     "react-addons-test-utils": "^15.3.0", | ||||
|     "react-copy-to-clipboard": "^4.2.3", | ||||
|  | ||||
| @ -17,10 +17,8 @@ | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| import ContentClear from 'material-ui/svg-icons/content/clear'; | ||||
| import ContentSave from 'material-ui/svg-icons/content/save'; | ||||
| // import ChipInput from 'material-ui-chip-input';
 | ||||
| import ChipInput from 'material-ui-chip-input/src/ChipInput'; | ||||
| 
 | ||||
| import { Button, Form, Input, Modal } from '../../ui'; | ||||
| import { Button, Form, Input, InputChip, Modal } from '../../ui'; | ||||
| import { validateName } from '../../util/validation'; | ||||
| 
 | ||||
| export default class EditMeta extends Component { | ||||
| @ -104,53 +102,20 @@ export default class EditMeta extends Component { | ||||
|     const { tags } = meta || []; | ||||
| 
 | ||||
|     return ( | ||||
|       <ChipInput | ||||
|         ref='tagsInput' | ||||
|         value={ tags } | ||||
|         onRequestAdd={ this.onAddTag } | ||||
|         onRequestDelete={ this.onDeleteTag } | ||||
|         floatingLabelText='(optional) tags' | ||||
|         hintText='press <Enter> to add a tag' | ||||
|         onUpdateInput={ this.onTagsInputChange } | ||||
|         floatingLabelFixed | ||||
|         fullWidth | ||||
|       <InputChip | ||||
|         tokens={ tags } | ||||
|         onTokensChange={ this.onTagsChange } | ||||
|         label='(optional) tags' | ||||
|         hint='press <Enter> to add a tag' | ||||
|         clearOnBlur | ||||
|       /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   onAddTag = (tag) => { | ||||
|     const { meta } = this.state; | ||||
|     const { tags } = meta || []; | ||||
| 
 | ||||
|     this.onMetaChange('tags', [].concat(tags, tag)); | ||||
|   } | ||||
| 
 | ||||
|   onDeleteTag = (tag) => { | ||||
|     const { meta } = this.state; | ||||
|     const { tags } = meta || []; | ||||
| 
 | ||||
|     const newTags = tags | ||||
|       .filter(t => t !== tag); | ||||
| 
 | ||||
|   onTagsChange = (newTags) => { | ||||
|     this.onMetaChange('tags', newTags); | ||||
|   } | ||||
| 
 | ||||
|   onTagsInputChange = (value) => { | ||||
|     const { meta } = this.state; | ||||
|     const { tags = [] } = meta; | ||||
| 
 | ||||
|     const tokens = value.split(/[\s,;]+/); | ||||
| 
 | ||||
|     const newTokens = tokens | ||||
|       .slice(0, -1) | ||||
|       .filter(t => t.length > 0); | ||||
| 
 | ||||
|     const inputValue = tokens.slice(-1)[0].trim(); | ||||
| 
 | ||||
|     this.onMetaChange('tags', [].concat(tags, newTokens)); | ||||
|     this.refs.tagsInput.setState({ inputValue }); | ||||
|   } | ||||
| 
 | ||||
|   onNameChange = (name) => { | ||||
|     this.setState(validateName(name)); | ||||
|   } | ||||
|  | ||||
| @ -41,11 +41,3 @@ | ||||
|   width: 0; | ||||
|   height: 0; | ||||
| } | ||||
| 
 | ||||
| .chip > svg { | ||||
|   width: 1.2rem !important; | ||||
|   height: 1.2rem !important; | ||||
|   margin: initial !important; | ||||
|   margin-right: 4px !important; | ||||
|   padding: 4px 0 !important; | ||||
| } | ||||
|  | ||||
| @ -15,14 +15,9 @@ | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| import { Chip } from 'material-ui'; | ||||
| import { blue300 } from 'material-ui/styles/colors'; | ||||
| // import ChipInput from 'material-ui-chip-input';
 | ||||
| import ChipInput from 'material-ui-chip-input/src/ChipInput'; | ||||
| import ActionSearch from 'material-ui/svg-icons/action/search'; | ||||
| import { uniq } from 'lodash'; | ||||
| 
 | ||||
| import { Button } from '../../'; | ||||
| import { Button, InputChip } from '../../'; | ||||
| 
 | ||||
| import styles from './search.css'; | ||||
| 
 | ||||
| @ -74,28 +69,14 @@ export default class ActionbarSearch extends Component { | ||||
|         className={ styles.searchcontainer } | ||||
|         key='searchAccount'> | ||||
|         <div className={ inputContainerClasses.join(' ') }> | ||||
|           <ChipInput | ||||
|             clearOnBlur={ false } | ||||
|           <InputChip | ||||
|             className={ styles.input } | ||||
|             chipRenderer={ this.chipRenderer } | ||||
|             hintText='Enter search input...' | ||||
|             ref='searchInput' | ||||
|             value={ tokens } | ||||
|             hint='Enter search input...' | ||||
|             tokens={ tokens } | ||||
| 
 | ||||
|             onBlur={ this.handleSearchBlur } | ||||
|             onRequestAdd={ this.handleTokenAdd } | ||||
|             onRequestDelete={ this.handleTokenDelete } | ||||
|             onUpdateInput={ this.handleInputChange } | ||||
|             hintStyle={ { | ||||
|               bottom: 16, | ||||
|               left: 2, | ||||
|               transition: 'none' | ||||
|             } } | ||||
|             inputStyle={ { | ||||
|               marginBottom: 18 | ||||
|             } } | ||||
|             textFieldStyle={ { | ||||
|               height: 42 | ||||
|             } } | ||||
|             onInputChange={ this.handleInputChange } | ||||
|             onTokensChange={ this.handleTokensChange } | ||||
|           /> | ||||
|         </div> | ||||
| 
 | ||||
| @ -108,72 +89,13 @@ export default class ActionbarSearch extends Component { | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   chipRenderer = (state, key) => { | ||||
|     const { value, isFocused, isDisabled, handleClick, handleRequestDelete } = state; | ||||
| 
 | ||||
|     return ( | ||||
|       <Chip | ||||
|         key={ key } | ||||
|         className={ styles.chip } | ||||
|         style={ { | ||||
|           margin: '8px 8px 0 0', | ||||
|           float: 'left', | ||||
|           pointerEvents: isDisabled ? 'none' : undefined, | ||||
|           alignItems: 'center' | ||||
|         } } | ||||
|         labelStyle={ { | ||||
|           paddingRight: 6, | ||||
|           fontSize: '0.9rem', | ||||
|           lineHeight: 'initial' | ||||
|         } } | ||||
|         backgroundColor={ isFocused ? blue300 : 'rgba(0, 0, 0, 0.73)' } | ||||
|         onTouchTap={ handleClick } | ||||
|         onRequestDelete={ handleRequestDelete } | ||||
|       > | ||||
|         { value } | ||||
|       </Chip> | ||||
|     ); | ||||
|   handleTokensChange = (tokens) => { | ||||
|     this.handleSearchChange(tokens); | ||||
|   } | ||||
| 
 | ||||
|   handleTokenAdd = (value) => { | ||||
|     const { tokens } = this.props; | ||||
|     const { inputValue } = this.state; | ||||
| 
 | ||||
|     const newSearchTokens = uniq([].concat(tokens, value)); | ||||
| 
 | ||||
|     this.setState({ | ||||
|       inputValue: inputValue === value ? '' : inputValue | ||||
|     }, () => { | ||||
|       this.handleSearchChange(newSearchTokens); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   handleTokenDelete = (value) => { | ||||
|     const { tokens } = this.props; | ||||
| 
 | ||||
|     const newSearchTokens = [] | ||||
|       .concat(tokens) | ||||
|       .filter(v => v !== value); | ||||
| 
 | ||||
|     this.handleSearchChange(newSearchTokens); | ||||
|     this.refs.searchInput.focus(); | ||||
|   } | ||||
| 
 | ||||
|   handleInputChange = (value) => { | ||||
|     const splitTokens = value.split(/[\s,;]/); | ||||
| 
 | ||||
|     const inputValue = (splitTokens.length <= 1) | ||||
|       ? value | ||||
|       : splitTokens.slice(-1)[0].trim(); | ||||
| 
 | ||||
|     this.refs.searchInput.setState({ inputValue }); | ||||
|   handleInputChange = (inputValue) => { | ||||
|     this.setState({ inputValue }, () => { | ||||
|       if (splitTokens.length > 1) { | ||||
|         const tokensToAdd = splitTokens.slice(0, -1); | ||||
|         tokensToAdd.forEach(token => this.handleTokenAdd(token)); | ||||
|       } else { | ||||
|       this.handleSearchChange(); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
| @ -182,12 +104,10 @@ export default class ActionbarSearch extends Component { | ||||
|     const { inputValue } = this.state; | ||||
| 
 | ||||
|     const newSearchTokens = [] | ||||
|       .concat(searchTokens || tokens) | ||||
|       .filter(v => v.length > 0); | ||||
|       .concat(searchTokens || tokens); | ||||
| 
 | ||||
|     const newSearchValues = [] | ||||
|       .concat(searchTokens || tokens, inputValue) | ||||
|       .filter(v => v.length > 0); | ||||
|       .concat(searchTokens || tokens, inputValue); | ||||
| 
 | ||||
|     onChange(newSearchTokens, newSearchValues); | ||||
|   } | ||||
| @ -214,19 +134,15 @@ export default class ActionbarSearch extends Component { | ||||
|   } | ||||
| 
 | ||||
|   handleOpenSearch = (showSearch, force) => { | ||||
|     if (this.state.stateChanging && !force) return false; | ||||
|     if (this.state.stateChanging && !force) { | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     this.setState({ | ||||
|       showSearch: showSearch, | ||||
|       stateChanging: true | ||||
|     }); | ||||
| 
 | ||||
|     if (showSearch) { | ||||
|       this.refs.searchInput.focus(); | ||||
|     } else { | ||||
|       this.refs.searchInput.getInputNode().blur(); | ||||
|     } | ||||
| 
 | ||||
|     const timeoutId = window.setTimeout(() => { | ||||
|       this.setState({ stateChanging: false }); | ||||
|     }, 450); | ||||
|  | ||||
							
								
								
									
										17
									
								
								js/src/ui/Form/InputChip/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								js/src/ui/Form/InputChip/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| // Copyright 2015, 2016 Ethcore (UK) Ltd.
 | ||||
| // 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/>.
 | ||||
| 
 | ||||
| export default from './inputChip'; | ||||
							
								
								
									
										26
									
								
								js/src/ui/Form/InputChip/inputChip.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								js/src/ui/Form/InputChip/inputChip.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| /* Copyright 2015, 2016 Ethcore (UK) Ltd. | ||||
| /* 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/>. | ||||
| */ | ||||
| 
 | ||||
| .chip { | ||||
|   & > svg { | ||||
|     width: 1.2rem !important; | ||||
|     height: 1.2rem !important; | ||||
|     margin: initial !important; | ||||
|     margin-right: 4px !important; | ||||
|     padding: 4px 0 !important; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										164
									
								
								js/src/ui/Form/InputChip/inputChip.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								js/src/ui/Form/InputChip/inputChip.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,164 @@ | ||||
| // Copyright 2015, 2016 Ethcore (UK) Ltd.
 | ||||
| // 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'; | ||||
| 
 | ||||
| import styles from './inputChip.css'; | ||||
| 
 | ||||
| export default class InputChip extends Component { | ||||
|   static propTypes = { | ||||
|     tokens: PropTypes.array.isRequired, | ||||
|     className: PropTypes.string, | ||||
|     hint: PropTypes.string, | ||||
|     label: PropTypes.string, | ||||
|     onTokensChange: PropTypes.func, | ||||
|     onInputChange: PropTypes.func, | ||||
|     onBlur: PropTypes.func, | ||||
|     clearOnBlur: PropTypes.bool | ||||
|   } | ||||
| 
 | ||||
|   static defaultProps = { | ||||
|     clearOnBlur: false | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { clearOnBlur, className, hint, label, tokens } = this.props; | ||||
|     const classes = `${className}`; | ||||
| 
 | ||||
|     return ( | ||||
|       <ChipInput | ||||
|         className={ classes } | ||||
|         ref='chipInput' | ||||
| 
 | ||||
|         value={ tokens } | ||||
|         clearOnBlur={ clearOnBlur } | ||||
|         floatingLabelText={ label } | ||||
|         hintText={ hint } | ||||
| 
 | ||||
|         chipRenderer={ this.chipRenderer } | ||||
| 
 | ||||
|         onBlur={ this.handleBlur } | ||||
|         onRequestAdd={ this.handleTokenAdd } | ||||
|         onRequestDelete={ this.handleTokenDelete } | ||||
|         onUpdateInput={ this.handleInputChange } | ||||
| 
 | ||||
|         floatingLabelFixed | ||||
|         fullWidth | ||||
| 
 | ||||
|         hintStyle={ { | ||||
|           bottom: 16, | ||||
|           left: 1, | ||||
|           transition: 'none' | ||||
|         } } | ||||
|         inputStyle={ { | ||||
|           marginBottom: 18 | ||||
|         } } | ||||
|         textFieldStyle={ { | ||||
|           height: 42 | ||||
|         } } /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   chipRenderer = (state, key) => { | ||||
|     const { value, isFocused, isDisabled, handleClick, handleRequestDelete } = state; | ||||
| 
 | ||||
|     return ( | ||||
|       <Chip | ||||
|         key={ key } | ||||
|         className={ styles.chip } | ||||
|         style={ { | ||||
|           margin: '8px 8px 0 0', | ||||
|           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> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   handleBlur = () => { | ||||
|     const { onBlur } = this.props; | ||||
| 
 | ||||
|     if (typeof onBlur === 'function') { | ||||
|       onBlur(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   handleTokenAdd = (value) => { | ||||
|     const { tokens, onInputChange } = this.props; | ||||
| 
 | ||||
|     const newTokens = uniq([].concat(tokens, value)); | ||||
| 
 | ||||
|     this.handleTokensChange(newTokens); | ||||
| 
 | ||||
|     if (value === this.refs.chipInput.state.inputValue && typeof onInputChange === 'function') { | ||||
|       onInputChange(''); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   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)); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| @ -19,6 +19,7 @@ import FormWrap from './FormWrap'; | ||||
| import Input from './Input'; | ||||
| import InputAddress from './InputAddress'; | ||||
| import InputAddressSelect from './InputAddressSelect'; | ||||
| import InputChip from './InputChip'; | ||||
| import InputInline from './InputInline'; | ||||
| import Select from './Select'; | ||||
| 
 | ||||
| @ -29,6 +30,7 @@ export { | ||||
|   Input, | ||||
|   InputAddress, | ||||
|   InputAddressSelect, | ||||
|   InputChip, | ||||
|   InputInline, | ||||
|   Select | ||||
| }; | ||||
|  | ||||
| @ -25,7 +25,7 @@ import ConfirmDialog from './ConfirmDialog'; | ||||
| import Container, { Title as ContainerTitle } from './Container'; | ||||
| import ContextProvider from './ContextProvider'; | ||||
| import Errors from './Errors'; | ||||
| import Form, { AddressSelect, FormWrap, Input, InputAddress, InputAddressSelect, InputInline, Select } from './Form'; | ||||
| import Form, { AddressSelect, FormWrap, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select } from './Form'; | ||||
| import IdentityIcon from './IdentityIcon'; | ||||
| import IdentityName from './IdentityName'; | ||||
| import MethodDecoding from './MethodDecoding'; | ||||
| @ -57,6 +57,7 @@ export { | ||||
|   Input, | ||||
|   InputAddress, | ||||
|   InputAddressSelect, | ||||
|   InputChip, | ||||
|   InputInline, | ||||
|   Select, | ||||
|   IdentityIcon, | ||||
|  | ||||
| @ -17,6 +17,7 @@ | ||||
| const HappyPack = require('happypack'); | ||||
| const path = require('path'); | ||||
| const postcssImport = require('postcss-import'); | ||||
| const postcssNested = require('postcss-nested'); | ||||
| const postcssVars = require('postcss-simple-vars'); | ||||
| const rucksack = require('rucksack-css'); | ||||
| const webpack = require('webpack'); | ||||
| @ -113,6 +114,7 @@ module.exports = { | ||||
|     postcssImport({ | ||||
|       addDependencyTo: webpack | ||||
|     }), | ||||
|     postcssNested({}), | ||||
|     postcssVars({ | ||||
|       unknown: function (node, name, result) { | ||||
|         node.warn(result, `Unknown variable ${name}`); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user