import { colors } from '@0xproject/react-shared'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import TextField from 'material-ui/TextField'; import * as React from 'react'; import { Link } from 'react-router-dom'; import { RequiredLabel } from 'ts/components/ui/required_label'; import { InputErrMsg, ValidatedBigNumberCallback, WebsitePaths } from 'ts/types'; import { utils } from 'ts/utils/utils'; interface BalanceBoundedInputProps { label?: string; balance: BigNumber; amount?: BigNumber; onChange: ValidatedBigNumberCallback; shouldShowIncompleteErrs?: boolean; shouldCheckBalance: boolean; validate?: (amount: BigNumber) => InputErrMsg; onVisitBalancesPageClick?: () => void; shouldHideVisitBalancesLink?: boolean; isDisabled?: boolean; } interface BalanceBoundedInputState { errMsg: InputErrMsg; amountString: string; } export class BalanceBoundedInput extends React.Component { public static defaultProps: Partial = { shouldShowIncompleteErrs: false, shouldHideVisitBalancesLink: false, isDisabled: false, }; constructor(props: BalanceBoundedInputProps) { super(props); const amountString = this.props.amount ? this.props.amount.toString() : ''; this.state = { errMsg: this._validate(amountString, props.balance), amountString, }; } public componentWillReceiveProps(nextProps: BalanceBoundedInputProps) { if (nextProps === this.props) { return; } const isCurrentAmountNumeric = utils.isNumeric(this.state.amountString); if (!_.isUndefined(nextProps.amount)) { let shouldResetState = false; if (!isCurrentAmountNumeric) { shouldResetState = true; } else { const currentAmount = new BigNumber(this.state.amountString); if (!currentAmount.eq(nextProps.amount) || !nextProps.balance.eq(this.props.balance)) { shouldResetState = true; } } if (shouldResetState) { const amountString = nextProps.amount.toString(); this.setState({ errMsg: this._validate(amountString, nextProps.balance), amountString, }); } } else if (isCurrentAmountNumeric) { const amountString = ''; this.setState({ errMsg: this._validate(amountString, nextProps.balance), amountString, }); } } public render() { let errorText = this.state.errMsg; if (this.props.shouldShowIncompleteErrs && this.state.amountString === '') { errorText = 'This field is required'; } let label: React.ReactNode | string = ''; if (!_.isUndefined(this.props.label)) { label = ; } return ( amount} onChange={this._onValueChange.bind(this)} underlineStyle={{ width: 'calc(100% + 50px)' }} disabled={this.props.isDisabled} /> ); } private _onValueChange(e: any, amountString: string) { const errMsg = this._validate(amountString, this.props.balance); this.setState( { amountString, errMsg, }, () => { const isValid = _.isUndefined(errMsg); if (utils.isNumeric(amountString) && !_.includes(amountString, '-')) { this.props.onChange(isValid, new BigNumber(amountString)); } else { this.props.onChange(isValid); } }, ); } private _validate(amountString: string, balance: BigNumber): InputErrMsg { if (!utils.isNumeric(amountString)) { return amountString !== '' ? 'Must be a number' : ''; } const amount = new BigNumber(amountString); if (amount.eq(0)) { return 'Cannot be zero'; } if (this.props.shouldCheckBalance && amount.gt(balance)) { return Insufficient balance. {this._renderIncreaseBalanceLink()}; } const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount); return errMsg; } private _renderIncreaseBalanceLink() { if (this.props.shouldHideVisitBalancesLink) { return null; } const increaseBalanceText = 'Increase balance'; const linkStyle = { cursor: 'pointer', color: colors.darkestGrey, textDecoration: 'underline', display: 'inline', }; if (_.isUndefined(this.props.onVisitBalancesPageClick)) { return ( {increaseBalanceText} ); } else { return (
{increaseBalanceText}
); } } }