From f94888bf4ccb2ab0e7b67e5a0076a53ed4afa8fd Mon Sep 17 00:00:00 2001 From: "wallace.r.w.west" Date: Sun, 9 Sep 2018 21:13:28 +0200 Subject: [PATCH 1/5] create AdjustingLayout and AdjustingTextInput, use them in Invoice view --- src/component/field.js | 15 ++-- src/component/input.js | 85 +++++++++++++++++++ .../AdjustingLayout/AdjustingLayout.jsx | 56 ++++++++++++ src/component/layout/AdjustingLayout/index.js | 3 + src/component/style.js | 7 ++ src/view/invoice.js | 7 +- 6 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 src/component/layout/AdjustingLayout/AdjustingLayout.jsx create mode 100644 src/component/layout/AdjustingLayout/index.js diff --git a/src/component/field.js b/src/component/field.js index 21b92f158..e5a7a78b0 100644 --- a/src/component/field.js +++ b/src/component/field.js @@ -2,25 +2,28 @@ import React from 'react'; import { Text as RNText, StyleSheet, View } 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 }) => ( - { + const predictedTextLength = text.length ? text.length : 1; + const calculatedWidth = Math.min( + maxWidth, + predictedTextLength * defaultFontSize * fontWidthHeightRatio + ); + const calculatedFontSize = + calculatedWidth >= maxWidth + ? Math.min( + defaultFontSize, + calculatedWidth / predictedTextLength / fontWidthHeightRatio + ) + : defaultFontSize; + + return { calculatedWidth, calculatedFontSize }; + }; + + const onChangeTextHandler = changedText => { + const { calculatedWidth, calculatedFontSize } = calculateWidthAndFontSize( + changedText + ); + + this.setState({ + text: changedText, + width: calculatedWidth, + fontSize: calculatedFontSize, + }); + onChangeText && onChangeText(changedText); + }; + + return ( + onChangeTextHandler(changedText)} + style={calculatedStyle} + {...props} + /> + ); + } +} + +AdjustingTextInput.propTypes = { + value: PropTypes.string, + fontWidthHeightRatio: PropTypes.number.isRequired, + defaultFontSize: PropTypes.number.isRequired, + maxWidth: PropTypes.number.isRequired, + onChangeText: PropTypes.func, + style: RNText.propTypes.style, +}; + 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..f2b549b52 100644 --- a/src/component/style.js +++ b/src/component/style.js @@ -51,3 +51,10 @@ export const font = { lineHeightXXL: 60, lineHeightXXXL: 100, }; + +export const fontFamily = { + WorkSansExtraLight: { + name: 'WorkSans ExtraLight', + widthHeightRatio: 0.6, + }, +}; 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} - + Date: Sun, 9 Sep 2018 21:14:27 +0200 Subject: [PATCH 2/5] remove placeholderTextColor from AmountInputField --- src/component/field.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/component/field.js b/src/component/field.js index e5a7a78b0..f07fad01e 100644 --- a/src/component/field.js +++ b/src/component/field.js @@ -26,7 +26,6 @@ export const AmountInputField = ({ style, ...props }) => ( defaultFontSize={font.sizeXXXL} keyboardType="numeric" placeholder="0" - placeholderTextColor={color.blackText} {...props} /> ); From dc0db3cd5afa5ac0e92fa8118fc45d137179fc55 Mon Sep 17 00:00:00 2001 From: "wallace.r.w.west" Date: Sun, 16 Sep 2018 19:23:24 +0200 Subject: [PATCH 3/5] restructured and refactored adjusting text input component --- src/component/field.js | 25 ++++++------ src/component/input.js | 88 +++++++++++++++++++++++++++--------------- 2 files changed, 71 insertions(+), 42 deletions(-) diff --git a/src/component/field.js b/src/component/field.js index f07fad01e..fbe3f3968 100644 --- a/src/component/field.js +++ b/src/component/field.js @@ -1,5 +1,5 @@ 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, AdjustingTextInput } from './input'; @@ -19,16 +19,19 @@ const amountStyles = StyleSheet.create({ }, }); -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 2b3df0ac3..f82cd6681 100644 --- a/src/component/input.js +++ b/src/component/input.js @@ -79,6 +79,28 @@ HorizontalExpandingTextInput.propTypes = { // Adjusting Text Input // +const calculateWidthAndFontSize = ( + text, + maxWidth, + defaultFontSize, + fontWidthHeightRatio +) => { + const predictedTextLength = text.length || 1; + const calculatedWidth = Math.min( + maxWidth, + predictedTextLength * defaultFontSize * fontWidthHeightRatio + ); + const calculatedFontSize = + calculatedWidth >= maxWidth + ? Math.min( + defaultFontSize, + calculatedWidth / predictedTextLength / fontWidthHeightRatio + ) + : defaultFontSize; + + return { calculatedWidth, calculatedFontSize }; +}; + export class AdjustingTextInput extends Component { constructor(props) { super(props); @@ -89,6 +111,32 @@ export class AdjustingTextInput extends Component { width: defaultFontSize * fontWidthHeightRatio, fontSize: defaultFontSize, }; + + this.onChangeTextHandler = this.onChangeTextHandler.bind(this); + } + + onChangeTextHandler( + changedText, + maxWidth, + defaultFontSize, + fontWidthHeightRatio, + onChangeText + ) { + const { calculatedWidth, calculatedFontSize } = calculateWidthAndFontSize( + changedText, + maxWidth, + defaultFontSize, + fontWidthHeightRatio + ); + + if (changedText.match(/^(?:[1-9]\d*|0)?(?:\.\d*)?$/)) { + this.setState({ + text: changedText, + width: calculatedWidth, + fontSize: calculatedFontSize, + }); + onChangeText && onChangeText(changedText); + } } render() { @@ -110,40 +158,18 @@ export class AdjustingTextInput extends Component { }, ]; - const calculateWidthAndFontSize = text => { - const predictedTextLength = text.length ? text.length : 1; - const calculatedWidth = Math.min( - maxWidth, - predictedTextLength * defaultFontSize * fontWidthHeightRatio - ); - const calculatedFontSize = - calculatedWidth >= maxWidth - ? Math.min( - defaultFontSize, - calculatedWidth / predictedTextLength / fontWidthHeightRatio - ) - : defaultFontSize; - - return { calculatedWidth, calculatedFontSize }; - }; - - const onChangeTextHandler = changedText => { - const { calculatedWidth, calculatedFontSize } = calculateWidthAndFontSize( - changedText - ); - - this.setState({ - text: changedText, - width: calculatedWidth, - fontSize: calculatedFontSize, - }); - onChangeText && onChangeText(changedText); - }; - return ( onChangeTextHandler(changedText)} + onChangeText={changedText => + this.onChangeTextHandler( + changedText, + maxWidth, + defaultFontSize, + fontWidthHeightRatio, + onChangeText + ) + } style={calculatedStyle} {...props} /> From a8b4a26a225a1d8daeb41a1dcdf0426ad62d821d Mon Sep 17 00:00:00 2001 From: "wallace.r.w.west" Date: Sun, 23 Sep 2018 14:09:31 +0200 Subject: [PATCH 4/5] added mapping of number width/height ratio, use it to better calculate AdjustingTextInput properties --- src/component/input.js | 91 +++++++++++++++++++----------------------- src/component/style.js | 15 ++++++- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/src/component/input.js b/src/component/input.js index f82cd6681..0f92df3ec 100644 --- a/src/component/input.js +++ b/src/component/input.js @@ -35,66 +35,50 @@ TextInput.propTypes = { style: RNText.propTypes.style, }; -// -// Horizontal Expanding Text Input -// - -export class HorizontalExpandingTextInput extends Component { - constructor(props) { - super(props); - this.state = { text: '', width: 0 }; - } - render() { - const { value, charWidth, onChangeText, style, ...props } = this.props; - 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 - ), - }, - ]} - {...props} - /> - ); - } -} - -HorizontalExpandingTextInput.propTypes = { - value: PropTypes.string, - charWidth: PropTypes.number.isRequired, - onChangeText: PropTypes.func, - style: RNText.propTypes.style, -}; - // // Adjusting Text Input // +const getCharWidthHeightRatio = (fontWidthHeightRatio, char) => + fontWidthHeightRatio[char] || fontWidthHeightRatio['default']; + const calculateWidthAndFontSize = ( text, maxWidth, defaultFontSize, - fontWidthHeightRatio + 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, - predictedTextLength * defaultFontSize * fontWidthHeightRatio + 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 / fontWidthHeightRatio + calculatedWidth / predictedTextLength / averageFontWidthHeightRatio ) : defaultFontSize; @@ -104,12 +88,16 @@ const calculateWidthAndFontSize = ( export class AdjustingTextInput extends Component { constructor(props) { super(props); - const { defaultFontSize, fontWidthHeightRatio } = props; + const { defaultFontSize, fontWidthHeightRatio, placeholder } = props; + const placeholderFirstChar = placeholder.length + ? placeholder.charAt(0) + : '0'; this.state = { text: '', - width: defaultFontSize * fontWidthHeightRatio, + width: defaultFontSize * fontWidthHeightRatio[placeholderFirstChar], fontSize: defaultFontSize, + placeholderFirstChar, }; this.onChangeTextHandler = this.onChangeTextHandler.bind(this); @@ -120,13 +108,15 @@ export class AdjustingTextInput extends Component { maxWidth, defaultFontSize, fontWidthHeightRatio, - onChangeText + onChangeText, + placeholderFirstChar ) { const { calculatedWidth, calculatedFontSize } = calculateWidthAndFontSize( changedText, maxWidth, defaultFontSize, - fontWidthHeightRatio + fontWidthHeightRatio, + placeholderFirstChar ); if (changedText.match(/^(?:[1-9]\d*|0)?(?:\.\d*)?$/)) { @@ -149,7 +139,7 @@ export class AdjustingTextInput extends Component { onChangeText, ...props } = this.props; - const { text, width, fontSize } = this.state; + const { text, width, fontSize, placeholderFirstChar } = this.state; const calculatedStyle = [ style, { @@ -167,7 +157,8 @@ export class AdjustingTextInput extends Component { maxWidth, defaultFontSize, fontWidthHeightRatio, - onChangeText + onChangeText, + placeholderFirstChar ) } style={calculatedStyle} @@ -179,7 +170,7 @@ export class AdjustingTextInput extends Component { AdjustingTextInput.propTypes = { value: PropTypes.string, - fontWidthHeightRatio: PropTypes.number.isRequired, + fontWidthHeightRatio: PropTypes.object.isRequired, defaultFontSize: PropTypes.number.isRequired, maxWidth: PropTypes.number.isRequired, onChangeText: PropTypes.func, diff --git a/src/component/style.js b/src/component/style.js index f2b549b52..67a5f1105 100644 --- a/src/component/style.js +++ b/src/component/style.js @@ -55,6 +55,19 @@ export const font = { export const fontFamily = { WorkSansExtraLight: { name: 'WorkSans ExtraLight', - widthHeightRatio: 0.6, + 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, + }, }, }; From 3816aab83971046bd97572107fbd496e64d41d2a Mon Sep 17 00:00:00 2001 From: "wallace.r.w.west" Date: Sun, 23 Sep 2018 14:22:52 +0200 Subject: [PATCH 5/5] converted placeholder prop to defaultProp --- src/component/field.js | 1 - src/component/input.js | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/component/field.js b/src/component/field.js index fbe3f3968..974c8380c 100644 --- a/src/component/field.js +++ b/src/component/field.js @@ -26,7 +26,6 @@ export const AmountInputField = ({ style, ...props }) => { style={[amountStyles.input, style]} fontWidthHeightRatio={amountFont.widthHeightRatio} defaultFontSize={font.sizeXXXL} - placeholder="0" {...nonWebProps} {...props} /> diff --git a/src/component/input.js b/src/component/input.js index 0f92df3ec..20e50d108 100644 --- a/src/component/input.js +++ b/src/component/input.js @@ -175,6 +175,11 @@ AdjustingTextInput.propTypes = { maxWidth: PropTypes.number.isRequired, onChangeText: PropTypes.func, style: RNText.propTypes.style, + placeholder: PropTypes.string, +}; + +AdjustingTextInput.defaultProps = { + placeholder: '0', }; export default TextInput;