How to do word wrap with react-pdf - javascript

I have a pdf that is rendering as expected except for an issue where when a very long word is included in the info for the page, instead of wrapping the word onto a newline the word just continues right out the container, off the page, out the door to who knows where.
This is the set-up for one of the containers that's creating this issue. The styles followed by the actual structure.
const styles = StyleSheet.create({
section: {
paddingBottom: 20,
position: 'relative',
},
sectionTitle: {
fontSize: 12,
fontWeight: 700,
borderBottom: '2 solid #333333',
display: 'block',
marginBottom: 10,
textTransform: 'uppercase',
lineHeight: 1.2,
},
})
Below is the set-up of the section of the page
<View style={styles.section}>
<StyledText style={styles.sectionTitle}>Description</StyledText>
<StyledText style={{ width: '80%', fontWeight: 400 }}>
{data.description}
</StyledText>
</View>
here data.description is text in the form of:
"looooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnggggggggggggggggggggggggggggggggggggggggggggggggg"
something like this. The container should break the word but no matter how I try to style it the word doesn't respond.
How can I break the word when hits the end of the container?
I've tried using, wordWrap, wordBreak, flex, flexWrap, flexGrow/Shrink and overflowWrap so far. Nothing seems to have any effect

Try style={{ flex: 1 }} in the component you wish to word break.

You should cover your tags with <Text> and give attribute display:flex and flexDirection:row.
After these your text will break.

I Solved this problem by adding a maxWidth property to my column style.

This will split the text into word character and none word character chunks.
const WrapText = ({text}: {text?: string}) => (
<View style={{flexDirection: 'row', flexWrap: 'wrap'}}>
{text?.match(/\w+|\W+/g)?.map((seg, i) => (
<Text key={i}>{seg}</Text>
))}
</View>
);

In the style of {data.description}, try to use:
display: flex;
flex-direction:row;
flex-wrap: wrap;
Find more resources here : link

Related

React Native : Subviews of KeyboardAvoidingView have lag / move at different speeds

I would like them all the subviews to move in one clean motion, but if you look at the gif, you can see that the Text subview and the TextInput subview overlap and move at different speeds. It looks like the Text subview adjusts its position instantly where as the button and TextInput subviews adjust their position in more of an Ease in Ease out manner.
Main exported component
class SearchScreen extends React.Component {
state = {search:""}
render(){
getArguments = {
search: this.state.search
}
return (
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<Text style={styles.searchTitle}>Search for a Movie in the OMDB Database</Text>
<TextInput style={styles.searchField} onChangeText={text => this.setState({search:text})} ></TextInput>
<SearchButton navigation = {this.props.navigation} getArguments={getArguments}/>
</KeyboardAvoidingView>
)
}
}
Styling
const styles = StyleSheet.create({
container: {
flex:1,
backgroundColor: '#C8FEFE',
alignItems: 'center',
justifyContent: 'center'
},
searchButton: {
marginTop: 20,
backgroundColor: '#24D9E8',
borderRadius: 5,
padding: 5
},
searchField: {
backgroundColor: '#FFF',
textAlign: 'center',
width: 200,
borderRadius: 5,
margin: 20,
height: 30
},
searchTitle:{
fontWeight: 'bold',
fontSize: 20,
textAlign:'center'
}
});
Github
Full project on github
Solution 1: Quick fix for iOS
You can wrap your elements in a View, which will make them react to keyboard the way you want:
// styles
contentContainer: {
alignItems: 'center',
}
// SearchScreen
<View style={styles.contentContainer}>
<Text style={styles.searchTitle}>Search for a Movie in the OMDB Database</Text>
<TextInput style={styles.searchField} onChangeText={text => this.setState({search:text})} ></TextInput>
<SearchButton navigation = {this.props.navigation} getArguments={getArguments}/>
</View>
However, this will only work on iOS. Keyboard works slightly differently on Android. So solution 2 is a more solid way to do things.
Solution 2: Animations
Keyboard avoidance is quite tricky, Spencer Carli's article that Dominik referred to is a great resource for solutions using KeyboardAvoidingView. Those usually give you what you need. But if you want a smooth, controlled transition between keyboard states, you should use animations. The flow would go like this:
Add Keyboard event listeners to your component (keyboardDidShow and keyboardDidHide)
Wrap the content you want to move in an Animated.View
on keyboardDidShow, animate the y position of the Animated.View by the offset you want. The event returned (of type KeyboardEvent), along with the Dimensions API have all the measurements you need. Tip: use an Animated.timing animation with bezier Easing to control how the view moves.
on keyboardDidHide, repeat the animation but in the opposite direction.
Hope this helps!
<KeyboardAvoidingView keyboardVerticalOffset={Platform.OS === 'ios' ? 40 : 0}>
...
<KeyboardAvoidingView/>
You can try adding some offset to the view.
I would definitely style it with flexbox, try styling your container like this:
(it should move them at once, as flexbox will fit them on current width and height)
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;

Ellipsize text that is between two other texts without flexing trailing text to end

I have a view that has flexDirection: 'row' that will contain 3 text elements. I want the text elements to flow one after the other, but in the case that space is limited, I want the middle text element to be the one that is ellipsized.
Ex. where | is the edge of the screen
| John Smith johnsmith#test.com 9/24/19 |
but if the device is small
| John Smith johnsmith#t.. 9/24/19 |
A solution related to a different question suggested putting the middle text inside a view with flex: 1, which does cause the middle text to be the ellipsized text (when I also indicate the numberOfLines and ellipsizeMode properties) but that causes the trailing text to be pushed to the end, like the following
| John Smith johnsmith#test.com 9/24/19|
which I don't want.
Here is a snack with the code
https://snack.expo.io/Sk6_MldDH
Or here is an example component
export default class App extends React.Component {
render() {
return (
<View style={{ flex: 1 }}>
// If space is tight, I want the middle text ellipsized
<View style={{flexDirection: 'row', width: 250, borderWidth: 1}}>
<Text>John Smith</Text>
<View style={{flex: 1}}>
<Text ellipsizeMode="tail" numberOfLines={1} style={{ color: 'gray', marginHorizontal: 8}}>johnsmith#test.com</Text>
</View>
<Text style={{ color: 'gray'}}>9/24/19</Text>
</View>
// If plenty of space, I want the date to immediately follow the email, not have it pushed to the end
<View style={{flexDirection: 'row', width: 350, borderWidth: 1}}>
<Text>John Smith</Text>
<View style={{flex: 1}}>
<Text ellipsizeMode="tail" numberOfLines={1} style={{ color: 'gray', marginHorizontal: 8}}>johnsmith#test.com</Text>
</View>
<Text style={{ color: 'gray'}}>9/24/19</Text>
</View>
</View>
);
}
}
Here's a snack with my solution: https://snack.expo.io/rkEncIYvS
Within the View:
<Text ellipsizeMode='tail' numberOfLines={1}>John Smith <Text style={{ color: 'gray', marginHorizontal: 8}}>johnsmith#test.com</Text></Text>
<Text style={{ color: 'gray', flex: 1}}>9/24/19</Text>
I could not find a way to keep the name and email separate without the name overflowing into the next line.
However, with nested Text components, you can achieve a similar result.
Setting flex: 1 on the date ensures that the date Text component will attempt to fill the remaining space.

I am trying to place buttons at the bottom of the screen with React-Native and I really struggle to understand how to do it properly with flex support

I am trying to build up an application with React-Native and I need to place two buttons next to each other at the bottom of the screen. So far I wrote this code:
render() {
const { subtitle } = this.state;
return (
<View style={styles.container}>
<ActionBar onBackAction={this.backActionHandler} isBackAvailable />
<View style={styles.contentContainer}>
<Text>{subtitle}</Text>
</View>
<View style={styles.buttonsContainer}>
<Button title="BTN #1" />
<Button title="BTN #2" />
</View>
</View>
);
}
With these styles:
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'space-between'
},
contentContainer: {
flex: 1,
flexGrow: 1,
backgroundColor: 'purple'
},
buttonsContainer: {
flex: 1,
justifyContent: 'flex-end',
flexDirection: 'row',
marginBottom: 40,
backgroundColor: 'red'
}
});
And this is the result so far...
And this is what I want to achieve:
Could anybody point me where is my mistake? If this is important, the whole screen above is nested in another View with just flex: 1 property. I am trying to understand this flex conception, but I can't manage to do that.
Just change the flex to 0 for buttonsContainer style. When you put flex: 1 to both buttonsContainer and contentContainer, the available space is distributed among these two containers. But as per your requirement, contentContainer should take all the available space. So, setting contentContainer to flex: 1 is correct. To get the desired result set flex: 0 to buttonsContainer or you can avoid the flex property for buttonsContainer.
Read more about flex here.
Hope this will help!

The <Image> component cannot contain children. If you want to render content on top of the image, consider using absolute positioning

I am completing a tutorial on react native. For a certain screen, the instructor recommends the following code:
<Image source={bgImage} style={styles.backgroundContainer}>
<View style={styles.container}>
<Quote quoteText={this.props.text} quoteSource={this.props.source}/>
</View>
</Image>
But when I use this, I get the above error.
I've tried alternatives to this, but when I do, the background image doesn't even load.
EDIT: As requested below, here's my styling code. What I'm going for is using a background gradient image stored locally to the app code with text overlayed over that background. What I currently get by using the suggestion below is just the text at the very top of the screen and no background image.
const styles = StyleSheet.create({
backgroundContainer: {
flex: 1,
resizeMode: 'cover',
width: undefined,
height: undefined,
backgroundColor: '#889DAD',
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'transparent',
},
});
Since react-native 0.50 you can't nest components inside <Image> tag, rather you have to use <ImageBackground> for background images. Release Notes for v0.50.0
You are not allowed to put other components inside an image component. You need to wrap a View around your Image and positioned it as absolute.
<View style={{ flex: 1}}>
<Image source={bgImage} style={styles.backgroundContainer} />
<View style={styles.container}>
<Quote quoteText={this.props.text} quoteSource={this.props.source}/>
</View>
</View>
container: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
justifyContent: 'center',
alignItems: 'center',
},
EDIT:
Since react-native version 50.0, you can simply use ImageBackground
Use
<ImageBackground
source={image}
style={styles.contentContainer} >
// Your view content goes here
</ImageBackground>
const styles = StyleSheet.create({
contentContainer: {
flex: 1,
},
});
Works like a charm

ImageBackground ResizeMode

I recently updated React-native and it introduced a warning, with the following code:
<Image
source={require('../../assets/icons/heart.png')}
style={{
resizeMode: 'contain',
height: 25,
width: 25
}}
>
<Text>foobar</Text>
</Image>
And the warning:
index.ios.bundle:50435 Using <Image> with children is deprecated and
will be an error in the near future. Please reconsider the layout or
use <ImageBackground> instead.
Trouble is when I use ImageBackground component instead it gives me a warning that you can't use ResizeMode style with it.
<ImageBackground
source={require('../../assets/icons/heart.png')}
style={{
resizeMode: 'contain',
height: 25,
width: 25
}}
>
<Text>foobar</Text>
</ImageBackground>
And the warning:
Warning: Failed prop type: Invalid props.style key 'resizeMode'
supplied to 'View'. Bad object: { ResizeMode: 'contain, height: 25,
width: 25}
How are you supposed to use ImageBackgrounds? There doens't seem to be any documentation about it online.
ImageBackground accepts two style props – style and imageStyle – which are (obviously) applied to the internal View and Image respectively. It's also worth noting that height and width values from the container style are applied to the image style automatically.
For details visit this.
Move the resizeMode: 'contain' outside the inline style.
example:
<ImageBackground
source={require('../../assets/icons/heart.png')}
resizeMode= 'contain'
style={{
height: 25,
width: 25
}}
>
<Text>foobar</Text>
</ImageBackground>
I had exactly this issue; I ended up giving up on <ImageBackground> and went back to using <Image> but removed the elements inside it. I then wrapped the whole thing in a new <View> tag and positioned the <Image> absolutely in the styles. Here's where my code ended up if it's of use:
JSX
render() {
return (
<View style={{ alignItems: 'center' }}>
<Image
source={require('../images/pages/myImage.jpg')}
style={styles.backgroundImage}
/>
Style
const { width: viewportWidth, height: viewportHeight } = Dimensions.get('window');
const styles = StyleSheet.create({
backgroundImage: {
flex: 1,
position: 'absolute',
resizeMode: 'cover',
width: viewportWidth,
height: viewportHeight,
backgroundColor: 'transparent',
justifyContent: 'center',
alignItems: 'center'
},

Categories

Resources