Skip to content

Instantly share code, notes, and snippets.

@halilb
Last active November 10, 2023 07:31
Show Gist options
  • Save halilb/9ac8e43e95ffbda42d52c34d420e78a4 to your computer and use it in GitHub Desktop.
Save halilb/9ac8e43e95ffbda42d52c34d420e78a4 to your computer and use it in GitHub Desktop.
import React, { useState } from 'react'
import { StyleSheet, Text, ScrollView, Button } from 'react-native'
import TextField from './components/TextField'
export default function App() {
const [value, setValue] = useState('')
const [error, setError] = useState<string | null>(null)
return (
<ScrollView contentContainerStyle={styles.content}>
<Text style={styles.title}>Payment details</Text>
<TextField
style={styles.textField}
value={value}
label="Cardholder name"
errorText={error}
onChangeText={(text) => setValue(text)}
/>
<Button
title="Set error"
onPress={() => setError('This field is required.')}
/>
</ScrollView>
)
}
const styles = StyleSheet.create({
content: {
paddingTop: 96,
paddingHorizontal: 36,
},
title: {
fontFamily: 'Avenir-Heavy',
color: 'black',
fontSize: 32,
fontWeight: 'bold',
marginBottom: 32,
},
textField: {
marginBottom: 32,
},
})
import React, { useEffect, useRef, useState } from 'react'
import {
Text,
TextInput,
StyleSheet,
View,
Animated,
Easing,
TouchableWithoutFeedback,
} from 'react-native'
type Props = React.ComponentProps<typeof TextInput> & {
label: string
errorText?: string | null
}
const TextField: React.FC<Props> = (props) => {
const {
label,
errorText,
value,
style,
onBlur,
onFocus,
...restOfProps
} = props
const [isFocused, setIsFocused] = useState(false)
const inputRef = useRef<TextInput>(null)
const focusAnim = useRef(new Animated.Value(0)).current
useEffect(() => {
Animated.timing(focusAnim, {
toValue: isFocused || !!value ? 1 : 0,
duration: 150,
easing: Easing.bezier(0.4, 0, 0.2, 1),
useNativeDriver: true,
}).start()
}, [focusAnim, isFocused, value])
let color = isFocused ? '#080F9C' : '#B9C4CA'
if (errorText) {
color = '#B00020'
}
return (
<View style={style}>
<TextInput
style={[
styles.input,
{
borderColor: color,
},
]}
ref={inputRef}
{...restOfProps}
value={value}
onBlur={(event) => {
setIsFocused(false)
onBlur?.(event)
}}
onFocus={(event) => {
setIsFocused(true)
onFocus?.(event)
}}
/>
<TouchableWithoutFeedback onPress={() => inputRef.current?.focus()}>
<Animated.View
style={[
styles.labelContainer,
{
transform: [
{
scale: focusAnim.interpolate({
inputRange: [0, 1],
outputRange: [1, 0.75],
}),
},
{
translateY: focusAnim.interpolate({
inputRange: [0, 1],
outputRange: [24, -12],
}),
},
{
translateX: focusAnim.interpolate({
inputRange: [0, 1],
outputRange: [16, 0],
}),
},
],
},
]}
>
<Text
style={[
styles.label,
{
color,
},
]}
>
{label}
{errorText ? '*' : ''}
</Text>
</Animated.View>
</TouchableWithoutFeedback>
{!!errorText && <Text style={styles.error}>{errorText}</Text>}
</View>
)
}
const styles = StyleSheet.create({
input: {
padding: 24,
borderWidth: 1,
borderRadius: 4,
fontFamily: 'Avenir-Medium',
fontSize: 16,
},
labelContainer: {
position: 'absolute',
paddingHorizontal: 8,
backgroundColor: 'white',
},
label: {
fontFamily: 'Avenir-Heavy',
fontSize: 16,
},
error: {
marginTop: 4,
marginLeft: 12,
fontSize: 12,
color: '#B00020',
fontFamily: 'Avenir-Medium',
},
})
export default TextField
@halilb
Copy link
Author

halilb commented Nov 16, 2021

is there any way that we can do focus on the next keyboard button click? If multiple text fields we are using and on next button click it will focus on next textfield. How we can achive with custom above textfield?

I believe this needs to be handled at the parent of the text input. You may check this component which handles focusing to the next input: https://github.com/halilb/rn-credit-card/blob/master/src/components/CreditCardForm.tsx#L71

@gtbhopale2412
Copy link

okay thanks i will check that

@iaminarush
Copy link

The label's position onFocus position is different if the label text have different width, demo here

@deckyfx
Copy link

deckyfx commented Dec 20, 2022

It's been two years, but a hack to solve @iaminarush issue is to modify the translationX into

translateX: focusAnim.interpolate({
  inputRange: [0, 1],
  outputRange: [16, 16 - props.label.length],
}) 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment