diff --git a/src/component/field.js b/src/component/field.js index 21b92f158..974c8380c 100644 --- a/src/component/field.js +++ b/src/component/field.js @@ -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 }) => ( - -); +export const AmountInputField = ({ style, ...props }) => { + const nonWebProps = Platform.OS === 'web' ? {} : { keyboardType: 'numeric' }; + return ( + + ); +}; AmountInputField.propTypes = { style: RNText.propTypes.style, diff --git a/src/component/input.js b/src/component/input.js index 388aaed02..20e50d108 100644 --- a/src/component/input.js +++ b/src/component/input.js @@ -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 ( { - 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; diff --git a/src/component/layout/AdjustingLayout/AdjustingLayout.jsx b/src/component/layout/AdjustingLayout/AdjustingLayout.jsx new file mode 100644 index 000000000..d064078e9 --- /dev/null +++ b/src/component/layout/AdjustingLayout/AdjustingLayout.jsx @@ -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 ( + this.onLayoutHandler(event, 'containerWidth')} + {...rest} + > + {React.cloneElement(children[0], { + maxWidth, + })} + this.onLayoutHandler(event, 'elementWidth')}> + {children[1]} + + + ); + } +} + +AdjustingLayout.propTypes = { + children: PropTypes.node.isRequired, + style: View.propTypes.style, +}; + +export default AdjustingLayout; diff --git a/src/component/layout/AdjustingLayout/index.js b/src/component/layout/AdjustingLayout/index.js new file mode 100644 index 000000000..537c8c3bb --- /dev/null +++ b/src/component/layout/AdjustingLayout/index.js @@ -0,0 +1,3 @@ +import AdjustingLayout from './AdjustingLayout'; + +export default AdjustingLayout; diff --git a/src/component/style.js b/src/component/style.js index 9a956e8e0..67a5f1105 100644 --- a/src/component/style.js +++ b/src/component/style.js @@ -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, + }, + }, +}; diff --git a/src/view/invoice.js b/src/view/invoice.js index 507245b23..e85463ae9 100644 --- a/src/view/invoice.js +++ b/src/view/invoice.js @@ -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: { @@ -37,7 +38,7 @@ const InvoiceView = ({ store, nav, invoice }) => ( - + ( {store.unitFiatLabel} - +