Skip to content
This repository was archived by the owner on Feb 23, 2021. It is now read-only.

Resize balance label font size based on content width in Payment Request View #643

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions src/component/field.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
import React from 'react';
import { Text as RNText, StyleSheet, View } from 'react-native';
import { Text as RNText, StyleSheet, View, Platform } from 'react-native';
import PropTypes from 'prop-types';
import Text from './text';
import { TextInput, HorizontalExpandingTextInput } from './input';
import { color, font } from './style';
import { TextInput, AdjustingTextInput } from './input';
import { color, font, fontFamily } from './style';

//
// Amount Input Field
//

const amountFont = fontFamily.WorkSansExtraLight;

const amountStyles = StyleSheet.create({
input: {
textAlign: 'right',
fontFamily: 'WorkSans ExtraLight',
fontSize: font.sizeXXXL,
fontFamily: amountFont.name,
height: 105,
},
});

export const AmountInputField = ({ style, ...props }) => (
<HorizontalExpandingTextInput
style={[amountStyles.input, style]}
charWidth={46}
keyboardType="numeric"
placeholder="0"
placeholderTextColor={color.blackText}
{...props}
/>
);
export const AmountInputField = ({ style, ...props }) => {
const nonWebProps = Platform.OS === 'web' ? {} : { keyboardType: 'numeric' };
return (
<AdjustingTextInput
style={[amountStyles.input, style]}
fontWidthHeightRatio={amountFont.widthHeightRatio}
defaultFontSize={font.sizeXXXL}
{...nonWebProps}
{...props}
/>
);
};

AmountInputField.propTypes = {
style: RNText.propTypes.style,
Expand Down
147 changes: 127 additions & 20 deletions src/component/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,43 +36,150 @@ TextInput.propTypes = {
};

//
// Horizontal Expanding Text Input
// Adjusting Text Input
//

export class HorizontalExpandingTextInput extends Component {
const getCharWidthHeightRatio = (fontWidthHeightRatio, char) =>
fontWidthHeightRatio[char] || fontWidthHeightRatio['default'];

const calculateWidthAndFontSize = (
text,
maxWidth,
defaultFontSize,
fontWidthHeightRatio,
placeholderFirstChar
) => {
const predictedTextLength = text.length || 1;
const predictedTextCharArray = text.split('') || [placeholderFirstChar];
const predictedTextWidth = predictedTextCharArray.reduce(
(accumulator, currentValue) =>
getCharWidthHeightRatio(fontWidthHeightRatio, currentValue) *
defaultFontSize +
accumulator,
0
);
const calculatedWidth = Math.min(
maxWidth,
Math.max(
predictedTextWidth,
getCharWidthHeightRatio(fontWidthHeightRatio, placeholderFirstChar) *
defaultFontSize
)
);

const averageFontWidthHeightRatio =
predictedTextCharArray.reduce(
(accumulator, currentValue) =>
getCharWidthHeightRatio(fontWidthHeightRatio, currentValue) +
accumulator,
0
) / predictedTextLength;
const calculatedFontSize =
calculatedWidth >= maxWidth
? Math.min(
defaultFontSize,
calculatedWidth / predictedTextLength / averageFontWidthHeightRatio
)
: defaultFontSize;

return { calculatedWidth, calculatedFontSize };
};

export class AdjustingTextInput extends Component {
constructor(props) {
super(props);
this.state = { text: '', width: 0 };
const { defaultFontSize, fontWidthHeightRatio, placeholder } = props;
const placeholderFirstChar = placeholder.length
? placeholder.charAt(0)
: '0';

this.state = {
text: '',
width: defaultFontSize * fontWidthHeightRatio[placeholderFirstChar],
fontSize: defaultFontSize,
placeholderFirstChar,
};

this.onChangeTextHandler = this.onChangeTextHandler.bind(this);
}

onChangeTextHandler(
changedText,
maxWidth,
defaultFontSize,
fontWidthHeightRatio,
onChangeText,
placeholderFirstChar
) {
const { calculatedWidth, calculatedFontSize } = calculateWidthAndFontSize(
changedText,
maxWidth,
defaultFontSize,
fontWidthHeightRatio,
placeholderFirstChar
);

if (changedText.match(/^(?:[1-9]\d*|0)?(?:\.\d*)?$/)) {
this.setState({
text: changedText,
width: calculatedWidth,
fontSize: calculatedFontSize,
});
onChangeText && onChangeText(changedText);
}
}

render() {
const { value, charWidth, onChangeText, style, ...props } = this.props;
const {
value,
style,
fontWidthHeightRatio,
defaultFontSize,
maxWidth,
onChangeText,
...props
} = this.props;
const { text, width, fontSize, placeholderFirstChar } = this.state;
const calculatedStyle = [
style,
{
width,
fontSize,
},
];

return (
<TextInput
value={value || this.state.text}
onChangeText={text => {
this.setState({ text, width: charWidth * (text.length + 1) });
onChangeText && onChangeText(text);
}}
style={[
style,
{
width: Math.max(
charWidth * 2,
value ? charWidth * (value.length + 1) : this.state.width
),
},
]}
value={value || text}
onChangeText={changedText =>
this.onChangeTextHandler(
changedText,
maxWidth,
defaultFontSize,
fontWidthHeightRatio,
onChangeText,
placeholderFirstChar
)
}
style={calculatedStyle}
{...props}
/>
);
}
}

HorizontalExpandingTextInput.propTypes = {
AdjustingTextInput.propTypes = {
value: PropTypes.string,
charWidth: PropTypes.number.isRequired,
fontWidthHeightRatio: PropTypes.object.isRequired,
defaultFontSize: PropTypes.number.isRequired,
maxWidth: PropTypes.number.isRequired,
onChangeText: PropTypes.func,
style: RNText.propTypes.style,
placeholder: PropTypes.string,
};

AdjustingTextInput.defaultProps = {
placeholder: '0',
};

export default TextInput;
56 changes: 56 additions & 0 deletions src/component/layout/AdjustingLayout/AdjustingLayout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import PropTypes from 'prop-types';

const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignSelf: 'stretch',
justifyContent: 'center',
},
});

export class AdjustingLayout extends Component {
constructor(props) {
super(props);
this.state = {
containerWidth: 0,
elementWidth: 0,
};
this.onLayoutHandler = this.onLayoutHandler.bind(this);
}

onLayoutHandler(event, key) {
const { width } = event.nativeEvent.layout;
this.setState({ [key]: width });
}

render() {
const { children, style, ...rest } = this.props;
const { containerWidth, elementWidth } = this.state;
const maxWidth = containerWidth - elementWidth;

return (
<View
style={[styles.container, style]}
onLayout={event => this.onLayoutHandler(event, 'containerWidth')}
{...rest}
>
{React.cloneElement(children[0], {
maxWidth,
})}
<View onLayout={event => this.onLayoutHandler(event, 'elementWidth')}>
{children[1]}
</View>
</View>
);
}
}

AdjustingLayout.propTypes = {
children: PropTypes.node.isRequired,
style: View.propTypes.style,
};

export default AdjustingLayout;
3 changes: 3 additions & 0 deletions src/component/layout/AdjustingLayout/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import AdjustingLayout from './AdjustingLayout';

export default AdjustingLayout;
20 changes: 20 additions & 0 deletions src/component/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,23 @@ export const font = {
lineHeightXXL: 60,
lineHeightXXXL: 100,
};

export const fontFamily = {
WorkSansExtraLight: {
name: 'WorkSans ExtraLight',
widthHeightRatio: {
'0': 0.596,
'1': 0.333,
'2': 0.543,
'3': 0.533,
'4': 0.561,
'5': 0.544,
'6': 0.555,
'7': 0.507,
'8': 0.572,
'9': 0.577,
'.': 0.186,
default: 0.793,
},
},
};
7 changes: 4 additions & 3 deletions src/view/invoice.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import MainContent from '../component/main-content';
import { InputField, AmountInputField } from '../component/field';
import { Header, Title } from '../component/header';
import { CancelButton, PillButton, Button } from '../component/button';
import { BalanceLabel, BalanceLabelUnit } from '../component/label';
import { BalanceLabelUnit } from '../component/label';
import Card from '../component/card';
import LightningBoltIcon from '../asset/icon/lightning-bolt';
import { FormStretcher, FormSubText } from '../component/form';
import { color } from '../component/style';
import AdjustingLayout from '../component/layout/AdjustingLayout';

const styles = StyleSheet.create({
balance: {
Expand All @@ -37,7 +38,7 @@ const InvoiceView = ({ store, nav, invoice }) => (
</Header>
<MainContent>
<Card>
<BalanceLabel style={styles.balance}>
<AdjustingLayout style={styles.balance}>
<AmountInputField
autoFocus={true}
value={store.invoice.amount}
Expand All @@ -47,7 +48,7 @@ const InvoiceView = ({ store, nav, invoice }) => (
<BalanceLabelUnit style={styles.unit}>
{store.unitFiatLabel}
</BalanceLabelUnit>
</BalanceLabel>
</AdjustingLayout>
<FormStretcher>
<InputField
placeholder="Note"
Expand Down