feat: have basic scaling amount input working

This commit is contained in:
fragosti
2018-10-22 10:50:25 -07:00
parent 6510454337
commit 77a4d7e2b7
5 changed files with 131 additions and 52 deletions

View File

@@ -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,
});
}
};
}

View File

@@ -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}>

View File

@@ -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);
};
}

View File

@@ -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));
};
}

View File

@@ -17,7 +17,7 @@ import { AssetAmountInput } from '../components/asset_amount_input';
export interface SelectedAssetAmountInputProps {
fontColor?: ColorOption;
fontSize?: string;
startingFontSizePx: number;
}
interface ConnectedState {