React .map creating another container for images - reactjs

I have a map with display flex with some images and a firebase storage where I can upload a pic, and the pic gets shown in that flex gallery, but there should not be more than 6 in that gallery, and when more than 6 pictures are uploaded, another container should be made where the new pics get placed, the images are held in a state,the code is here
//This is the container styles
.container {
display: flex;
justify-content: center;
align-items: center;
margin: 10vmin;
overflow: hidden;
transform: skew(5deg);
// This is the map
<div className=" grid grid-rows-3 bg-black">
<div className={styles.container}>
{imageList.map((url) => {
return (
<div className={styles.card}>
<img src={url} />
</div>
);
})}
</div>
// This is the
I need to make that map somehow know that once a container gets to 6 pictures, it should make another one in the row below,
Thank you for helping

You could try using the Array.prototype.slice() method to separate the image list into two arrays and then render them with two separate maps.
const imageListLength = imageList.length;
let imageListTop, imageListBottom;
// Split image list into two arrays
if (imageListLength > 6) {
imageListTop = imageList.slice(0, 6);
imageListBottom = imageList.slice(6);
} else {
imageListTop = imageList;
imageListBottom = [];
}
return (
<div className="grid grid-rows-3 bg-black">
<div className={styles.container}>
{imageListTop.map((url) => {
return (
<div className={styles.card}>
<img src={url} />
</div>
);
})}
</div>
{imageListBottom.length > 0 && (
<div className={styles.container}>
{imageListBottom.map((url) => {
return (
<div className={styles.card}>
<img src={url} />
</div>
);
})}
</div>
)}
</div>
);
EDIT Taking your answer:
You could expand on your code by adding a loop to the code that renders the images. This loop would check the length of the image list and render the images in groups of 6 until the list is exhausted.
const imageListLength = imageList.length;
let imageListTop, imageListBottom;
if (imageListLength > 6) {
imageListTop = imageList.slice(0, 6);
imageListBottom = imageList.slice(6);
} else {
imageListTop = imageList;
imageListBottom = [];
}
return (
<div className="grid grid-rows-3 bg-black">
{/* Loop through the image list */}
{imageList.map((url, index) => {
if (index % 6 === 0) {
return (
<div className={styles.container}>
{imageList.slice(index, index + 6).map((url) => {
return (
<div className={styles.card}>
<img src={url} />
</div>
);
})}
</div>
);
}
})}
</div>
);

Related

Problem with row-alignment using React & Bootstrap GRID system

Using React.JS and BootStrap grid system, I'm aiming to build the following page.
However, with the code linked at the bottom, the page looks like this (the alignment of the elements is wrong in multiple places. For example, the banner and search bar aren't aligned with the rest of the elements.
What am I doing wrong in using the BootStrap Grid System?
Before that, I'd like to specify that all the 4 MinWidget.jsx components are wrapped inside a MinWidgetCollection.jsx component. And all the elements encompased by the red border are wrapped inside a PageInformation.jsx component. The MiniWidgetCollection must take 7/12 of the page width while the BigWidget one the rest of 5/12 (with BootStrap grid having a maximum of 12 columns per row). Similarly, the first chart below must have 7/12 of width while the second chart only 5/12.
This is the current code which produces errors in alignment.
function App() {
return (
<div className='container'>
<Searchbar/>
<Banner/>
<PageInformation/>
</div>
)
}
export const Searchbar = (props) => {
return (
<div className='row mb-5' style={{border: "1px solid green"}}>
<div className='card-header p-7 w-50'>
</div>
)
export const Banner = (props) => {
return (
<div className='row'>
<div className='card mb-5 mb-xl-10'>
</div>
</div>
)
export const PageInformation = (props) => {
return (
<div style={{border: "1px solid red"}}>
<div className='row'>
<MinWidgetCollection />
<BigWidget />
</div>
<div className='row'>
<Chart newClassname='col-md-7 col-xs-12' />
<Chart newClassname='col-md-5 col-xs-12' />
</div>
</div>
)
}
export const MinWidgetCollection = (props) => {
return (
<div className='row col-md-7 col-xs-12'>
<MinWidget />
<MinWidget />
<MinWidget />
<MinWidget />
</div>
)
}
export const MinWidget = (props) => {
return (
<div className='col-md-6 col-xs-12'>
<div className='card card-md-6 card-xs-12 mb-xl-8'>
</div>
</div>
)
}
export const BigWidget = (props) => {
return (
<div className='col-md-5 col-xs-12'>
<div className="card card-xl-stretch mb-5 mb-xl-8 h-100">
</div>
</div>
)
}
export const Chart = (props) => {
<div className={props.newClassname}>
<div className={`card mt-5 `}>
</div>
</div>
}

Grabbing an index to map around it to style my blogs page

I'm trying to style my blogs page for my website. I integrated my Contentful blog using API's and its all displayed correctly, but I just don't know how to target individual elements to style them individually. I would like to be able to style my main blogs page as a grid layout, attached is the specific grid layout I would like to achieve. I just don't know the code syntax to achieve this. I've been told I need to target the index and use .map but I'm still quite confused
[![the grid layout I want to achieve ][1]][1]
I would also like to be able to add tags to fetch the tags from my Contentful and display them on my blogs main page and on the individual slugs page but I dont know the syntax to fetch this data
import Footer from "../components/Footer/Footer";
import { createClient } from "contentful";
import BlogCard from "../components/BlogCard/BlogCard";
export async function getStaticProps() {
const array = [1, 4, 9, 16];
const map = array.map((x) => x * 2);
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
previewacessToken: process.env.PREVIEW_ACCESS_TOKEN,
});
const res = await client.getEntries({ content_type: "menuItem" });
return {
props: {
blog: res.items,
},
};
}
export default function Blog({ blog }) {
return (
<div>
<div className="bg-[#F5f5f5] pt-16 max-md:pt-10 max-lg:pt-4">
<div className="flex flex-col">
<header className="z-10">
<Navbar />{" "}
</header>
<main className="flex-1">
<div className="relative left-1/2 mt-12 min-w[100vw] translate-x-[-50%]">
<div className="relative mx-auto pb-30 lg:pb-30">
<div className="px-8 md:px-8.5 lg:px-40">
<div className="mx-auto max-w-container">
<div className="max-md:w-full">
<div className="grid grid-cols-2 grid-rows-3 gap-10 mt-24 mb-32 h-full sm:grid-cols-2 sm:h-full sm:w-full md:mb-24 md:w-full max-lg:block max-md:mt-16 max-md:mb-4">
{/* {blog.map((index) => (
<BlogCard key={index} menuItem={index} />
))} */}
{/* {blog.map((menuItem) => (
<BlogCard key={menuItem.sys.id} menuItem={menuItem} />
))} */}
{blog.map((menuItem) => (
<BlogCard key={menuItem.sys.id} menuItem={menuItem} />
))}
</div>
</div>
</div>
<Footer />
</div>
</div>
</div>
</main>
</div>
</div>
</div>
);````
[1]: https://i.stack.imgur.com/YzU1P.png

How to add custom arrow buttons to Alice-Carousel?

I am making a carousel component with alice-carousel (https://github.com/maxmarinich/react-alice-carousel/blob/master/README.md) but having trouble customising the arrows. Code as follows
export const Carousel: React.FC<CarouselProps> = ({text }) => {
const [activeIndex, setActiveIndex] = useState(0);
const items = ["item1","item2","item3"]
const slidePrev = () => {
activeIndex==0?(
setActiveIndex(items.length-1)):
setActiveIndex(activeIndex - 2);
};
const slideNext = () => {
activeIndex==items.length-1?(
setActiveIndex(0))
: setActiveIndex(activeIndex + 2)
};
return(
<div className="grid grid-cols-3">
<div className="col-span-2>
{text}
</div>
<div className="flex justify-end">
<button className="px-8" onClick={slidePrev}><ArrowL/></button>
<button className="px-8" onClick={slideNext}><ArrowR/></button>
</div>
<div className="col-span-3 px-10">
<AliceCarousel
mouseTracking
disableDotsControls
disableButtonsControls
activeIndex={activeIndex}
items={items}
responsive={responsive}
controlsStrategy="responsive"
autoPlay={true}
autoPlayInterval={5000}
infinite={true}
keyboardNavigation={true}
/>
</div>
</div>
)}
Above code changes the activeIndex therefore changing the items display order but it does so without the "slide" animation. I've looked at examples provided in the library used however cant seem to get it to slide smoothly. What am I doing wrong?
I encourage you to use the library's options to reduce the complexity of your implementation and stop unwanted behavior.
According to the documentation, there are two functions renderPrevButton and renderNextButton with the AliceCarousel to render your custom component (any elements, icons, buttons, ...) for the Prev and Next item on the gallery.
So, instead of defining a custom button with a custom action handler, pass your desired component to the mentioned function and give them some styles for customization.
export const Carousel: React.FC<CarouselProps> = ({text}) => {
const items = ["item1","item2","item3"]
return (
<div className="grid grid-cols-3">
<div className="col-span-2"> // ---> you forgot to add closing string quotation mark
{text}
</div>
<div className="col-span-3 px-10">
<AliceCarousel
mouseTracking
disableDotsControls
// disableButtonsControls // ---> also remove this
// activeIndex={activeIndex} // ---> no need to this anymore
items={items}
responsive={responsive}
controlsStrategy="responsive"
autoPlay={true}
autoPlayInterval={5000}
infinite={true}
keyboardNavigation={true}
renderPrevButton={() => {
return <p className="p-4 absolute left-0 top-0">Previous Item</p>
}}
renderNextButton={() => {
return <p className="p-4 absolute right-0 top-0">Next Item</p>
}}
/>
</div>
</div>
)
}
Note: you need to remove the disableButtonsControls option from the AliceCarousel to handle the custom buttons properly. also, you don't need to use the activeIndex option anymore since the carousel will handle them automatically.
As a sample, I passed a p element with my renderPrevButton without any onClick action. you can define your custom icon, image, or any element and passed them.
Hi for the renderNextButton/renderPrevButton you have to declare a function first, then pass that function to the render option of the Alice Carousel.
import ArrowBackIosIcon from '#mui/icons-material/ArrowBackIos';
import ArrowForwardIosIcon from '#mui/icons-material/ArrowForwardIos';
export const Carousel: React.FC<CarouselProps> = ({text}) => {
const items = ["item1","item2","item3"]
const renderNextButton = ({ isDisabled }) => {
return <ArrowForwardIosIcon style={{ position: "absolute", right: 0, top: 0 }} />
};
const renderPrevButton = ({ isDisabled }) => {
return <ArrowBackIosIcon style={{ position: "absolute", left: 0, top: 0 }} />
};
return (
<div className="grid grid-cols-3">
<div className="col-span-2"> // ---> you forgot to add closing string quotation mark
{text}
</div>
<div className="col-span-3 px-10">
<AliceCarousel
mouseTracking
disableDotsControls
// disableButtonsControls // ---> also remove this
// activeIndex={activeIndex} // ---> no need to this anymore
items={items}
responsive={responsive}
controlsStrategy="responsive"
autoPlay={true}
autoPlayInterval={5000}
infinite={true}
keyboardNavigation={true}
renderPrevButton={renderPrevButton}
renderNextButton={renderNextButton}
/>
</div>
</div>
)
}

hover over cards with React using the useref hook

I have used a useRef hook so that when I mouseover to particular card then the opacity of the other card becomes 0.4 I have a figure out a solution to this but I am thinking this might not be best solution and its quite lengthy too. Feel free to recommend me best solution regarding this. Here is my code and i have used bootstrap to create the card.
import React, { useRef } from 'react'
export default function Cardss() {
const cardOne = useRef();
const cardTwo = useRef();
const cardThree = useRef();
const mouseOverOpacityForCardOne = (e) => {
cardTwo.current.style.opacity = "0.4";
cardThree.current.style.opacity = "0.4";
}
const mouseOutOpacityForCardOne = (e) => {
cardTwo.current.style.opacity = "1";
cardThree.current.style.opacity = "1";
}
const mouseOverOpacityForCardTwo = (e) => {
cardOne.current.style.opacity = "0.4";
cardThree.current.style.opacity = "0.4";
}
const mouseOutOpacityForCardTwo = (e) => {
cardOne.current.style.opacity = "1";
cardThree.current.style.opacity = "1";
}
const mouseOverOpacityForCardThree = (e) => {
cardOne.current.style.opacity = "0.4";
cardTwo.current.style.opacity = "0.4";
}
const mouseOutOpacityForCardThree = (e) => {
cardOne.current.style.opacity = "1";
cardTwo.current.style.opacity = "1";
}
return (
<section className="container-fluid section-three">
<h2 className="display-3">Projects</h2>
<div className="row">
<div ref={cardOne} onMouseOver={mouseOverOpacityForCardOne} onMouseOut={mouseOutOpacityForCardOne} className={"col-md-4 col-12 mb-5"}>
<div className="card cards">
<img className="card-img-top" src="..." alt="Card image cap"/>
<div className="card-body">
<h5 className="card-title">Special title treatment</h5>
<p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
</div>
</div>
</div>
<div ref={cardTwo} className={"col-md-4 col-12 mb-5"} onMouseOver={mouseOverOpacityForCardTwo} onMouseOut={mouseOutOpacityForCardTwo}>
<div className="card cards">
<img className="card-img-top" src="..." alt="Card image cap"/>
<div className="card-body">
<h5 className="card-title">Special title treatment</h5>
<p className="card-text">With supporting text below as a natural lead-in to additional content.</p>
</div>
</div>
</div>
<div ref={cardThree} onMouseOver={mouseOverOpacityForCardThree} onMouseOut={mouseOutOpacityForCardThree} className={"col-md-4 col-12 mb-5"}>
<div className="card cards">
<img className="card-img-top" src="..." alt="Card image cap"/>
<div className="card-body">
<h5 className="card-title">Special title treatment</h5>
<p className="card-text">With supporting text below as a natural lead-in to additional content.</p>
</div>
</div>
</div>
</div>
</section>
)
}
You can accomplish using a combination of state variables and onMouseOver and onMouseLeave props.
Essentially, when the mouse is over a card, you store its index in the state variable, then have the class of the card be dynamic such that any index not equal to the state variable gets a class that applies the opacity: 0.4 to that card.
Here's a Codepen example illustrating this. I used opacity: 0.2 instead
To make the code less lengthy, let's first turn a card into a component.
Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.
const Card = ({ // I'm using default parameters here
imageSrc = "https://source.unsplash.com/random/400x400",
title = "Special title treatment",
text = "With supporting text below as a natural lead-in to additional content.",
...props // pass the rest props to the wrapping div
}) => (
<div {...props}>
<div className="card cards">
<img className="card-img-top" src={imageSrc} alt="Unsplash Random" />
<div className="card-body">
<h5 className="card-title">{title}</h5>
<p className="card-text">{text}</p>
</div>
</div>
</div>
);
Then, to achieve the opacity change, you can track the active card (the one with mouse over) with state and apply CSS classes to style the cards:
// Cards.js
function Cards() {
const [active, setActive] = useState(-1); // I'm using -1 to indicate no active cards
const getCardClassName = index => {
if (active > -1 && index !== active) return "fadeOut";
return "";
};
return (
<section
className="container-fluid section-three"
>
<h2 className="display-3">Projects</h2>
<div className="row">
{[0, 1, 2].map(i => ( // or [...Array(3).keys()].map
<Card
key={i}
className={`col-md-4 col-12 mb-5 ${getCardClassName(i)}`}
onMouseOver={() => {
setActive(i);
}}
onMouseOut={() => {
setActive(-1);
}}
/>
))}
</div>
</section>
);
}
// style.css
.fadeOut {
opacity: 0.4;
}
Here is a working example:

Issue mapping over array with .map()

I have a react component which maps over an array of movies in the render function, and logging out the image of each one. I'm having trouble mapping out each image itself to an <img /> tag. It currently appears that all of the images are being spit out into the one <img /> tag, so how can I loop through the images and insert each one into its own image tag?
render () {
const movies = this.state.movies.map((movie, i) => {
console.log("Image url: ", movie.images.boxArt)
})
return (
<div className='welcome'>
<div className='container'>
<div className='movies'>
{ this.state.loading &&
<Loader size='10' />
}
{ !this.state.loading &&
<div className='movie'>
<img src={ movies } /> <===SHOULD END UP BEING A ROW OF MOVIE POSTERS
</div>
}
</div>
</div>
</div>
)
}
}
For each array element render an image and specify the src. Like this:
const movies = this.state.movies.map((movie, i) => <img key={i} src={ movie } />)
<div className='movie'>
{ movies }
</div>

Resources