Skip to content

Create custom TextInput #3414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 57 commits into from
Aug 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
1d1847a
create custom textinput
kakajann Jun 8, 2021
39a395c
merge from main
kakajann Jun 20, 2021
32ac54d
restyling
kakajann Jun 20, 2021
9bb3acc
change password screen implementation and small refactors
kakajann Jun 20, 2021
0001f43
login screen implementation
kakajann Jun 20, 2021
2e21718
payment page implementation
kakajann Jun 25, 2021
e95de93
merge from main
kakajann Jun 25, 2021
587328d
update all textinputs to expensitextinput
kakajann Jun 25, 2021
1a9687c
resolve conflicts
kakajann Jun 27, 2021
c9ed8ee
make ExpensiTextInput cross-platform
kakajann Jun 27, 2021
edbfcea
picker styles and implementations
kakajann Jul 2, 2021
3ac6d2c
picker proptypes refactoring
kakajann Jul 2, 2021
57de005
expensiTextInput desktop hotfixes
kakajann Jul 2, 2021
f2e5667
resolve conflicts
kakajann Jul 2, 2021
dc7b471
fix autoFocus issue
kakajann Jul 2, 2021
28fa320
Merge branch 'main' of github.com:studio-504/Expensify.cash into form…
kakajann Jul 11, 2021
b3303c3
picker pressable issue
kakajann Jul 11, 2021
0db97b8
merge from main to resolve conflicts
kakajann Jul 11, 2021
8bdc856
fix lint issue
kakajann Jul 11, 2021
f0a779e
resolve conflicts
kakajann Jul 16, 2021
229e704
add custom translate x prop to expensiInput for label transformation
kakajann Jul 20, 2021
4edf386
Trigger Build
kakajann Jul 20, 2021
54e20a0
Merge branch 'main' into form-styles
kakajann Jul 21, 2021
d5a0b00
resolve conflicts for the 9th time
kakajann Jul 22, 2021
7495a56
resolve conflicts for the 10th time
kakajann Jul 22, 2021
f7a4b49
resolve conflicts
kakajann Jul 26, 2021
dc9ab22
revert podfile.lock
kakajann Jul 26, 2021
048436f
android related bugfixes
kakajann Jul 26, 2021
90db061
merge from main
kakajann Jul 29, 2021
9cfd7e3
add size prop to ExpensiPicker
kakajann Jul 29, 2021
7271cf1
change ExpensiPicker default prop
kakajann Jul 29, 2021
388d015
fix desktop related issue
kakajann Jul 29, 2021
1c582ce
use addOutlineWidth instead of outline: none
kakajann Jul 30, 2021
b6e36ea
remove android generated file
kakajann Jul 30, 2021
4662b06
merge defaultProps and propTypes files for ExpensiPicker
kakajann Jul 30, 2021
27c967b
remove toggleFocus on ExpensiPicker and use inline setState on onOpen…
kakajann Jul 30, 2021
f3c1c79
more refactors on ExpensiPicker
kakajann Jul 30, 2021
19f3257
prop type refactoring for ExpensiTextInput
kakajann Jul 30, 2021
221565d
refactor ExpensiTextInputLabel
kakajann Jul 30, 2021
f6fc27a
refactor ExpensiTextInputWrapper
kakajann Jul 30, 2021
598f0cc
change containerStyles comment
kakajann Jul 30, 2021
9a74f0a
more refactors for ExpensiTextInputWrapper
kakajann Jul 30, 2021
bd5c05b
refactor ExpensiTextInput
kakajann Jul 31, 2021
83c7d18
more refactors for ExpensiTextInput
kakajann Jul 31, 2021
abf866f
use themeColors instead of hex
kakajann Jul 31, 2021
739ce4d
styles refactor
kakajann Jul 31, 2021
2f0281a
refactor ExpensiPicker styles
kakajann Jul 31, 2021
b2067e4
resolve conflicts
kakajann Jul 31, 2021
3c2cd72
forward ExpensiTextInput ref for external usage
kakajann Aug 3, 2021
bc5ce35
more style refactors
kakajann Aug 3, 2021
ca971f3
remove redundant prop destructing from ExpensiPicker
kakajann Aug 4, 2021
7976869
change Picker with ExpensiPicker
kakajann Aug 4, 2021
dd461b5
ExpensiTextInput refactors
kakajann Aug 4, 2021
31a9efd
fix a propType typo
kakajann Aug 4, 2021
adeddc7
dry inputIOS and inputAndroid
kakajann Aug 6, 2021
534651e
remove propTypes from style component
kakajann Aug 9, 2021
8a0df8f
resolve conflict
kakajann Aug 9, 2021
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
32 changes: 16 additions & 16 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,7 @@ SPEC CHECKSUMS:
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
EXHaptics: 337c160c148baa6f0e7166249f368965906e346b
FBLazyVector: 7b423f9e248eae65987838148c36eec1dbfe0b53
FBReactNativeSpec: 825b0f0851f5cc5c6268a920286281f62fc96c37
FBReactNativeSpec: c783a75db87c963c60afcd461fc38358805fe5ba
Firebase: 54cdc8bc9c9b3de54f43dab86e62f5a76b47034f
FirebaseABTesting: 4cb61aeeb50f60680af1c01fff781dfaf9293916
FirebaseAnalytics: 4751d6a49598a2b58da678cc07df696bcd809ab9
Expand All @@ -899,9 +899,9 @@ SPEC CHECKSUMS:
Onfido: 116a268e4cb8b767c15285e8071c2e8304673cdf
onfido-react-native-sdk: b8f1b7cbe1adab6479d735275772390161630dcd
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
Permission-LocationAccuracy: 76669f87b4c276f5ae803cc0ddd1862a4c0e9dd8
Permission-LocationAlways: a274bc04bb386068782468dbdaca3859f51634ca
Permission-LocationWhenInUse: 3a2b0dbc167d79e8e920a4377ff9520cdc108407
Permission-LocationAccuracy: e8adff9ede1b23b43b7054a4500113d515fc87a8
Permission-LocationAlways: 7f7f373d086af7a81b2f4f20d65d29266ca2043b
Permission-LocationWhenInUse: 3ae82a9feb5da4e94e386dba17c7dd3531af9feb
Plaid: f55c6acdc249245c6778a4045757eb4e839cca61
PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58
Protobuf: 7327d4444215b5f18e560a97f879ff5503c4581c
Expand All @@ -917,15 +917,15 @@ SPEC CHECKSUMS:
React-jsiexecutor: 124e8f99992490d0d13e0649d950d3e1aae06fe9
React-jsinspector: 500a59626037be5b3b3d89c5151bc3baa9abf1a9
react-native-config: d8b45133fd13d4f23bd2064b72f6e2c08b2763ed
react-native-document-picker: f2f73db94328c84e22144e369fb4a3ede47bc1f5
react-native-document-picker: 0e3602a4064da040321bafad6848d8b0edcb1d55
react-native-flipper: 1943b82f2e494c77b741eb1ed257b6734a334b83
react-native-image-picker: 474cf2c33c2b6671da53d293a16c97995f0aec15
react-native-netinfo: 30fb89fa913c342be82a887b56e96be6d71201dd
react-native-image-picker: 4089335b89b625d4e34d53fb249c48a7a791b3ea
react-native-netinfo: 52cf0ee8342548a485e28f4b09e56b477567244d
react-native-pdf: 4b5a9e4465a6a3b399e91dc4838eb44ddf716d1f
react-native-plaid-link-sdk: 59b7376efca9f00e9693321c5cf7c6ab2c567635
react-native-progress-bar-android: be43138ab7da30d51fc038bafa98e9ed594d0c40
react-native-progress-view: 21b1e29e70c7559c16c9e0a04c4adc19fce6ede2
react-native-safe-area-context: 79fea126c6830c85f65947c223a5e3058a666937
react-native-plaid-link-sdk: 1a6593e2d3d790e8113c29178d883eb883f8c032
react-native-progress-bar-android: ce95a69f11ac580799021633071368d08aaf9ad8
react-native-progress-view: 5816e8a6be812c2b122c6225a2a3db82d9008640
react-native-safe-area-context: 01158a92c300895d79dee447e980672dc3fb85a6
React-perflogger: aad6d4b4a267936b3667260d1f649b6f6069a675
React-RCTActionSheet: fc376be462c9c8d6ad82c0905442fd77f82a9d2a
React-RCTAnimation: ba0a1c3a2738be224a08092fa7f1b444ab77d309
Expand All @@ -939,17 +939,17 @@ SPEC CHECKSUMS:
React-runtimeexecutor: ff951a0c241bfaefc4940a3f1f1a229e7cb32fa6
ReactCommon: bedc99ed4dae329c4fcf128d0c31b9115e5365ca
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNBootSplash: 24175aa28fe203b10c48dc34e78d946fd33c77af
RNBootSplash: 3123ba68fe44d8be09a014e89cc8f0f55b68a521
RNCAsyncStorage: 8324611026e8dc3706f829953aa6e3899f581589
RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495
RNCMaskedView: fc29d354a40316a990e8fb46391f08aea829c3aa
RNCClipboard: 5e299c6df8e0c98f3d7416b86ae563d3a9f768a3
RNCMaskedView: 138134c4d8a9421b4f2bf39055a79aa05c2d47b1
RNCPicker: 6780c753e9e674065db90d9c965920516402579d
RNFBAnalytics: 8ba84c2d31c64374d054c8621b998f25145ffddc
RNFBApp: 64c90ab78b6010ed5c3ade026dfe5ff6442c21fd
RNFBCrashlytics: 1de18b8cc36d9bcf86407c4a354399228cc84a61
RNFBPerf: e3a7269f573a4787810a32de51647cdcbe08dfb4
RNGestureHandler: 9b7e605a741412e20e13c512738a31bd1611759b
RNPermissions: 4c8a37b4dde50f1f152bf8cd08c4a43d2355829e
RNPermissions: eb94f9fdc0a8ecd02fcce0676d56ffb1395d41e1
RNReanimated: 833ebd229b31e18a8933ebd0cd744a0f47d88c42
RNScreens: e8e8dd0588b5da0ab57dcca76ab9b2d8987757e0
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
Expand All @@ -966,7 +966,7 @@ SPEC CHECKSUMS:
UMReactNativeAdapter: 7b458ca3d4497b5114e6bb766b223432bad22d8a
UMSensorsInterface: 50439b47826e716a514cbd7384aebe9ab4fde5f4
UMTaskManagerInterface: 482155764886069beb1bc7fcf6036f12e4ad0751
urbanairship-react-native: a05a913d6f9559141d477ff4d380bcd616b5c59d
urbanairship-react-native: d415a12e67ba93bf3ce914df9a310b66a88a5cc3
Yoga: a7de31c64fe738607e7a3803e3f591a4b1df7393
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a

Expand Down
13 changes: 5 additions & 8 deletions src/components/AddPlaidBankAccount.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React from 'react';
import {
ActivityIndicator,
View,
TextInput,
} from 'react-native';
import PropTypes from 'prop-types';
import lodashGet from 'lodash/get';
Expand All @@ -20,8 +19,9 @@ import canFocusInputOnScreenFocus from '../libs/canFocusInputOnScreenFocus';
import compose from '../libs/compose';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import Button from './Button';
import Picker from './Picker';
import ExpensiPicker from './ExpensiPicker';
import Text from './Text';
import ExpensiTextInput from './ExpensiTextInput';

const propTypes = {
...withLocalizePropTypes,
Expand Down Expand Up @@ -161,7 +161,7 @@ class AddPlaidBankAccount extends React.Component {
https://d2k5nsl2zxldvw.cloudfront.net/images/plaid/[email protected] */}
<Text style={[styles.mb5, styles.h1]}>{this.state.institution.name}</Text>
<View style={[styles.mb5]}>
<Picker
<ExpensiPicker
onChange={(index) => {
this.setState({selectedIndex: Number(index)});
}}
Expand All @@ -175,12 +175,9 @@ class AddPlaidBankAccount extends React.Component {
</View>
{!_.isUndefined(this.state.selectedIndex) && (
<View style={[styles.mb5]}>
<Text style={[styles.formLabel]}>
{this.props.translate('addPersonalBankAccountPage.enterPassword')}
</Text>
<TextInput
<ExpensiTextInput
label={this.props.translate('addPersonalBankAccountPage.enterPassword')}
secureTextEntry
style={[styles.textInput, styles.mb2]}
value={this.state.password}
autoCompleteType="password"
textContentType="password"
Expand Down
58 changes: 58 additions & 0 deletions src/components/ExpensiPicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, {PureComponent} from 'react';
import {Text, View} from 'react-native';
import PropTypes from 'prop-types';
import Picker from './Picker';
import styles from '../styles/styles';

const propTypes = {
/** Picker label */
label: PropTypes.string,

/** Should the picker appear disabled? */
isDisabled: PropTypes.bool,
};

const defaultProps = {
label: '',
isDisabled: false,
};

class ExpensiPicker extends PureComponent {
constructor() {
super();
this.state = {
isOpen: false,
};
}

render() {
const {
label, isDisabled, ...pickerProps
} = this.props;
return (
<View
style={[
styles.expensiPickerContainer,
this.state.isOpen && styles.borderColorFocus,
isDisabled && styles.inputDisabled,
]}
>
{label && (
<Text style={[styles.expensiPickerLabel, styles.textLabelSupporting]}>{label}</Text>
)}
<Picker
onOpen={() => this.setState({isOpen: true})}
onClose={() => this.setState({isOpen: false})}
disabled={isDisabled}
// eslint-disable-next-line react/jsx-props-no-spreading
{...pickerProps}
Comment on lines +47 to +48
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like pickerProps is only size for now, so why don't you just pass size={size} (and only destructure size out of this.props above) so you can avoid the eslint override?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's more props than only size
onChange, items, icon also passed with pickerProps

I should remove placeholder, value from this.props destruction to make the code clearer

/>
</View>
);
}
}

ExpensiPicker.propTypes = propTypes;
ExpensiPicker.defaultProps = defaultProps;

export default ExpensiPicker;
139 changes: 139 additions & 0 deletions src/components/ExpensiTextInput/BaseExpensiTextInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React, {Component} from 'react';
import {
Animated, TextInput, View, TouchableWithoutFeedback,
} from 'react-native';
import ExpensiTextInputLabel from './ExpensiTextInputLabel';
import {propTypes, defaultProps} from './propTypes';
import themeColors from '../../styles/themes/default';
import styles from '../../styles/styles';

const ACTIVE_LABEL_TRANSLATE_Y = -10;
const ACTIVE_LABEL_TRANSLATE_X = (translateX = -22) => translateX;
const ACTIVE_LABEL_SCALE = 0.8668;

const INACTIVE_LABEL_TRANSLATE_Y = 0;
const INACTIVE_LABEL_TRANSLATE_X = 0;
const INACTIVE_LABEL_SCALE = 1;

class BaseExpensiTextInput extends Component {
constructor(props) {
super(props);

const hasValue = props.value.length > 0;

this.state = {
isFocused: false,
labelTranslateY: new Animated.Value(hasValue ? ACTIVE_LABEL_TRANSLATE_Y : INACTIVE_LABEL_TRANSLATE_Y),
labelTranslateX: new Animated.Value(hasValue
? ACTIVE_LABEL_TRANSLATE_X(props.translateX) : INACTIVE_LABEL_TRANSLATE_X),
labelScale: new Animated.Value(hasValue ? ACTIVE_LABEL_SCALE : INACTIVE_LABEL_SCALE),
};

this.input = null;
this.onFocus = this.onFocus.bind(this);
this.onBlur = this.onBlur.bind(this);
}

onFocus() {
if (this.props.onFocus) { this.props.onFocus(); }
this.setState({isFocused: true});
if (this.props.value.length === 0) {
this.animateLabel(
ACTIVE_LABEL_TRANSLATE_Y,
ACTIVE_LABEL_TRANSLATE_X(this.props.translateX),
ACTIVE_LABEL_SCALE,
);
}
}

onBlur() {
if (this.props.onBlur) { this.props.onBlur(); }
this.setState({isFocused: false});
if (this.props.value.length === 0) {
this.animateLabel(INACTIVE_LABEL_TRANSLATE_Y, INACTIVE_LABEL_TRANSLATE_X, INACTIVE_LABEL_SCALE);
}
}

animateLabel(translateY, translateX, scale) {
Animated.parallel([
Animated.spring(this.state.labelTranslateY, {
toValue: translateY,
duration: 80,
useNativeDriver: true,
}),
Animated.spring(this.state.labelTranslateX, {
toValue: translateX,
duration: 80,
useNativeDriver: true,
}),
Animated.spring(this.state.labelScale, {
toValue: scale,
duration: 80,
useNativeDriver: true,
}),
]).start();
}

render() {
const {
label,
value,
placeholder,
hasError,
containerStyles,
inputStyle,
ignoreLabelTranslateX,
innerRef,
...inputProps
} = this.props;

const hasLabel = Boolean(label.length);
return (
<View style={[styles.componentHeightLarge, ...containerStyles]}>
<TouchableWithoutFeedback onPress={() => this.input.focus()}>
<View
style={[
styles.expensiTextInputContainer,
!hasLabel && styles.pv0,
this.state.isFocused && styles.borderColorFocus,
hasError && styles.borderColorDanger,
]}
>
{hasLabel ? (
<ExpensiTextInputLabel
label={label}
labelTranslateX={
ignoreLabelTranslateX
? new Animated.Value(0)
: this.state.labelTranslateX
}
labelTranslateY={this.state.labelTranslateY}
labelScale={this.state.labelScale}
/>
) : null}
<TextInput
ref={(ref) => {
if (typeof innerRef === 'function') { innerRef(ref); }
this.input = ref;
}}
// eslint-disable-next-line
{...inputProps}
value={value}
placeholder={(this.state.isFocused || !label) ? placeholder : null}
placeholderTextColor={themeColors.placeholderText}
underlineColorAndroid="transparent"
style={inputStyle}
onFocus={this.onFocus}
onBlur={this.onBlur}
/>
</View>
</TouchableWithoutFeedback>
</View>
);
}
}

BaseExpensiTextInput.propTypes = propTypes;
BaseExpensiTextInput.defaultProps = defaultProps;

export default BaseExpensiTextInput;
30 changes: 30 additions & 0 deletions src/components/ExpensiTextInput/ExpensiTextInputLabel/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, {memo} from 'react';
import {Animated} from 'react-native';
import styles from '../../../styles/styles';
import propTypes from './propTypes';

const ExpensiTextInputLabel = ({
label,
labelTranslateY,
labelTranslateX,
labelScale,
}) => (
<Animated.Text
style={[
styles.expensiTextInputLabel,
styles.expensiTextInputLabelDesktop,
styles.expensiTextInputLabelTransformation(
labelTranslateY,
labelTranslateX,
labelScale,
),
]}
>
{label}
</Animated.Text>
);

ExpensiTextInputLabel.propTypes = propTypes;
ExpensiTextInputLabel.displayName = 'ExpensiTextInputLabel';

export default memo(ExpensiTextInputLabel);
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, {memo} from 'react';
import {Animated} from 'react-native';
import styles from '../../../styles/styles';
import propTypes from './propTypes';

const ExpensiTextInputLabel = ({
label,
labelTranslateX,
labelTranslateY,
labelScale,
}) => (
<Animated.Text
style={[
styles.expensiTextInputLabel,
styles.expensiTextInputLabelTransformation(
labelTranslateY,
labelTranslateX,
labelScale,
),
]}
>
{label}
</Animated.Text>
);

ExpensiTextInputLabel.propTypes = propTypes;
ExpensiTextInputLabel.displayName = 'ExpensiTextInputLabel';

export default memo(ExpensiTextInputLabel);
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import PropTypes from 'prop-types';
import {Animated} from 'react-native';

const propTypes = {
/** Label */
label: PropTypes.string,

/** Label vertical translate */
labelTranslateY: PropTypes.instanceOf(Animated.Value).isRequired,

/** Label horizontal translate */
labelTranslateX: PropTypes.instanceOf(Animated.Value).isRequired,

/** Label scale */
labelScale: PropTypes.instanceOf(Animated.Value).isRequired,
};

export default propTypes;
Loading