I am fetching a list of questions from the Question.js file to Home.js
How can I change the answer border color when the user clicks on that.
I want to make green if the user clicks on the right answer and red if the user clicks on the wrong answer.
If the user clicks on the wrong answer then it should show the right answer by making the background green and the rest all should become the red border.
See output:
Home.js file:
import React from 'react'
import Questions from './Questions'
const Home = () => {
function action(){
}
return (
<>
<div className="main">
{
Questions.map((item)=>(
<div className="box">
<div className="title">
<h2 className="qno">{item.numb}</h2>
<h2> {item.question}</h2>
</div>
<div className="options">
<span onClick={()=>action()} >{item.options.q1}</span>
<span>{item.options.q2}</span>
<span>{item.options.q3}</span>
<span>{item.options.q4}</span>
</div>
</div>
))
}
</div>
</>
)
}
export default Home;
Questions.js file:
let Questions = [
{
numb: 1,
question: "What does HTML stand for?",
answer: "Hyper Text Markup Language",
options: {
q1: "Hyper Text Preprocessor",
q2: "Hyper Text Markup Language",
q3: "Hyper Text Multiple Language",
q4: "Hyper Tool Multi Language",
},
},
{
numb: 2,
question: "Who is Ankit Yadav?",
answer: "Engineer",
options: {
q1: "Engineer",
q2: "Doctor",
q3: "CEO",
q4: "Scientist",
},
},
];
export default Questions;
style.css file:
/* styling */
.main{
width: 70vw;
height: 100%;
box-shadow: 0 0 14px 0;
margin: 30px auto;
border-radius: 5px;
}
.box{
padding: 10px;
border-bottom: 2px solid #4c4c4c;
}
.title .qno{
font-size: 1.7rem;
font-weight: 800;
font-family: 'Courier New', Courier, monospace;
background-color: #4c4c4c;
padding: 5px;
border-radius: 70px;
color: #fff;
}
.title h2{
font-size: 1.7rem;
font-weight: 500;
margin-left: 10px;
}
.box .title{
display: flex;
}
.options{
display: flex;
flex-direction: column;
justify-content: space-between;
margin: 20px 25px;
}
You can achieve it using inline styles and a few util functions
const Home = () => {
const [answerStatus, setAnswerStatus] = useState(() => {
return Questions.map((item) => {
return {
numb: item.numb,
answered: false,
givenAnswer: ""
};
});
});
const action = (questionNumber, answer) => {
setAnswerStatus((prevState) => {
return prevState.map((item) =>
item.numb === questionNumber
? { ...item, answered: true, givenAnswer: answer }
: item
);
});
};
const isAnswerCorrect = (questionNumber) => {
const status = answerStatus.find((item) => item.numb === questionNumber);
const question = Questions.find((item) => item.numb === questionNumber);
return status.answered && question.answer === status.givenAnswer;
};
const questionAnswered = (questionNumber) => {
const status = answerStatus.find((item) => item.numb === questionNumber);
return status.answered;
};
const getGivenAnswer = (questionNumber) => {
return answerStatus.find((item) => item.numb === questionNumber)
?.givenAnswer;
};
return (
<>
<div className="main">
{Questions.map((item) => (
<div className="box">
<div className="title">
<h2 className="qno">{item.numb}</h2>
<h2> {item.question}</h2>
</div>
<div className="options">
{Object.entries(item.options).map(([optionId, optionDesc]) => {
return (
<span
onClick={() => action(item.numb, optionDesc)}
style={{
backgroundColor: questionAnswered(item.numb)
? isAnswerCorrect(item.numb) &&
getGivenAnswer(item.numb) === optionDesc
? "lightgreen"
: isAnswerCorrect(item.numb)
? "lightblue"
: item.answer !== optionDesc
? "tomato"
: "lightgreen"
: "lightblue",
padding: "5px",
borderRadius: "3px",
margin: "3px",
cursor: "pointer"
}}
>
{optionDesc}
{questionAnswered(item.numb) &&
getGivenAnswer(item.numb) === optionDesc &&
" (given answer)"}
</span>
);
})}
</div>
</div>
))}
</div>
</>
);
};
export default Home;
Code sandbox => https://codesandbox.io/s/quirky-hill-cwoc5?file=/src/App.js
I would recommend to create separate component called Question to avoid working with arrays of data and put state login inside. But if this is not possible I would do it this way:
Home.js
import React from 'react'
import Questions from './Questions'
const Home = () => {
// Add this two state values
const { highlightedRightIds, setHighlightedRightIds } = useState([]);
const { highlightedWrongIds, setHighlightedWrongIds } = useState([]);
// Handle click here
const handleClick = (questionId, isRight) => {
setHighlightedWrongIds([...highlightedWrongIds, questionId]);
if (isRight) {
setHighlightedRightIds([...highlightedRightIds, questionId]);
}
}
return (
<>
<div className="main">
{
Questions.map((item)=>(
<div className="box">
<div className="title">
<h2 className="qno">{item.numb}</h2>
<h2> {item.question}</h2>
</div>
<div className="options">
{
// use map to render a list of options
item.options.map(option => {
const isRight = option === item.answer;
const highlight = isRight
? highlightedRightIds.includes(item.numb)
: highlightedWrongIds.includes(item.numb);
const highlightClass = isRight
? "rightAnswer"
: "wrongAnswer";
return <span
onClick={() => handleClick(item.numb, isRight)}
className={highlight ? highlightClass : ""}
>
{option}
</span>;
})
}
</div>
</div>
))
}
</div>
</>
)
}
export default Home;
style.css (add new classes)
// ... other styles
.rightAnswer{
background-color: green;
}
.wrongAnswer{
border-color: red;
}
Related
The <div className="image-box-wrapper row justify-content-center"> on my portfolio component isn't laying out the portfolio pictures side by side. I can't seem to find the issue why they won't layout side by side. I think this would be the main piece of CSS that would affect this.
.portfolio-image {
width: 15rem;
margin: 0 auto;
border: 1px solid var(--primary-green);
padding: 0 0.5rem;
justify-content: center;
}
I checked the spelling and made sure the div was closed and still nothing. I'm still getting a column instead of side by side (horizontal). You can see the issue on my portfolio page at the link below. I appreciate help!
https://rinzler8806.github.io/reactPortfolio/
Portfolio Component
import React from "react";
import prw from "../images/prw.png";
import fitnesstracker from "../images/fitnesstracker.png";
import budgettracker from "../images/budgettracker.png";
import pangea from "../images/pangea.png";
//Font Awesome Imports
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearchPlus } from "#fortawesome/free-solid-svg-icons";
//React POPUPBOX
import { PopupboxManager, PopupboxContainer } from "react-popupbox";
import "react-popupbox/dist/react-popupbox.css";
const Portfolio = () => {
//Prow
const openPopupboxProw = () => {
const content = (
<>
<img className="portfolio-image-popupbox" src={prw} alt="prw"/>
<p> Description of Popular Restaurants of the World
As a traveler, I want to be able to search a location for surrounding restaurants so that I can find a local establishment to fit my needs.
<br></br>
<br></br>
uses Foundation css framework
application utilizes Zomato, and Google API's to retrieve restaurant data
user can search restaurants around the world based on location and food type
user searched cities are saved to local storage and can be accessed via the navbar
when user inputs a location and food type the page will scroll to an embedded map with markers displaying local restaurants that meet search criteria
when user clicks on map marker info about the restaurant will be displayed as well as a get directions link
</p>
<b>GitHub: </b> <a className="hyper-link" onClick={() => window.open("https://github.com/Rinzler8806/Popular-Restaurants-of-the-World-1")}>https://github.com/Rinzler8806/Popular-Restaurants-of-the-World-1</a>
</>
)
PopupboxManager.open({content});
PopupboxManager.update({
content,
config: {
titleBar: {
text: "Popular Restaurants of the World",
},
},
});
}
const popupboxConfigProw = {
titleBar: {
enable: true,
text: "Popular Restaurants of the World"
},
fadeIn: true,
fadeInSpeed: 500
}
//fitnesstracker
const openPopupboxFitnesstracker = () => {
const content = (
<>
<img className="portfolio-image-popupbox" src={fitnesstracker} alt="fitnesstracker"/>
<p>
<br></br>
<br></br>
This app has been created with the intention to be able to view create and track daily workouts. The user will be able to log multiple exercises in a workout on a given day. The user will also be able to track the name, type, weight, sets, reps, and duration of exercise. If the exercise is a cardio exercise, the user will be able to track their distance traveled.
</p>
<b>Demo: </b> <a className="hyper-link" onClick={() => window.open("https://infinite-beach-02354.herokuapp.com/exercise?id=6065391b3f573d0015bbd57e", "_blank")}>https://infinite-beach-02354.herokuapp.com/exercise?id=6065391b3f573d0015bbd57e</a>
<br></br>
<b>GitHub: </b> <a className="hyper-link" onClick={() => window.open("https://github.com/Rinzler8806/fitnessTracker")}>https://github.com/Rinzler8806/fitnessTracker</a>
</>
)
PopupboxManager.open({content});
PopupboxManager.update({
content,
config: {
titleBar: {
text: "Fitness Tracker",
},
},
});
}
const popupboxConfigFitnesstracker = {
titleBar: {
enable: true,
text: "Fitness Tracker"
},
fadeIn: true,
fadeInSpeed: 500
}
//budgettracker
const openPopupboxBudgettracker = () => {
const content = (
<>
<img className="portfolio-image-popupbox" src={budgettracker} alt="budgettracker"/>
<p>
<br></br>
<br></br>
The user will be able to add expenses and deposits to their budget with or without a connection. When entering transactions offline, they should populate the total when brought back online.
<br></br>
Offline Functionality:
Enter deposits offline.
Enter expenses offline.
<br></br>
When brought back online:
Offline entries should be added to tracker.
</p>
<b>Demo: </b> <a className="hyper-link" onClick={() => window.open("https://fathomless-sea-54648.herokuapp.com/", "_blank")}>https://fathomless-sea-54648.herokuapp.com/</a>
<br></br>
<b>GitHub: </b> <a className="hyper-link" onClick={() => window.open("https://github.com/Rinzler8806/BudgetTracker")}>https://github.com/Rinzler8806/BudgetTracker</a>
</>
)
PopupboxManager.open({content});
PopupboxManager.update({
content,
config: {
titleBar: {
text: "Budget Tracker",
},
},
});
}
const popupboxConfigBudgettracker = {
titleBar: {
enable: true,
text: "Budget Tracker"
},
fadeIn: true,
fadeInSpeed: 500
}
//Pangea
const openPopupboxPangea = () => {
const content = (
<>
<img className="portfolio-image-popupbox" src={pangea} alt="Pangea"/>
<p> Pangea
<br></br>
<br></br>
A dynamic messaging application that connects users around the globe. It offers many features and the ability to actively translate your message to a selected language.
<br></br>
Log in as: Batman and use password: password to view demo.
</p>
<b>Demo: </b> <a className="hyper-link" onClick={() => window.open("https://pangeachat.herokuapp.com/", "_blank")}>https://pangeachat.herokuapp.com/</a>
<br></br>
<b>GitHub: </b> <a className="hyper-link" onClick={() => window.open("https://github.com/Rinzler8806/pangea-chat")}>https://github.com/Rinzler8806/pangea-chat</a>
</>
)
PopupboxManager.open({content});
PopupboxManager.update({
content,
config: {
titleBar: {
text: "Pangea",
},
},
});
}
const popupboxConfigPangea = {
titleBar: {
enable: true,
text: "Pangea"
},
fadeIn: true,
fadeInSpeed: 500
}
return (
<div id="Portfolio" className="portfolio-wrapper">
<div className="container">
<h1 className="text-uppercase text-center py-5">Portfolio</h1>
<div Classname="image-box-wrapper row justify-content-center">
<div Classname="portfolio-image-box" onClick={openPopupboxProw}>
<img className="portfolio-image" src={prw} alt="prw" />
<div className="overflow"></div>
<FontAwesomeIcon className="portfolio-icon" icon={faSearchPlus} />
</div>
{/* - */}
<div Classname="portfolio-image-box" onClick={openPopupboxFitnesstracker}>
<img className="portfolio-image" src={fitnesstracker} alt="fitnesstracker" />
<div className="overflow"></div>
<FontAwesomeIcon className="portfolio-icon" icon={faSearchPlus} />
</div>
{/* - */}
<div Classname="portfolio-image-box" onClick={openPopupboxBudgettracker}>
<img className="portfolio-image" src={budgettracker} alt="budgettracker" />
<div className="overflow"></div>
<FontAwesomeIcon className="portfolio-icon" icon={faSearchPlus} />
</div>
{/* - */}
<div Classname="portfolio-image-box" onClick={openPopupboxPangea}>
<img className="portfolio-image" src={pangea} alt="pangea" />
<div className="overflow"></div>
<FontAwesomeIcon className="portfolio-icon" icon={faSearchPlus} />
</div>
</div>
</div>
<PopupboxContainer {...popupboxConfigProw} />
<PopupboxContainer {...popupboxConfigFitnesstracker} />
<PopupboxContainer {...popupboxConfigBudgettracker} />
<PopupboxContainer {...popupboxConfigPangea} />
</div>
)
}
export default Portfolio;
CSS
.portfolio-wrapper {
background: var(--primary-light-grey);
padding: 3rem 0;
}
.portfolio-wrapper h1 {
color: var(--primary-green);
}
.portfolio-image {
width: 15rem;
border: 1px solid var(--primary-green);
padding: 0 0.5rem;
justify-content: center;
}
.portfolio-image-box {
position: relative;
margin: 1rem;
}
.popupbox-wrapper {
margin-top: 7rem;
}
/*OVERFLOW BOX*/
.overflow {
position: absolute;
opacity: 0;
background: var(--primary-black);
width: 15rem;
height: 8rem;
top: 0;
border: 1px solid var(--primary-purple);
cursor: pointer;
}
.portfolio-image-box:hover .overflow {
opacity: 60%;
transition: 0.3s ease-in-out;
}
.portfolio-icon {
position: absolute;
opacity: 0;
margin-left: -8rem;
margin-top: 3.6rem;
color: var(--primary-purple);
font-size: 1.5rem;
}
.portfolio-image-box:hover .portfolio-icon {
opacity: 70%;
transition: 0.3s ease-in-out;
cursor: pointer;
}
/*POPUPBOX*/
.portfolio-image-popupbox {
width: 45rem;
padding: 0 0.5rem;
}
.hyper-link {
cursor: pointer;
color: var(--primary-blue);
}
.hyper-link:hover {
color: var(--primary-purple);
}
/*MEDIA*/
#media(max-width: 768px) {
.portfolio-image-popupbox {
width: 100%;
}
}
#media(max-height: 570px) {
.popupbox-wrapper {
height: 100%;
}
.portfolio-image-popupbox {
width: 50%;
}
}
#media(max-height: 570px) {
.popupbox-content {
height: 100%;
}
.portfolio-image-popupbox {
width: 100%;
}
}
#media(max-height: 280px) {
.popupbox-wrapper {
height: 90%;
}
.portfolio-image-popupbox {
width: 30%;
}
}
you seems to have missed the implementations of your image-box-wrapper, row
and justify-content-center classes.
That is why no styling is being applied to the container.
Adding a simple display: flex in chrome dev tools solved the issue.
I have a problem on which I cannot find a simple solution. So this is my Header:
const Header = ({ title }) => {
return (
<div className={styles.Header}>
<h1>{title}</h1>
<button>
{EXIT}
</button>
</div>
);
};
How can I apply custom styles with styled-components for h1 and button elements? I tried
const CustomHeader = styled(Header)`
${h1} ${button}
`;
const h1 = styled(h1)`
max-width: 500px
`
const button = styled(button)`
padding-left: 100px
`
but this is not working, I get an error in terminal.
I also tried this:
return (
<CustomHeader>
<div className={styles.Header}>
<h1>{title}</h1>
<button>
{EXIT}
</button>
</div>
</CustomHeader>
);
};
const CustomHeader = styled(Header)`
h1 {
max-width: 500px;
}
button {
padding-left: 100px;
}
`;
Any help will be appreciated.
First you need to define styled component in your React function and create a wrapper like following:
// added demo css here for h1 tag, you can add your own
const CustomHeader = styled.div`
h1 {
font-family: Poppins;
font-size: 14px;
font-weight: 600;
font-stretch: normal;
font-style: normal;
line-height: 1.5;
letter-spacing: 0.02px;
text-align: left;
color: #0f173a;
}
`;
Then wrap your return inside the CustomHeader wrapper.
const Header = ({ title }) => {
return (
<CustomHeader>
<h1>{title}</h1>
<button>
{EXIT}
</button>
</CustomHeader>
);
};
You can add any tag inside CustomHeader that you want to customize.
You're almost there.
Its not working because you are setting className directly on div element of your Header component.
According to the styled-component documentation:
The styled method works perfectly on all of your own or any third-party components, as long as they attach the passed className prop to a DOM element.
https://styled-components.com/docs/basics#styling-any-component
So, in your case you need to:
const Header = ({ title, className }) => {
return (
<div className={className}>
<h1>{title}</h1>
<button>EXIT</button>
</div>
);
};
const CustomHeader = window.styled(Header)`
h1 {
max-width: 500px;
}
button {
padding-left: 100px;
}
`;
const App = () => {
return (
<React.Fragment>
<Header className='' title={"title"} />
<CustomHeader title={"title"} />
</React.Fragment>
);
};
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="//unpkg.com/styled-components#4.0.1/dist/styled-components.min.js"></script>
<div id="root"></div>
So, i set Header like this:
const Header = ({ title, className }) => {
return (
<div className={className}>
And where i did <Header className='' title={"title"} /> you can do like this:
<Header className={styles.Header} title={"title"} />
// Code
const Header = ({ title }) => {
return (
<Header>
<h1>{title}</h1>
<button>
{EXIT}
</button>
</Header>
);
};
// Styles
const Header = styled.div`
h1{
styles...
}
button{
styles...
}
`;
I want to build an indexed letters. When I click the letter (for example A,B,C). It should be scrolling to the belongs title. I used scrollIntoView here. Everythings okey except "block:start". When I want to the scrolling to title of start, whole page scrolling with container.
Here my gif about issue
Here my html & css & javascript code.
const scrollTo = id => {
let ref = document.getElementById(id)
if (ref /* + other conditions */) {
ref.scrollIntoView({
behavior: "smooth",
block: "start",
inline: "start",
})
}
}
<div className="glossary">
<div className="glossary-items grid">
<div className="d-flex ">
<div className="glossary-letters ">
<ul>
{letter.map(item => (
<li onClick={() => scrollTo(item)}>
<a> {item}</a>
</li>
))}
</ul>
</div>
<div className="glossary-titles">
<ul>
{glossary.map((item, index) => (
<div key={item.group}>
<li id={item.group}>{item.group}</li>
{item.children.map((item2, i) => (
<li key={i}>
<Link
activeClassName="glossary-titles-active"
to={`/en/sozluk/${item2.first_name + item2.id}`}
>
{item2.first_name}
</Link>
</li>
))}
</div>
))}
</ul>
</div>
</div>
<div className="glossary-content ml-5">
<Router>
<Glossary path="en/sozluk/:id" />
</Router>
</div>
</div>
</div>
There is related question to that
You could use scrollTo function of the parent element to scroll to specific child element.
Example
const { useState, useEffect, useRef } = React;
const App = () => {
const [list, setList] = useState([]);
const [keywords, setKeywords] = useState([]);
const refs = useRef(null);
const parentRef = useRef(null);
useEffect(() => {
const newList = Array(25).fill(0).map((pr, index) => String.fromCharCode(65 + index));
const newKeywords = newList.flatMap(pr => [pr, ...newList.map(npr => pr + npr)]);
refs.current = newList.reduce((acc, letter) => {
acc[letter] = React.createRef();
return acc;
}, {});
setList(newList);
setKeywords(newKeywords);
}, [])
const onClick = ({target: {dataset: {letter}}}) => {
const element = refs.current[letter].current;
const parent = parentRef.current;
const onScroll = () => {
const relativeTop = window.scrollY > parent.offsetTop ? window.scrollY : parent.offsetTop
parent.scrollTo({
behavior: "smooth",
top: element.offsetTop - relativeTop
});
}
window.removeEventListener("scroll", onScroll);
window.addEventListener("scroll", onScroll);
onScroll();
/*
element.scrollIntoView({
behavior: "smooth",
block: "start",
inline: "start",
})
*/
}
const tryAssignRef = (letter) => {
return list.indexOf(letter) > -1 ? {ref: refs.current[letter]} : {};
}
/// ...{ref: {refs.current['A']}}
return <div className="container">
<div className="header">
</div>
<div className="body">
<div className="left">
<div className="letters">{list.map(pr => <div className="letter" onClick={onClick} data-letter={pr} key={pr}>{pr}</div>)}</div>
<div ref={parentRef} className="keywords">{keywords.map(pr => <div {...tryAssignRef(pr)} key={pr}>{pr}</div>)}</div>
</div>
<div className="right">
</div>
</div>
</div>
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
.container, .body, .left {
display: flex;
}
.container {
flex-direction: column;
}
.header {
flex: 0 0 100px;
border: 1px solid black;
}
.body {
height: 1000px;
}
.left {
flex: 1;
}
.right {
flex: 2;
}
.left {
justify-content: space-between;
}
.letter {
padding: 2px 0;
cursor: pointer;
}
.letters, .keywords {
padding: 0 10px;
}
.keywords {
overflow-y: scroll;
}
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root"></div>
QUESTION
I have a few photos saved as png files that are in the same folder as the React component and are imported correctly as well.
How and what would be a good practice way to display all the images, let's say there are 4 images, in their proper box shown in the picture below and have them be displayed side by side, along with their name/price aligned below the image.
Similar to craigslist's gallery setting when looking at posts with images.
Ex:
<img src={Logo} alt=“website logo”/>
<img src={Mogo} alt=“website mogo”/>
<img src={Jogo} alt=“website jogo”/>
<img src={Gogo} alt=“website gogo”/>
Could I do something with products.map((product, index, image?))...?
CODE
const Product = props => {
const { product, children } = props;
return (
<div className="products">
{product.name} ${product.price}
{children}
</div>
);
};
function App() {
const [products] = useState([
{ name: "Superman Poster", price: 10 },
{ name: "Spider Poster", price: 20 },
{ name: "Bat Poster", price: 30 }
]);
const [cart, setCart] = useState([]);
const addToCart = index => {
setCart(cart.concat(products[index]));
};
const calculatePrice = () => {
return cart.reduce((price, product) => price + product.price, 0);
};
return (
<div className="App">
<h2>Shopping cart example using React Hooks</h2>
<hr />
{products.map((product, index) => (
<Product key={index} product={product}>
<button onClick={() => addToCart(index)}>Add to cart</button>
</Product>
))}
YOUR CART TOTAL: ${calculatePrice()}
{cart.map((product, index) => (
<Product key={index} product={product}>
{" "}
</Product>
))}
</div>
);
}
Wrap the list of products with a div (<div className="productsContainer">), and display it as a flex with wrap.
Set the width of the items (products) to 50% or less.
To render the image, render the <img> tag as one of the children, or add it directly to the product. Also change the data to include the src.
const { useState } = React;
const Product = ({ product, children }) => (
<div className="products">
{product.name} ${product.price}
{children}
</div>
);
function App() {
const [products] = useState([
{ name: "Superman Poster", price: 10, logo: 'https://picsum.photos/150/150?1' },
{ name: "Spider Poster", price: 20, logo: 'https://picsum.photos/150/150?2' },
{ name: "Bat Poster", price: 30, logo: 'https://picsum.photos/150/150?3' }
]);
const [cart, setCart] = useState([]);
const addToCart = index => {
setCart(cart.concat(products[index]));
};
const calculatePrice = () => {
return cart.reduce((price, product) => price + product.price, 0);
};
return (
<div className="App">
<h2>Shopping cart example using React Hooks</h2>
<hr />
<div className="productsContainer">
{products.map((product, index) => (
<Product key={index} product={product}>
<img src={product.logo} alt="website logo" />
<button onClick={() => addToCart(index)}>Add to cart</button>
</Product>
))}
</div>
YOUR CART TOTAL: ${calculatePrice()}
{cart.map((product, index) => (
<Product key={index} product={product}>
{" "}
</Product>
))}
</div>
);
}
ReactDOM.render(
<App />,
root
);
* {
box-sizing: border-box;
}
.productsContainer {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.products {
display: flex;
flex-direction: column;
align-items: center;
width: 45%;
margin: 0 0 1em 0;
padding: 1em;
border: 1px solid black;
}
.products img {
margin: 0.5em 0;
}
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Best way to display those side by side you can display it by giving css classes flex-row for horizontal view and flex-column for vertical view in the main div component
const Product = props => {
const { product, children, image } = props;
return (
<div className="products">
{product.name} ${product.price} ${product.image}
{children}
</div>
);
};
products.map((product, index, image?))...?
Something along the lines of this?
can someone tells me why the dropdown menu is not displaying in this demo? the dropdown menu should show when I hover over the word 'collective'?
https://codesandbox.io/s/funny-river-c76hu
For the app to work, you would have to type in the input box "collective", click analyse, then a progressbar will show, click on the blue line in the progressbar, an underline would show under the word "collective" then you should hover over "collective" word and a drop down menu should be displayed but the whole window disappears when I hover over the word "collective" instead of the drop down menu
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { Content, Dropdown, Label, Progress, Button, Box } from "rbx";
import "rbx/index.css";
function App() {
const [serverResponse, setServerResponse] = useState(null);
const [text, setText] = useState([]);
const [loading, setLoading] = useState(false);
const [modifiedText, setModifiedText] = useState(null);
const [selectedSentiment, setSentiment] = useState(null);
const [dropdownContent, setDropdownContent] = useState([]);
const [isCorrected, setIsCorrected] = useState(false);
const [displayDrop, setDisplayDrop] = useState(false);
useEffect(() => {
if (serverResponse && selectedSentiment) {
const newText = Object.entries(serverResponse[selectedSentiment]).map(
([word, recommendations]) => {
const parts = text[0].split(word);
const newText = [];
parts.forEach((part, index) => {
newText.push(part);
if (index !== parts.length - 1) {
newText.push(
<u
className="dropbtn"
data-replaces={word}
onMouseOver={() => {
setDropdownContent(recommendations);
setDisplayDrop(true);
}}
>
{word}
</u>
);
}
});
return newText;
}
);
setModifiedText(newText.flat());
}
}, [serverResponse, text, selectedSentiment]);
const handleAnalysis = () => {
setLoading(true);
setTimeout(() => {
setLoading(false);
setServerResponse({ joy: { collective: ["inner", "constant"] } });
}, 1500);
};
const handleTextChange = event => {
setText([event.target.innerText]);
};
const replaceText = wordToReplaceWith => {
const replacedWord = Object.entries(serverResponse[selectedSentiment]).find(
([word, recommendations]) => recommendations.includes(wordToReplaceWith)
)[0];
setText([
text[0].replace(new RegExp(replacedWord, "g"), wordToReplaceWith)
]);
setModifiedText(null);
setServerResponse(null);
setIsCorrected(true);
setDropdownContent([]);
};
const hasResponse = serverResponse !== null;
return (
<Box>
<Content>
<div
onInput={handleTextChange}
contentEditable={!hasResponse}
style={{ border: "1px solid red" }}
>
{hasResponse && modifiedText
? modifiedText.map((text, index) => <span key={index}>{text}</span>)
: isCorrected
? text[0]
: ""}
</div>
<br />
{displayDrop ? (
<div
id="myDropdown"
class="dropdown-content"
onClick={() => setDisplayDrop(false)}
>
dropdownContent.map((content, index) => (
<>
<strong onClick={() => replaceText(content)} key={index}>
{content}
</strong>{" "}
</>
))
</div>
) : null}
<br />
<Button
color="primary"
onClick={handleAnalysis}
disabled={loading || text.length === 0}
>
analyze
</Button>
<hr />
{hasResponse && (
<Label>
Joy{" "}
<Progress
value={Math.random() * 100}
color="info"
onClick={() => setSentiment("joy")}
/>
</Label>
)}
</Content>
</Box>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
css file
.App {
font-family: sans-serif;
text-align: center;
}
.highlight {
background: red;
text-decoration: underline;
}
.dropbtn {
color: white;
font-size: 16px;
border: none;
cursor: pointer;
}
.dropbtn:hover,
.dropbtn:focus {
background-color: #2980b9;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
position: relative;
background-color: #f1f1f1;
min-width: 160px;
overflow: auto;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
}
.show {
display: block;
}
The problem is this:
{displayDrop ? (
<div
id="myDropdown"
class="dropdown-content"
onClick={() => setDisplayDrop(false)}
>
dropdownContent.map((content, index) => (
<>
<strong onClick={() => replaceText(content)} key={index}>
{content}
</strong>{" "}
</>
))
</div>
) : null}
You are missing a pair of curly brackets around dropdownContent. It should be:
{displayDrop ? (
<div
id="myDropdown"
class="dropdown-content"
onClick={() => setDisplayDrop(false)}
>
{dropdownContent.map((content, index) => (
<>
<strong onClick={() => replaceText(content)} key={index}>
{content}
</strong>{" "}
</>
))}
</div>
) : null}
A working sandbox here https://codesandbox.io/embed/fast-feather-lvpk7 which is now displaying this content.