From 77a4d7e2b76a1808c37ed9f0a97cbd3b34a0ebe9 Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 22 Oct 2018 10:50:25 -0700 Subject: feat: have basic scaling amount input working --- packages/instant/src/components/amount_input.tsx | 51 ----------- .../instant/src/components/asset_amount_input.tsx | 41 +++++++-- .../instant/src/components/instant_heading.tsx | 2 +- .../src/components/scaling_amount_input.tsx | 52 +++++++++++ packages/instant/src/components/scaling_input.tsx | 101 +++++++++++++++------ .../src/containers/selected_asset_amount_input.ts | 2 +- 6 files changed, 164 insertions(+), 85 deletions(-) delete mode 100644 packages/instant/src/components/amount_input.tsx create mode 100644 packages/instant/src/components/scaling_amount_input.tsx (limited to 'packages/instant') diff --git a/packages/instant/src/components/amount_input.tsx b/packages/instant/src/components/amount_input.tsx deleted file mode 100644 index f6f562be8..000000000 --- a/packages/instant/src/components/amount_input.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { BigNumber } from '@0x/utils'; -import * as _ from 'lodash'; -import * as React from 'react'; - -import { ColorOption } from '../style/theme'; -import { util } from '../util/util'; - -import { ScalingInput } from './scaling_input'; -import { Container, Text } from './ui'; - -export interface AmountInputProps { - fontColor?: ColorOption; - fontSize?: string; - value?: BigNumber; - onChange: (value?: BigNumber) => void; -} - -export class AmountInput extends React.Component { - public static defaultProps = { - onChange: util.boundNoop, - }; - public render(): React.ReactNode { - const { fontColor, fontSize, value } = this.props; - return ( - - - - ); - } - private readonly _handleChange = (event: React.ChangeEvent): void => { - const value = event.target.value; - let bigNumberValue; - if (!_.isEmpty(value)) { - try { - bigNumberValue = new BigNumber(event.target.value); - } catch { - // We don't want to allow values that can't be a BigNumber, so don't even call onChange. - return; - } - } - this.props.onChange(bigNumberValue); - }; -} diff --git a/packages/instant/src/components/asset_amount_input.tsx b/packages/instant/src/components/asset_amount_input.tsx index c03ef1cf3..0b22c889c 100644 --- a/packages/instant/src/components/asset_amount_input.tsx +++ b/packages/instant/src/components/asset_amount_input.tsx @@ -7,33 +7,62 @@ import { ERC20Asset } from '../types'; import { assetUtils } from '../util/asset'; import { util } from '../util/util'; -import { AmountInput, AmountInputProps } from './amount_input'; +import { ScalingAmountInput } from './scaling_amount_input'; import { Container, Text } from './ui'; // Asset amounts only apply to ERC20 assets -export interface AssetAmountInputProps extends AmountInputProps { +export interface AssetAmountInputProps { asset?: ERC20Asset; onChange: (value?: BigNumber, asset?: ERC20Asset) => void; + startingFontSizePx: number; + fontColor?: ColorOption; } -export class AssetAmountInput extends React.Component { +export interface AssetAmountInputState { + currentFontSizePx: number; +} + +export class AssetAmountInput extends React.Component { public static defaultProps = { onChange: util.boundNoop, }; + constructor(props: AssetAmountInputProps) { + super(props); + this.state = { + currentFontSizePx: props.startingFontSizePx, + }; + } public render(): React.ReactNode { const { asset, onChange, ...rest } = this.props; return ( - + + + - + {assetUtils.bestNameForAsset(asset)} ); } - private readonly _handleChange = (value?: BigNumber): void => { + private readonly _handleChange = (value?: BigNumber, fontSize?: number): void => { this.props.onChange(value, this.props.asset); + if (!_.isUndefined(fontSize)) { + this.setState({ + currentFontSizePx: fontSize, + }); + } }; } diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx index a36d35a93..defe3b8b1 100644 --- a/packages/instant/src/components/instant_heading.tsx +++ b/packages/instant/src/components/instant_heading.tsx @@ -69,7 +69,7 @@ export const InstantHeading: React.StatelessComponent = pro - + diff --git a/packages/instant/src/components/scaling_amount_input.tsx b/packages/instant/src/components/scaling_amount_input.tsx new file mode 100644 index 000000000..a0b7b74af --- /dev/null +++ b/packages/instant/src/components/scaling_amount_input.tsx @@ -0,0 +1,52 @@ +import { BigNumber } from '@0x/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; +import { util } from '../util/util'; + +import { ScalingInput } from './scaling_input'; +import { Container, Text } from './ui'; + +export interface ScalingAmountInputProps { + fontSizePx: number; + startWidthCh: number; + endWidthCh: number; + fontColor?: ColorOption; + value?: BigNumber; + onChange: (value?: BigNumber, fontSize?: number) => void; +} + +export class ScalingAmountInput extends React.Component { + public static defaultProps = { + onChange: util.boundNoop, + onFontSizeChange: util.boundNoop, + }; + public render(): React.ReactNode { + const { startWidthCh, endWidthCh, fontColor, fontSizePx, value } = this.props; + return ( + + ); + } + private readonly _handleChange = (event: React.ChangeEvent, fontSize?: number): void => { + const value = event.target.value; + let bigNumberValue; + if (!_.isEmpty(value)) { + try { + bigNumberValue = new BigNumber(event.target.value); + } catch { + // We don't want to allow values that can't be a BigNumber, so don't even call onChange. + return; + } + } + this.props.onChange(bigNumberValue, fontSize); + }; +} diff --git a/packages/instant/src/components/scaling_input.tsx b/packages/instant/src/components/scaling_input.tsx index ea0a925d2..a2c4ba342 100644 --- a/packages/instant/src/components/scaling_input.tsx +++ b/packages/instant/src/components/scaling_input.tsx @@ -17,47 +17,79 @@ export enum ScalingInputPhase { export interface ScalingInputProps { startWidthCh: number; endWidthCh: number; - startFontSizePx: number; + fontSizePx: number; value?: string; - onChange?: (event: React.ChangeEvent) => void; + onChange: (event: React.ChangeEvent, fontSize: number) => void; fontColor?: ColorOption; placeholder?: string; } -// Magic value obtained via trial-and-error -const scalingRateToMaintainSameWidth = 0.1; + +export interface ScalingInputProps { + fixedWidthInPxIfExists?: number; +} + export class ScalingInput extends React.Component { public static defaultProps = { onChange: util.boundNoop, + onFontSizeChange: util.boundNoop, }; + public state = { + fixedWidthInPxIfExists: undefined, + }; + private _inputRef = React.createRef(); + public static getPhase(startWidthCh: number, endWidthCh: number, value?: string): ScalingInputPhase { + if (_.isUndefined(value) || value.length <= startWidthCh) { + return ScalingInputPhase.Start; + } + if (value.length > startWidthCh && value.length <= endWidthCh) { + return ScalingInputPhase.Scaling; + } + return ScalingInputPhase.End; + } + public static getPhaseFromProps(props: ScalingInputProps): ScalingInputPhase { + const { value, startWidthCh, endWidthCh } = props; + return ScalingInput.getPhase(startWidthCh, endWidthCh, value); + } + public componentDidUpdate(prevProps: ScalingInputProps): void { + const prevPhase = ScalingInput.getPhaseFromProps(prevProps); + const curPhase = ScalingInput.getPhaseFromProps(this.props); + // if we went from anything else to end, fix to the current width as it shouldn't change as we grow + if (prevPhase !== ScalingInputPhase.End && curPhase === ScalingInputPhase.End) { + this.setState({ + fixedWidthInPxIfExists: this._getInputWidthInPx(), + }); + } + // if we end from end to to anything else, un-fix the width + if (prevPhase === ScalingInputPhase.End && curPhase !== ScalingInputPhase.End) { + this.setState({ + fixedWidthInPxIfExists: undefined, + }); + } + } public render(): React.ReactNode { const { fontColor, onChange, placeholder, value } = this.props; - const phase = this._getPhase(); + const phase = ScalingInput.getPhaseFromProps(this.props); return ( ); } - private readonly _calculateFontSize = (phase: ScalingInputPhase): string => { - const { value, endWidthCh, startFontSizePx } = this.props; - if (_.isUndefined(value) || phase !== ScalingInputPhase.End) { - return `${startFontSizePx}px`; - } - const charactersOverMax = value.length - endWidthCh; - const pixelsToReduceFontSizeBy = charactersOverMax * 2; - const newFontSizePx = startFontSizePx - pixelsToReduceFontSizeBy; - return `${newFontSizePx}px`; - }; - private readonly _calculateWidth = (phase: ScalingInputPhase): string => { + private readonly _calculateWidth = (): string => { + const phase = ScalingInput.getPhaseFromProps(this.props); const { value, startWidthCh, endWidthCh } = this.props; if (_.isUndefined(value)) { return `${startWidthCh}ch`; } + if (!_.isUndefined(this.state.fixedWidthInPxIfExists)) { + return `${this.state.fixedWidthInPxIfExists}px`; + } switch (phase) { case ScalingInputPhase.Start: return `${startWidthCh}ch`; @@ -69,14 +101,31 @@ export class ScalingInput extends React.Component { return `${startWidthCh}ch`; } }; - private readonly _getPhase = (): ScalingInputPhase => { - const { value, startWidthCh, endWidthCh } = this.props; - if (_.isUndefined(value) || value.length <= this.props.startWidthCh) { - return ScalingInputPhase.Start; + private readonly _getInputWidthInPx = (): number => { + const ref = this._inputRef.current; + if (!ref) { + return 0; } - if (value.length > startWidthCh && value.length <= endWidthCh) { - return ScalingInputPhase.Scaling; + return (ref as any).getBoundingClientRect().width; + }; + private readonly _calculateNextFontSize = ( + currentFontSizePx: number, + value: string, + startWidthCh: number, + endWidthCh: number, + ): number => { + const phase = ScalingInput.getPhase(startWidthCh, endWidthCh, value); + if (_.isUndefined(value) || phase !== ScalingInputPhase.End) { + return currentFontSizePx; } - return ScalingInputPhase.End; + const charactersOverMax = value.length - endWidthCh; + const pixelsToReduceFontSizeBy = charactersOverMax * 5; + const fontSize = currentFontSizePx - pixelsToReduceFontSizeBy; + return fontSize; + }; + private readonly _handleChange = (event: React.ChangeEvent) => { + const value = event.target.value; + const { fontSizePx, startWidthCh, endWidthCh } = this.props; + this.props.onChange(event, this._calculateNextFontSize(fontSizePx, value, startWidthCh, endWidthCh)); }; } diff --git a/packages/instant/src/containers/selected_asset_amount_input.ts b/packages/instant/src/containers/selected_asset_amount_input.ts index 6cd39b855..1b5f3cd22 100644 --- a/packages/instant/src/containers/selected_asset_amount_input.ts +++ b/packages/instant/src/containers/selected_asset_amount_input.ts @@ -17,7 +17,7 @@ import { AssetAmountInput } from '../components/asset_amount_input'; export interface SelectedAssetAmountInputProps { fontColor?: ColorOption; - fontSize?: string; + startingFontSizePx: number; } interface ConnectedState { -- cgit v1.2.3