From e4c75bde4c691be26fd5727cc2c6897c58abdec1 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 2 Nov 2016 17:25:34 +0100 Subject: [PATCH] Add read-only inputs to UI plus Copy to Clipboard buttons (#3095) * Adds readOnly prop to Input, convert disabled props to it (#3066) * WIP * Adds copy icon to readOnly Input (#3009) * Added Copy to Clipboard buttons on the UI (#3009) * copiable to allowCopy props #3095 * Padded copy icons (#3095) * Fixed password width in account creation * Copyable value in MethodDecoding --- .../AccountDetails/accountDetails.js | 6 +- .../CreateAccount/NewAccount/newAccount.js | 2 - .../CreateAccount/NewImport/newImport.js | 1 - js/src/modals/CreateAccount/RawKey/rawKey.js | 2 - .../RecoveryPhrase/recoveryPhrase.js | 2 - js/src/modals/CreateAccount/createAccount.css | 9 + js/src/ui/Container/Title/title.css | 7 +- js/src/ui/Container/Title/title.js | 16 +- js/src/ui/Form/Input/input.css | 31 +++ js/src/ui/Form/Input/input.js | 193 +++++++++++++++--- js/src/ui/Form/InputAddress/inputAddress.css | 1 + js/src/ui/Form/InputAddress/inputAddress.js | 4 +- js/src/ui/MethodDecoding/methodDecoding.js | 3 +- js/src/ui/Tooltips/tooltips.js | 1 - js/src/views/Accounts/Summary/summary.js | 13 +- js/src/views/Application/application.js | 12 +- js/src/views/Contract/Events/Event/event.js | 6 +- js/src/views/Contract/Queries/inputQuery.js | 23 ++- js/src/views/Contract/Queries/queries.css | 26 +-- js/src/views/Contract/Queries/queries.js | 12 +- .../views/Settings/Background/background.js | 4 +- .../MiningSettings/MiningSettings.js | 23 ++- .../views/Status/components/Status/Status.js | 12 +- 23 files changed, 314 insertions(+), 95 deletions(-) create mode 100644 js/src/ui/Form/Input/input.css diff --git a/js/src/modals/CreateAccount/AccountDetails/accountDetails.js b/js/src/modals/CreateAccount/AccountDetails/accountDetails.js index 079d3cac9..646c7c180 100644 --- a/js/src/modals/CreateAccount/AccountDetails/accountDetails.js +++ b/js/src/modals/CreateAccount/AccountDetails/accountDetails.js @@ -31,7 +31,8 @@ export default class AccountDetails extends Component { return (
@@ -54,7 +55,8 @@ export default class AccountDetails extends Component { return ( diff --git a/js/src/modals/CreateAccount/NewAccount/newAccount.js b/js/src/modals/CreateAccount/NewAccount/newAccount.js index 323152213..b05a6db75 100644 --- a/js/src/modals/CreateAccount/NewAccount/newAccount.js +++ b/js/src/modals/CreateAccount/NewAccount/newAccount.js @@ -84,7 +84,6 @@ export default class CreateAccount extends Component {
. */ .byline { - color: #aaa; overflow: hidden; position: relative; line-height: 1.2em; @@ -24,6 +23,12 @@ display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; + + color: #aaa; + + * { + color: #aaa !important; + } } .title { diff --git a/js/src/ui/Container/Title/title.js b/js/src/ui/Container/Title/title.js index 03ba4be76..5be85e48e 100644 --- a/js/src/ui/Container/Title/title.js +++ b/js/src/ui/Container/Title/title.js @@ -24,7 +24,9 @@ export default class Title extends Component { title: PropTypes.oneOfType([ PropTypes.string, PropTypes.node ]), - byline: PropTypes.string + byline: PropTypes.oneOfType([ + PropTypes.string, PropTypes.node + ]) } state = { @@ -34,15 +36,21 @@ export default class Title extends Component { render () { const { className, title, byline } = this.props; + const byLine = typeof byline === 'string' + ? ( + + { byline } + + ) + : byline; + return (

{ title }

- - { byline } - + { byLine }
); diff --git a/js/src/ui/Form/Input/input.css b/js/src/ui/Form/Input/input.css new file mode 100644 index 000000000..62c2e39b2 --- /dev/null +++ b/js/src/ui/Form/Input/input.css @@ -0,0 +1,31 @@ +/* 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 . +*/ + +.container { + display: flex; + flex-direction: row; + align-items: flex-end; + position: relative; +} + +.copy { + margin-right: 0.5em; + + svg { + transition: all .5s ease-in-out; + } +} diff --git a/js/src/ui/Form/Input/input.js b/js/src/ui/Form/Input/input.js index 0c9206d0e..24a0a8089 100644 --- a/js/src/ui/Form/Input/input.js +++ b/js/src/ui/Form/Input/input.js @@ -16,11 +16,22 @@ import React, { Component, PropTypes } from 'react'; -import { TextField } from 'material-ui'; +import CopyToClipboard from 'react-copy-to-clipboard'; +import CopyIcon from 'material-ui/svg-icons/content/content-copy'; +import { TextField, IconButton } from 'material-ui'; +import { lightWhite, fullWhite } from 'material-ui/styles/colors'; + +import styles from './input.css'; // TODO: duplicated in Select const UNDERLINE_DISABLED = { - borderColor: 'rgba(255, 255, 255, 0.298039)' // 'transparent' // 'rgba(255, 255, 255, 0.298039)' + borderBottom: 'dotted 2px', + borderColor: 'rgba(255, 255, 255, 0.125)' // 'transparent' // 'rgba(255, 255, 255, 0.298039)' +}; + +const UNDERLINE_READONLY = { + ...UNDERLINE_DISABLED, + cursor: 'text' }; const UNDERLINE_NORMAL = { @@ -34,6 +45,12 @@ export default class Input extends Component { children: PropTypes.node, className: PropTypes.string, disabled: PropTypes.bool, + readOnly: PropTypes.bool, + allowCopy: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool + ]), + floatCopy: PropTypes.bool, error: PropTypes.string, hint: PropTypes.string, label: PropTypes.string, @@ -45,17 +62,24 @@ export default class Input extends Component { rows: PropTypes.number, type: PropTypes.string, submitOnBlur: PropTypes.bool, + hideUnderline: PropTypes.bool, value: PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]) - } + }; static defaultProps = { - submitOnBlur: true + submitOnBlur: true, + readOnly: false, + allowCopy: false, + hideUnderline: false, + floatCopy: false } state = { - value: this.props.value || '' + value: this.props.value || '', + timeoutId: null, + copied: false } componentWillReceiveProps (newProps) { @@ -64,36 +88,145 @@ export default class Input extends Component { } } + componentWillUnmount () { + const { timeoutId } = this.state; + + if (timeoutId) { + window.clearTimeout(timeoutId); + } + } + render () { const { value } = this.state; - const { children, className, disabled, error, label, hint, multiLine, rows, type } = this.props; + const { children, className, hideUnderline, disabled, error, label, hint, multiLine, rows, type } = this.props; + + const readOnly = this.props.readOnly || disabled; + + const inputStyle = { overflow: 'hidden' }; + const textFieldStyle = {}; + + if (readOnly) { + inputStyle.cursor = 'text'; + } + + if (hideUnderline && !hint) { + textFieldStyle.height = 'initial'; + } return ( - - { children } - +
+ { this.renderCopyButton() } + + { children } + +
); } + renderCopyButton () { + const { allowCopy, hideUnderline, label, hint, floatCopy } = this.props; + const { copied, value } = this.state; + + if (!allowCopy) { + return null; + } + + const style = { + marginBottom: 13 + }; + + const text = typeof allowCopy === 'string' + ? allowCopy + : value; + + const scale = copied ? 'scale(1.15)' : 'scale(1)'; + + if (hideUnderline && !label) { + style.marginBottom = 2; + } else if (label && !hint) { + style.marginBottom = 4; + } else if (label && hint) { + style.marginBottom = 10; + } + + if (floatCopy) { + style.position = 'absolute'; + style.left = -24; + style.bottom = style.marginBottom; + style.marginBottom = 0; + } + + return ( +
+ + + + + +
+ ); + } + + handleCopy = () => { + if (this.state.timeoutId) { + window.clearTimeout(this.state.timeoutId); + } + + this.setState({ copied: true }, () => { + const timeoutId = window.setTimeout(() => { + this.setState({ copied: false }); + }, 500); + + this.setState({ timeoutId }); + }); + } + onChange = (event, value) => { this.setValue(value); @@ -130,8 +263,6 @@ export default class Input extends Component { } setValue (value) { - this.setState({ - value - }); + this.setState({ value }); } } diff --git a/js/src/ui/Form/InputAddress/inputAddress.css b/js/src/ui/Form/InputAddress/inputAddress.css index 2f65476dc..2783130d6 100644 --- a/js/src/ui/Form/InputAddress/inputAddress.css +++ b/js/src/ui/Form/InputAddress/inputAddress.css @@ -29,4 +29,5 @@ .icon { position: absolute; top: 35px; + left: 24px; } diff --git a/js/src/ui/Form/InputAddress/inputAddress.js b/js/src/ui/Form/InputAddress/inputAddress.js index b7ad95216..f2c801edc 100644 --- a/js/src/ui/Form/InputAddress/inputAddress.js +++ b/js/src/ui/Form/InputAddress/inputAddress.js @@ -60,7 +60,9 @@ class InputAddress extends Component { error={ error } value={ text && hasAccount ? account.name : value } onChange={ this.handleInputChange } - onSubmit={ onSubmit } /> + onSubmit={ onSubmit } + allowCopy={ disabled ? value : false } + /> { icon }
); diff --git a/js/src/ui/MethodDecoding/methodDecoding.js b/js/src/ui/MethodDecoding/methodDecoding.js index fab293784..d35f12d5b 100644 --- a/js/src/ui/MethodDecoding/methodDecoding.js +++ b/js/src/ui/MethodDecoding/methodDecoding.js @@ -268,7 +268,8 @@ class MethodDecoding extends Component { default: return ( + ); + return ( @@ -54,7 +63,7 @@ export default class Summary extends Component { address={ address } /> { } } - byline={ address } /> + byline={ addressComponent } /> { children } diff --git a/js/src/views/Application/application.js b/js/src/views/Application/application.js index af213faf8..6b38f90d8 100644 --- a/js/src/views/Application/application.js +++ b/js/src/views/Application/application.js @@ -42,7 +42,8 @@ class Application extends Component { children: PropTypes.node, netChain: PropTypes.string, isTest: PropTypes.bool, - pending: PropTypes.array + pending: PropTypes.array, + blockNumber: PropTypes.object } state = { @@ -73,7 +74,7 @@ class Application extends Component { } renderApp () { - const { children, pending, netChain, isTest } = this.props; + const { children, pending, netChain, isTest, blockNumber } = this.props; const { showFirstRun } = this.state; return ( @@ -85,7 +86,7 @@ class Application extends Component { isTest={ isTest } pending={ pending } /> { children } - + { blockNumber ? () : null } ); } @@ -124,7 +125,7 @@ class Application extends Component { } function mapStateToProps (state) { - const { netChain, isTest } = state.nodeStatus; + const { netChain, isTest, blockNumber } = state.nodeStatus; const { hasAccounts } = state.personal; const { pending } = state.signer; @@ -132,7 +133,8 @@ function mapStateToProps (state) { hasAccounts, netChain, isTest, - pending + pending, + blockNumber }; } diff --git a/js/src/views/Contract/Events/Event/event.js b/js/src/views/Contract/Events/Event/event.js index bf9bf593c..ba24c1c8f 100644 --- a/js/src/views/Contract/Events/Event/event.js +++ b/js/src/views/Contract/Events/Event/event.js @@ -129,10 +129,12 @@ class Event extends Component { return ( + label={ name } + /> ); } } diff --git a/js/src/views/Contract/Queries/inputQuery.js b/js/src/views/Contract/Queries/inputQuery.js index 5f528a5e0..f21379a23 100644 --- a/js/src/views/Contract/Queries/inputQuery.js +++ b/js/src/views/Contract/Queries/inputQuery.js @@ -16,7 +16,6 @@ import BigNumber from 'bignumber.js'; import React, { Component, PropTypes } from 'react'; -import Chip from 'material-ui/Chip'; import LinearProgress from 'material-ui/LinearProgress'; import { Card, CardActions, CardTitle, CardText } from 'material-ui/Card'; @@ -104,13 +103,21 @@ export default class InputQuery extends Component { display: this.renderValue(results[index]) })) .sort((outA, outB) => outA.display.length - outB.display.length) - .map((out, index) => (
-
{ out.name }
- - { out.display } - -
-
)); + .map((out, index) => ( +
+
+ { out.name } +
+ + +
+
+ )); } renderInput (input) { diff --git a/js/src/views/Contract/Queries/queries.css b/js/src/views/Contract/Queries/queries.css index 0caa1006f..9a47511b4 100644 --- a/js/src/views/Contract/Queries/queries.css +++ b/js/src/views/Contract/Queries/queries.css @@ -63,25 +63,25 @@ } .methodResults > div { - margin: 0.5rem; + padding: 0.25rem 0.5rem; display: flex; flex-direction: column; align-items: center; - max-width: 100%; + flex: 1 1 50%; + box-sizing: border-box; + + & > div { + width: 100%; + } +} + +.queryValue { + width: 100%; } .queryValue, .queryValue * { - user-select: text !important; - max-width: 100%; - box-sizing: border-box; white-space: normal !important; overflow-wrap: break-word !important; -} - -.queryValue:hover { - cursor: text !important; -} - -.queryResultName { - margin-bottom: 0.25rem; + max-width: 100%; + box-sizing: border-box; } diff --git a/js/src/views/Contract/Queries/queries.js b/js/src/views/Contract/Queries/queries.js index a2dcba16e..b880140ef 100644 --- a/js/src/views/Contract/Queries/queries.js +++ b/js/src/views/Contract/Queries/queries.js @@ -16,11 +16,10 @@ import BigNumber from 'bignumber.js'; import React, { Component, PropTypes } from 'react'; -import Chip from 'material-ui/Chip'; import { Card, CardTitle, CardText } from 'material-ui/Card'; import InputQuery from './inputQuery'; -import { Container, ContainerTitle } from '../../../ui'; +import { Container, ContainerTitle, Input } from '../../../ui'; import styles from './queries.css'; @@ -126,9 +125,12 @@ export default class Queries extends Component { } return ( - - { valueToDisplay } - + ); } diff --git a/js/src/views/Settings/Background/background.js b/js/src/views/Settings/Background/background.js index 77c9b1810..e34c5d348 100644 --- a/js/src/views/Settings/Background/background.js +++ b/js/src/views/Settings/Background/background.js @@ -82,9 +82,9 @@ class Background extends Component { const { settings } = this.props; const { seeds } = this.state; - return seeds.map((seed) => { + return seeds.map((seed, index) => { return ( -
+
+ allowCopy + floatCopy + { ...this._test('author') } + /> + + allowCopy + floatCopy + { ...this._test('extra-data') } + /> + + allowCopy={ minGasPrice.toString() } + floatCopy + { ...this._test('min-gas-price') } + /> + + allowCopy={ gasFloorTarget.toString() } + floatCopy + { ...this._test('gas-floor-target') } + />
); } diff --git a/js/src/views/Status/components/Status/Status.js b/js/src/views/Status/components/Status/Status.js index 3304ee233..949efc85d 100644 --- a/js/src/views/Status/components/Status/Status.js +++ b/js/src/views/Status/components/Status/Status.js @@ -97,21 +97,21 @@ export default class Status extends Component {
@@ -119,21 +119,21 @@ export default class Status extends Component {