Allow tags for Accounts, Addresses and Contracts (#2712)

* Added tag to the editMeta Modal (#2643)

* Added Tags to ui and to contract/address/account Header (#2643)

* Added tags to summary (#2643)

* Added Search capabilities to contracts/address book/accounts from tokens
(#2643)

* fixes eslint

* Using Chips/Tokens for search (#2643)

* Add search tokens, clickable from List (#2643)

* Add sort capabilities to Accounts / Addresses / Contracts (#2643)

* Fixes formatting issues + state updates after component unmount bug
(#2643)

* Remove unused import

* Small fixes for PR #2697

* Added default sort order for Contracts/Addresses/Accounts

* Using official `material-ui-chip-input` NPM package

* Removed LESS from webpack
This commit is contained in:
Nicolas Gotchac
2016-10-19 10:51:02 +01:00
committed by Gav Wood
parent dadd6b1e7c
commit cc10f412dc
21 changed files with 723 additions and 36 deletions

View 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 './search';

View File

@@ -0,0 +1,43 @@
/* 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/>.
*/
.searchcontainer {
display: flex;
overflow: hidden;
}
.searchButton {
min-width: 50px !important;
}
.input {
width: 500px !important;
}
.inputContainer {
transition: width 450ms ease-in-out 0ms, height 0ms ease-in-out 0ms;
white-space: nowrap;
overflow: hidden;
width: 500px;
height: 100%;
position: relative;
}
.inputContainerShown {
transition: width 450ms ease-in-out 0ms, height 0ms ease-in-out 400ms;
width: 0;
height: 0;
}

View File

@@ -0,0 +1,176 @@
// 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 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 styles from './search.css';
export default class ActionbarSearch extends Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
tokens: PropTypes.array
};
state = {
showSearch: false,
stateChanging: false,
inputValue: '',
timeoutIds: []
}
componentWillReceiveProps (nextProps) {
const { tokens } = nextProps;
if (tokens.length > 0 && this.props.tokens.length === 0) {
this.handleOpenSearch(true, true);
}
}
componentWillUnmount () {
const { timeoutIds } = this.state;
if (timeoutIds.length > 0) {
timeoutIds.map(id => window.clearTimeout(id));
}
}
render () {
const { showSearch } = this.state;
const { tokens } = this.props;
const inputContainerClasses = [ styles.inputContainer ];
if (!showSearch) {
inputContainerClasses.push(styles.inputContainerShown);
}
return (
<div
className={ styles.searchcontainer }
key='searchAccount'>
<div className={ inputContainerClasses.join(' ') }>
<ChipInput
clearOnBlur={ false }
className={ styles.input }
hintText='Enter search input...'
hintStyle={ {
transition: 'none'
} }
ref='searchInput'
value={ tokens }
onBlur={ this.handleSearchBlur }
onRequestAdd={ this.handleTokenAdd }
onRequestDelete={ this.handleTokenDelete }
onUpdateInput={ this.handleInputChange } />
</div>
<Button
className={ styles.searchButton }
icon={ <ActionSearch /> }
label=''
onClick={ this.handleSearchClick } />
</div>
);
}
handleTokenAdd = (value) => {
const { tokens } = this.props;
const newSearchValues = uniq([].concat(tokens, value));
this.setState({
inputValue: ''
});
this.handleSearchChange(newSearchValues);
}
handleTokenDelete = (value) => {
const { tokens } = this.props;
const newSearchValues = []
.concat(tokens)
.filter(v => v !== value);
this.setState({
inputValue: ''
});
this.handleSearchChange(newSearchValues);
this.refs.searchInput.focus();
}
handleInputChange = (value) => {
this.setState({ inputValue: value });
}
handleSearchChange = (searchValues) => {
const { onChange } = this.props;
const newSearchValues = searchValues.filter(v => v.length > 0);
onChange(newSearchValues);
}
handleSearchClick = () => {
const { showSearch } = this.state;
this.handleOpenSearch(!showSearch);
}
handleSearchBlur = () => {
const timeoutId = window.setTimeout(() => {
const { inputValue } = this.state;
const { tokens } = this.props;
if (tokens.length === 0 && inputValue.length === 0) {
this.handleOpenSearch(false);
}
}, 250);
this.setState({
timeoutIds: [].concat(this.state.timeoutIds, timeoutId)
});
}
handleOpenSearch = (showSearch, force) => {
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);
this.setState({
timeoutIds: [].concat(this.state.timeoutIds, timeoutId)
});
}
}

View 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 './sort';

View File

@@ -0,0 +1,20 @@
/* 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/>.
*/
.sortButton {
min-width: 50px !important;
}

View File

@@ -0,0 +1,73 @@
// 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 IconMenu from 'material-ui/IconMenu';
import MenuItem from 'material-ui/MenuItem';
import SortIcon from 'material-ui/svg-icons/content/sort';
import { Button } from '../../';
import styles from './sort.css';
export default class ActionbarSort extends Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
order: PropTypes.string
};
state = {
menuOpen: false
}
render () {
return (
<IconMenu
iconButtonElement={
<Button
className={ styles.sortButton }
label=''
icon={ <SortIcon /> }
onClick={ this.handleMenuOpen }
/>
}
open={ this.state.menuOpen }
onRequestChange={ this.handleMenuChange }
onItemTouchTap={ this.handleSortChange }
targetOrigin={ { horizontal: 'right', vertical: 'top' } }
anchorOrigin={ { horizontal: 'right', vertical: 'top' } }
>
<MenuItem value='' primaryText='Default' />
<MenuItem value='tags' primaryText='Sort by tags' />
<MenuItem value='name' primaryText='Sort by name' />
</IconMenu>
);
}
handleSortChange = (event, child) => {
const order = child.props.value;
this.props.onChange(order);
}
handleMenuOpen = () => {
this.setState({ menuOpen: true });
}
handleMenuChange = (open) => {
this.setState({ menuOpen: open });
}
}

View File

@@ -15,13 +15,15 @@
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.container {
padding: 0em
padding: 0em;
}
.padded {
padding: 1.5em;
background: rgba(0, 0, 0, 0.8) !important;
border-radius: 0 !important;
position: relative;
overflow: auto;
}
.light .padded {

17
js/src/ui/Tags/index.js Normal file
View 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 './tags';

36
js/src/ui/Tags/tags.css Normal file
View File

@@ -0,0 +1,36 @@
/* 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/>.
*/
.tags {
display: flex;
flex-wrap: wrap;
position: absolute;
right: 0.25rem;
top: 0;
}
.tag {
font-size: 0.75rem;
background: rgba(255, 255, 255, 0.07);
border-radius: 16px;
margin: 0.75em 0.5em 0 0;
padding: 0.25em 1em;
}
.tagClickable:hover {
cursor: pointer;
}

56
js/src/ui/Tags/tags.js Normal file
View File

@@ -0,0 +1,56 @@
// 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 styles from './tags.css';
export default class Tags extends Component {
static propTypes = {
tags: PropTypes.array,
handleAddSearchToken: PropTypes.func
}
render () {
return (<div className={ styles.tags }>
{ this.renderTags() }
</div>);
}
renderTags () {
const { handleAddSearchToken } = this.props;
const tags = this.props.tags || [];
const tagClasses = handleAddSearchToken
? [ styles.tag, styles.tagClickable ]
: [ styles.tag ];
return tags.map((tag, idx) => {
const onClick = handleAddSearchToken
? () => handleAddSearchToken(tag)
: null;
return (
<div
key={ idx }
className={ tagClasses.join(' ') }
onClick={ onClick }>
{ tag }
</div>
);
});
}
}

View File

@@ -15,6 +15,8 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import Actionbar from './Actionbar';
import ActionbarSearch from './Actionbar/Search';
import ActionbarSort from './Actionbar/Sort';
import Badge from './Badge';
import Balance from './Balance';
import Button from './Button';
@@ -31,11 +33,14 @@ import muiTheme from './Theme';
import Page from './Page';
import ParityBackground from './ParityBackground';
import SignerIcon from './SignerIcon';
import Tags from './Tags';
import Tooltips, { Tooltip } from './Tooltips';
import TxHash from './TxHash';
export {
Actionbar,
ActionbarSearch,
ActionbarSort,
AddressSelect,
Badge,
Balance,
@@ -62,6 +67,7 @@ export {
Page,
ParityBackground,
SignerIcon,
Tags,
Tooltip,
Tooltips,
TxHash