Skip to content

Adding style and ref props to individual TextInputs, updating UX on LiteCreditCardInput, replacing TouchableOpacity with Pressable #243

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,12 @@ yarn example web
|--------------------|-------------------------------------------|---------------------------------------------------------------|
| `autoFocus` | `boolean` | Optional. Specifies if the input should auto-focus. |
| `style` | `ViewStyle` | Optional. Custom style for the component's container. |
| `inputStyle` | `TextStyle` | Optional. Custom style for the input fields. |
| `numberInputStyle` | `TextStyle` | Optional. Custom style for the card number input field. |
| `expiryInputStyle` | `TextStyle` | Optional. Custom style for the expiry input field. |
| `cvcInputStyle` | `TextStyle` | Optional. Custom style for the CVC input field. |
| `numberInputRef` | `Ref<TextInput>` | Optional. Reference object for the card number input field. |
| `expiryInputRef` | `Ref<TextInput>` | Optional. Reference object for the expiry input field. |
| `cvcInputRef` | `Ref<TextInput>` | Optional. Reference object for the CVC input field. |
| `placeholderColor` | `string` | Optional. Color for the placeholder text. |
| `placeholders` | `{ number: string; expiry: string; cvc: string; }` | Optional. Custom placeholders for the input fields. |
| `onChange` | `(formData: CreditCardFormData) => void` | Required. Callback function called when form data changes. |
Expand All @@ -108,7 +113,12 @@ yarn example web
| `autoFocus` | `boolean` | Optional. Specifies if the input should auto-focus. |
| `style` | `ViewStyle` | Optional. Custom style for the component's container. |
| `labelStyle` | `TextStyle` | Optional. Custom style for the labels. |
| `inputStyle` | `TextStyle` | Optional. Custom style for the input fields. |
| `numberInputStyle` | `TextStyle` | Optional. Custom style for the card number input field. |
| `expiryInputStyle` | `TextStyle` | Optional. Custom style for the expiry input field. |
| `cvcInputStyle` | `TextStyle` | Optional. Custom style for the CVC input field. |
| `numberInputRef` | `Ref<TextInput>` | Optional. Reference object for the card number input field. |
| `expiryInputRef` | `Ref<TextInput>` | Optional. Reference object for the expiry input field. |
| `cvcInputRef` | `Ref<TextInput>` | Optional. Reference object for the CVC input field. |
| `placeholderColor` | `string` | Optional. Color for the placeholder text. |
| `labels` | `{ number: string; expiry: string; cvc: string; }` | Optional. Custom labels for the input fields. |
| `placeholders` | `{ number: string; expiry: string; cvc: string; }` | Optional. Custom placeholders for the input fields. |
Expand Down
32 changes: 24 additions & 8 deletions src/CreditCardInput.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef } from 'react';
import { useEffect, useRef, type RefObject } from 'react';
import {
StyleSheet,
Text,
Expand All @@ -17,7 +17,12 @@ interface Props {
autoFocus?: boolean;
style?: ViewStyle;
labelStyle?: TextStyle;
inputStyle?: TextStyle;
numberInputStyle?: TextStyle;
expiryInputStyle?: TextStyle;
cvcInputStyle?: TextStyle;
numberInputRef?: RefObject<TextInput>;
expiryInputRef?: RefObject<TextInput>;
cvcInputRef?: RefObject<TextInput>;
placeholderColor?: string;
labels?: {
number: string;
Expand Down Expand Up @@ -76,7 +81,12 @@ const CreditCardInput = (props: Props) => {
autoFocus,
style,
labelStyle,
inputStyle,
numberInputStyle,
expiryInputStyle,
cvcInputStyle,
numberInputRef,
expiryInputRef,
cvcInputRef,
placeholderColor = 'darkgray',
labels = {
number: 'CARD NUMBER',
Expand All @@ -98,7 +108,11 @@ const CreditCardInput = (props: Props) => {
const numberInput = useRef<TextInput>(null);

useEffect(() => {
if (autoFocus) numberInput.current?.focus();
if (autoFocus) {
(numberInputRef)
? numberInputRef.current?.focus()
: numberInput.current?.focus();
}
}, [autoFocus]);

return (
Expand All @@ -109,9 +123,9 @@ const CreditCardInput = (props: Props) => {
<View style={[s.numberInput]}>
<Text style={[s.inputLabel, labelStyle]}>{labels.number}</Text>
<TextInput
ref={numberInput}
ref={numberInputRef || numberInput}
keyboardType="numeric"
style={[s.input, inputStyle]}
style={[s.input, numberInputStyle]}
placeholderTextColor={placeholderColor}
placeholder={placeholders.number}
value={values.number}
Expand All @@ -127,8 +141,9 @@ const CreditCardInput = (props: Props) => {
<View style={s.expiryInputContainer}>
<Text style={[s.inputLabel, labelStyle]}>{labels.expiry}</Text>
<TextInput
ref={expiryInputRef}
keyboardType="numeric"
style={[s.input, inputStyle]}
style={[s.input, expiryInputStyle]}
placeholderTextColor={placeholderColor}
placeholder={placeholders.expiry}
value={values.expiry}
Expand All @@ -143,8 +158,9 @@ const CreditCardInput = (props: Props) => {
<View style={s.cvcInputContainer}>
<Text style={[s.inputLabel, labelStyle]}>{labels.cvc}</Text>
<TextInput
ref={cvcInputRef}
keyboardType="numeric"
style={[s.input, inputStyle]}
style={[s.input, cvcInputStyle]}
placeholderTextColor={placeholderColor}
placeholder={placeholders.cvc}
value={values.cvc}
Expand Down
73 changes: 50 additions & 23 deletions src/LiteCreditCardInput.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
useCallback,
useEffect,
useMemo,
useRef,
useState,
type RefObject
} from 'react';
import {
Image,
LayoutAnimation,
Pressable,
StyleSheet,
TextInput,
TouchableOpacity,
View,
type TextStyle,
type ViewStyle,
Expand All @@ -19,7 +26,12 @@ import {
interface Props {
autoFocus?: boolean;
style?: ViewStyle;
inputStyle?: TextStyle;
numberInputStyle?: TextStyle;
expiryInputStyle?: TextStyle;
cvcInputStyle?: TextStyle;
numberInputRef?: RefObject<TextInput>;
expiryInputRef?: RefObject<TextInput>;
cvcInputRef?: RefObject<TextInput>;
placeholderColor?: string;
placeholders?: {
number: string;
Expand Down Expand Up @@ -86,7 +98,12 @@ const LiteCreditCardInput = (props: Props) => {
const {
autoFocus = false,
style,
inputStyle,
numberInputStyle,
expiryInputStyle,
cvcInputStyle,
numberInputRef,
expiryInputRef,
cvcInputRef,
placeholderColor = 'darkgray',
placeholders = {
number: '1234 5678 1234 5678',
Expand All @@ -100,13 +117,15 @@ const LiteCreditCardInput = (props: Props) => {

const _onChange = (formData: CreditCardFormData): void => {
// Focus next field when number/expiry field become valid
if (status.number !== 'valid' && formData.status.number === 'valid') {
toggleFormState();
expiryInput.current?.focus();
if (status.number !== 'valid' && formData.status.number === 'valid' &&
(formData.status.expiry === "incomplete" || formData.status.expiry === "invalid")) {
toggleFormState();
(expiryInputRef) ? expiryInputRef.current?.focus() : expiryInput.current?.focus();
}

if (status.expiry !== 'valid' && formData.status.expiry === 'valid') {
cvcInput.current?.focus();
if (status.expiry !== 'valid' && formData.status.expiry === 'valid' &&
(formData.status.cvc === "incomplete" || formData.status.cvc === "invalid")) {
(cvcInputRef) ? cvcInputRef.current?.focus() : cvcInput.current?.focus();
}

onChange(formData);
Expand All @@ -126,7 +145,11 @@ const LiteCreditCardInput = (props: Props) => {
const cvcInput = useRef<TextInput>(null);

useEffect(() => {
if (autoFocus) numberInput.current?.focus();
if (autoFocus) {
(numberInputRef)
? numberInputRef.current?.focus()
: numberInput.current?.focus();
}
}, [autoFocus]);

const cardIcon = useMemo(() => {
Expand All @@ -142,9 +165,9 @@ const LiteCreditCardInput = (props: Props) => {
<View style={[s.leftPart, showRightPart ? s.hidden : s.expanded]}>
<View style={[s.numberInput]}>
<TextInput
ref={numberInput}
ref={numberInputRef || numberInput}
keyboardType="numeric"
style={[s.input, inputStyle]}
style={[s.input, numberInputStyle]}
placeholderTextColor={placeholderColor}
placeholder={placeholders.number}
value={values.number}
Expand All @@ -157,21 +180,25 @@ const LiteCreditCardInput = (props: Props) => {
</View>
</View>

<TouchableOpacity
activeOpacity={0.8}
<Pressable
style={({ pressed }) => [
{ opacity: pressed ? 0.8 : 1}
]}
onPress={toggleFormState}
>
<Image
style={s.icon}
source={{ uri: cardIcon }}
/>
</TouchableOpacity>
</Pressable>

<View style={[s.rightPart, showRightPart ? s.expanded : s.hidden]}>
<TouchableOpacity
activeOpacity={0.8}
<Pressable
style={({ pressed }) => [
s.last4,
{ opacity: pressed ? 0.8 : 1}
]}
onPress={toggleFormState}
style={s.last4}
>
<View pointerEvents={'none'}>
<TextInput
Expand All @@ -185,13 +212,13 @@ const LiteCreditCardInput = (props: Props) => {
readOnly
/>
</View>
</TouchableOpacity>
</Pressable>

<View style={s.expiryInput}>
<TextInput
ref={expiryInput}
ref={expiryInputRef || expiryInput}
keyboardType="numeric"
style={[s.input, inputStyle]}
style={[s.input, expiryInputStyle]}
placeholderTextColor={placeholderColor}
placeholder={placeholders.expiry}
value={values.expiry}
Expand All @@ -205,9 +232,9 @@ const LiteCreditCardInput = (props: Props) => {

<View style={s.cvcInput}>
<TextInput
ref={cvcInput}
ref={cvcInputRef || cvcInput}
keyboardType="numeric"
style={[s.input, inputStyle]}
style={[s.input, cvcInputStyle]}
placeholderTextColor={placeholderColor}
placeholder={placeholders.cvc}
value={values.cvc}
Expand Down