Reputation: 6007
So I have gotten really close here: https://snack.expo.dev/@taustinligonier/f42d1f to get this working. I seem to be able to easily get the grid size I want (2x2, 3x3, etc...). However when it comes to adding a gap to only the middle part of the grid (no padding/margin on the outside) I have come to a complete stop.
I thought that doing margin
on the children and -margin
on the parent would work, but after some research negative margin on the bottom and right don't work the same as the top and left so I get some weird results (as in the wrapping no longer works for flex).
I could calculate the width of the item and use justify-content: space-between
, but I think I can only do that horizontally or vertically not both with flex.
I can get this "working" with margin and padding but then there is spacing on the outside of the children which is what I don't want.
Maybe there is a way to add another container or something that would make it "look" like the children are touching the outer edge, but really they are not?
If you have any ideas I would be delighted to know! Thanks in advance.
Upvotes: 1
Views: 650
Reputation: 3560
I think using flex on all components in a grid and using justifyContent:'space-between'
are incompatible. Flex distribute the remaining width/height base upon the flex ratios and space-between shares the remaining width/height on the axis between all non first/last styled components. Because flex has already used the remaining width/height to increase the components size, then there will be no remaining width/height to use to add spacing between the components.
Here's where I realized this.
What I came up with was to apply margin to the opposite axis, e.g marginVertical is applied to the row, and marginHorizontal to columns. It works out ok-ish:
import * as React from 'react';
import { Text, View, StyleSheet, ScrollView } from 'react-native';
import Constants from 'expo-constants';
import { colorGenerator } from '@phantom-factotum/colorutils';
import Grid from './components/Grid';
const items = colorGenerator(11).map((color, i) => {
return {
title: 'Item' + (i + 1),
color,
};
});
export default function App() {
return (
<ScrollView style={styles.container}>
<Grid
rows={3}
columns={4}
items={items}
containerStyle={{
height: 400,
width: '100%',
backgroundColor: 'pink',
marginVertical: 15,
}}
rowStyle={{ marginVertical: 10 }}
columnStyle={{ marginHorizontal: 5 }}
/>
<Grid
rows={4}
columns={3}
items={items}
containerStyle={{
height: 400,
width: '100%',
backgroundColor: 'lightgreen',
marginVertical: 10,
}}
rowStyle={{ marginVertical: 20 }}
columnStyle={{ marginHorizontal: 5 }}
/>
<Grid
rows={2}
columns={6}
items={items}
containerStyle={{
height: 400,
width: '100%',
backgroundColor: 'lightblue',
marginVertical: 10,
}}
rowStyle={{ marginVertical: 20 }}
columnStyle={{ marginHorizontal: 5 }}
/>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
import * as React from 'react';
import { Text, View, StyleSheet, Image } from 'react-native';
const isRowEdge = (i, totalRows) =>
i % totalRows == 0 || i % totalRows == totalRows - 1;
export default function Grid({
rows = 3,
columns = 3,
items,
containerStyle,
itemStyle,
rowStyle,
columnStyle,
}) {
const initialValue = Array(rows)
.fill([])
.map(() => Array(columns).fill({}));
let rowIx = 0;
const renderRows = items.reduce((prev, curr, i) => {
let index = i % columns;
if (i % columns == 0 && i > 0) {
rowIx++;
}
prev[rowIx][index] = curr;
return prev;
}, initialValue);
console.log('rows:', renderRows.length, '\ncolumns:', renderRows[0].length);
return (
<>
<Text>
Rows:{rows} Columns:{columns}
</Text>
<View style={[styles.container, containerStyle]}>
{renderRows.map((cols) => {
return (
<View style={[styles.row, rowStyle]}>
{cols.map((item) => {
return (
<View
style={[
styles.col,
styles.item,
columnStyle,
{ backgroundColor: item.color },
!item.title && styles.emptySpace,
]}>
<Text>{item.title || ''}</Text>
</View>
);
})}
</View>
);
})}
</View>
</>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
flexWrap: 'wrap',
},
row: {
flexDirection: 'row',
// flex: 1,
width: '100%',
justifyContent: 'space-between',
margin: 2,
},
col: {
flexDirection: 'column',
justifyContent: 'space-between',
margin: 2,
flex: 1,
height: '100%',
},
item: {
borderWidth: 1,
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
emptySpace: {
borderWidth: 0,
},
});
Upvotes: 1