add sendmax and button link for the amount field in sending funds form

This commit is contained in:
mmv 2019-05-21 16:16:12 +04:00
parent 7bac5ae822
commit 2010a6b7eb
9 changed files with 197 additions and 46 deletions

View File

@ -81,6 +81,7 @@
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-final-form": "5.1.0", "react-final-form": "5.1.0",
"react-final-form-listeners": "^1.0.2",
"react-hot-loader": "4.8.7", "react-hot-loader": "4.8.7",
"react-infinite-scroll-component": "^4.5.2", "react-infinite-scroll-component": "^4.5.2",
"react-redux": "7.0.3", "react-redux": "7.0.3",

View File

@ -15,6 +15,7 @@ type Props = {
padding?: number, padding?: number,
validation?: (values: Object) => Object | Promise<Object>, validation?: (values: Object) => Object | Promise<Object>,
initialValues?: Object, initialValues?: Object,
formMutators: Object,
} }
const stylesBasedOn = (padding: number): $Shape<CSSStyleDeclaration> => ({ const stylesBasedOn = (padding: number): $Shape<CSSStyleDeclaration> => ({
@ -24,15 +25,16 @@ const stylesBasedOn = (padding: number): $Shape<CSSStyleDeclaration> => ({
}) })
const GnoForm = ({ const GnoForm = ({
onSubmit, validation, initialValues, children, padding = 0, onSubmit, validation, initialValues, children, padding = 0, formMutators,
}: Props) => ( }: Props) => (
<Form <Form
validate={validation} validate={validation}
onSubmit={onSubmit} onSubmit={onSubmit}
initialValues={initialValues} initialValues={initialValues}
mutators={formMutators}
render={({ handleSubmit, ...rest }) => ( render={({ handleSubmit, ...rest }) => (
<form onSubmit={handleSubmit} style={stylesBasedOn(padding)}> <form onSubmit={handleSubmit} style={stylesBasedOn(padding)}>
{children(rest.submitting, rest.validating, rest)} {children(rest.submitting, rest.validating, rest, rest.form.mutators)}
</form> </form>
)} )}
/> />

View File

@ -0,0 +1,29 @@
// @flow
/* eslint-disable react/button-has-type */
/* eslint-disable react/default-props-match-prop-types */
import * as React from 'react'
import cn from 'classnames/bind'
import styles from './index.scss'
const cx = cn.bind(styles)
type Props = {
type: 'button' | 'submit' | 'reset',
size?: 'sm' | 'md' | 'lg' | 'xl' | 'xxl',
weight?: 'light' | 'regular' | 'bolder' | 'bold',
color?: 'soft' | 'medium' | 'dark' | 'white' | 'fancy' | 'primary' | 'secondary' | 'warning' | 'disabled',
}
const GnoButtonLink = ({ type, size, weight, color, ...props }: Props) => (
<button type={type} className={cx(styles.btnLink, size, color, weight)} {...props} />
)
GnoButtonLink.defaultProps = {
type: 'button',
size: 'md',
weight: 'regular',
color: 'secondary',
}
export default GnoButtonLink

View File

@ -0,0 +1,79 @@
.btnLink {
background: transparent;
border: none;
text-decoration: underline;
font-family: 'Roboto Mono', monospace;
cursor: pointer;
}
.sm {
font-size: $smallFontSize;
}
.md {
font-size: $mediumFontSize;
}
.lg {
font-size: $largeFontSize;
}
.xl {
font-size: $extraLargeFontSize;
}
.xxl {
font-size: $xxlFontSize;
}
.light {
font-weight: $lightFont;
}
.regular {
font-weight: $regularFont;
}
.bolder {
font-weight: $bolderFont;
}
.bold {
font-weight: $boldFont;
}
.soft {
color: #888888;
}
.medium {
color: #686868;
}
.dark {
color: black;
}
.fancy {
color: $fancy;
}
.warning {
color: $warning;
}
.primary {
color: $fontColor;
}
.secondary {
color: $secondary;
}
.disabled {
color: $disabled;
}
.white {
color: white;
}

View File

@ -9,7 +9,7 @@ const cx = classNames.bind(styles)
type Props = { type Props = {
className?: string, className?: string,
children: React$Node, children: React$Node,
margin?: 'sm' | 'md' | 'lg' | 'xl', margin?: 'xs' | 'sm' | 'md' | 'lg' | 'xl',
align?: 'center' | 'end' | 'start', align?: 'center' | 'end' | 'start',
grow?: boolean, grow?: boolean,
} }

View File

@ -7,6 +7,11 @@
.grow { .grow {
flex: 1 1 auto; flex: 1 1 auto;
} }
.marginXs {
margin-bottom: $xs;
}
.marginSm { .marginSm {
margin-bottom: $sm; margin-bottom: $sm;
} }

View File

@ -1,6 +1,7 @@
// @flow // @flow
import React from 'react' import React from 'react'
import { List } from 'immutable' import { List } from 'immutable'
import { OnChange } from 'react-final-form-listeners'
import { withStyles } from '@material-ui/core/styles' import { withStyles } from '@material-ui/core/styles'
import MenuItem from '@material-ui/core/MenuItem' import MenuItem from '@material-ui/core/MenuItem'
import ListItemIcon from '@material-ui/core/ListItemIcon' import ListItemIcon from '@material-ui/core/ListItemIcon'
@ -17,6 +18,7 @@ import { selectedTokenStyles, selectStyles } from './style'
type SelectFieldProps = { type SelectFieldProps = {
tokens: List<Token>, tokens: List<Token>,
classes: Object, classes: Object,
onTokenChange: Function,
} }
type SelectedTokenProps = { type SelectedTokenProps = {
@ -47,7 +49,7 @@ const SelectedToken = ({ token, classes }: SelectedTokenProps) => (
const SelectedTokenStyled = withStyles(selectedTokenStyles)(SelectedToken) const SelectedTokenStyled = withStyles(selectedTokenStyles)(SelectedToken)
const TokenSelectField = ({ tokens, classes }: SelectFieldProps) => ( const TokenSelectField = ({ tokens, onTokenChange, classes }: SelectFieldProps) => (
<Field <Field
name="token" name="token"
component={SelectField} component={SelectField}
@ -65,6 +67,11 @@ const TokenSelectField = ({ tokens, classes }: SelectFieldProps) => (
<ListItemText primary={token.name} secondary={`${token.balance} ${token.symbol}`} /> <ListItemText primary={token.name} secondary={`${token.balance} ${token.symbol}`} />
</MenuItem> </MenuItem>
))} ))}
<OnChange name="token">
{() => {
onTokenChange()
}}
</OnChange>
</Field> </Field>
) )

View File

@ -11,11 +11,12 @@ import Row from '~/components/layout/Row'
import GnoForm from '~/components/forms/GnoForm' import GnoForm from '~/components/forms/GnoForm'
import Link from '~/components/layout/Link' import Link from '~/components/layout/Link'
import Col from '~/components/layout/Col' import Col from '~/components/layout/Col'
import Field from '~/components/forms/Field'
import TextField from '~/components/forms/TextField'
import Block from '~/components/layout/Block' import Block from '~/components/layout/Block'
import Bold from '~/components/layout/Bold' import Bold from '~/components/layout/Bold'
import Hairline from '~/components/layout/Hairline' import Hairline from '~/components/layout/Hairline'
import ButtonLink from '~/components/layout/ButtonLink'
import Field from '~/components/forms/Field'
import TextField from '~/components/forms/TextField'
import { import {
lg, md, sm, secondary, xs, lg, md, sm, secondary, xs,
} from '~/theme/variables' } from '~/theme/variables'
@ -73,6 +74,16 @@ const SendFunds = ({
classes, onClose, safeAddress, etherScanLink, safeName, ethBalance, tokens, classes, onClose, safeAddress, etherScanLink, safeName, ethBalance, tokens,
}: Props) => { }: Props) => {
const handleSubmit = () => {} const handleSubmit = () => {}
const formMutators = {
setMax: (args, state, utils) => {
const { token } = state.formState.values
utils.changeValue(state, 'amount', () => token && token.balance)
},
onTokenChange: (args, state, utils) => {
utils.changeValue(state, 'amount', () => '')
},
}
return ( return (
<React.Fragment> <React.Fragment>
@ -121,45 +132,55 @@ ETH
<Hairline /> <Hairline />
</Col> </Col>
</Row> </Row>
<GnoForm onSubmit={handleSubmit}> <GnoForm onSubmit={handleSubmit} formMutators={formMutators}>
{() => ( {(...args) => {
<React.Fragment> const mutators = args[3]
<Row margin="md"> return (
<Col xs={12}> <React.Fragment>
<Field <Row margin="md">
name="recipientAddress" <Col xs={12}>
component={TextField} <Field
type="text" name="recipientAddress"
validate={composeValidators(required, mustBeEthereumAddress)} component={TextField}
placeholder="Recipient*" type="text"
text="Recipient*" validate={composeValidators(required, mustBeEthereumAddress)}
className={classes.addressInput} placeholder="Recipient*"
/> text="Recipient*"
</Col> className={classes.addressInput}
</Row> />
<Row> </Col>
<Col> </Row>
<TokenSelectField tokens={tokens} /> <Row margin="sm">
</Col> <Col>
</Row> <TokenSelectField tokens={tokens} onTokenChange={mutators.onTokenChange} />
<Row> </Col>
<Col layout="column"> </Row>
<Paragraph size="md" color="disabled" style={{ letterSpacing: '-0.5px' }}> <Row margin="xs">
Amount <Col between="lg">
</Paragraph> <Paragraph size="md" color="disabled" style={{ letterSpacing: '-0.5px' }} noMargin>
<Field Amount
name="amount" </Paragraph>
component={TextField} <ButtonLink weight="bold" onClick={mutators.setMax}>
type="text" Send max
validate={composeValidators(required)} </ButtonLink>
placeholder="Amount*" </Col>
text="Amount*" </Row>
className={classes.addressInput} <Row>
/> <Col>
</Col> <Field
</Row> name="amount"
</React.Fragment> component={TextField}
)} type="text"
validate={composeValidators(required)}
placeholder="Amount*"
text="Amount*"
className={classes.addressInput}
/>
</Col>
</Row>
</React.Fragment>
)
}}
</GnoForm> </GnoForm>
</Block> </Block>
</React.Fragment> </React.Fragment>

View File

@ -1416,7 +1416,7 @@
dependencies: dependencies:
regenerator-runtime "^0.13.2" regenerator-runtime "^0.13.2"
"@babel/runtime@^7.4.4": "@babel/runtime@^7.1.5", "@babel/runtime@^7.4.4":
version "7.4.4" version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d"
integrity sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg== integrity sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==
@ -13669,6 +13669,13 @@ react-fast-compare@^2.0.2:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-final-form-listeners@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/react-final-form-listeners/-/react-final-form-listeners-1.0.2.tgz#b52da984300281cf1f69a6412e86df6249e2bf1c"
integrity sha512-AaUUHcXRhD3esC80yUfYPI8FJ3TUiMu0f4hk18QL8NMCWjokg6NWS32WkRJsH3bLWDoiy7+uNVOAAyO/XoupyA==
dependencies:
"@babel/runtime" "^7.1.5"
react-final-form@5.1.0: react-final-form@5.1.0:
version "5.1.0" version "5.1.0"
resolved "https://registry.yarnpkg.com/react-final-form/-/react-final-form-5.1.0.tgz#f9402dfdf9325e5605cd8c9c7f4ef3e9ecf0702c" resolved "https://registry.yarnpkg.com/react-final-form/-/react-final-form-5.1.0.tgz#f9402dfdf9325e5605cd8c9c7f4ef3e9ecf0702c"