So, here's my predicament
Here's what my cards currently look like:
Which looks all fine and dandy, however, when you change the screen size, this is what it looks like:
Which isn't great, because we want bootstrap apps, and really any website to be responsive these days.
What i want is for the cards to horizontally stretch, that way no matter what the width of our display is the cards will fill up the entire card deck - which is that bottom section titled 'Instrument List'
Now, if i put
style={{flexGrow: 1}}
onto each of the cards, then it seems to kind of work, however, not work as i really want it to.
If i have a row of cards that doesen't contain 5 cards, it will stretch them out:
I just want my card deck to stretch to fill the full container, but i also want it to keep a standard size for my cards
Here is the code i have for the Card Deck that generates the instruments from a JSON file, and the Card items.
The list code:
import InstrumentCard from './InstrumentCard'
import styles from '../styles/InstrumentList.module.scss'
import {CardDeck} from 'react-bootstrap'
const InstrumentList = ({instruments, onDel, onLoc, onShuf, onRep, onRepButClick, setRepInstrumentID}) => {
return (
<CardDeck key={1} className={styles.container} style={{display: 'flex', flexDirection: 'row'}}>
{instruments.map((instrument, i) => (
<InstrumentCard
key={i}
id={i}
instr = {instrument}
name={instrument.name}
description={instrument.description}
imagePath={instrument.image}
wikiLink={instrument.wikipedia}
tubeLink={instrument.youtube}
onDelete = {onDel}
isLocked = {instrument.locked}
onLock = {onLoc}
onShuffle = {onShuf}
onReplace = {onRep}
onRepButtonClick = {onRepButClick}
setReplacementInstrumentID = {setRepInstrumentID}
style={{flex: 1}}>
</InstrumentCard>
)) }
</CardDeck>
)
}
export default InstrumentList
The card code:
import {Card, Button, ButtonGroup, ButtonToolbar} from 'react-bootstrap'
import styles from '../styles/InstrumentCard.module.scss'
import {useState} from 'react'
const InstrumentCard = ({id, name, description, imagePath, wikiLink, tubeLink, onDelete, isLocked, onLock, onShuffle, setReplacementInstrumentID, onRepButtonClick}) => {
return (
<Card style={{ minWidth: '18rem', flexGrow: 1, margin:'1rem', minHeight:'32rem'}}>
<div>
<button id="lockButton" onClick={() => {onLock(id)}} className={isLocked ? styles.btnOverrideLocked : styles.btnOverride}>{isLocked ? "🔒" : "🔓"}</button>
<button id="shuffleButton" autoFocus={true} onClick={()=> {onShuffle(id)}} className={styles.btnOverrideShuffle} style={{float: "right"}}><span>🔀</span></button>
</div>
<Card.Img variant="top" src={imagePath} style={{padding:'1rem', maxHeight: '10rem', height:'100%', objectFit: 'contain'}} />
<Card.Body style={{display: 'flex', flexDirection: 'column', justifyContent: 'space-between'}}>
<div>
<Card.Title>{name}</Card.Title>
<Card.Text>
{description}
</Card.Text>
</div>
<div style={{marginTop: '.5rem'}}>
<ButtonGroup style={{width: "100%"}}>
<Button variant="danger" onClick={()=> onDelete(id)}>Delete</Button>
<Button variant="secondary" onClick={()=> {setReplacementInstrumentID(id); onRepButtonClick()}} >Replace</Button>
</ButtonGroup>
</div>
</Card.Body>
<Card.Footer>
<Card.Link target="_blank" rel="noopener noreferrer" href={wikiLink}>Wikipedia</Card.Link>
<Card.Link target="_blank" rel="noopener noreferrer" href={tubeLink}>Youtube</Card.Link>
</Card.Footer>
</Card>
)
}
export default InstrumentCard
Any help or explanations are very much appreciated! Thank you in advance!
Edit: Something i tried and failed was using map and creating a variable "let newIndex = (i*5) and attempt to use like newIndex + 1 to get to the other indexes, and try and generate 5 bootstrap columns within a row, however, this does not work, atleast with the map function, as there is no way to stop map from overshooting the actual amount of instruments, among other issues.
I have also tried adding a max width and height to my cards, however, that still allows them to grow and shrink by row, which we dont want
I feel like some kind of bootstrap Col and Row generation would be ideal, but i have no idea how to get it to generate only 5 elements per row using javascript in the return statement.
I have figured out a solution!
So, basically in my InstrumentList, i forgo the use of a card deck entirely, and just use a Row element instead. Inside the instrument.map, i surround every instrument card with a Col element that i then apply some custom styling to, because i want exactly five columns, and 12 does not divide by 5, so i had to fudge it a little.
Here's my final InstrumentList
import InstrumentCard from './InstrumentCard'
import styles from '../styles/InstrumentList.module.scss'
import {CardDeck, Row, Col} from 'react-bootstrap'
const InstrumentList = ({instruments, onDel, onLoc, onShuf, onRep, onRepButClick, setRepInstrumentID}) => {
return (
<Row key={1} className={styles.container} style={{display: 'flex', flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'flex-start' }}>
{instruments.map((instrument, i) => (
<Col className={styles.properCol}>
<InstrumentCard
key={i}
id={i}
instr = {instrument}
name={instrument.name}
description={instrument.description}
imagePath={instrument.image}
wikiLink={instrument.wikipedia}
tubeLink={instrument.youtube}
onDelete = {onDel}
isLocked = {instrument.locked}
onLock = {onLoc}
onShuffle = {onShuf}
onReplace = {onRep}
onRepButtonClick = {onRepButClick}
setReplacementInstrumentID = {setRepInstrumentID}
style={{flex: 1}}>
</InstrumentCard>
</Col>
)) }
</Row>
)
}
export default InstrumentList
Final InstrumentCard
import {Card, Button, ButtonGroup, Col} from 'react-bootstrap'
import styles from '../styles/InstrumentCard.module.scss'
import {useState} from 'react'
const InstrumentCard = ({id, name, description, imagePath, wikiLink, tubeLink, onDelete, isLocked, onLock, onShuffle, setReplacementInstrumentID, onRepButtonClick}) => {
return (
<Card style={{minWidth: '16rem', flexGrow: 1, margin:'1rem', minHeight:'32rem'}}>
<div>
<button id="lockButton" onClick={() => {onLock(id)}} className={isLocked ? styles.btnOverrideLocked : styles.btnOverride}>{isLocked ? "🔒" : "🔓"}</button>
<button id="shuffleButton" autoFocus={true} onClick={()=> {onShuffle(id)}} className={styles.btnOverrideShuffle} style={{float: "right"}}><span>🔀</span></button>
</div>
<Card.Img variant="top" src={imagePath} style={{padding:'1rem', maxHeight: '10rem', height:'100%', objectFit: 'contain'}} />
<Card.Body style={{display: 'flex', flexDirection: 'column', justifyContent: 'space-between'}}>
<div>
<Card.Title>{name}</Card.Title>
<Card.Text>
{description}
</Card.Text>
</div>
<div style={{marginTop: '.5rem'}}>
<ButtonGroup style={{width: "100%"}}>
<Button variant="danger" onClick={()=> onDelete(id)}>Delete</Button>
<Button variant="secondary" onClick={()=> {setReplacementInstrumentID(id); onRepButtonClick()}} >Replace</Button>
</ButtonGroup>
</div>
</Card.Body>
<Card.Footer>
<Card.Link target="_blank" rel="noopener noreferrer" href={wikiLink}>Wikipedia</Card.Link>
<Card.Link target="_blank" rel="noopener noreferrer" href={tubeLink}>Youtube</Card.Link>
</Card.Footer>
</Card>
)
}
export default InstrumentCard
And the styling for InstrumentList
.container {
margin: 2rem;
}
.properCol {
flex: 0 0 20%;
max-width: 20%;
position: relative;
width: 100%;
padding: 0;
}
@media (max-width: 1400px) {
.properCol {
flex: 0 0 25%;
max-width: 25%;
position: relative;
width: 25%;
padding: 0;
}
}
@media (max-width: 1150px) {
.properCol {
flex: 0 0 33.333333%;
max-width: 33.333333%;
position: relative;
width: 33.333333%;
padding: 0;
}
}
@media (max-width: 900px) {
.properCol {
flex: 0 0 50%;
max-width: 50%;
position: relative;
width: 50%;
padding: 0;
}
}
@media (max-width: 620px) {
.properCol {
flex: 0 0 100%;
max-width: 100%;
position: relative;
width: 100%;
padding: 0;
}
}
Sincerely hope this helps someone else in the future! One of the biggest PITA ive had to deal with in awhile. Cheers!