diff options
-rw-r--r-- | packages/instant/src/components/asset_amount_input.tsx | 41 | ||||
-rw-r--r-- | packages/instant/src/components/instant_heading.tsx | 2 | ||||
-rw-r--r-- | packages/instant/src/components/scaling_amount_input.tsx (renamed from packages/instant/src/components/amount_input.tsx) | 37 | ||||
-rw-r--r-- | packages/instant/src/components/scaling_input.tsx | 101 | ||||
-rw-r--r-- | packages/instant/src/containers/selected_asset_amount_input.ts | 2 |
5 files changed, 131 insertions, 52 deletions
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<AssetAmountInputProps> { +export interface AssetAmountInputState { + currentFontSizePx: number; +} + +export class AssetAmountInput extends React.Component<AssetAmountInputProps, AssetAmountInputState> { 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 ( <Container> - <AmountInput {...rest} onChange={this._handleChange} /> + <Container borderBottom="1px solid rgba(255,255,255,0.3)" display="inline-block"> + <ScalingAmountInput + {...rest} + startWidthCh={3.5} + endWidthCh={5} + fontSizePx={this.state.currentFontSizePx} + onChange={this._handleChange} + /> + </Container> <Container display="inline-block" marginLeft="10px"> - <Text fontSize={rest.fontSize} fontColor={ColorOption.white} textTransform="uppercase"> + <Text + fontSize={`${this.state.currentFontSizePx}px`} + fontColor={ColorOption.white} + textTransform="uppercase" + > {assetUtils.bestNameForAsset(asset)} </Text> </Container> </Container> ); } - 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<InstantHeadingProps> = pro </Text> </Container> <Flex direction="row" justify="space-between"> - <SelectedAssetAmountInput fontSize="45px" /> + <SelectedAssetAmountInput startingFontSizePx={45} /> <Flex direction="column" justify="space-between"> <Container marginBottom="5px"> <Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}> diff --git a/packages/instant/src/components/amount_input.tsx b/packages/instant/src/components/scaling_amount_input.tsx index f6f562be8..a0b7b74af 100644 --- a/packages/instant/src/components/amount_input.tsx +++ b/packages/instant/src/components/scaling_amount_input.tsx @@ -8,34 +8,35 @@ import { util } from '../util/util'; import { ScalingInput } from './scaling_input'; import { Container, Text } from './ui'; -export interface AmountInputProps { +export interface ScalingAmountInputProps { + fontSizePx: number; + startWidthCh: number; + endWidthCh: number; fontColor?: ColorOption; - fontSize?: string; value?: BigNumber; - onChange: (value?: BigNumber) => void; + onChange: (value?: BigNumber, fontSize?: number) => void; } -export class AmountInput extends React.Component<AmountInputProps> { +export class ScalingAmountInput extends React.Component<ScalingAmountInputProps> { public static defaultProps = { onChange: util.boundNoop, + onFontSizeChange: util.boundNoop, }; public render(): React.ReactNode { - const { fontColor, fontSize, value } = this.props; + const { startWidthCh, endWidthCh, fontColor, fontSizePx, value } = this.props; return ( - <Container borderBottom="1px solid rgba(255,255,255,0.3)" display="inline-block"> - <ScalingInput - startWidthCh={3.5} - endWidthCh={6} - startFontSizePx={45} - fontColor={fontColor} - onChange={this._handleChange} - value={!_.isUndefined(value) ? value.toString() : ''} - placeholder="0.00" - /> - </Container> + <ScalingInput + startWidthCh={startWidthCh} + endWidthCh={endWidthCh} + fontSizePx={fontSizePx} + fontColor={fontColor} + onChange={this._handleChange} + value={!_.isUndefined(value) ? value.toString() : ''} + placeholder="0.00" + /> ); } - private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => { + private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>, fontSize?: number): void => { const value = event.target.value; let bigNumberValue; if (!_.isEmpty(value)) { @@ -46,6 +47,6 @@ export class AmountInput extends React.Component<AmountInputProps> { return; } } - this.props.onChange(bigNumberValue); + 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<HTMLInputElement>) => void; + onChange: (event: React.ChangeEvent<HTMLInputElement>, 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<ScalingInputProps> { 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 ( <Input + ref={this._inputRef as any} fontColor={fontColor} - onChange={onChange} + onChange={this._handleChange} value={value} placeholder={placeholder} - fontSize={this._calculateFontSize(phase)} - width={this._calculateWidth(phase)} + fontSize={`${this.props.fontSizePx}px`} + width={this._calculateWidth()} /> ); } - 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<ScalingInputProps> { 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<HTMLInputElement>) => { + 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 { |