I'm trying to make a masked TextInput for BRL currency using regex, so every time the user types a number it is formated as follow:
First the number pressed appears inside the TextInput and then you can change the state to your desired value. Isn't there a way to change the text before it is displayed for the user?
Basically what I'm doing is:
const MoneyTextInput = ({onChangeText, ok}) => {
let [text, setText] = useState('00,00');
return (
<TextInput
value={text}
keyboardType="number-pad"
style={{
width: '100%',
height: '100%',
fontSize: 22,
}}
onChange={({nativeEvent}) => {
setText(prettifyCurrency(nativeEvent.text));
onChangeText(prettifyCurrency(nativeEvent.text));
}}
/>
);
};
And the function with the regex is this:
function prettifyCurrency(value: String): String {
if (!value) {
return '00,00';
}
let newText = value.replace(/(\D)/g, '').replace(/^0+/, '');
if (newText.length < 4) {
for (let i = newText.length; i < 4; i++) {
newText = '0' + newText;
}
}
newText = newText
.replace(/(\d{2}$)/g, ',$&')
.replace(/(\d{1})(\d{3})([,])/, '$1.$2$3')
.replace(/(\d{1})(\d{3})([.])/, '$1.$2$3')
.replace(/(\d{1})(\d{3})([.])/, '$1.$2$3');
return newText;
}
Shouldn't it wait for the value prop to be updated? Is there anyway I achieve what I want? Because I know in Android (Java) I had a function to change the text before it was displayed.
PS.: I'm using OnChange function because I found it was a little bit faster than onChangeText
Related
I am currently working on small project.
What I am trying to do is to create text hightlight in my speech to text react native app
My project has Flask API server to detect text data whether it is voice phishing or not.
I am using [react-native-voice]https://github.com/react-native-voice/voice for speech to text
my react native app will send text data segment every 5 seconds to Flask API server to find out whether it is voice phishing or not then Flask API server will return score that is 0 to 1 so over 0.5 is voice phishing
anyway what i am trying to say is that when the text data is considered to be voice phishing (score over 0.5) text data on my app will be highlighted
here is my TextBox Component code
is that possible my setHighlight function
input
hi my name is woo young from Well Bing Bank give me your account my number is 112-22314-12314. Thank you.
return
hi my name is woo young from Well Bing Bank
<Text style={styles.highlighted}>
give me your account
<Text>
my number is 112-22314-12314. Thank you.
but it returns
hi my name is woo young from Well Bing Bank
[object Object]
my number is 112-22314-12314. Thank you.
import { Text, View, StyleSheet } from "react-native";
const NO_WIDTH_SPACE = '';
const highlight = string =>
string.split(' ').map((word, i) => (
<Text key={i}>
<Text style={styles.highlighted}>{word} </Text>
{NO_WIDTH_SPACE}
</Text>
));
export default class TextBox extends Component {
setHighlight = (string, array) => {
for (let i = 0; i < (array.length)/2; i++) {
let seg = string.slice(array[2*i], array[2*i+1])
let firstIdx = string.slice(0, array[2*i-1])
let lastIdx = string.slice(array[2*i+2], array.length)
let segText = highlight(seg)
let result = firstIdx + segText + lastIdx
return result
}
}
render = () => {
return(
<View style={styles.textBox}>
{this.props.partialResults.map((result, index) => {
const hightlightText = this.setHighlight(result,[3,4])
return (
<Text style={styles.resultText} key={`partial-result-${index}`}>
{ hightlightText }
</Text>
)
})}
</View>
)
}
}
const styles = StyleSheet.create({
textBox: {
marginTop: 10,
width: 330,
height: 400,
backgroundColor: "white",
borderWidth: 2,
padding: 10,
borderColor: "dodgerblue"
},
resultText: {
fontSize: 18
},
highlighted: {
padding:2,
backgroundColor: 'red',
},
})
Not sure if your logic is sound but you can return the result as JSON Stringify, that may indicate what that object contains and give you a better idea what to return.
setHighlight = (string, array) => {
for (let i = 0; i < (array.length)/2; i++) {
let seg = string.slice(array[2*i], array[2*i+1])
let firstIdx = string.slice(0, array[2*i-1])
let lastIdx = string.slice(array[2*i+2], array.length)
let segText = highlight(seg)
let result = firstIdx + segText + lastIdx
return JSON.stringify(result)
}
}
I am using React Native. What I need to do is from a text that I extract from the DB to apply a format (font color, link) to make #mentions, when searching if in the text finds 1 single match makes replacement all good, but if there are several #mentions in the text, it throws me an error.
Text example:
hey what happened #-id:1- everything ok ?? and you #-id:2- #-id:3- #-id:4-
//// listusers its array, example: [idusuario: "1", usuario: "#luigbren", format: "#-id:1-".....]
const PatternToComponent = (text, usuarios,) => {
let mentionsRegex = new RegExp(/#-(id:[0-9]+)-/, 'gim');
let matches = text.match(mentionsRegex);
if (matches && matches.length) {
matches = matches.map(function (match, idx) {
let usrTofind = matches[idx]
//////////////////////////////////////////////////////////////////
const mentFormat = listusers.filter(function (item) {
const itemData = item.format;
return itemData.indexOf(usrTofind) > -1;
});
if (mentFormat.length > 0) {
let idusuario = mentFormat[0].idusuario
let replc = mentFormat[0].usuario
console.log(usrTofind) //// here find #-id:1-
console.log(replc) //// here is #luigbren for replace
////////// now here replace part of the string, #-id:1- with a <Text> component
///////// with #luigbren and the link, this is repeated for every #mention found
parts = text.split(usrTofind);
for (var i = 1; i < parts.length; i += 2) {
parts[i] = <Text key={i} style={{ color: '#00F' }} onPress={() => { alert('but this is'); }}>{replc}</Text>;
}
return text = parts;
/////////////////////////////////////////////////////////////////////
} else {
return text
}
});
} else {
return text
}
return text
};
in this way the code works well for me only when in the text there is only one mention, example 'hey what happened #-id:1- everything ok ??' , but when placing more than one mention it gives me an error, example: 'hey what happened #-id:1- everything ok ?? #-id:2- #-id:3-' ...
Error:
TypeError: text.split is not a function
And if instead of placing parts = text.split(usrTofind); I place parts = text.toString().split(usrTofind); it gives me this error:
[Object Object]
Update
I found the solution. I found another simple way to replace part of the text with the <Text> component, using this method:
funcMentToLinks = (text, idusuario) => {
const regex = /\#[id:([0-9]+]/g;
const splitText = text.split(regex)
if (splitText.length <= 1) {
return text;
}
const matches = text.match(regex);
return splitText.reduce(function (arr, element, index) {
if (matches[index]) {
return [arr, element, <Text key={index} style={{ backgroundColor: '#ceedf1', fontWeight: 'bold', color: '#4f73c4' }} onPress={() => this.callmyopc(idusuario)}>{replc}</Text>,]
} else {
return [arr, element]
}
}, []);
}
but now I have a problem, I can't find a way to call a function to navigate to another section or invoke direct the this.props.navigation.navigate('opc'), if i put my function .. example: onPress={() => this.callmyopc(idusuario)} gives me an error
Error:
_this2.callmyopc is not a function
if i put it this way onPress={() => {this.props.navigation.navigate('opc', { idperf: idusuario })}} gives me an error..
Error:
Cannot read property 'navigation' of undefined
Note: the functions this.props.navigation.navigate if they work for me in this same file, I use it in other functions, but in this function I cannot invoke it.
After entering text in TextInput
I want to know the currunt number of lines in TextInput.
Or
The current number of strings is also possible.
I've tried string.split('\n').length, but this code does not detect that the line is automatically incremented when the text is larger than the screen.
How do I get the number of lines
When I make this feature in Swift
Implemented using the string.enumerateLines function.
Do you have a similar function?
As far as I know, there is no offical way to get the currently used number of lines, but I have a workaround for you:
We make use of the onLayout method of our TextInput and we keep track of the currently used height. We need two state variables:
this.state = {
height: 0, // here we track the currently used height
usedLines: 0,// here we calculate the currently used lines
}
onLayout:
_onLayout(e) {
console.log('e', e.nativeEvent.layout.height);
// the height increased therefore we also increase the usedLine counter
if (this.state.height < e.nativeEvent.layout.height) {
this.setState({usedLines: this.state.usedLines+1});
}
// the height decreased, we subtract a line from the line counter
if (this.state.height > e.nativeEvent.layout.height){
this.setState({usedLines: this.state.usedLines-1});
}
// update height if necessary
if (this.state.height != e.nativeEvent.layout.height){
this.setState({height: e.nativeEvent.layout.height});
}
}
Render Method
render() {
console.log('usedLines', this.state.usedLines);
return (
<View style={styles.container}>
<TextInput multiline style={{backgroundColor: 'green'}} onLayout={(e)=> this._onLayout(e)} />
</View>
);
}
Working Example:
https://snack.expo.io/B1vC8dvJH
For react-native version >= 0.46.1
You can use onContentSizeChange for a more accurate line track, for example with react hooks as following:
/**
* used to tracker content height and current lines
*/
const [contentHeightTracker, setContentHeightTracker] = useState<{
height: number,
usedLines: number;
}>({
height: 0,
usedLines: 0
});
useEffect(() => {
// console.log('used line change : ' + lineAndHeightTracker.usedLines);
// console.log('props.extremeLines : ' + props.extremeLines);
if (contentHeightTracker.usedLines === props.extremeLines) {
if (extremeHeight.current === undefined) {
extremeHeight.current = contentHeightTracker.height;
}
}
//callback height change
if (contentHeightTracker.height !== 0) {
props.heightChange && props.heightChange(contentHeightTracker.height,
contentHeightTracker.usedLines >= props.extremeLines,
extremeHeight.current);
}
}, [contentHeightTracker]);
const _onContentHeightChange = (event: NativeSyntheticEvent<TextInputContentSizeChangeEventData>) => {
// console.log('event height : ', event.nativeEvent.contentSize.height);
// console.log('tracker height : ', lineAndHeightTracker.height);
// the height increased therefore we also increase the usedLine counter
if (contentHeightTracker.height < event.nativeEvent.contentSize.height) {
setContentHeightTracker({
height: event.nativeEvent.contentSize.height,
usedLines: contentHeightTracker.usedLines + 1
});
} else {
// the height decreased, we subtract a line from the line counter
if (contentHeightTracker.height > event.nativeEvent.contentSize.height) {
setContentHeightTracker({
height: event.nativeEvent.contentSize.height,
usedLines: contentHeightTracker.usedLines - 1
});
}
}
};
render() {
console.log('usedLines', this.state.usedLines);
return (
<View style={styles.container}>
<TextInput
multiline
style={{backgroundColor: 'green'}}
onContentSizeChange={_onContentHeightChange}
/>
</View>
);
}
The other solutions might fail if the user:
Highlights a lot of lines and deletes them all at once
Pastes a lot of lines at once
One way to solve this issue would be to set a lineHeight to the <TextInput> and use the onContentSizeChange prop:
<TextInput
style={{
lineHeight: 20,
}}
onContentSizeChange={e =>
console.log(e.nativeEvent.contentSize.height / 20) // prints number of lines
}
/>
I've created a text input where the length of the text entered determines the font size of the text.
The idea is to make something similar to the way Instagram has it: https://media.giphy.com/media/1AgCjLrT0XWhUh4UqY/giphy.gif
Now, this is what I'm able to do, that does things abruptly giving a bad user experience (reason being delay in updating fontSize): https://media.giphy.com/media/p37FFA1RhCuRHxIH2P/giphy.gif
This is my code:
render() {
let updatedFontSize = 32;
if(this.state.inputText.length > 0) {
updatedFontSize = 150 - 8*(this.state.inputText.length);
updatedFontSize = (updatedFontSize < 32 ? 32 : updatedFontSize);
}
return (
<TextInput
style={{ fontSize: updatedFontSize }}
onChangeText={ (inputText) => { this.setState({inputText}) } }
value={this.state.inputText}
multiline={true}
maxLength={25}
/>
)
}
Let's think about how to approach this.
The requirements are:
The effect should only happen with at least 3 characters in the text field
There's a maximum and minimum size that the font size can reach
The font should get smaller if the new text incoming is longer than the previous value.
The font should get larger if the new text incoming is shorter than the previous value.
Given these requirements based on the gif, we can come up with a clean solution.
changeText = value => {
if (value.length > 3) {
if (
this.state.value.length < value.length &&
this.state.fontSize > MIN_FONT_SIZE
) {
this.setState(({ fontSize }) => ({ fontSize: fontSize - INCREMENT, value }));
} else if (
this.state.value.length > value.length &&
this.state.fontSize < MAX_FONT_SIZE
) {
this.setState(({ fontSize }) => ({ fontSize: fontSize + INCREMENT, value }));
}
}
};
Here's the fully working example, https://snack.expo.io/#roach_iam/increasing-fontsize-textinput.
Hello i want to replace a font style inside <Text>,
this assumption variable x is dynamic variable.
for example :
when x="Hell" => Helloooo
when x="He" => Helloooo
when x="lloooo" => Helloooo
I tried with this code :
render() {
var x = "Hell"; // Hell/He/lloooo
var myOldString = "Helloooo";
var reg = new RegExp(x, "g");
var myNewString = myOldString.replace(reg, `<Text style={{fontWeight:'bold'}}>${x}</Text>`);
return (
<View>
<Text>{myNewString}</Text>
</View>
);}
But the result is just an ordinary string :
when x="Hell" => <Text style={{fontWeight:'bold'}}>Hell</Text>ooo
when x="He" => <Text style={{fontWeight:'bold'}}>He</Text>llooo
when x="lloooo" => He<Text style={{fontWeight:'bold'}}>lloooo</Text>
How to render a string inside <Text>, in order to be nested <Text>, not the ordinary string ?
this was a deceptively simple question, after much tinkering I think I have something for you.
This may not be the most efficient/elegant approach, but it worked when I tested it.
First you'll need 2 state values
constructor(props) {
super(props);
this.state = {
textToBold:'ell',
myArr:[],
};
};
One to keep a reference to the parts you are trying to bold and another to keep an array after we split the string.
Next, to fill 'myArr' with values will be done so via this code; all of which takes place at componentWillMount
componentWillMount() {
var reg = new RegExp(this.state.textToBold); //builds a RegExp from what you are trying to bold
var myOldString = "Helloooo"
var res = myOldString.split(reg); //splits the original string based on regex match
this.setState({
myArr: res //now you have an array of all remaining values
})
}
Finally in your render method, include a way to generate Components dynamically based on the array.
render() {
let result = this.state.myArr.map((a, i) => {
if ( i > 0) {
return (
<Text key={i}>
<Text style={{ fontWeight:'bold'}}>{this.state.textToBold}</Text>{ a }
</Text>
)
} else { //Case to accommodate for extra repeating values
return (<Text key={i}>{ a }</Text>)
}
});
return (
<View style={styles.container}>
<Text>
{ result }
</Text>
</View>
)
}
So now to "highlight" whatever text you want, you just change the state value, 'textToBold'.
I have solved the problem with Markdown
Try this code :
render() {
var x = "Hell"; // Hell/He/lloooo
var myOldString = "Helloooo";
var reg = new RegExp(x, "g");
var myNewString = myOldString.replace(reg, `**${x}**`);
return (
<View>
<Markdown>{myNewString}</Markdown>
</View>
);}