react native textInput, change position of cursor on press - react-native

Right now, in my react native app, I'm not able to change the intermediate value of every input. Because everytime that i presso on a text input, the caret is at the end of the inserted string.
For example:
Value: 123456 ----> I want to delete/change only 23, but i cannot because the caret is over the last value.
i tried to use selectTextOnFocus but it will fired only when i click on other text input, and it doesn't work if the data is pre-filled. I try to explain better
in the image down below I jumped from the first text Mobile Number to the second id RICE.
In the image down below I entered the new values 12321 but i if press a second time on the same input, the text will not be selected. I tried to click and stay for like 10 secs, but nothing.
I'm using xcode to make the build of the app.
this is my input component
const InputField: React.FC<Props> = (
{
placeholder,
iconLeft,
iconRight,
dateTimeIcon,
iconBackground,
type,
id,
style,
name,
value,
keyboardType,
iconRightFunction,
maxLength = 100,
inputBackground,
onChangeText,
onChange,
editable,
textAlign,
setMarginBottom,
onFocus,
multiline,
numberOfLines,
label,
error,
errorLabel,
containerHeight,
onBlur,
},
ref,
) => {
const [hidePassword, showOrHidePassword] = useState(true);
const _handleShowPassword = () => {
showOrHidePassword(!hidePassword);
};
const _handleOnChange = ({ nativeEvent }) => {
onChange && onChange(name, nativeEvent.text);
};
const _handleOnBlur = () => {
onBlur && onBlur(name, value);
};
const input = useRef(null);
const showPassword = type === 'password' && !error && value.length !== 0;
return (
<>
{label && (
<StyledLabelContainer setMargin={setMarginBottom}>
<StyledLabel error={error}>{label}</StyledLabel>
{error && <StyledLabel error={error}>{errorLabel}</StyledLabel>}
</StyledLabelContainer>
)}
<StyledContainer containerHeight={containerHeight} setMargin={setMarginBottom}>
{iconLeft && <StyledIconLeft bgColor={iconBackground}>{iconLeft}</StyledIconLeft>}
<TouchableOpacity
activeOpacity={0}
onPress={() => {
console.log('inputcurrent', input, input.current);
input.current.focus();
}}
style={{ flex: 1 }}
>
<View pointerEvents="none" style={{ flex: 1 }}>
<StyledInput
style={style}
ref={input}
textAlign={textAlign}
maxLength={maxLength}
editable
selectTextOnFocus
clearTextOnFocus={false}
secureTextEntry={type === 'password' && hidePassword}
placeholder={placeholder}
type={type}
autoCapitalize="none"
onChangeText={onChangeText}
onChange={_handleOnChange}
value={value}
id={id}
name={name}
bgColor={inputBackground}
keyboardType={keyboardType}
blurOnSubmit={true}
onFocus={onFocus}
returnKeyType={'done'}
onBlur={_handleOnBlur}
error={error}
multiline={multiline}
numberOfLines={numberOfLines}
/>
</View>
</TouchableOpacity>
Question
How can i set the right position of cursor during a press action ?
Ex: 123456 ----> user press in the middle of the string, i expect to see the caret between the numbers 3 and 4.
My tries
I read about selection but i didn't succeed to implement it, every kind of advices will be very very welcome.
Thanks for the time.

You can use the selection prop to position the cursor where you want it to be. See docs: https://reactnative.dev/docs/textinput.html#selection
You can create a view that looks like a text input but is instead a group of small buttons, one for each character. When the user clicks one of these buttons, you know where they want the caret (cursor) to be. Then you render the real TextInput and use the selection prop to move the caret to the correct position.

Related

Chnage the color of button once selected and deselect the selected button if 2nd button is selected

const renderitem = () => {
return (
<TouchableHighlight
onPress={props.onClick}
>
<Text>{props.title}</Text>
</TouchableHighlight>
);
};
I am creating a function to render a button in Flatlist, there can be multiple button based on the data in flatlist now when I select one button I want to change the color of the selected button and when I select another button then I want to deselect the 1st button which was selected and also change the color of 1st button to origin color and now change the color of the 2nd button as it has been currently selected, please let me know how I can achieve this.
const App = () => {
const [options, setOptions] = useState<string[]>(['One', 'Two', 'Three'])
const [selected, setSelected] = useState('One')
const Item = ({ item, index }) => (
<Pressable onPress={() => setSelected(item)}>
<Text style={{ color: selected === item ? '#ff00ff' : '#000000' }}>
{item}
</Text>
</Pressable>
)
return (
<FlatList
data={options}
renderItem={Item}
/>
)
}
Please use state variable as style color.
e.g. style={color:buttonColor1}

How to animate multiple entries using react native reanimated v2?

I am new to reanimated. I am now trying to reanimating multiple items.
These items will not show up at the start of the component on load.
But they items will show up when the corresponding item is pressed that my intentions.
eg when button 1 is pressed item 1 will pop up slowly, button 2 is pressed and item 2
like that.
I got 2 component in my app
ItemsList screen and item component
I don't have any animation code in itemList screen. I am just retuning the item component.
{items.map(item => (
<OtherItem
key={item._id}
item={item}
selectedItem={selectedItem}
setSelectedItem={setSelectedItem}
/>
))}
Inside the item component.I got a sharedValue, useEffecthooks, and useState which I use to animate according to user interaction.
ITEM COMPONENT
const [selected, setSelected] = useState(false);
const [count, setCount] = useState(1);
// Animation
const progress = useSharedValue(0);
const reAnimatedStyle = useAnimatedStyle(() => {
return {
opacity: progress.value,
transform: [{scale: progress.value}],
};
});
useEffect(() => {
progress.value = withTiming(1, {duration: 2000});
}, [selected]);
return (
<TouchableOpacity
onPress={() => selectItem(item)}
style={[
globalStyle.pageContainer,
]}>
{selected && (
<Animated.View
style={[
{flexDirection: 'row', alignItems: 'center'},
reAnimatedStyle,
]}>
...
</Animated.View>
)}
</TouchableOpacity>)
As you can see in the code, My intention is that when user press button 1 hidden details inside button 1 will show up.
But the thing Is only the first time works. I think its because of the shared value. What I want is I want every item to work. So can any one suggest the solution
Here is one of many solutions.
Track the selected item on "ItemsList screen" using the following snippet
const [selectedId, setSelectedId] = useState(null);
const handleSelection = (id) => setSelectedId(id);
return (
<SafeAreaView style={styles.container}>
{ITEMS.map((item) => (
<OtherItem
key={item._id}
item={item}
handleSelection={handleSelection}
selectedId={selectedId}
/>
))}
<StatusBar style="auto" />
</SafeAreaView>
);
}
on "ITEM COMPONENT" screen, use useEffect to change progress.value. If the "ITEM COMPONENT" sees that the current rendered item is selected then it will increase the progreass.value 1 other wise it will decrease it to 0.
use the following snippet
useEffect(() => {
if (selectedId === item._id)
progress.value = withTiming(1, { duration: 2000 });
else progress.value = withTiming(0, { duration: 2000 }); // un comment this line if you want to see hidden element of just one item and hide the other item
}, [selectedId]);
also send a function ( handleSelection in this example ) from "ITEM LIST screen" to "ITEM COMPONENT" screen to track which item is selected.
Here is a expo snack with full source code.

Change only one icon in a list of objects - React Native

I want to be able to change the icon in a list of todos (see picture) from an exclamation mark, to a checkmark. That should happen if the user puts the finger on the icon, or the developer clicks with the mouse in the emulator.
Through the code below, I manage to change it, but the new icon only appears if I close the modal containing the list, and reopen it. So the modal does not re-render, neither partly nor in whole.
How can I make the changes appear live, immediately after I click the exclamation icon? I suspect it has to do with state, but it doesn't seem possible to create a React hook inside the map function. If I let onPress call a function, then the state is only known within that external function, and I don't know how to export it.
export const TeacherMessages = (props) => {
return (
<View
style={[
styles.borderBox,
props.todos.length > 0 || props.notes.length > 0
? styles.whiteBox
: null,
]}
>
{
props.todos.map((todo) => (
<View key={todo.id} style={styles.listNotes}>
<AntDesign
style={styles.listIcon}
onPress={() => todo.isChecked = true}
name={todo.isChecked ? "checksquare" : "exclamationcircle"}
color={todo.isChecked ? "green" : "red"}
size={18}
/>
<Text style={styles.listText}> {todo.description}</Text>
</View>
))
}
);
I think you need to store the todos array in a react hook so that way the changes you do to it becomes live instantly, You can have this changeTodo function in the parent component and pass it as props to call it from the child component with the index needed. I think this might help:
export const TeacherMessages = (props) => {
const [todosArr, setTodosArr] = React.useState(props.todos)
const checkTodo = (todoIndex) =>{
let arr = [...todosArr]
arr[todoIndex].isChecked= true
setTodosArr(arr)
}
return (
<View
style={[
styles.borderBox,
todosArr.length > 0 || props.notes.length > 0
? styles.whiteBox
: null,
]}
>
{
todosArr.map((todo, index) => (
<View key={todo.id} style={styles.listNotes}>
<AntDesign
style={styles.listIcon}
onPress={() => checkTodo(index)}
name={todo.isChecked ? "checksquare" : "exclamationcircle"}
color={todo.isChecked ? "green" : "red"}
size={18}
/>
<Text style={styles.listText}> {todo.description}</Text>
</View>
))
}
);

react-native-community/slider value can be handled by text box and viceversa?

So my app demands the use of slider whose value can be given by sliding or by text box and viceversa . I am not able to understand whenever i am printing value in text its printing but when i put it inside text box its not printing ,same whenever i am typing number in text box slider value is not changing.
Here is my code
export const Slider = props => {
const [value, setValue] = useState();
// const [isTouchEnded, setIsTouchEnded] = useState(false);
return (
<View style={styles.content}>
<Slider
style={styles.slider}
minimumValue={0}
maximumValue={100}
value={value}
minimumTrackTintColor="#00E487"
maximumTrackTintColor="#00E487"
onValueChange={e => setValue(e)}
step={1}
/>
<View>
<TextView>{value}</TextView>
<TextBoxInput
testID="tiDimLevel"
style={styles.textBox}
keyboardType="numeric"
value={value}
returnKeyType="next"
onChangeText={e => setValue(e)}
maxLength={3}
/>
</View>
</View>
);
[slider with editable textbox whenever i am editing the text box slider value is
setting to 0 Expected is slider also should move to 58]};
Pass a ref to slider and then create a useEffect like this:
useEffect(() => {
sliderRef.current.setNativeProps({ value: +value });
}, [value])

Can I be able to show a static message below as a dropdown in the textinput in react-native

I need to show a static message while typing an input or if the user focuses on the TextInput in react-native.
I need to show the Input field must contain the following letters.
How can I show this below the box in a floating manner, not inside the view of the screen in react-native
<TextInput>
underlineColorAndroid="transparent"
secureTextEntry={this.state.passwordDisplayed}
textContentType="password"
onChangeText={text => this.setState({ password: text })}
bluronSubmit={true}
placeholderTextColor={'grey'}
placeholder={'Password'}
</TextInput>
React Native's TextInput does not have a prop for showing an arbitrary message, but you can do this by creating a custom component that has both your text input and the message, and controlling when the message is shown by observing your text input focus.
To show the message as 'floating' outside of your input's bounds, position it as 'absolute' and add a top margin equal to the input height. You can use onLayout callback to read your input's size:
const TextInputWithMessage = ({ message, ...textInputProps }) => {
const [showMessage, setShowMessage] = useState(false);
const [inputHeight, setInputHeight] = useState(0);
return (
<View onLayout={({ nativeEvent }) => setInputHeight(nativeEvent.layout.height)}>
<TextInput
{...textInputProps}
onFocus={() => setShowMessage(true)}
onBlur={() => setShowMessage(false)}
/>
{showMessage && <Text style={{ ...StyleSheet.absoluteFillObject, top: inputHeight }}>{message}</Text>}
<View>
)
}
Note that this will override any onFocus and onBlur callback you pass in textInputProps. If you need some custom behaviour there, you could preserve it like so:
onFocus={e => {
setShowMessage(true);
if (textInputProps.onFocus) textInputProps.onFocus(e);
}}
Omitted for brevity.