react-native-credit-card-input-plus
Version:
React native credit card input component
265 lines (245 loc) • 5.89 kB
JavaScript
import React, { useMemo, useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import {
View,
ImageBackground,
Image,
Text,
StyleSheet,
Platform,
} from "react-native";
import defaultIcons from "./Icons";
import CardFlip from "./CardFlip";
const BASE_SIZE = { width: 300, height: 190 };
const s = StyleSheet.create({
cardContainer: {},
cardFace: {},
icon: {
position: "absolute",
top: 15,
right: 15,
width: 60,
height: 40,
resizeMode: "contain",
},
baseText: {
color: "rgba(255, 255, 255, 0.8)",
backgroundColor: "transparent",
},
placeholder: {
color: "rgba(255, 255, 255, 0.5)",
},
focused: {
fontWeight: "bold",
color: "rgba(255, 255, 255, 1)",
},
number: {
fontSize: 21,
position: "absolute",
top: 95,
left: 28,
},
name: {
fontSize: 16,
position: "absolute",
bottom: 20,
left: 25,
right: 100,
},
expiryLabel: {
fontSize: 9,
position: "absolute",
bottom: 40,
left: 218,
},
expiry: {
fontSize: 16,
position: "absolute",
bottom: 20,
left: 220,
},
amexCVC: {
fontSize: 16,
position: "absolute",
top: 73,
right: 30,
},
cvc: {
fontSize: 16,
position: "absolute",
top: 80,
right: 30,
},
});
const CardView = ({
focused,
brand,
name,
number,
expiry,
cvc,
customIcons,
placeholder,
imageFront,
imageBack,
scale,
fontFamily,
}) => {
const Icons = { ...defaultIcons, ...customIcons };
const containerSize = { ...BASE_SIZE, height: BASE_SIZE.height * scale };
const transform = {
transform: [
{ scale },
{ translateY: (BASE_SIZE.height * (scale - 1)) / 2 },
],
};
/**
* Ref
*/
const cardFlipRef = useRef(null);
/**
* States
*/
const [sideFlip, setSideFlip] = useState("FRONT");
/**
* Check if is amex card
*/
const isAmex = useMemo(() => brand === "american-express", [brand]);
/**
* Check if can flip card
*/
const shouldFlip = useMemo(
() => !isAmex && focused === "cvc",
[isAmex, focused]
);
const onFlipCard = (side) => {
if (!cardFlipRef.current) {
return;
}
setSideFlip(side);
cardFlipRef.current.flip();
};
/**
* Manage state of the flip
*/
useEffect(() => {
if (shouldFlip && sideFlip === "FRONT") {
onFlipCard("BACK");
} else if (sideFlip === "BACK") {
onFlipCard("FRONT");
}
}, [shouldFlip]);
return (
<View style={[s.cardContainer, containerSize]}>
<CardFlip ref={cardFlipRef} perspective={2000} flipZoom={0.02}>
<ImageBackground
style={[BASE_SIZE, s.cardFace, transform]}
source={imageFront}
>
<Image style={[s.icon]} source={Icons[brand]} />
<Text
style={[
s.baseText,
{ fontFamily },
s.number,
!number && s.placeholder,
focused === "number" && s.focused,
]}
>
{!number ? placeholder.number : number}
</Text>
<Text
style={[
s.baseText,
{ fontFamily },
s.name,
!name && s.placeholder,
focused === "name" && s.focused,
]}
numberOfLines={1}
>
{!name ? placeholder.name : name.toUpperCase()}
</Text>
<Text
style={[
s.baseText,
{ fontFamily },
s.expiryLabel,
s.placeholder,
focused === "expiry" && s.focused,
]}
>
MONTH/YEAR
</Text>
<Text
style={[
s.baseText,
{ fontFamily },
s.expiry,
!expiry && s.placeholder,
focused === "expiry" && s.focused,
]}
>
{!expiry ? placeholder.expiry : expiry}
</Text>
{isAmex && (
<Text
style={[
s.baseText,
{ fontFamily },
s.amexCVC,
!cvc && s.placeholder,
focused === "cvc" && s.focused,
]}
>
{!cvc ? placeholder.cvc : cvc}
</Text>
)}
</ImageBackground>
<ImageBackground
style={[BASE_SIZE, s.cardFace, transform]}
source={imageBack}
>
<Text
style={[
s.baseText,
s.cvc,
!cvc && s.placeholder,
focused === "cvc" && s.focused,
]}
>
{!cvc ? placeholder.cvc : cvc}
</Text>
</ImageBackground>
</CardFlip>
</View>
);
};
CardView.propTypes = {
focused: PropTypes.string,
brand: PropTypes.string,
name: PropTypes.string,
number: PropTypes.string,
expiry: PropTypes.string,
cvc: PropTypes.string,
placeholder: PropTypes.object,
scale: PropTypes.number,
fontFamily: PropTypes.string,
imageFront: PropTypes.number,
imageBack: PropTypes.number,
customIcons: PropTypes.object,
};
CardView.defaultProps = {
name: "",
placeholder: {
number: "•••• •••• •••• ••••",
name: "FULL NAME",
expiry: "••/••",
cvc: "•••",
},
scale: 1,
fontFamily: Platform.select({ ios: "Courier", android: "monospace" }),
imageFront: require("../images/card-front.png"),
imageBack: require("../images/card-back.png"),
};
export default CardView;