simplify scaling input logic

This commit is contained in:
fragosti 2018-12-13 12:45:38 -08:00
parent 6e54514013
commit 33c6e40b70
3 changed files with 18 additions and 50 deletions

View File

@ -58,6 +58,7 @@ export class ScalingAmountInput extends React.Component<ScalingAmountInputProps,
const { textLengthThreshold, fontColor, maxFontSizePx, onFontSizeChange } = this.props; const { textLengthThreshold, fontColor, maxFontSizePx, onFontSizeChange } = this.props;
return ( return (
<ScalingInput <ScalingInput
type="number"
maxFontSizePx={maxFontSizePx} maxFontSizePx={maxFontSizePx}
textLengthThreshold={textLengthThreshold} textLengthThreshold={textLengthThreshold}
onFontSizeChange={onFontSizeChange} onFontSizeChange={onFontSizeChange}

View File

@ -13,10 +13,10 @@ export enum ScalingInputPhase {
export interface ScalingSettings { export interface ScalingSettings {
percentageToReduceFontSizePerCharacter: number; percentageToReduceFontSizePerCharacter: number;
constantPxToIncreaseWidthPerCharacter: number;
} }
export interface ScalingInputProps { export interface ScalingInputProps {
type?: string;
textLengthThreshold: number; textLengthThreshold: number;
maxFontSizePx: number; maxFontSizePx: number;
value: string; value: string;
@ -31,21 +31,16 @@ export interface ScalingInputProps {
hasAutofocus: boolean; hasAutofocus: boolean;
} }
export interface ScalingInputState {
inputWidthPxAtPhaseChange?: number;
}
export interface ScalingInputSnapshot { export interface ScalingInputSnapshot {
inputWidthPx: number; inputWidthPx: number;
} }
// These are magic numbers that were determined experimentally. // These are magic numbers that were determined experimentally.
const defaultScalingSettings: ScalingSettings = { const defaultScalingSettings: ScalingSettings = {
percentageToReduceFontSizePerCharacter: 0.125, percentageToReduceFontSizePerCharacter: 0.1,
constantPxToIncreaseWidthPerCharacter: 4,
}; };
export class ScalingInput extends React.Component<ScalingInputProps, ScalingInputState> { export class ScalingInput extends React.Component<ScalingInputProps> {
public static defaultProps = { public static defaultProps = {
onChange: util.boundNoop, onChange: util.boundNoop,
onFontSizeChange: util.boundNoop, onFontSizeChange: util.boundNoop,
@ -54,9 +49,6 @@ export class ScalingInput extends React.Component<ScalingInputProps, ScalingInpu
isDisabled: false, isDisabled: false,
hasAutofocus: false, hasAutofocus: false,
}; };
public state: ScalingInputState = {
inputWidthPxAtPhaseChange: undefined,
};
private readonly _inputRef = React.createRef<HTMLInputElement>(); private readonly _inputRef = React.createRef<HTMLInputElement>();
public static getPhase(textLengthThreshold: number, value: string): ScalingInputPhase { public static getPhase(textLengthThreshold: number, value: string): ScalingInputPhase {
if (value.length <= textLengthThreshold) { if (value.length <= textLengthThreshold) {
@ -93,36 +85,15 @@ export class ScalingInput extends React.Component<ScalingInputProps, ScalingInpu
scalingSettings.percentageToReduceFontSizePerCharacter, scalingSettings.percentageToReduceFontSizePerCharacter,
); );
} }
public getSnapshotBeforeUpdate(): ScalingInputSnapshot {
return {
inputWidthPx: this._getInputWidthInPx(),
};
}
public componentDidMount(): void { public componentDidMount(): void {
// Trigger an initial notification of the calculated fontSize. // Trigger an initial notification of the calculated fontSize.
const currentPhase = ScalingInput.getPhaseFromProps(this.props); const currentPhase = ScalingInput.getPhaseFromProps(this.props);
const currentFontSize = ScalingInput.calculateFontSizeFromProps(this.props, currentPhase); const currentFontSize = ScalingInput.calculateFontSizeFromProps(this.props, currentPhase);
this.props.onFontSizeChange(currentFontSize); this.props.onFontSizeChange(currentFontSize);
} }
public componentDidUpdate( public componentDidUpdate(prevProps: ScalingInputProps): void {
prevProps: ScalingInputProps,
prevState: ScalingInputState,
snapshot: ScalingInputSnapshot,
): void {
const prevPhase = ScalingInput.getPhaseFromProps(prevProps); const prevPhase = ScalingInput.getPhaseFromProps(prevProps);
const curPhase = ScalingInput.getPhaseFromProps(this.props); const curPhase = ScalingInput.getPhaseFromProps(this.props);
// if we went from fixed to scaling, save the width from the transition
if (prevPhase !== ScalingInputPhase.ScalingFontSize && curPhase === ScalingInputPhase.ScalingFontSize) {
this.setState({
inputWidthPxAtPhaseChange: snapshot.inputWidthPx,
});
}
// if we went from scaling to fixed, revert back to scaling using `ch`
if (prevPhase === ScalingInputPhase.ScalingFontSize && curPhase !== ScalingInputPhase.ScalingFontSize) {
this.setState({
inputWidthPxAtPhaseChange: undefined,
});
}
const prevFontSize = ScalingInput.calculateFontSizeFromProps(prevProps, prevPhase); const prevFontSize = ScalingInput.calculateFontSizeFromProps(prevProps, prevPhase);
const curFontSize = ScalingInput.calculateFontSizeFromProps(this.props, curPhase); const curFontSize = ScalingInput.calculateFontSizeFromProps(this.props, curPhase);
// If font size has changed, notify. // If font size has changed, notify.
@ -131,14 +102,14 @@ export class ScalingInput extends React.Component<ScalingInputProps, ScalingInpu
} }
} }
public render(): React.ReactNode { public render(): React.ReactNode {
const { hasAutofocus, isDisabled, fontColor, onChange, placeholder, value, maxLength } = this.props; const { type, hasAutofocus, isDisabled, fontColor, onChange, placeholder, value, maxLength } = this.props;
const phase = ScalingInput.getPhaseFromProps(this.props); const phase = ScalingInput.getPhaseFromProps(this.props);
return ( return (
<Input <Input
type="number" type={type}
ref={this._inputRef as any} ref={this._inputRef as any}
fontColor={fontColor} fontColor={fontColor}
onChange={onChange} onChange={this._handleChange}
value={value} value={value}
placeholder={placeholder} placeholder={placeholder}
fontSize={`${this._calculateFontSize(phase)}px`} fontSize={`${this._calculateFontSize(phase)}px`}
@ -154,19 +125,7 @@ export class ScalingInput extends React.Component<ScalingInputProps, ScalingInpu
if (_.isEmpty(value)) { if (_.isEmpty(value)) {
return `${this.props.emptyInputWidthCh}ch`; return `${this.props.emptyInputWidthCh}ch`;
} }
switch (phase) { return `${value.length}ch`;
case ScalingInputPhase.FixedFontSize:
return `${value.length}ch`;
case ScalingInputPhase.ScalingFontSize:
const { inputWidthPxAtPhaseChange } = this.state;
if (!_.isUndefined(inputWidthPxAtPhaseChange)) {
const charactersOverMax = value.length - textLengthThreshold;
const scalingAmount = scalingSettings.constantPxToIncreaseWidthPerCharacter * charactersOverMax;
const width = inputWidthPxAtPhaseChange + scalingAmount;
return `${width}px`;
}
return `${textLengthThreshold}ch`;
}
}; };
private readonly _calculateFontSize = (phase: ScalingInputPhase): number => { private readonly _calculateFontSize = (phase: ScalingInputPhase): number => {
return ScalingInput.calculateFontSizeFromProps(this.props, phase); return ScalingInput.calculateFontSizeFromProps(this.props, phase);
@ -178,4 +137,12 @@ export class ScalingInput extends React.Component<ScalingInputProps, ScalingInpu
} }
return ref.getBoundingClientRect().width; return ref.getBoundingClientRect().width;
}; };
private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
const value = event.target.value;
const { maxLength } = this.props;
if (!_.isUndefined(value) && !_.isUndefined(maxLength) && value.length > maxLength) {
return;
}
this.props.onChange(event);
};
} }

View File

@ -2,7 +2,7 @@ import * as React from 'react';
import { ColorOption, styled } from '../../style/theme'; import { ColorOption, styled } from '../../style/theme';
export interface InputProps { export interface InputProps extends React.HTMLAttributes<HTMLInputElement> {
tabIndex?: number; tabIndex?: number;
className?: string; className?: string;
value?: string; value?: string;