select only one card reactjs - javascript

I am creating 3 cards and I need you to be able to select one of them and it has a div inside when I select it and change the css, the example that I have the selected card is the second one and I would like the same to happen with the others but that only one can be selected at the same time
import React from "react";
import "./styles.css";
export default function App() {
return (
<div className="App">
<div className="card"></div>
<div className="card cardSelect">
<div className="select">select</div>
</div>
<div className="card"></div>
</div>
);
}
.card {
width: 5rem;
height: 5rem;
background: red;
margin: 1rem;
}
.cardSelect {
box-shadow: 2px 4px 30px 0px rgba(0, 0, 0, 0.75);
}

You can achieve this using an orchestrator, or a parent component.
I have created you a simpler example at code sandbox
The active card needs to be controlled from a higher instance. In this case it is the App component it self. But you can create a Cards and Card component to encapsulate it. e.g.
<Cards>
<Card />
<Card />
<Card />
</Cards>
Here is the code of the example in the code sandbox:
import React from "react";
import "./styles.css";
const cardStyle = {
width: "5rem",
height: "5rem",
background: "red",
margin: "1rem"
};
const cardSelect = {
boxShadow: "2px 4px 30px 0px rgba(0, 0, 0, 0.75)"
};
export default function App() {
const [selected, setSelected] = React.useState(0);
return (
<div className="App">
<div
style={{ ...cardStyle, ...(selected === 0 && cardSelect) }}
onClick={() => setSelected(0)}
>
{selected === 0 && <div className="select">select</div>}
</div>
<div
style={{ ...cardStyle, ...(selected === 1 && cardSelect) }}
onClick={() => setSelected(1)}
>
{selected === 1 && <div className="select">select</div>}
</div>
<div
style={{ ...cardStyle, ...(selected === 2 && cardSelect) }}
onClick={() => setSelected(2)}
>
{selected === 2 && <div className="select">select</div>}
</div>
</div>
);
}

What you need to do is to
Add a state in the app level selection somewhere, eg. `const [selection, setSelection] = useState('card-0')
Create a component for cards, e.g.
Render that component three times <CardComponent isSelected={selection === 'card-2'} />`
Inside Cardcomponent, add that class only if it is selected. className={"card " + prop.isSelected ? "cardSelect" : ""}

Related

Same page section navigation using dynamic useRef hook in React

There are many packages available to navigate between sections on same page. But I don't want to integrate any package as dependency for doing a simple task. Here is a sample code for navigation which is not dynamic
import React, { useRef } from "react";
function Navigation() {
const top = useRef(null);
const scrollAnySection = (ref) => {
window.scrollTo({
top: ref.current.offsetTop - 10,
behavior: 'smooth',
});
};
return (
<>
<div className="navigation" style={{ width: "80%", margin: "3rem auto" }}>
<section className="menu--container">
<h3 onClick={() => scrollAnySection(top)}>Top</h3>
</section>
<section>
<h3 ref={top} className="section--items"
style={{ margin:"100rem 0",textAlign:"center",border:"1px solid blue" }}> Top </h3>
</section>
</div>
</>
);
}
export default Navigation;
But I am getting difficulty with dynamic useRef here where the ref of HTML element will be called from an array. Below is a sample what I am trying to do for dynamic contents. But I am not getting the expected result.
import React, { useRef } from "react";
function Navigation() {
const listSection = ["Head", "Body", "Main", "Footer", "Bottom"];
const navigateToSection = useRef([]);
const scrollAnySection = (ref) => {
window.scrollTo({
top: ref.current.offsetTop - 10,
behavior: 'smooth',
});
};
return (
<>
<div className="navigation" style={{ width: "80%", margin: "3rem auto" }}>
<section className="menu--container" style={{ display: "flex", justifyContent: "space-between"}}>
{ listSection.map((item) => (
<h3
style={{ padding: ".5rem 2rem", borderRadius: "25rem", border: "1px solid blue" }}
className="menu--items"
onClick={() => scrollAnySection ({ item }) }
>{ item }</h3>
))
}
</section>
<section>
{ listSection.map((item, index) => (
<h3
className="section--items"
style={{ margin: "100rem 0", textAlign: "center", border: "1px solid blue" }}
ref={ ref => { navigateToSection.current[index] = ref; }}
>This is { item } section</h3>
))
}
</section>
</div>
</>
);
}
export default Navigation;
I am getting Uncaught TypeError: ref.current is undefined at scrollAnySection and onClick() event.
Any suggestion or a better approach will be appreciated. Thanks.

I am trying to align movieCards side by side. But they are aligning in a column

This is my movie list component. I tried different attributes in the div section to get the cards in a row. But all the cards are aligned in a column.should i use either flex or grid.If yes how can i use them. Even i referred to many resources for this.But didn't worked for me. What should I do to get the cards as I wish . Please assist me.
import React, { useState } from 'react'
import { useSelector } from 'react-redux'
import MovieCard from './MovieCard'
const MoviesList = (props) => {
const [search, setSearch] = useState('')
const [filterBy, setFilterBy] = useState([])
const [orderBy, setOrderBy] = useState('')
const movies = useSelector((state) => {
console.log(state.movies)
return state.movies
})
const handleChange = (e) => {
const inputValue = e.target.value
setSearch(inputValue)
const filteredValue = movies.filter((movie) => {
return movie.Title.toLowerCase().includes(inputValue)
})
setFilterBy(filteredValue)
console.log(filteredValue)
}
const handleSelectChange = (e) => {
setOrderBy(e.target.value)
}
const show = (movies) => {
switch (orderBy) {
case 'a-z': return [...movies.sort((a, b) => a.Title.localeCompare(b.Title))]
case 'z-a': return [...movies.sort((a, b) => b.Title.localeCompare(a.Title))]
case '1-100': return [...movies.sort((a, b) => a.imdbRating - b.imdbRating)]
case '100-1': return [...movies.sort((a, b) => b.imdbRating - a.imdbRating)]
default: return [...movies]
}
}
return (
<div className='container'>
<div className='d-flex mb-3 '>
<h1 style={{ textAlign: 'center', border: 'solid lightgreen', backgroundColor: 'white' }} >My Movie List</h1>
<form style={{ float: 'right', marginRight: '35px' }}>
<input type="text" placeholder='search by name' value={search} onChange={handleChange} />
<select value={orderBy} onChange={handleSelectChange} >
<option value="">orderBy</option>
<option value="a-z">a-z</option>
<option value="z-a">z-a</option>
<option value="1-100">1-100</option>
<option value="100-1">100-1</option>
</select>
</form>
</div>
<div className=" row pt-2 justify-content-around " style={{ textAlign: 'center', width: '100%' }}>
{
filterBy.length > 0 ? (
filterBy.map((movie) => {
return <MovieCard key={movie.imdbID} Poster={movie.Poster} Title={movie.Title} imdbRating={movie.imdbRating} imdbID={movie.imdbID} />
})
) : (
show(movies).map((movie) => {
return <MovieCard key={movie.imdbID} {...movie} />
})
)
}
</div>
</div >
)
}
export default MoviesList
And this is Movie card component
import React from 'react'
import { useDispatch } from 'react-redux'
import { removeMovie } from '../actions/moviesAction'
const MovieCard = (props) => {
console.log(props)
const { Title, Poster, imdbRating, imdbID } = props
const dispatch = useDispatch()
return (
<div className="card mt-2 p-2 bd-highlight border shadow rounded" style={{ width: '16rem' }}>
<img className="card-img-left pt-2" src={Poster} alt={Title} style={{ width: '200px', height: '200px' }} />
<div className="card-body">
<h4 className="card-title">Name : {Title}</h4>
<h5 className="card-title">Ranking : #{imdbRating}</h5>
<ion-icon name="trash" style={{ color: 'red' }} onClick={() => {
dispatch(removeMovie(imdbID))
}}>
</ion-icon>
</div>
</div>
)}
export default MovieCard
Please help me to resolve this. Thankyou
Try providing display: flex, to the div rendering MovieCard, like this:
<div className=" row pt-2 justify-content-around "
style={{ textAlign: 'center', width: '100%', display: 'flex' }}>
{
filterBy.length > 0 ? (
filterBy.map((movie) => {
return <MovieCard key={movie.imdbID} Poster={movie.Poster} Title={movie.Title} imdbRating={movie.imdbRating} imdbID={movie.imdbID} />
})
) : (
show(movies).map((movie) => {
return <MovieCard key={movie.imdbID} {...movie} />
})
)
}
</div>
You can just use flex here.
add the below code to your parent div (div containing all the cards).
If you have a css file add a class to that div and add the following styles.
display: flex;
flex-wrap: wrap;
justify-content: center;
or if you want to use inline styling do the following:
<div className=" row pt-2 justify-content-around " style={{ textAlign: 'center', width: '100%', display: "flex", flexWrap: "wrap", justifyContent: "center" }}>
{filterBy.length > 0 ? (
filterBy.map((movie) => {
return <MovieCard key={movie.imdbID} Poster={movie.Poster} Title={movie.Title} imdbRating={movie.imdbRating} imdbID={movie.imdbID} />
})
) : (
show(movies).map((movie) => {
return <MovieCard key={movie.imdbID} {...movie} />
}
)
)}
</div>
Try using a grid method something like the code below will allow you control the number of rows and columns you want as well as size them.
**
<h1>Example { grid-template-rows: 40% 50% 50px;}</h1>
<p><strong style="color: red">The shorthand property, grid-template, can replace both </strong>grid-template-rows and grid-template-columns.</p>
<hr>
<p style="font-family: Courier">When using grid-template, the values before the slash will determine the size of each row. The values after the slash determine the size of each column. </p><hr>
<p style="font-family: Courier">In this example there are 3 rows and 3 columns added to the grid.</p><h1>Example {grid-template: 40% 50% 50px/100px 50% 200px;}</h1><br>
<p><strong style="color: red">By using the (fr) unit, we can define the size of columns and rows as a fraction of the grid’s length and width.</strong><br>Using (fr) makes it easier to prevent grid items from overflowing the boundaries of the grid.</p>
<h1>Example {grid-template: 1fr 1fr 1fr / 3fr 50% 1fr;}<br> width: 400px;<br>
**
If you're using Bootstrap then you don't need to use flex nor grid directly. This is the Bootstrap's way to achieve the desired layout:
<div className="row">
{movies.map(item => (
<div className="col">
// then here yo can put your card for the movie
</div>
))}
</div>
This is the Bootstrap doc about it

How to pass var from jsx to pure css file in react js?

I want to pass var form parent component to child component (--my-custom) that will set color for child component , but when i write like this it gives me error , if you see i use my custom variable in MyButton.css in this manner it will adjust my button to any color i want.
// my jsx file
import React from "react";
import "./styles/MyButton.css";
const MyButton = ({ title, handelClick, color }) => {
return (
<div class="parentbutton">
<a class="mybutton" onClick={() => handelClick()} style={{ --my-custom: color }}>
<span>{title}</span>
<i></i>
</a>
</div>
);
};
export default MyButton;
//MyButton.css
a.mybutton:hover {
letter-spacing: 0.25em;
background-color: var(--my-custom);
box-shadow: 0 0 2.5em var(--my-custom);
color: var(--my-custom);
}
You can replace your style like this, it might works!
<div style={{ "--my-css-var": 10 } as React.CSSProperties} />
Just use the inline styling without a .css file
import React from "react";
const MyButton = ({ title, handelClick, color }) => {
return (
<div className="parentbutton">
<a className="mybutton" onClick={() => handelClick()} style={{
backgroundColor: color,
boxShadow: `0 0 2.5em ${color}`,
color: color
}}>
<span>{title}</span>
<i></i>
</a>
</div >
);
};
export default MyButton;

Is there a way to perform a click event only on a parent element?

In my app I would like to be able to click an item (background color, text, etc), have a modal pop up with a color picker, then change the color of the item.
The issue I'm having is that I made an onClick handler for the parent element to update a background color, but it's also activating when anything within the parent element is clicked.
I have an example in Codesandbox and you can see that whether you click the background or the buttons, the color picker comes up when I only want it activated for the background.
If anyone is familiar with Chakra-ui, this is my code:
const Navbar = () => {
const [display, setDisplay] = useState('none');
const [color, setColor] = useState('#1A202C');
const [showColorPicker, setShowColorPicker] = useState(false);
const { isOpen, onOpen, onClose } = useDisclosure();
/*
On click, showColorPicker becomes true and isOpen also becomes true
in order to display the modal with a color picker
*/
const handleModalClick = () => {
onOpen();
if (!showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
};
return (
<div>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent
bg='gray.600'
style={{ boxShadow: '2px 2px 2px 1px rgba(0, 0, 0, 0.2)' }}>
<ModalCloseButton color='gray.200' />
<ModalBody style={{ borderRadius: '10px' }}>
<Center>
{showColorPicker && (
<ChromePicker
color={color}
onChange={(updatedColor) => setColor(updatedColor.hex)}
/>
)}
</Center>
</ModalBody>
</ModalContent>
</Modal>
// Flex === a div with display flex
<Flex
bg={color}
color='gray.200'
style={{
textTransform: 'uppercase',
fontWeight: 'bold',
}}
onClick={handleModalClick}>
<Link p='5' _hover={{ color: 'cyan.400' }}>
<Text fontSize='xl'>Color Selector</Text>
</Link>
<Spacer />
<Flex
display={['none', 'none', 'flex', 'flex']}
fontSize='md'
align='center'>
<Link p='5' _hover={{ color: 'cyan.400' }}>
About
</Link>
<Link p='5' _hover={{ color: 'cyan.400' }}>
Portfolio
</Link>
<Link p='5' _hover={{ color: 'cyan.400' }}>
Contact
</Link>
</Flex>
</Flex>
...
</div>
);
};
Is there a way to show the color picker only when the background is clicked?
The app is also deployed on Netlify if you want to see the real example or all of the code on GitHub.
The event object has a target property, which holds the exact element that the user interacted with to trigger the event. So, you can just check if the target element is the parent element to know if they interacted with the parent directly or one of their children.
Here's one way of doing it:
if (e.target.classList.contains('navbar') && !showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
A more robust way of doing it would be to store the parent in a React ref, and make sure that e.target is exactly the same as that ref. (This is one of the places where it's ok to use a ref).
Here's a complete example that uses a ref. (in won't run in StackOverflow, because I didn't properly load up the libraries, but it'll work).
import "./styles.css";
import React, { useState, useRef } from "react";
import { ChromePicker } from "react-color";
export default function App() {
const [display, setDisplay] = useState("none");
const [color, setColor] = useState("#1A202C");
const [showColorPicker, setShowColorPicker] = useState(false);
const navBarRef = useRef();
const handleModalClick = e => {
if (e.target === navBarRef.current && !showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
};
return (
<>
<div
className="navbar"
ref={navBarRef}
style={{ backgroundColor: `${color}`, color: "white" }}
onClick={handleModalClick}
>
<button style={{ padding: "10px 15px 10px 15px", margin: "20px" }}>
Left
</button>
<button style={{ padding: "10px 15px 10px 15px", margin: "20px" }}>
Right
</button>
</div>
{showColorPicker && (
<ChromePicker
color={color}
onChange={(updatedColor) => setColor(updatedColor.hex)}
/>
)}
</>
);
}
Whats happening is called "Event Bubbling" and it is the intended behavior (you can read more about it here). Eventually, you'll find that it is very useful.
If you want to only handle events that are triggered from the same element where the handler is attached, you can do something like this:
const parent = document.getElementById('parent');
const handler = (e) => {
if (e.target !== e.currentTarget) {
return;
}
console.log('PARENT CLICKED!');
};
parent.addEventListener('click', handler);
#parent {
background-color: #123ff0;
}
.box {
display: inline-block;
height: 50px;
width: 50px;
background-color: #000000;
}
p {
color: #ffffff;
background-color: #000000;
}
<div id='parent'>
<span class='box'></span>
<span class='box'></span>
<p>This is a paragraph.</p>
<span class='box'></span>
<span class='box'></span>
</div>
The event object provided gives you some default preventions that you can use.
Example:
const handleClick = (event) => {
event.stopPropagation();
}
Should be added on clickable elements that are part of the parent and that should not trigger the event.
This will prevent your event to be propagated to the parent element.
I've forked your codesanbox and added my solution in.
https://codesandbox.io/s/infallible-hellman-66sln?file=/src/App.js
I think the above solutions are also correct and everything will depend on the situation.
More info here: https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation

React.js Popup modal for shopping cart

I am currently working on a shopping cart and I am trying to figure out how to have the modal appear once I click on the shopping cart icon. I have looked at the documentation for the semantic-ui for modals but it is vague as to how to get the modal to appear when clicking on something. I am using the semantic-ui class="ui modal" for the modal.
I was thinking of putting an onClick on the icon but was still confused as to how to go from there. Currently, I have the icon in another component and the shopping cart in another separate component. I want the items to appear inside of the pop-up modal which should be the shopping cart.
import React from 'react'
import { Icon } from 'semantic-ui-react';
const ShoppingCartIcon = () => {
return(
<Icon.Group size='big' className="shopping_cart_icon">
<Icon link name='shopping cart'/>
<Icon corner='top right'/>
</Icon.Group>
)
}
export default ShoppingCartIcon;
import React from 'react'
import Shirt from './Shirt';
class ShoppingCart extends React.Component {
render() {
const listShirts = this.props.shirts.map(shirt => {
return <Shirt key={shirt.id} {...shirt}/>
})
return(
<div className="ui modal">
<div className="content">
{listShirts}
</div>
</div>
)
}
}
export default ShoppingCart;
Currently, I do not have the functionality for adding items to the cart working yet. I just want to focus on getting the modal to show up once I click on the shopping cart icon
as far as I see, you are not using neither redux nor context api. you are passing props with props drilling.
so this is how you should organize your code step by step.
we render cartIcon component in the header.js. here is a classic header
Header.js
import CartDropdown from "../cart-dropdown/cart-dropdown.component";
class Header extends React.Component {
constructor(props) {
super(props);
state = { hidden: true, cartItems:[]};
}
toggleHidden() {
this.setState(() => ({ hidden: !this.state.hidden }));
}
render() {
return (
<div className="header">
<Link className="logo-container" to="/">
<Logo className="logo" />
</Link>
<div className="options">
<Link className="option" to="/shop">
SHOP
</Link>
<Link to="/contact" className="option">
CONTACT
</Link>
{/* <Link></Link> */}
<CartIcon />
</div>
{hidden ? null : (
<CartDropdown
toggle={this.toggleHidden}
cartItems={this.state.cartItems}
></CartDropdown>
)}
</div>
);
}
}
you said you have not set the addItem functionality yet. as you add items to the cartItems array you will display them in the cart.
now we need to set up the cartDropdown component.
const CartDropdown = ({ cartItems, toggleHidden }) => (
<div className="cart-dropdown">
<div className="cart-items">
{cartItems.length ? (
cartItems.map(item => <CartItem key={item.id} item={item} />)
) : (
<span className="empty-message"> Your cart is empty </span>
)}
</div>
<CustomButton
onClick={() => {
toggleHidden();
}}
>
GO TO CHECKOUT
</CustomButton>
</div>
);
here we need to add css for cartDropdown. I do not how you are dealing with your css. prop-types or scss but here is the css code so you can apply to your project.
css for cartDropdown component
.cart-dropdown {
position: absolute;
width: 240px;
height: 340px;
display: flex;
flex-direction: column;
padding: 20px;
border: 1px solid black;
background-color: white;
top: 80px;
right: 0;
z-index: 5;
.cart-items {
height: 240px;
display: flex;
flex-direction: column;
overflow: scroll;
}
.empty-message {
font-size: 18px;
margin: 50px auto;
}
button {
margin-top: auto;
}
}

Categories

Resources