I'm trying to build a custom modal off this react carousel library
Basically, I'm at the point where the modal is implemented correctly, but I can't seem to find out how to wire the featured image on the modal to always be the same like the one which is currently also being featured in the carousel.
const Modal = ({ modal, items, handleModalFalse, currentIndex, featured }) => {
return (
<div className={modal ? "modal" : "hide"} onClick={handleModalFalse}>
{/* this only features the first image */}
{/* <img src={featured} /> */}
{/* shows every image on modal*/}
{items.map((item, i) => (
<div key={i} className="wrapper">
<img src={item} />
</div>
))}
</div>
);
};
These are all the handlers that power the Carousel.
I'm struggling to wire the featured image correctly (perhaps inside componentDidMount() ?)
class Gallery2 extends React.Component {
state = {
currentIndex: 0,
items: [
"https://homepages.cae.wisc.edu/~ece533/images/airplane.png",
"https://homepages.cae.wisc.edu/~ece533/images/baboon.png",
"https://homepages.cae.wisc.edu/~ece533/images/cat.png",
"https://homepages.cae.wisc.edu/~ece533/images/boat.png"
],
modal: false,
featured: undefined
};
// what should go here instead??
componentDidMount() {
this.setState({ featured: this.state.items[0] });
}
slideTo = i => console.log("clicked") || this.setState({ currentIndex: i });
onSlideChanged = e => this.setState({ currentIndex: e.item });
slideNext = () =>
this.setState({ currentIndex: this.state.currentIndex + 1 });
slidePrev = () =>
this.setState({ currentIndex: this.state.currentIndex - 1 });
handleEnlargeClick = () => {
this.setState({ modal: true });
};
handleModalFalse = () => {
this.setState({ modal: false });
};
render() {
return (
<>
<h3>React Alice Carousel</h3>
{/* MODAL */}
<Modal
modal={this.state.modal}
items={this.state.items}
handleModalFalse={this.handleModalFalse}
currentIndex={this.state.currentIndex}
featured={this.state.featured}
/>
<RenderGallery
items={this.state.items}
responsive={this.responsive}
onSlideChanged={this.onSlideChanged}
currentIndex={this.state.currentIndex}
/>
<button onClick={() => this.handleEnlargeClick()}>
click to enlarge
</button>
<RenderThumbs
items={this.state.items}
slideNext={this.slideNext}
slidePrev={this.slidePrev}
slideTo={this.slideTo}
responsive={this.responsive}
/>
</>
);
}
}
*******
You can see the full Carousel implementation and bug in action here on this code sandbox. Please feel free to fork it.
Right now my Modal only either shows the first image or all of them...
I understand why I currently only get either the first or all of the images, I just can't figure a solution for my issue.
How can I make the featured image on the Modal to be the same as the one currently on the carousel?
The issue in your code is that your modal is looping over all of the items, and displaying them when you click the "enlarge" button. To fix this, you just need to use the selectedIndex with your items variable to get the currently selected item.
Sandbox example: https://codesandbox.io/s/21p19wmpyy
Modal Code:
const Modal = ({ modal, items, handleModalFalse, currentIndex, featured }) => {
return (
<div className={modal ? "modal" : "hide"} onClick={handleModalFalse}>
{/* this only features the first image */}
{/* <img src={featured} /> */}
{/* shows every image on modal*/}
<div className="wrapper">
<img src={items[currentIndex]} />
</div>
</div>
);
};
Related
In Short, Let's assume that I have a list of items that I am displaying, and on each item there is a button, like a Quick View button to view extra information about the product in a modal pop-up. Now, everything is working perfectly, except when I click on the button, it brings the information related to the last item of my API/JSON file .. whatever the source of the file is. So, the last item in the Array. when I console.log(index)
it brings the correct index for each card, but it doesn't show the information about each product separately.
import React, { Component } from "react";
import { productsInfo } from "./ProductsData"; // the JS file containing the data
import Modal from "react-modal";
import "./Products.css";
import ProductDetailsPopUp from "./ProductDetailsPopUp";
import axios from "axios";
Modal.setAppElement("#root");
export default class Products extends Component {
state = {
productsData: [],
modalIsOpen: false,
};
OpenModal = () => {
this.setState({ modalIsOpen: true });
};
CloseModal = () => {
this.setState({ modalIsOpen: false });
};
changeProduct = (item, index) => {
this.setState({ showProduct: item });
};
render() {
// const {id, img, title, price, isNew, country, currency} = productsInfo
return (
<div className="ProductsContainer">
{productsInfo.map((item, index) => {
return (
<div className="cardHolder" key={item.id}>
<img src={item.img} alt="Products" />
<button
onClick={() => {
this.OpenModal();
console.log(index);
}}
className="MainProductBtn"
>
QUICK VIEW
</button>
<Modal
key={index}
className="mainModal"
style={{
overlay: {
backgroundColor: "#3333",
opacity: 0.4,
transition: "0.4s",
},
}}
isOpen={this.state.modalIsOpen}
onRequestClose={this.CloseModal}
>
<div className="popupHeader" key={item.id}>
<h3>{item.title}</h3>
<button onClick={this.CloseModal}>×</button>
</div>
<ProductDetailsPopUp />
</Modal>
<p>{item.title}</p>
<small>{`${item.price} USD`}</small>
</div>
);
})}
</div>
);
}
}
I tried including the index in onClick function and also passing it in the state, didn't work
You create Modal for every item inside your map method meaning that it will display productsInfo.length modals and the last one will be on top.
Remote Modal tag from the map method and onClick set the current item at your state, change the display of the dialog to true and read the current item from the state inside your Modal.
e.g.
state = {
productsData: [],
modalIsOpen: false,
currentItem: null
};
OpenModal = item => {
this.setState({ modalIsOpen: true, currentItem: item });
};
onClick={() => {
this.OpenModal(item);
console.log(index);
}}
I have images for every Project when i click on a image i want to open a modal for that specific object and display some info. But when i click on it now it opens a modal for every project i want it to only display a modal for the specific image i clicked on. ( on the image the red X button is the content that the modal renders and "Volvo Blog" is the projectName when selecting the image to the right.) when i click on the image to the right i want it to only display "Volvo Blog" under that image instead of all images
class ProjectsSection extends Component {
state = {
Projects: [],
openModal: false,
selectedModal: null,
};
async componentDidMount() {
const { data } = await getProjects();
this.setState({ Projects: data });
console.log(this.state.Projects);
}
openModal = project => {
this.setState({ openModal: true, selectedModal: project });
};
closeModal = project => {
this.setState({ openModal: false });
};
render() {
return (
<div className="Section">
<h1>Welcome to my Projects 😀</h1>
<ul className="Projects">
{this.state.Projects.map(project => (
<li key={project._id} className="ProjectsShowcase">
{" "}
when this image is clicked the modal opens.
<img
className="ProjectImage"
src={project.image}
alt=" of Project"
onClick={() => {
this.openModal(project);
console.log(project._id);
}}
/>
here i conditionally render the modal. EDIT: passed the values of the
selected object as props
{this.state.openModal ? (
<ProjectModal closeModal={this.closeModal}
projectName={this.state.selectedProject.projectName}
projectImage={this.state.selectedProject.image}
projectDescription={this.state.selectedProject.description}
/>
) : null}
</li>
))}
</ul>
</div>
);
}
}
I think that you could make life a bit simpler for yourself by reducing the amount of state. The final piece of the puzzle then is just what logical check you need to make to determine if the modal is there. Below is what I think you could use, with some explanation in the comments.
class ProjectsSection extends Component {
state = {
Projects: [],
// openModal: false, - don't need, can tell just by if we have an id or not
selectedModalId: null // use null to represent no selection
};
async componentDidMount() {
const { data } = await getProjects();
this.setState({ Projects: data });
}
openModal = (project) => {
this.setState({ selectedModalId: project._id }); // just use an id
};
closeModal = () => {
this.setState({ selectedModalId: null }); // just reset to initial state
};
render() {
return (
<div className="Section">
<h1>Welcome to my Projects 😀</h1>
<ul className="Projects">
{this.state.Projects.map((project) => {
// doing it with a variable for ease of reading
const shouldShowModal = project._id === this.state.selectedModalId;
return (
<li key={project._id} className="ProjectsShowcase">
<img
className="ProjectImage"
src={project.image}
alt=" of Project"
onClick={() => {
this.openModal(project);
}}
/>
{shouldShowModal ? (
// here note change to use the mapped project, not what is in state as that's more likely
// to result in problems down the line
<ProjectModal
closeModal={this.closeModal}
projectName={project.projectName}
projectImage={project.image}
projectDescription={project.description}
/>
) : null}
</li>
);
})}
</ul>
</div>
);
}
}
I want to make the Mantine burger auto close after a nav choice is selected in the modal. It seems as if it is not possible for the Mantine burger to automatically close (because the x icon stays). I am using NextJS with Mantine & react-icons.
export default function HeaderMiddle({ links }: HeaderMiddleProps) {
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
const dark = colorScheme === 'dark';
const [opened, { toggle, open, close }] = useDisclosure(false, {
onOpen: () => burgerClick(),
onClose: () => closeAllModals(),
});
const [active, setActive] = useState(links[0].link);
const { classes, cx } = useStyles();
const items = links.map((link) => (
<a
key={link.label}
href={link.link}
className={cx(classes.link, {
[classes.linkActive]: active === link.link,
})}
onClick={(event) => {
event.preventDefault();
setActive(link.link);
}}
>
{link.label}
</a>
));
const burgerItems = links.map((link) => (
<a
key={link.label}
href={link.link}
className={cx(classes.link, {
[classes.linkActive]: active === link.link,
})}
onClick={(event) => {
event.preventDefault();
setActive(link.link);
closeAllModals();
}}
>
{link.label}
</a>
));
const burgerClick = () => {
openModal({
title: 'Subscribe to newsletter',
children: <>{burgerItems}</>,
});
};
return (
<Header height={56} mb={120}>
<Container className={classes.inner}>
<Burger
opened={opened}
onClick={toggle}
size="sm"
className={classes.burger}
/>
<Group className={classes.links} spacing={5}>
{items}
</Group>
{/* <MantineLogo size={28} /> */}
<Group spacing={0} className={classes.social} position="right" noWrap>
...
</Group>
</Container>
</Header>
);
}
Any help with this would be appreciated.
next: 12.1.5
#mantine/core: 4.2.12
After clicking the link call close method of useDisclosure, it will make opened to false. Burger state is depend on opened so, it wil close the burger on false.
const burgerItems = links.map((link) => (
<a
key={link.label}
href={link.link}
className={cx(classes.link, {
[classes.linkActive]: active === link.link,
})}
onClick={(event) => {
event.preventDefault();
setActive(link.link);
closeAllModals();
// It will set opened to false (so it will close Burger)
close()
}}
>
{link.label}
</a>
));
I found something that might help you
On the Mantine Doc, one header called 'Responsive header' is working fine after you clicked on any of the burger items.
Simply follow the codes and should be fine!
I have created a component in my ReactJS app with a Button and a div. My goal is to press the button and show/hide the div, which currently works. But I will re-use the component so I will have multiple Buttons and divs. But I always only want one div to show.
If I press a button, hide all the divs from the same component and show the div from the button I just pressed, otherwise if the button I just pressed div is open hide it. It work the same as Bootstrap's dropdown button, but I prefer not to use Bootstrap's dropdown as I would like to create my own custom button.
I import the below Hide component in my App.js file. It works by hiding or showing the div, but would like to hide all other open div's apart from the button I currently pressed if it is not open yet.
Here is the code I currently have and use my Mycomp twice,
function hide () {
return (
<div>
<Mycomp />
<Mycomp />
</div>
);
}
function Mycomp(){
const[dp, setDp] = useState("none");
const toggle = () => {
if (dp === "none"){
setDp("block")
}else{
setDp("none")
}
}
return(
<div>
<button onClick={toggle}>Test</button>
<div style={{display: dp}}>{dp}</div>
</div>
)
}
export default hide;
You can change your component this way to get what you want . try to run this code to see the result
function Hide() {
const [visibleDivIndex, setVisibleDivIndex] = React.useState(0);
const handleVisibleDiv = id => setVisibleDivIndex(id);
const divArr = [1, 2, 3]; // just to show haw many component we will use
return (
<div>
{divArr.map((item, index) => (
<Mycomp
key={index}
id={index}
visibleDivIndex={visibleDivIndex}
handleVisibleDiv={handleVisibleDiv}
/>
))}
</div>
);
}
function Mycomp({ id, visibleDivIndex, handleVisibleDiv }) {
return (
<div>
<button onClick={e => handleVisibleDiv(id)}>Test</button>
<div style={{ display: id === visibleDivIndex ? "block" : "none" }}>
My Div Content
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<Hide />
</React.StrictMode>,
rootElement
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
The basic idea would be to move the state up to the container.
function Hide() {
const defaultDisplay = () => Array.from({length: 2}).fill(false);
const [displays, setDisplays] = useState(defaultDisplay());
function onClick(id) {
const temp = defaultDisplay();
temp[id] = true;
setDisplays(temp);
}
return (
<div>
{
displays.map((display, i) => {
return <Mycomp display={display} id={i} onClick={onClick} />;
}
}
</div>
);
}
function Mycomp({display, id, onClick}) {
const[dp, setDp] = useState(display);
useEffect(() => {
setDp(display ? 'block' : 'none');
}, [display]);
return(
<div>
<button onClick={() => onClick(id)}>Test</button>
<div style={{display: dp}}>{dp}</div>
</div>
)
}
export default Hide;
I am new to StackOverflow so pardon me for bad wording of the problem.
I am learning React currently and as a small project, I am creating a course-search app which will filter the courses on the basis of input entered by the user from a JSON file and display them.
I have added modals to each card component which should open on the click of a button within the card.
The problem I'm facing is that when I click the button, it only opens the modal first clicked one, and none else.
Here is the code:
import MyModal from '../Modal/Modal'
import courseList from "./courses.json";
class App extends Component {
state = {
search: "",
modal: false,
};
selectModal = (id) => {
this.setState({
modal: {
[id]: !this.state.modal
}
})
}
rendercourse = course => {
var dep = course.dep;
return (
<div className="col-md-3" style={{ marginTop: "20px" }}>
<Card className="card">
<CardBody className={dep}>
<CardTitle title={course.id}>
{course.code}
</CardTitle>
<CardText className="title">{course.name}</CardText>
<p className="ic">{course.ic}</p>
Units: {course.unit}<br />
<button onClick= {
this.selectModal.bind(this, course.code)}>More Info</button>
</CardBody>
<MyModal
displayModal={this.state.modal[course.code]}
coursename={course.name}
coursecode={course.code}
courseic={course.ic}
coursedep={course.dep}
courseunit={course.unit}
closeModal={this.selectModal} />
</Card>
</div>
);
};
onchange = e => {
this.setState({ search: e.target.value });
};
render() {
const { search } = this.state;
const filteredcourses = courseList.filter(course => {
return course.name.toLowerCase().indexOf(search.toLowerCase()) !== -1;
});
return (
<div className="flyout">
<main style={{ marginTop: "4rem" }}>
<div className="container">
<div className="row">
<div className="col-12">
<center>
<h3>
Search for a course
</h3>
</center>
</div>
<div className="col">
<Input
label="Enter the name of the course"
icon="search" className="in"
onChange={this.onchange}
/>
</div>
<div className="col" />
</div>
<div className="row">
{filteredcourses.map(course => {
return this.rendercourse(course);
})}
</div>
</div>
</main>
</div>
);
}
}
export default App;
And here is the modal component:
const MyModal = props => {
function displayInfo () {
return (
<div>
<div>{props.coursename}</div>
<div>{props.courseic}</div>
<div>{props.courseunit}</div>
<div>{props.coursedep}</div>
<div>{props.coursecode}</div>
</div>
);
}
function closeModal (e) {
e.stopPropagation()
props.closeModal()
}
let modal = (
<div
className="modal"
onClick={ closeModal }>
<div className="modal-content"
onClick={ e => e.stopPropagation() }>
<span
className="close"
onClick={ closeModal }>×
</span>
<div className="modal-flex">
{displayInfo()}
</div>
</div>
</div>
)
return ( props.displayModal ? modal : null);
}
export default MyModal;
I want the card-specific modal to open up whenever the button is clicked.
Your selectModal function doesn't flip the state of each modal, but only that of the first one.
If any modal is defined in the state (be it the first or any other modal), this.state.modal will evaluate to true. (an object with at least some content is true)
To allow all your modals to be opened at the same time simply flip the value of the modal element in your modal state object.
this.state.modal[id] is undefined by default, which will evaluate to boolean false.
selectModal = (id) => {
this.setState({
modal: {
[id]: !this.state.modal[id]
}
})
}
If you'd rather only open one modal at a time, you should simply store the id of your modal in the state instead of tracking all modals in there:
selectModal = (id) => {
this.setState({
modal: id
})
}
To close a modal change your closeModal prop to pass undefined/false instead of the modal ID and change your modal display prop so it will perform the check whether your modal state and the coursecode match:
this.state.modal === course.code