Skip to content

Instantly share code, notes, and snippets.

@betiol
Created February 8, 2018 18:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save betiol/2f4c3479078b4486e3170dbecde6e8b1 to your computer and use it in GitHub Desktop.
Save betiol/2f4c3479078b4486e3170dbecde6e8b1 to your computer and use it in GitHub Desktop.
import React, { Component } from "react";
import PropTypes from "prop-types";
import ReactNative, {
NativeModules,
View,
Text,
StyleSheet,
ScrollView,
Dimensions,
TextInput,
ViewPropTypes
} from "react-native";
import CreditCard from "./CardView";
import CCInput from "./CCInput";
import { InjectedProps } from "./connectToState";
const s = StyleSheet.create({
container: {
alignItems: "center"
},
form: {
marginTop: 20
},
inputContainer: {
marginLeft: 20
},
inputLabel: {
fontWeight: "bold"
},
input: {
height: 40
}
});
const CVC_INPUT_WIDTH = 70;
const EXPIRY_INPUT_WIDTH = CVC_INPUT_WIDTH;
const CARD_NUMBER_INPUT_WIDTH_OFFSET = 40;
const CARD_NUMBER_INPUT_WIDTH =
Dimensions.get("window").width -
EXPIRY_INPUT_WIDTH -
CARD_NUMBER_INPUT_WIDTH_OFFSET;
const NAME_INPUT_WIDTH = CARD_NUMBER_INPUT_WIDTH;
const PREVIOUS_FIELD_OFFSET = 40;
const POSTAL_CODE_INPUT_WIDTH = 120; // https://github.com/yannickcr/eslint-plugin-react/issues/106
/* eslint react/prop-types: 0 */ export default class CreditCardInput extends Component {
static propTypes = {
...InjectedProps,
labels: PropTypes.object,
placeholders: PropTypes.object,
labelStyle: Text.propTypes.style,
inputStyle: Text.propTypes.style,
inputContainerStyle: ViewPropTypes.style,
validColor: PropTypes.string,
invalidColor: PropTypes.string,
placeholderColor: PropTypes.string,
cardImageFront: PropTypes.number,
cardImageBack: PropTypes.number,
cardScale: PropTypes.number,
cardFontFamily: PropTypes.string,
cardBrandIcons: PropTypes.object,
allowScroll: PropTypes.bool,
horizontal: PropTypes.bool,
additionalInputsProps: PropTypes.objectOf(
PropTypes.shape(TextInput.propTypes)
)
};
static defaultProps = {
cardViewSize: {},
labels: {
name: "CARDHOLDER'S NAME",
number: "CARD NUMBER",
expiry: "EXPIRY",
cvc: "CVC/CCV",
postalCode: "POSTAL CODE"
},
placeholders: {
name: "Full Name",
number: "1234 5678 1234 5678",
expiry: "MM/YY",
cvc: "CVC",
postalCode: "34567"
},
inputContainerStyle: {
borderBottomWidth: 1,
borderBottomColor: "black"
},
validColor: "",
invalidColor: "red",
placeholderColor: "gray",
allowScroll: false,
horizontal: true,
additionalInputsProps: {}
};
componentDidMount = () => this._focus(this.props.focused);
componentWillReceiveProps = newProps => {
if (this.props.focused !== newProps.focused) this._focus(newProps.focused);
};
_focus = field => {
if (!field) return;
const scrollResponder = this.refs.Form.getScrollResponder();
const nodeHandle = ReactNative.findNodeHandle(this.refs[field]);
NativeModules.UIManager.measureLayoutRelativeToParent(
nodeHandle,
e => {
throw e;
},
x => {
scrollResponder.scrollTo({
x: Math.max(x - PREVIOUS_FIELD_OFFSET, 0),
animated: true
});
this.refs[field].focus();
}
);
};
_inputProps = field => {
const {
inputStyle,
labelStyle,
validColor,
invalidColor,
placeholderColor,
placeholders,
labels,
values,
status,
onFocus,
onChange,
onBecomeEmpty,
onBecomeValid,
additionalInputsProps
} = this.props;
return {
inputStyle: [s.input, inputStyle],
labelStyle: [s.inputLabel, labelStyle],
validColor,
invalidColor,
placeholderColor,
ref: field,
field,
label: labels[field],
placeholder: placeholders[field],
value: values[field],
status: status[field],
onFocus,
onChange,
onBecomeEmpty,
onBecomeValid,
additionalInputProps: additionalInputsProps[field]
};
};
render() {
const {
cardImageFront,
cardImageBack,
inputContainerStyle,
values: { number, expiry, cvc, name, type },
focused,
allowScroll,
requiresName,
requiresCVC,
requiresPostalCode,
cardScale,
cardFontFamily,
cardBrandIcons,
horizontal
} = this.props;
return (
<View style={s.container}>
<CreditCard
focused={focused}
brand={type}
scale={cardScale}
fontFamily={cardFontFamily}
imageFront={cardImageFront}
imageBack={cardImageBack}
customIcons={cardBrandIcons}
name={requiresName ? name : " "}
number={number}
expiry={expiry}
cvc={cvc}
/>
<ScrollView
ref="Form"
horizontal={horizontal}
keyboardShouldPersistTaps="always"
scrollEnabled={allowScroll}
showsHorizontalScrollIndicator={false}
style={s.form}
>
<CCInput
{...this._inputProps("number")}
keyboardType="numeric"
containerStyle={[
s.inputContainer,
inputContainerStyle,
{ width: CARD_NUMBER_INPUT_WIDTH }
]}
/>
<CCInput
{...this._inputProps("expiry")}
keyboardType="numeric"
containerStyle={[
s.inputContainer,
inputContainerStyle,
{ width: EXPIRY_INPUT_WIDTH }
]}
/>
{requiresCVC && (
<CCInput
{...this._inputProps("cvc")}
keyboardType="numeric"
containerStyle={[
s.inputContainer,
inputContainerStyle,
{ width: CVC_INPUT_WIDTH }
]}
/>
)}
{requiresName && (
<CCInput
{...this._inputProps("name")}
containerStyle={[
s.inputContainer,
inputContainerStyle,
{ width: NAME_INPUT_WIDTH }
]}
/>
)}
{requiresPostalCode && (
<CCInput
{...this._inputProps("postalCode")}
keyboardType="numeric"
containerStyle={[
s.inputContainer,
inputContainerStyle,
{ width: POSTAL_CODE_INPUT_WIDTH }
]}
/>
)}
</ScrollView>
</View>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment