Using Material-UI, the width of the Masonry Component doesn't fill the width of the parent container. The width of this missing space is exactly the width of the spacing, which makes sense if there's an element next to it.
I tried to calculate the width of the masonry to be the width of the Box element plus 8 * spacing, but this breaks as soon as there is a scrollbar involved.
How can I use the full width of the container for Masonry?
mwe (just an example from the documentation with a Box added on top):
const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80];
const Item = styled(Paper)(({ theme }) => ({
...theme.typography.body2,
color: theme.palette.text.secondary,
border: '1px solid black',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}));
<Container>
<Box style={{ border: '1px solid black', padding: '20px' }}>
<Typography variant="h5">
An Element to show the width of the contianer
</Typography>
</Box>
<Box style={{ marginTop: '20px' }}>
<Masonry columns={4} spacing={4}>
{heights.map((height, index) => (
<Item key={index} sx={{ height }}>
{index + 1}
</Item>
))}
</Masonry>
</Box>
</Container>
Screenshot of the MWE. Missing Area marked in red:
You can fix this by setting marginRight with the negation of your masonry spacing in the sx prop.
<Box sx={{ marginTop: '20px', marginRight: -4 }}>
{/* Masonry code */}
</Box>
I fix it simply by changing Masonry component width from "100%" to "auto",
I don't know why, but it works great.
<Masonry columns={4} spacing={4} sx={{ width: "auto" }}>
{* Masonry items *}
</Masonry>
Related
Im using Material UI on my react app and I'm using useMediaQuery and useTheme from mui. This is the code I have right now. Is there a better way to optimize less code? There are only a few style changes between the 2 codes.
const MainPage = () => {
const theme = useTheme();
const isMatch = useMediaQuery(theme.breakpoints.down('md'))
return (
<div className='mainPage'>
{
isMatch ? (
<>
<Box sx={{ display: "flex", justifyContent: "center", alignContent: "center", flexDirection: "column", padding: "60px 10px 10px 10px" }}>
<Box component="img" src={LandingImage} sx={{ width: "100%" }} />
<Box sx={{ paddingTop: 8 }}>
<Typography sx={{ fontSize: 26, fontWeight: "bold", fontFamily: "sans-serif", textAlign: "center", paddingBottom: 5 }}>About us</Typography>
</Box>
</Box>
</>
) : (
<>
<Box sx={{ display: "flex", justifyContent: "center", alignContent: "center", flexDirection: "row", paddingTop: 20 }}>
<Box component="img" src={LandingImage} />
<Box sx={{ width: 700, paddingTop: 8 }}>
<Typography sx={{ fontSize: 30, fontWeight: "bold", fontFamily: "sans-serif", textAlign: "center", paddingBottom: 5 }}>About us</Typography>
</Box>
</Box>
</>
)}
</div>
)
}
There really is no need to do a media query like this, as the sx prop provides per-breakpoint customization if you want it.
Notice, for example, the flexDirection styling on the first Box component. Everything up to the md breakpoint gets column, then it becomes row.
const MainPage = () => {
const theme = useTheme();
return (
<div className='mainPage'>
<Box sx={{
display: "flex",
justifyContent: "center",
alignContent: "center",
flexDirection: { xs: "column", md: "row" },
padding: { xs: "60px 10px 10px 10px", md: "20px 0 0 0" }
}}>
<Box
component="img"
src={LandingImage}
sx={{
width: { xs: "100%", md: 'unset' }
}}/>
<Box sx={{
paddingTop: 8,
width: { md: 700 }
}}>
<Typography
sx={{
fontSize: { xs: 26, md: 30 },
fontWeight: "bold",
fontFamily: "sans-serif",
textAlign: "center",
paddingBottom: 5
}}>
About us
</Typography>
</Box>
</Box>
</div>
)
}
https://mui.com/system/basics/#responsive-values
As the structure of JSX for mobile / desktop is the same, you could drop one of those two JSX templates, build a variable to store component configuration based on isMatch and pass this variable into component template.
const MainPage = () => {
const theme = useTheme();
const isMatch = useMediaQuery(theme.breakpoints.down('md'))
// Subset of props, to illustrate the idea.
const config = isMatch ? {fontSize: 26} : {fontSize: 30};
// Here only root <Box/> is configured, but you can configure all the nested components the same way.
return (
<div className='mainPage'>
<Box sx={config}>[...]</Box>
</div>
)
}
(Same with components nested inside <Box/> - the idea is the same - declare some variables with value based on your state and pass them to JSX declaration)
There are several options. The first is to create a style object in javascript, which can be interacted with like normal javascript. You are already doing this in-line, but if we do it in the code above, we can make it dynamic.
const myOuterBoxStyle = {
display: "flex",
justifyContent: "center",
alignContent: "center",
flexDirection: "row",
paddingTop: 20
}
if (isMatch) {
myOuterBoxStyle.flexDirection = "column";
myOuterBoxStyle.paddingTop = undefined;
myOuterBoxStyle.padding = "60px 10px 10px 10px";
}
Once you do all of the dynamic styling you need, you can make a single return for your component and simply put
<Box sx={{myOuterBoxStyle}}>
The other option is to make a seperate CSS sheet and import it, and then use classes. Something like
.outer-box {
display: "flex";
justify-content: "center";
align-content: "center";
}
.is-match {
flex-direction: "column";
padding: "60px 10px 10px 10px"
}
And then you can either add the is-match class or, perhaps, the is-not-match class depending.
The final option is to use a third-party package that does most of this for you, like Tailwind
Mui has breakpoints shorthand syntax, as you can check here.
So, for example, your code will also work with:
const MainPage = () => {
return (
<div className="mainPage">
<Box
sx={{
display: "flex",
justifyContent: "center",
alignContent: "center",
flexDirection: ["column", "column", "row"],
paddingTop: ["60px 10px 10px 10px", "60px 10px 10px 10px", 20]
}}
>
<Box component="img" />
<Box sx={{ width: ["unset", "unset", 700], paddingTop: 8 }}>
<Typography
sx={{
fontSize: [26, 26, 30],
fontWeight: "bold",
fontFamily: "sans-serif",
textAlign: "center",
paddingBottom: 5
}}
>
About us
</Typography>
</Box>
</Box>
</div>
);
};
In the example above i use the Breakpoints as an array and mui docs says:
The second option is to define your breakpoints as an array, from the smallest to the largest breakpoint.
So imagine that array positions would be: [xs, sm, md, lg, xl] and the breakpoints are equivalent to theme.breakpoints.up.
Another way to do it is use Breakpoints as an object:
Simple example
<Box
sx={{
width: {
xs: 100, // theme.breakpoints.up('xs')
sm: 200, // theme.breakpoints.up('sm')
md: 300, // theme.breakpoints.up('md')
lg: 400, // theme.breakpoints.up('lg')
xl: 500, // theme.breakpoints.up('xl')
},
}}
>
I'm using libraries: "react-native-svg": "12.1.0" & "react-native-svg-transformer": "0.14.3"
import FooIcon from '../assets/images/icons/fooIcon.svg';
<View style={{ flex: 1, paddingTop: 8 }}>
<FooIcon></FooIcon>
<View style={{ position: 'absolute', top: 35, bottom: 0, left: 14, right: 0 }} >
<Text>foo</Text>
</View>
</View>
How can I have "foo" text centered over the FooIcon. The solution above does not center the text which is important because "foo" text length can change and it has to be in center in every case.
this chunk of code should do the work for you
<View
style={{
justifyContent: 'center',
alignItems: 'center'
}}
>
<SVG /> //your svg image component goes here
<Text
style={{
position: 'absolute'
}}
>
Test
</Text>
</View>
I would recommend not to use an absolute position if the content size can change dynamically. I would build this with just using flex-box:
...
<View style={styles.itemContainer}>
<Text>Foo</Text>
<View style={styles.iconMock} />
</View>
...
const styles = StyleSheet.create({
itemContainer: {
alignItems: "center",
//this has default flex: 0, which means the container will always have the size the children take.
//we then tell it to centre all the children (items) which gives the needed effect.
},
iconMock: {
backgroundColor: "blue",
height: 50,
width: 150,
}
});
Building it this way the text will always be centred:
I have created a React Bootstrap Card with four columns as per the grid system layout. I'm not the greatest at styling or CSS but I would like the text to be wrapped if it is too long- so for instance, in the image below, "BosniaAndHerzegovina" has pushed one of the columns to the bottom of the card, when I really want the text to be wrapped according to the column size.
Here is my JSX for the first column:
return (
<Card style={{ width: '25rem' }}>
<Card.Body>
<Row>
<Col sm='auto'>
<Row style={{ marginTop: '5px', marginBottom: '2.5px' }}>
{teams[0].name}
</Row>
<Row style={{ marginTop: '5px', marginBottom: '2.5px' }}>
{teams[1].name}
</Row>
<Row style={{ marginTop: '5px', marginBottom: '2.5px' }}>
{teams[2].name}
</Row>
<Row style={{ marginTop: '5px', marginBottom: '2.5px' }}>
{teams[3].name}
</Row>
</Col>
.
.
.
How do I get the text to wrap around a fixed column width?
to break long texts into multiple lines you can use the css property word-break passing break-all:
word-break: break-all;
.box {
width: 50px;
word-break: break-all;
}
<div class="box">myreallylongsentenceshouldbebrokeninmultiplelines</div>
I want to use the fontAwesome + icon such that it is in the middle of a circle. I want to use it as one icon item. I read that we can use it along with the circle icon and place it inside that but I couldn't make it work.
import IconFA from 'react-native-vector-icons/FontAwesome';
<IconFA
name="plus"
size={moderateScale(30)}
color="black"
style={styles.thumbnail}
/>
{/* <IconFA
name="circle-thin"
size={moderateScale(67)}
color="white"
/> */}
thumbnail: {
height: 68,
width: 68,
position: 'relative',
},
Alternatively, I read about 'stacked' font awesome icons but couldn't understand how to use it in react native.
Reference: https://jsfiddle.net/1d7fvLy5/1/
Snack Expo:
https://snack.expo.io/9Ild0Q1zG
I want to make something like this:
I am also open to using a <Thumbnail> if I find a similar icon's link but I couldn't find any such free icon online.
The JSFiddle example that you posted creates the circle using a CSS border with border-radius to make it circular. We can do pretty much the same thing in react-native, though borderRadius in react-native can only be a fixed number and not a percent (edit: this limitation is specific to typescript since the borderRadius property has type number. Percentage strings do work at runtime).
You can tweak this code however you want, but this will get the job done. You can use IconFA and CircleBorder as two separate nested components but I also made a component IconInCircle which combines the two.
const IconInCircle = ({ circleSize, borderWidth = 2, borderColor = 'black', ...props}) => (
<CircleBorder
size={circleSize}
borderWidth={borderWidth}
borderColor={borderColor}
>
<IconFA {...props} />
</CircleBorder>
);
const CircleBorder = ({ size, borderWidth, borderColor, children }) => (
<View
style={{
width: size,
height: size,
borderRadius: 0.5 * size,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
borderColor,
borderWidth,
}}>
{children}
</View>
);
The IconInCircle component takes three props specific to the border: circleSize, borderWidth, and borderColor. All other props are passed through into the IconFA child component.
Basically what we are doing is placing the icon inside of a fixed-size View with a circular border and centered contents.
Now we can use it like so:
<IconInCircle
name="plus"
size={30}
color="black"
style={styles.thumbnail}
borderWidth={1}
circleSize={50}
/>
Expo Link
Try this, just adjust according to your needs, and also don't forget to support other browsers for flex.
const styles = StyleSheet.create({
thumbnail: {
height: 68,
width: 68,
position: 'relative',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
border: '1px solid #333',
borderRadius: '50%'
},
});
import IconFA from 'react-native-vector-icons/FontAwesome';
<View style={{
position:'relative',
justifyContent:'center',
alignItems:'center',
width:40,
height:40,
backgroundColor:'black'
}}>
<IconFA name='circle-thin' size={40} color='grey'/>
<IconFA name='plus' size={20} color='white' style={{position: 'absolute', zIndex: 99}} />
</View>
I am new to ReactNative, but above snippet should work in your case
Snack Expo
I'm trying to create a header with an image so I write this:
<View style={{
flex: 1,
backgroundColor: "#FFFFFF",
padding: 20
}}
>
<View style={{ flex: 3 }}>
<Image
source={this.images.header}
style={{
flex: 1,
alignSelf: "flex-end", // HERE
resizeMode: "contain",
marginTop: -20,
marginLeft: -20
}}
/>
</View>
</View>
The weird part is alignSelf: "flex-end" - this align the image at the left side! As far as I know, it must be alignSelf: "flex-start" to align left.
Am I wrong?
PS: I use marginTop: -20 and marginLeft: -20 to stick the image to borders of the device (because of padding: 20 the container)
Any idea?
Thank in advance!
I think it is because the Image cover the whole space but the image data is resized so that you think it is only in a part of the view. Try removing flex: 1and set the widthand heightproperly or at leaset one of both.