feat: have basic scaling amount input working
This commit is contained in:
@@ -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,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
@@ -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));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { AssetAmountInput } from '../components/asset_amount_input';
|
||||
|
||||
export interface SelectedAssetAmountInputProps {
|
||||
fontColor?: ColorOption;
|
||||
fontSize?: string;
|
||||
startingFontSizePx: number;
|
||||
}
|
||||
|
||||
interface ConnectedState {
|
||||
|
||||
Reference in New Issue
Block a user