How to map() items in row with React-Bootstrap - css

I'm developping a React App with React-Bootstrap library.
I display a ShopResume component which render CardGroup element within a map funciton inside to map my data on each Card in my CardGroup.
I would like to Have a max of 3 Cards display by row. But none of all my change seems to work. Somebody could help me to fix it?
For now I have all my Card displayed on only one row. I would like to my render create new row when it displayed already 2 item on a row.
BONUS Question: My ternary operator don't display what it is supposed to even when the condition is true. If any idea where that could come. I will be gratefull.
Thanks a lot for your help and your precious answer like always
class ShopResume extends Component {
state = {};
render() {
return (
<React.Fragment>
{(this.props.filteredResults === [""]) ? "Aucun Résultat" :
( <CardGroup className="card_container">
{this.props.filteredResults.map((detail, index) => {
return (
<Card className="card_item" key={index}>
<Card.Img variant="top" src={detail.imgURL} id="card_img" />
<Card.Body>
<Card.Title className="shopTitle">{detail.nom}</Card.Title>
<Card.Text>{detail.resume}</Card.Text>
<Button variant="primary" onClick={this.props.filterClick}>Détails</Button>
<ListGroup className="list-group-flush">
<ListGroupItem>
{detail.startPrice === "" ? "Sur devis" : 'A partir de ' + detail.startPrice + ' €'}
</ListGroupItem>
</ListGroup>
</Card.Body>
</Card>
);
})}
</CardGroup>)}
</React.Fragment>
);
}
}
export default ShopResume;

You can wrap your list in a Container and a Row. Each item will be a Col item with an xs prop of 4, so that only 3 items will be displayed in one row (12 colums per row / each Card 4 columns = 3 Cards per row). Here is a codepen.

Related

Menu Opened/Closed States Inherited in Children with Staggered Transitions in Tailwind/React

I'm struggling to figure out how to code a header/menu in my React/Tailwind app.
Here are the requirements:
Header contains two sub-sections: Primary navigation and secondary navigation.
On desktop the primary navigation is on the left, and the secondary navigation is on the right.
On mobile the header contains a hamburger menu that contains both the primary and secondary navigation.
When this hamburger menu opens each navigation item appears with a staggered transition-delay.
What is the cleanest/most elegant approach to implement this with Tailwind?
I thought about separating Header/Navigation/NavigationItem into their own components, but the issue with this is that the Header needs to be aware of the NavigationItem components so that it can pass a isMenuOpen parameter to tell them to animate-in/out when the menu opens/closes.
It looks like this:
const App = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false)
const handleMenuToggle = (isMenuOpen) => {
setIsMenuOpen(isMenuOpen)
}
return (
<Header onMenuToggle={handleMenuToggle}>
<Navigation appearance="primary">
<NavigationItem appearance="primary" index={0} isMenuOpen={isMenuOpen}>
Menu Item #1
</NavigationItem>
<NavigationItem appearance="primary" index={1} isMenuOpen={isMenuOpen}>
Menu Item #2
</NavigationItem>
<NavigationItem appearance="primary" index={2} isMenuOpen={isMenuOpen}>
Menu Item #3
</NavigationItem>
<NavigationItem appearance="primary" index={3} isMenuOpen={isMenuOpen}>
Menu Item #4
</NavigationItem>
</Navigation>
<Navigation appearance="secondary">
<NavigationItem appearance="secondary" index={4} isMenuOpen={isMenuOpen}>
Facebook
</NavigationItem>
<NavigationItem appearance="secondary" index={5} isMenuOpen={isMenuOpen}>
Twitter
</NavigationItem>
</Navigation>
</Header>
)
}
This isn't elegant and the components are too heavily tied to each other.
Another option was to do it purely in CSS with a class like .header.is-open .navigation-item {} but this feels like it goes against the Tailwind patterns.
Is there another way to do this?
I thought about separating Header/Navigation/NavigationItem into their own components, but the issue with this is that the Header needs to be aware of the NavigationItem components so that it can pass a isMenuOpen parameter to tell them to animate-in/out when the menu opens/closes.
I don't see any reason why you can't pass isMenuOpen down as a prop. We can also clean up a lot of the duplication of similar JSX elements. The only difference from one NavigationItem to the next is the children content and the index, which increases by 1 each time.
We can create a MenuSection component that loops through items and applies the same props to all, incrementing the index.
const MenuSection = ({ appearance, isMenuOpen, startIndex = 0, menuItems }) => {
return (
<Navigation appearance={appearance}>
{menuItems.map((node, i) => (
<NavigationItem
key={i}
appearance={appearance}
index={i + startIndex}
isMenuOpen={isMenuOpen}
>
{node}
</NavigationItem>
))}
</Navigation>
);
};
This menuItems prop is an array of anything which can be used as the children property of the NavigationItem. So the elements can be a string or an element.
If we try to use this in App then it gets weird because we have to set the startIndex on the second MenuSection based on the count of items in the first. Does the index for the secondary navigation really need to continue from where the primary left off? If it does then we should extract one level deeper so that we can look at the length property of the first list.
const MenuPair = ({ isMenuOpen, primaryItems, secondaryItems }) => {
return (
<>
<MenuSection
isMenuOpen={isMenuOpen}
appearance="primary"
startIndex={0}
menuItems={primaryItems}
/>
<MenuSection
isMenuOpen={isMenuOpen}
appearance="secondary"
startIndex={primaryItems.length}
menuItems={secondaryItems}
/>
</>
);
};
const App = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const handleMenuToggle = (isMenuOpen) => {
setIsMenuOpen(isMenuOpen);
};
return (
<Header onMenuToggle={handleMenuToggle}>
<MenuPair
isMenuOpen={isMenuOpen}
primaryItems={[
"Menu Item #1",
"Menu Item #2",
"Menu Item #3",
"Menu Item #4"
]}
secondaryItems={["Facebook", "Twitter"]}
/>
</Header>
);
};
For this 6-item menu we haven't shortened the code by abstracting pieces into their own components, but at least we've ensured that the index property will always index properly without having to hard-code the numbers.

How to make 2 columns with React and Material-UI Grid with only one map to iterate over

I am using Material-UI Grid. What I’m trying to do is display the information iterated from my map function in two columns. However, currently it is only displaying in 1 column. As a test I added another Grid item sm={6} and used same map function. The 2 columns were achieved however both columns are displaying the exact same information. My question is how do you break a grid into two columns when there is only 1 map function to iterate off of?
const renderData = (person, picture, index) => {
return (
<Paper className={classes.Paper}>
<img src={person.picture.large} />
</Paper>
)
}
return (
<div className={classes.sectionContainer}>
<h3 className={classes.title}>Follow our instagram!</h3>
<h3 className={classes.title}>#platformdanceshowcase</h3>
<Grid direction='row' container spacing={1}>
<Grid item sm={6}>
{previewData.slice(0, visible).map(renderData)}
</Grid>
</Grid>
<Container className={classes.extendButtonArea}>
{visible < previewData.length && (
<Button className={classes.extendButton} onClick={loadMore}>
View More...
</Button>
)}
</Container>
</div>
)
}
Any help that can be provided would be greatly appreciated. I have walked myself in a circle and am further confusing myself, so thank you in advance!
It's hard to tell exactly what you're looking to do from the question, maybe you could markup a screenshot or provide a little sketch of what you're trying to achieve?
I'm not really sure why you have 2 return functions in your renderData component, but I'm going to interpret the first as representative of the elements of the previewData array and the second as the columns element you describe in the question.
My interpretation of your question is that you want two columns side by side that are rendering data in previewData from index 0 to visible. One way to achieve that might be to modify your code like this:
<Grid direction='row' container spacing={1}>
<Grid container item sm={6}>
{previewData.slice(0, visible).map((renderData, i) =>
i % 2 == 0 ? renderData : <></>)}
</Grid>
<Grid container item sm={6}>
{previewData.slice(0, visible).map((renderData, i) =>
i % 2 == 1 ? renderData : <></>)}
</Grid>
</Grid>
The map function provides a second parameter corresponding to the index, so you can distribute elements across columns using that parameter. You could also easily reconfigure this to show the first half of your items on the left and the second half of your items on the right.
<Grid direction='row' container spacing={1}>
<Grid container item sm={6}>
{previewData.slice(0, Math.Floor(visible / 2)).map(renderData)}
</Grid>
<Grid container item sm={6}>
{previewData.slice(Math.Ceil(visible / 2), visible).map(renderData)}
</Grid>
</Grid>

how to give one column gap between each item component using Material-ui Grid?

I am pretty new to Material UI for React,
As per design, i need to give One column gap between each item component (totaling 12 columns)
Ex. 3 Column item + 1 column Gap + 3 Column item + 1 column Gap + 3 Column item + 1 column Gap
How to achieve this using xs/lg etc without creating empty column item OR margin padding as that would be static.
You would need column offset, as with bootstrap grid, but it is not supported by material-ui.
In my opinion, there is nothing wrong with adding an extra column, you could event wrap it in a component:
function MyColumn({ children, ...rest }) {
return (
<React.Fragment>
<Grid item {...rest}>
{children}
</Grid>
<Grid item xs={1} />
</React.Fragment>
);
}

react native set state to text value from flatlist

In my react native app I have a sqlite storage solution and on this page I select a record from my table and use a flatlist to display the value, which works perfectly:
<FlatList
data={this.state.FlatListActivityItems}
ItemSeparatorComponent={this.ListViewItemSeparator}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<View key={item.id}>
<Text style={styles.textUnderlines}>{item.activity} <Text style={styles.recordsTypes}>
Minutes
</Text></Text>
</View>
)}
/>
The problem is, I need to use the same value of item.activity in another place in this page as a data object which currently uses state
this.state = {activity: ''}
Is there a way on page load that I can just take the value {item.activity} and set it to the activity state so that I can use it anywhere else in the page with the state call?
So where you are getting the data of flatlist, just there get the value and setstate it like below
vartemp = this.state.FlatListActivityItems[0].activity
this.setState({activity:vartemp})
I hope this helps....Thanks :)

How to make Material UI TextField less wide when in Table

I've got a Material UI Table.
I build it like this:
tableValues.map((curRow, row) => {
tableRows.push(
<TableRow key={this.state.key + "_row_" + row}>
{curRow.map((cellContent, col) => {
let adHocProps = {...this.props, type:"Text", label:"", value:cellContent}
return (
<TableCell className={classes.tableCell} key={this.props.key + "_row:" + row + "_col:" + col}>
{col===0 && this.props.rowHeaders ?
<div className={classes.header}>{cellContent}</div> :
<Question {...adHocProps} stateChangeHandler={this.handleTableChange("in build")} />}
</TableCell>
)})}
</TableRow>
);
return null;
});
return (
<Table key={this.props.key + "_table"} className={classes.table}>
<TableHead>
<TableRow>
{this.props.colHeaders.map((header) => <TableCell className={classes.tableCell} key={this.props.id + header}><div className={classes.header}>{header}</div></TableCell>)}
</TableRow>
</TableHead>
<TableBody>
{tableRows}
</TableBody>
</Table>
);
The Question is actually a glorified [TextField]2 created thusly:
<div>
<TextField
value={this.state.value}
onChange={this.handleTextChange(this.props.key)}
key={this.props.key}
id={this.props.id}
label={this.props.label}
placeholder={realPlaceholder}
className={classes.textField}
fullWidth
xmlvalue={this.props.XMLValue}
/>
</div>
... and then wrapped in Paper.
The styles are:
tableCell: {
padding: 5,
},
textField: {
padding: 0,
margin: 0,
backgroundColor: "#191",
}
This works, I get the appropriate content in each cell.... but the Question element is way wider than needed, and appear to have a min width and some padding I can't remove.
The table is full-width until you get to a certain point, then notice here:
that when the window is shrunk below a certain level, the table doesn't shrink any further. Acting as if the elements inside have a minimum width.
As a process of investigation, I change the Question element to simply return "Hi". When it does, the table then looks like this:
(which is to say, it condenses nicely... still too much padding on the tops and bottom and right, but WAY better)
So that leads me to believe the issue is with my Question component. I should note this happens on other Questions as well -- they all appear to have a min width when a width is not defined for them... UNLESS they are placed inside a container that has a designated width such as a Material UI Grid. For example, when placed in a `Grid and the window is shrunk, they shrink appropriately:
So why isn't the Table/TableCell also shrinking the TextField like the Grid does? (or: how do I remove the apparent "MinWidth" on my textFields?) Do Material UI TextFields have a minimum width if one isn't otherwise specified?
For what it's worth, I have tried specifying the column widths of the table -- with success when the table is wide, but it still doesn't solve the apparent minimum width issue.
I have also tried changing the Question component to <input type="text" name="fname" /> and still have the same problem. It's interesting that that the Question component is simply "hi" the problem disappears but that when it's an input, it shows up.
I have discovered that the native input fields default width is 20 characters: https://www.w3schools.com/tags/att_input_size.asp
The 'size' property is key here:
Specifies the width of an element, in characters. Default
value is 20
To set the width of the TextField, you must pass properties to the native input field.
If you wish to alter the properties applied to the native input, you
can do so as follows:
const inputProps = {
step: 300,
};
return <TextField id="time" type="time" inputProps={inputProps} />;
For my use case, the following modified the sizes of the TextFields to be 10 characters in size:
<TextField
value={this.state.value}
onChange={this.handleTextChange(this.props.key)}
key={this.props.key}
id={this.props.id}
label={this.props.label}
placeholder={realPlaceholder}
className={classes.textField}
fullWidth
xmlvalue={this.props.XMLValue}
inputProps={{
size: 10
}}
/>
Unfortunately, this is a bit squishy... it neither holds the input field at exactly size nor does it treat it like a minimum size.... There appears to be some heirarchy of sizing in play between GridItems, table Columns, free-flow flex areas, and the actual TextField elements... and I'm not well versed enough to know what always 'wins'.

Resources