React Buttons change color from parent component - reactjs

I'm learning react and making a simple quiz where the div changes color based on if the solution is correct or not, but I want to reset the color when the "Next" or "Previous" question is selected from the parent component. I tried creating a prop called clearColor and when that is true reset the color from within the component but this isn't quite working. I was wondering how to best go about this? Because I am so new to react I wasn't even quite sure what to google so any help is appreciated!
class Button extends React.Component {
constructor(props) {
super(props);
this.state = this.getInitialState();
}
componentWillMount() {
this.initialState = this.state
}
getInitialState = () => ({
bgColor: ""
})
resetState = () => {
if (this.props.clearColor) {
this.setState(this.initialState)
}
}
boxClick = (e) => {
if (this.props.isCorrect) {
this.setState({
bgColor: "#9ef0bc"
})
} else {
this.setState({
bgColor: "#f56342"
})
}
}
render() {
return (
<div className="boxClickCss"
style={{backgroundColor: this.state.bgColor}}
onClick={this.boxClick}>{this.props.name}</div>
);
}
}
function Quiz() {
const questions = [
{
questionText: 'What is the capital of France?',
answerOptions: [
{ answerText: 'New York', isCorrect: false },
{ answerText: 'London', isCorrect: false },
{ answerText: 'Paris', isCorrect: true },
{ answerText: 'Dublin', isCorrect: false },
],
},
{
questionText: 'Who is CEO of Tesla?',
answerOptions: [
{ answerText: 'Jeff Bezos', isCorrect: false },
{ answerText: 'Elon Musk', isCorrect: true },
{ answerText: 'Bill Gates', isCorrect: false },
{ answerText: 'Tony Stark', isCorrect: false },
],
},
{
questionText: 'The iPhone was created by which company?',
answerOptions: [
{ answerText: 'Apple', isCorrect: true },
{ answerText: 'Intel', isCorrect: false },
{ answerText: 'Amazon', isCorrect: false },
{ answerText: 'Microsoft', isCorrect: false },
],
},
{
questionText: 'How many Harry Potter books are there?',
answerOptions: [
{ answerText: '1', isCorrect: false },
{ answerText: '4', isCorrect: false },
{ answerText: '6', isCorrect: false },
{ answerText: '7', isCorrect: true },
],
},
];
const [currentQuestion, setCurrentQuestion] = React.useState(0);
const [clearColor, setClearColor] = React.useState(false)
const nextClicked = () => {
setClearColor(true);
const nextQuestion = currentQuestion + 1;
if (nextQuestion < questions.length) {
setCurrentQuestion(nextQuestion);
}
};
const previousClicked = () => {
setClearColor(true)
if (currentQuestion <= 0) {
setCurrentQuestion(0);
} else {
const nextQuestion = currentQuestion - 1;
setCurrentQuestion(nextQuestion);
}
}
return (
<div className='quiz'>
<div className='app'>
<div className='question-section'>
<div className='question-count'>
<span>Question {currentQuestion + 1}</span>/{questions.length}
</div>
<div className='question-text'>{questions[currentQuestion].questionText}</div>
</div>
<div className='answer-section'>
{questions[currentQuestion].answerOptions.map((answerOption) => (
<Button name={answerOption.answerText} isCorrect={answerOption.isCorrect} clearColor={clearColor} />
))}
</div>
</div>
<br></br>
<div className='buttons'>
<button className="prev" onClick={() => previousClicked()}>Previous</button>
<button className="next" onClick={() => nextClicked()}>Next</button>
</div>
</div>
);
}
ReactDOM.render(
<Quiz />,
document.getElementById('root')
);
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
* {
font-family: "Verdana", cursive, sans-serif;
color: #014650;
}
body {
background-color: #014650;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.app {
display: flex;
justify-content: space-evenly;
}
.score-section {
display: flex;
font-size: 24px;
align-items: center;
}
/* QUESTION/TIMER/LEFT SECTION */
.question-section {
width: 100%;
position: relative;
}
.question-count {
margin-bottom: 20px;
}
.question-count span {
font-size: 28px;
}
.question-text {
margin-bottom: 12px;
}
.timer-text {
background: rgb(230, 153, 12);
padding: 15px;
margin-top: 20px;
margin-right: 20px;
border: 5px solid rgb(255, 189, 67);
border-radius: 15px;
text-align: center;
}
/* ANSWERS/RIGHT SECTION */
.answer-section {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.boxClickCss {
width: 80%;
font-size: 16px;
color: #014650;
background-color: white;
border-radius: 15px;
display: flex;
padding: 5px;
justify-content: flex-start;
align-items: center;
border: 5px solid #234668;
cursor: pointer;
margin-top: 15px;
}
.boxClickCss:hover {
background-color: #6A939A;
}
button {
width: 100%;
font-size: 16px;
color: #7BC8C3;
background-color: #252d4a;
border-radius: 15px;
display: flex;
padding: 5px;
justify-content: flex-start;
align-items: center;
border: 5px solid #234668;
cursor: pointer;
margin-top: 15px;
}
.correct {
background-color: #2f922f;
}
.incorrect {
background-color: #ff3333;
}
button:hover {
background-color: #555e7d;
}
button:focus {
outline: none;
}
button svg {
margin-right: 5px;
}
.buttons {
display: flex;
justify-content: space-between;
}
.prev {
width: 30%;
text-align: center;
}
.next {
width: 30%;
text-align: center;
}
.quiz {
background-color: #DEF2F0;
width: 450px;
min-height: 200px;
height: min-content;
border-radius: 15px;
padding: 20px;
box-shadow: 10px 10px 42px 0px rgba(0, 0, 0, 0.75);
}
<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>
<div id="root">
<!-- This div's content will be managed by React. -->
</div>
I also have a codepen here if that is easier to work with: https://codepen.io/mayagans/pen/mdOypGG?editors=1111 I tried looking into lifting up state so I can change the bgColor in the parent component but I'm not sure that is react-y? Any help on how to tackle this appreciated!

This is how I would do it. I would just lift all the state up to Quiz component. Also would keep boxClick function in Quiz component and pass it to Button component. And I am colouring the button only which title match currently selected answer using setAnswer hook.
const Button = ({answer, name, bgColor, isCorrect, boxClick, setAnswer}) => {
return (
<div className="boxClickCss"
style={{backgroundColor: (answer === name) && bgColor}}
onClick={(e) => {
boxClick(isCorrect)
setAnswer(name)
}}>
{name}
</div>
)
}
const Quiz = () => {
const questions = [
{
questionText: 'What is the capital of France?',
answerOptions: [
{ answerText: 'New York', isCorrect: false },
{ answerText: 'London', isCorrect: false },
{ answerText: 'Paris', isCorrect: true },
{ answerText: 'Dublin', isCorrect: false },
],
},
{
questionText: 'Who is CEO of Tesla?',
answerOptions: [
{ answerText: 'Jeff Bezos', isCorrect: false },
{ answerText: 'Elon Musk', isCorrect: true },
{ answerText: 'Bill Gates', isCorrect: false },
{ answerText: 'Tony Stark', isCorrect: false },
],
},
{
questionText: 'The iPhone was created by which company?',
answerOptions: [
{ answerText: 'Apple', isCorrect: true },
{ answerText: 'Intel', isCorrect: false },
{ answerText: 'Amazon', isCorrect: false },
{ answerText: 'Microsoft', isCorrect: false },
],
},
{
questionText: 'How many Harry Potter books are there?',
answerOptions: [
{ answerText: '1', isCorrect: false },
{ answerText: '4', isCorrect: false },
{ answerText: '6', isCorrect: false },
{ answerText: '7', isCorrect: true },
],
},
];
const [currentQuestion, setCurrentQuestion] = React.useState(0);
const [bgColor, setbgColor] = React.useState('')
const [answer, setAnswer] = React.useState('')
const boxClick = isCorrect => {
if(isCorrect) {
setbgColor("#9ef0bc")
} else {
setbgColor("#f56342")
}
}
const nextClicked = () => {
const nextQuestion = currentQuestion + 1;
if (nextQuestion < questions.length) {
setCurrentQuestion(nextQuestion);
}
};
const previousClicked = () => {
if (currentQuestion <= 0) {
setCurrentQuestion(0);
} else {
const nextQuestion = currentQuestion - 1;
setCurrentQuestion(nextQuestion);
}
}
return (
<div className='quiz'>
<div className='app'>
<div className='question-section'>
<div className='question-count'>
<span>Question {currentQuestion + 1}</span>/{questions.length}
</div>
<div className='question-text'>{questions[currentQuestion].questionText}</div>
</div>
<div className='answer-section'>
{questions[currentQuestion].answerOptions.map((answerOption) => (
<Button name={answerOption.answerText} isCorrect={answerOption.isCorrect} boxClick={boxClick} bgColor={bgColor} setAnswer={setAnswer} answer={answer} />
))}
</div>
</div>
<br></br>
<div className='buttons'>
<button className="prev" onClick={() => previousClicked()}>Previous</button>
<button className="next" onClick={() => nextClicked()}>Next</button>
</div>
</div>
);
}
ReactDOM.render(
<Quiz />,
document.getElementById('root')
);

You need to lsiten to props changes during componentDidUdpate like this, to check:
componentDidUpdate() {
if (this.props.clearColor && this.state.bgColor) {
this.setState(this.initialState)
}
}
Moving the state up is reacty and you could be using that.
I would recommend that or moving it into a context.
Also maybe look into function components, which will be the main way to write react in the future.

Related

How to make google maps search box with Multiple markers and and with different infowindow/info Popup Open onClick

I am trying to make Google Map with multiple markers and search box with different Infowindow of our locations in only one country on click and I am not able to do further,
I have written this Code:-
<html>
<head>
<title>Medplus Clinic Locator</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<!-- <link rel="stylesheet" type="text/css" href="./style.css" />
<script type="module" src="./index.js"></script> -->
<link rel="stylesheet" href="map-style.css">
<script src="mapjs.js"></script>
</head>
<style>
/*
* Always set the map height explicitly to define the size of the div element
* that contains the map.
*/
#map {
height: 100%;
}
/*
* Optional: Makes the sample page fill the window.
*/
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
#description {
font-family: Roboto;
font-size: 15px;
font-weight: 300;
}
#infowindow-content .title {
font-weight: bold;
}
#infowindow-content {
display: none;
}
#map #infowindow-content {
display: inline;
}
.pac-card {
background-color: #fff;
border: 0;
border-radius: 2px;
box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3);
margin: 10px;
padding: 0 0.5em;
font: 400 18px poppins, Arial, sans-serif;
overflow: hidden;
font-family: Roboto;
padding: 0;
}
#pac-container {
padding: 10px 5px;
margin-right: 12px;
}
.pac-controls {
display: inline-block;
padding: 5px 11px;
}
.pac-controls label {
font-family: Roboto;
font-size: 12px;
font-weight: 300;
}
#pac-input {
background-color: #fff;
font-family: Roboto;
font-size: 15px;
font-weight: 300;
margin-left: 12px;
padding: 0 11px 0 13px;
text-overflow: ellipsis;
width: 400px;
}
#pac-input:focus {
border-color: #1B5EA9;
}
#title {
color: #fff;
background-color: #1B5EA9;
font-size: 25px;
font-weight: 500;
padding: 6px 12px;
}
</style>
<script>
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 52.632469, lng: -1.689423 },
zoom: 7,
mapTypeControl: false,
});
const card = document.getElementById("pac-card");
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(card);
const center = { lat: 53.81078096004558, lng: -1.5359415045562201 }; /* old { lat: 50.064192, lng: -130.605469 }*/
// Create a bounding box with sides ~10km away from the center point
const defaultBounds = {
north: center.lat + 0.1,
south: center.lat - 0.1,
east: center.lng + 0.1,
west: center.lng - 0.1,
};
const input = document.getElementById("pac-input");
const options = {
bounds: defaultBounds,
componentRestrictions: { country: "uk" },
fields: ["address_components", "geometry", "icon", "name"],
strictBounds: false,
types: ["establishment"],
};
const autocomplete = new google.maps.places.Autocomplete(input, options);
// Set initial restriction to the greater list of countries.
autocomplete.setComponentRestrictions({
country: ["uk"],
});
const southwest = { lat: 5.6108, lng: 136.589326 };
const northeast = { lat: 61.179287, lng: 2.64325 };
const newBounds = new google.maps.LatLngBounds(southwest, northeast);
autocomplete.setBounds(newBounds);
const infowindow = new google.maps.InfoWindow();
const infowindowContent = document.getElementById("infowindow-content");
infowindow.setContent(infowindowContent);
const marker = new google.maps.Marker({
map, anchorPoint: new google.maps.Point(0, -29),
});
autocomplete.addListener("place_changed", () => {
infowindow.close();
marker.setVisible(false);
const place = autocomplete.getPlace();
if (!place.geometry || !place.geometry.location) {
// User entered the name of a Place that was not suggested and
// pressed the Enter key, or the Place Details request failed.
window.alert("No address available for input: '" + place.name + "'");
return;
}
// If the place has a geometry, then present it on a map.
if (place.geometry.viewport) {
map.fitBounds(place.geometry.viewport);
} else {
map.setCenter(place.geometry.location);
map.setZoom(14); // Why 17? Because it looks good.
}
marker.setPosition(place.geometry.location);
marker.setVisible(true);
let address = "";
ma
if (place.address_components) {
address = [
(place.address_components[0] &&
place.address_components[0].short_name) ||
"",
(place.address_components[1] &&
place.address_components[1].short_name) ||
"",
(place.address_components[2] &&
place.address_components[2].short_name) ||
"",
].join(" ");
}
infowindowContent.children["place-icon"].src = place.icon;
infowindowContent.children["place-name"].textContent = place.name;
infowindowContent.children["place-address"].textContent = address;
infowindow.open(map, marker);
});
// Sets a listener on a given radio button. The radio buttons specify
// the countries used to restrict the autocomplete search.
function setupClickListener(id, uk) {
const radioButton = document.getElementById(id);
radioButton.addEventListener("click", () => {
autocomplete.setComponentRestrictions({ country: uk });
});
}
setupClickListener("changecountry-usa", "uk");
setupClickListener("changecountry-usa-and-uot", ["uk"]);
// Set the map's style to the initial value of the selector.
const styleSelector = document.getElementById("style-selector");
map.setOptions({ styles: styles[styleSelector.value] });
// Apply new JSON when the user selects a different style.
styleSelector.addEventListener("change", () => {
map.setOptions({ styles: styles[styleSelector.value] });
});
}
const styles = {
default: [],
silver: [
{
elementType: "geometry",
stylers: [{ color: "#f5f5f5" }],
},
{
elementType: "labels.icon",
stylers: [{ visibility: "off" }],
},
{
elementType: "labels.text.fill",
stylers: [{ color: "#616161" }],
},
{
elementType: "labels.text.stroke",
stylers: [{ color: "#f5f5f5" }],
},
{
featureType: "administrative.land_parcel",
elementType: "labels.text.fill",
stylers: [{ color: "#bdbdbd" }],
},
{
featureType: "poi",
elementType: "geometry",
stylers: [{ color: "#eeeeee" }],
},
{
featureType: "poi",
elementType: "labels.text.fill",
stylers: [{ color: "#757575" }],
},
{
featureType: "poi.park",
elementType: "geometry",
stylers: [{ color: "#e5e5e5" }],
},
{
featureType: "poi.park",
elementType: "labels.text.fill",
stylers: [{ color: "#9e9e9e" }],
},
{
featureType: "road",
elementType: "geometry",
stylers: [{ color: "#ffffff" }],
},
{
featureType: "road.arterial",
elementType: "labels.text.fill",
stylers: [{ color: "#757575" }],
},
{
featureType: "road.highway",
elementType: "geometry",
stylers: [{ color: "#dadada" }],
},
{
featureType: "road.highway",
elementType: "labels.text.fill",
stylers: [{ color: "#616161" }],
},
{
featureType: "road.local",
elementType: "labels.text.fill",
stylers: [{ color: "#9e9e9e" }],
},
{
featureType: "transit.line",
elementType: "geometry",
stylers: [{ color: "#e5e5e5" }],
},
{
featureType: "transit.station",
elementType: "geometry",
stylers: [{ color: "#eeeeee" }],
},
{
featureType: "water",
elementType: "geometry",
stylers: [{ color: "#c9c9c9" }],
},
{
featureType: "water",
elementType: "labels.text.fill",
stylers: [{ color: "#9e9e9e" }],
},
],
/* adding multiple markers */
}
window.initMap = initMap;
</script>
<body>
<div class="pac-card" id="pac-card">
<div>
<div id="title">Find the Medplus nearest Clinic</div>
<div id="country-selector" class="pac-controls">
<input type="radio" name="type" id="changecountry-usa" />
<label for="changecountry-usa">Search by Post Code/Region</label>
<input type="radio" name="type" id="changecountry-usa-and-uot" checked="checked"/>
<label for="changecountry-usa-and-uot">Search by Address</label
>
</div>
</div>
<div id="pac-container">
<input id="pac-input" type="text" placeholder="Enter a location" />
</div>
</div>
<div id="map"></div>
<div id="infowindow-content">
<img src="" width="16" height="16" id="place-icon" />
<span id="place-name" class="title"></span><br />
<span id="place-address"></span>
</div>
<!-- map coloring to sliver -->
<div id="style-selector-control" class="map-control">
<select id="style-selector" class="selector-control">
<option value="silver">Silver</option>
</select>
</div>
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
</body>
</html>
I want to make like this as mentioned (screen shot)
enter image description here
anyone please resolve this if possible, thank you!

onClick is not working in Styled component in react

I am trying to implement a dropdown in react styled component. I am trying to set the submenu onClick. But on click function is not getting called. I want to set submenu on click of SidebarLink component. and display the dropdown submenu
I tried this https://github.com/styled-components/vue-styled-components/issues/22
https://www.geeksforgeeks.org/how-to-create-a-responsive-sidebar-with-dropdown-menu-in-reactjs/
SidebarData
import React from "react";
export const SidebarData = [
{
title: "Feeds",
path: "/feeds",
icon: "Images/images/feed_inactive.svg",
iconsrc: "Images/images/feed_active.svg",
},
{
title: "Groups",
path: "/groups",
icon: "Images/images/notactive_people.svg",
iconsrc: "Images/images/icon_ionic_ios_people.svg",
subNav: [
{
title: "All",
status: "all",
cName: "sub-nav",
},
{
title: "Have met",
status: "met",
cName: "sub-nav",
},
{
title: "Favorite",
status: "fav",
cName: "sub-nav",
},
{
title: "Expired",
status: "expired",
cName: "sub-nav",
},
],
},
{
title: "Chats",
path: "/chats",
icon: "Images/images/notactive_chat_bubble.svg",
iconsrc: "Images/images/icon_material_chat_bubble.svg",
subNav: [
{
title: "Open Threads",
cName: "sub-nav",
status: "threads",
},
{
title: "Favorite",
cName: "sub-nav",
status: "favMem",
},
{
title: "Expired",
cName: "sub-nav",
status: "expChat",
},
],
},
{
title: "Event",
path: "/event",
icon: "Images/images/intersection_51.svg",
iconsrc: "Images/images/intersection_act.svg",
},
];
SidebarLink
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
import { Image } from "react-bootstrap";
const SidebarLink = styled.button`
display: flex;
color: black;
justify-content: space-between;
align-items: center;
padding: 20px;
list-style: none;
height: 60px;
text-decoration: none;
font-size: 18px;
&:hover {
background: #252831;
border-left: 4px solid green;
cursor: pointer;
}
`;
const SidebarLabel = styled.span`
margin-left: 16px;
`;
const DropdownLink = styled(Link)`
background: #252831;
height: 60px;
padding-left: 3rem;
display: flex;
align-items: center;
text-decoration: none;
color: black;
font-size: 18px;
&:hover {
background: green;
cursor: pointer;
}
`;
const SubMenu = (props) => {
const [subnavigation, setSubnavigation] = useState(false);
const showSubnav = () => {
console.log("ddjhj");
setSubnavigation(!subnavigation);
};
useEffect(() => {
console.log("subnav", subnavigation);
}, [subnavigation]);
return (
<>
<SidebarLink onClick={showSubnav}>
{props.item.title}
<span>
{" "}
{props.item.path === window.location.pathname ? (
<Image src={props.item.iconsrc} />
) : (
<Image src={props.item.icon} />
)}
</span>
</SidebarLink>
{subnavigation &&
props.item.subNav.map((item, index) => {
return (
<DropdownLink onClick={() => props.status(item.status)} key={index}>
{item.icon}
<SidebarLabel>{item.title}</SidebarLabel>
</DropdownLink>
);
})}
</>
);
};
export default SubMenu;
Sidebar
import React, { useState, useContext, useEffect } from "react";
import styled from "styled-components";
import { Link } from "react-router-dom";
import * as FaIcons from "react-icons/fa";
import * as AiIcons from "react-icons/ai";
import { SidebarData } from "./SidebarData";
import SubMenu from "./SubMenu";
import { IconContext } from "react-icons/lib";
import { Image } from "react-bootstrap";
import { useDispatch } from "react-redux";
import { MdAccountBalanceWallet } from "react-icons/md";
import { getUserProfile } from "../../../Redux/Slices/DashboardSlice";
import { getPoints, logout } from "../../../Redux/Slices/SidebarSlice";
import {
sidebarToggle,
statusCheck,
userProfile,
} from "../../../Contexts/context";
import "./sidebar.css";
import Loading from "../../../Functions/Loading";
const NavIcon = styled(Link)`
margin-left: 2rem;
font-size: 2rem;
height: 80px;
display: flex;
justify-content: flex-start;
align-items: center;
`;
const SidebarNav = styled.nav`
width: 250px;
height: 100vh;
display: flex;
justify-content: center;
transition: 350ms;
z-index: 10;
`;
const SidebarWrap = styled.div`
width: 100%;
`;
const Sidebar = (props) => {
const dispatch = useDispatch();
const { sidebarShow, setSidebarShow } = useContext(sidebarToggle);
const [sidebar, setSidebar] = useState(false);
const [loading, setLoading] = useState(false);
const { UserData, setUserData } = useContext(userProfile);
const [Points, setPoints] = useState();
const { Status, setStatus } = useContext(statusCheck);
const statusChecked = (status) => {
switch (status) {
case "expired":
return setStatus({
all: false,
fav: false,
met: false,
expired: true,
});
case "met":
return setStatus({
all: false,
fav: false,
met: true,
expired: false,
});
case "fav":
return setStatus({
all: false,
fav: true,
met: false,
expired: false,
});
case "threads":
return setStatus({
favMem: false,
threads: true,
expChat: false,
});
case "favMem":
return setStatus({
favMem: true,
threads: false,
expChat: false,
});
case "expChat":
return setStatus({
favMem: false,
threads: false,
expChat: true,
});
default:
return setStatus({
all: true,
threads: true,
fav: false,
met: false,
expired: false,
});
}
};
useEffect(() => {
dispatch(getUserProfile()).then((response) =>
setUserData(response.payload && response.payload.userprofile)
);
dispatch(getPoints()).then((response) =>
setPoints(response.payload && response.payload.points)
);
}, []);
const logOut = () => {
setLoading(true);
dispatch(logout()).then((response) => {
setLoading(false);
window.location.replace("/");
});
};
return (
<>
<IconContext.Provider value={{ color: "#fff" }}>
<div
className={
sidebarShow
? " vertical-nav bg-white mt-5"
: "vertical-nav bg-white active mt-5"
}
id="sidebar"
>
<SidebarNav>
<SidebarWrap>
{SidebarData.map((item, index) => {
return <SubMenu item={item} key={index} />;
})}
</SidebarWrap>
</SidebarNav>
</div>
</IconContext.Provider>
</>
);
};
export default Sidebar;

How do I dynamically adjust the size of the React chart 2 pie chart

titles come dynamically. The number is not clear. That's why I can't give a fixed height. The graphic is getting smaller. How can I make the chart size dynamic ?
const options = {
legend: {
"position": "bottom",
align:'start',
display:true,
itemWrap: true,
},
responsive: true,
maintainAspectRatio: false,
animation: false,
};
<div className={styles['department-charts__card__altCard']}>
<Doughnut
data={doughnutData}
options={options}
/>
</div>
If the Legend is too much, the graphic gets smaller. With Legend, the chart does not appear in a separate container. When I check it looks like this
<canvas height="600" width="542" class="chartjs-render-monitor" style="display: block; height: 300px; width: 271px;"></canvas>
I want the cake to be at least 300 pixels. With Legend it will be more comfortable to check whether the pie is in a different container. How can I make this show dynamic? Pie does not appear at all on small screens. If there are 3 or 4 in some values, it is not a problem. If it's too much, it doesn't fit. I don't want to hide the legend
Amme hizmeti,
Ben bu şekilde çözdüm. Özel bir efsane yarattım
import { Doughnut } from 'react-chartjs-2';
const options = {
legend: {
"position": "bottom",
display:false,
},
responsive: true,
maintainAspectRatio: false,
animation: false,
offset:true
};
let chartInstance = null;
const DepartmentCharts = ({
t, departmentDistribution
}) => {
const keys = departmentDistribution.map(it => {
const key = it.key
return t(key)
}) || [t('salesAndMarketing'), t('ik'), t('management')]
const values = departmentDistribution.map(it => it.value) || [0, 0, 0]
const doughnutData = {
labels: keys,
datasets: [{
data: values,
backgroundColor: [
'#FF6384',
'#36A2EB',
'#FFCE56',
'#8bc34a',
'#6c429b',
'#00a860',
'#0154a3',
'#f78f1e',
"#CCCC99",
"#666666",
"#FFCC66",
"#6699CC",
"#663366",
"#9999CC",
"#CCCCCC",
"#669999",
"#CCCC66",
"#CC6600",
"#9999FF",
"#0066CC",
"#99CCCC",
"#999999",
"#FFCC00",
"#009999",
"#99CC33",
"#FF9900",
"#999966",
"#66CCCC",
"#339966",
"#CCCC33",
"#003f5c",
"#665191",
"#a05195",
"#d45087",
"#2f4b7c",
"#f95d6a",
"#ff7c43",
"#ffa600",
"#EF6F6C",
"#465775",
"#56E39F",
"#59C9A5",
"#5B6C5D",
"#0A2342",
"#2CA58D",
"#84BC9C",
"#CBA328",
"#F46197",
"#DBCFB0",
"#545775"
],
hoverBackgroundColor: [
'#FF6384',
'#36A2EB',
'#FFCE56',
'#8bc34a',
'#6c429b',
'#00a860',
'#0154a3',
'#f78f1e',
"#CCCC99",
"#666666",
"#FFCC66",
"#6699CC",
"#663366",
"#9999CC",
"#CCCCCC",
"#669999",
"#CCCC66",
"#CC6600",
"#9999FF",
"#0066CC",
"#99CCCC",
"#999999",
"#FFCC00",
"#009999",
"#99CC33",
"#FF9900",
"#999966",
"#66CCCC",
"#339966",
"#CCCC33",
"#003f5c",
"#665191",
"#a05195",
"#d45087",
"#2f4b7c",
"#f95d6a",
"#ff7c43",
"#ffa600",
"#EF6F6C",
"#465775",
"#56E39F",
"#59C9A5",
"#5B6C5D",
"#0A2342",
"#2CA58D",
"#84BC9C",
"#CBA328",
"#F46197",
"#DBCFB0",
"#545775"
]
}]
};
const handleClick = (e, index) => {
const ctx = chartInstance.chartInstance;
const meta = ctx.getDatasetMeta(0);
// See controller.isDatasetVisible comment
meta.data[index].hidden = !meta.data[index].hidden;
// Toggle strikethrough class
if (e.currentTarget.classList.contains("disable-legend")) {
//console.log("çizgiyi kaldır")
e.currentTarget.classList.remove("disable-legend");
e.currentTarget.style.textDecoration = "inherit";
} else {
//console.log("çizgi çiz")
e.currentTarget.classList.add("disable-legend");
e.currentTarget.style.textDecoration = "line-through";
}
chartInstance.chartInstance.update();
};
useEffect(() => {
const legend = chartInstance.chartInstance.generateLegend();
document.getElementById("legend").innerHTML = legend;
document.querySelectorAll("#legend li").forEach((item, index) => {
item.addEventListener("click", (e) => handleClick(e, index));
});
}, [doughnutData]);
return (
<div className={styles['department-charts']}>
<Card className={styles['department-charts__card']}>
<CardHeader
title={`${t('departmentTitle')}`}
align='center'
/>
<CardContent>
<div className={styles['department-charts__card__altCard']}>
<Doughnut
data={doughnutData}
options={options}
ref={input => {
chartInstance = input;
}}
/>
</div>
<div id="legend" className={styles['department-charts__card__altCard__legend']}/>
</CardContent>
</Card>
</div>
);
};
style.css - efsane kısmı sizin için yeterli olabilir
.department-charts {
height: 100%;
&__card {
height: 100%;
padding: 16px;
&__altCard{
min-height: 235px;
&__legend{
color: #666666;
ul{
list-style: none;
font: 12px Verdana;
white-space: nowrap;
display: inline-block;
padding-top: 20px;
font-family: "Helvetica Neue";
}
li{
cursor: pointer;
float: left;
padding-left: 7px;
span{
width: 36px;
height: 12px;
display: inline-block;
margin: 0 5px 8px 0;
vertical-align: -9.4px;
}
}
}
}
}
}
.disable-legend {
text-decoration: line-through;
}

How to display value with '%' react chart-race

i want to display at chart-race value with percentage. example 75%
however, the value of chart-race accepts only number. how would I able to include '%'
here's the code from 'https://www.npmjs.com/package/react-chart-race'
import React, { Component } from 'react';
import ChartRace from 'react-chart-race';
class Form extends Component{
constructor(props){
super(props);
this.state = {
data: []
};
setInterval(() => {
this.handleChange();
}, 0);
}
getRandomInt(min, max){
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
handleChange(){
const data = [
{ id: 0, title: 'Ayfonkarahisar', value: 10, color: '#50c4fe'},
{ id: 1, title: 'Kayseri', value: 20, color: '#3fc42d' },
{ id: 2, title: 'Muğla', value: 30, color: '#c33178' },
{ id: 3, title: 'Uşak', value: 40, color: '#423bce' },
{ id: 4, title: 'Sivas', value: 50, color: '#c8303b' },
{ id: 5, title: 'Konya', value: 60, color: '#2c2c2c' }
];
this.setState({ data });
}
render(){
return(
<div>
<ChartRace
data={this.state.data}
backgroundColor='#000'
width={760}
padding={12}
itemHeight={58}
gap={12}
titleStyle={{ font: 'normal 400 13px Arial', color: '#fff' }}
valueStyle={{ font: 'normal 400 11px Arial', color: 'rgba(255,255,255, 0.42)' }}
/>
</div>
);
}
}
export {Form}
I have made some changes in react-chart-race for label
Now, you do not need to use this dependency use below code in your project
Add this two files to your project
ChartRace.js
import React, { Component } from "react";
import "./chartstyle.css";
export default class ChartRace extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data.sort((a, b) => b.value - a.value),
temp: this.props.data,
maxValue: Math.max.apply(Math, this.props.data.map(item => item.value)),
valueLabel: this.props.valueLabel
};
}
static getDerivedStateFromProps(nextProps, prevState) {
let newProps = [...nextProps.data];
return {
data: nextProps.data,
temp: newProps.sort((a, b) => b.value - a.value),
maxValue: Math.max.apply(Math, nextProps.data.map(item => item.value))
};
}
draw(item, index) {
const indis = this.state.temp.findIndex(temp => temp.id === item.id);
const translateY =
indis === 0
? this.props.padding
: this.props.padding +
indis * this.props.itemHeight +
indis * this.props.gap;
return (
<div
key={index}
className="raceItem"
style={{
height: this.props.itemHeight,
transform:
"translateY(" +
translateY +
"px) translateX(" +
this.props.padding +
"px)"
}}
>
<b
style={{
backgroundColor: item.color,
width:
(item.value / this.state.maxValue) *
(this.props.width - 120 - 2 * this.props.padding)
}}
/>
<span>
<em style={this.props.titleStyle}>{item.title}</em>
<i style={this.props.valueStyle}>
{item.value} {this.state.valueLabel}{" "}
</i>
</span>
</div>
);
}
render() {
return (
<div
className="raceArea"
style={{
backgroundColor: this.props.backgroundColor,
paddingTop: this.props.padding,
paddingBottom: this.props.padding,
width: this.props.width,
height:
2 * this.props.padding +
this.state.temp.length * this.props.itemHeight +
(this.state.temp.length - 1) * this.props.gap
}}
>
{this.state.data.map((item, index) => this.draw(item, index))}
</div>
);
}
}
ChartRace.defaultProps = {
data: [],
backgroundColor: "#f9f9f9",
width: 680,
padding: 20,
itemHeight: 38,
gap: 4,
titleStyle: { font: "normal 400 13px Arial", color: "#212121" },
valueStyle: { font: "normal 400 11px Arial", color: "#777" },
valueLabel: ""
};
chartstyle.css
.raceArea {
box-sizing: border-box;
position: relative;
}
.raceItem {
display: flex;
align-items: center;
position: absolute;
top: 0px;
left: 0px;
transition: all 1200ms;
}
.raceItem b {
height: 100%;
border-radius: 4px;
transition: all 600ms;
background: #dadada;
}
.raceItem span {
width: 112px;
margin-left: 8px;
display: flex;
flex-direction: column;
}
.raceItem em {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.raceItem i {
margin-top: 2px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
Import it like
import ChartRace from "./ChartRace";
Use it like
<ChartRace
data={this.state.data}
backgroundColor="#000"
width={760}
padding={12}
itemHeight={58}
gap={12}
valueLabel="%" // pass your label here
titleStyle={{ font: "normal 400 13px Arial", color: "#fff" }}
valueStyle={{
font: "normal 400 11px Arial",
color: "rgba(255,255,255, 0.42)"
}}
/>

How would this React code look without React-hooks and with class components rather than functional components?

I just studied React from YouTube lessons, and there all the lessons were built on classes and the usual this.setState, without hooks. How would this React code look without React-hooks and with class components rather than functional components?
The code itself implements an image slider:
React:
function Slider({ items }) {
const [ active, setActive ] = React.useState(0);
const { length, [active]: slide } = items;
const next = e => setActive((active + +e.target.dataset.step + length) % length);
const goTo = e => setActive(+e.target.dataset.index);
React.useEffect(() => {
const timeout = setTimeout(() => setActive((active + 1 + length) % length), 5000);
return () => clearTimeout(timeout);
}, [active, length]);
return (
<div>
<div className="slideshow-container">
<div className="mySlides fade">
<div className="numbertext">{active + 1} / {length}</div>
<img src={slide.img} />
<div className="text">{slide.title}</div>
</div>
<a className="prev" onClick={next} data-step={-1}>❮</a>
<a className="next" onClick={next} data-step={+1}>❯</a>
</div>
<div className="dots">
{items.map((n, i) => (
<span
key={n.id}
className={`dot ${i === active ? 'active' : ''}`}
onClick={goTo}
data-index={i}
></span>
))}
</div>
</div>
);
}
const items = [
{ title: 'One', img: 'https://upload.wikimedia.org/wikipedia/commons/1/1f/Purity_of_nature.jpg' },
{ title: 'Two', img: 'https://mairie-balma.fr/wp-content/uploads/2016/06/Lhers.jpg' },
{ title: 'Three', img: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRt-b1iBqHQ_emkm1wFmkM7KQskzIqg7YQPZWW85Sa7k2nNLwgjMw' },
].map((n, i) => ({ ...n, id: i + 1 }));
ReactDOM.render(<Slider items={items} />, document.getElementById('app'));
HTML
<div id="app"></div>
CSS:
.slideshow-container {
max-width: 500px;
position: relative;
margin: auto;
}
.prev, .next {
cursor: pointer;
position: absolute;
top: 50%;
width: auto;
padding: 16px;
margin-top: -22px;
color: white;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
border-radius: 0 3px 3px 0;
user-select: none;
}
.next {
right: 0;
border-radius: 3px 0 0 3px;
}
.prev:hover, .next:hover {
background-color: rgba(0,0,0,0.8);
}
.text {
color: #f2f2f2;
font-size: 15px;
padding: 8px 12px;
position: absolute;
bottom: 8px;
width: 100%;
text-align: center;
box-sizing: border-box;
}
.numbertext {
color: #f2f2f2;
font-size: 12px;
padding: 8px 12px;
position: absolute;
top: 0;
}
.dots {
display: flex;
justify-content: center;
}
.dot {
cursor: pointer;
height: 15px;
width: 15px;
margin: 0 2px;
background-color: #bbb;
border-radius: 50%;
display: inline-block;
transition: background-color 0.6s ease;
}
.active, .dot:hover {
background-color: #717171;
}
.mySlides img {
width: 100%;
}
Something like this (not fully tested):
class Slider {
constructor(props) {
super(props);
this.state = {
active: 0
}
}
let timeout = null;
componentDidMount() {
this.timeout = setTimeout(() => this.setActive(), 5000);
}
componentDidUpdate(prevProps) {
const { active } = this.props;
if (prevProps.active !=== active {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => this.setActive(), 5000);
});
}
componentDidUnmount() {
if (this.timeout) {
clearTimeout(this.timeout);
}
}
const setActive = (newActive) => {
const { length } = items;
this.setState({
active: (newActive + 1 + length) % length
});
}
const next = e => {
const { length } = items;
this.setActive((this.state.active + +e.target.dataset.step + length) % length);
}
const goTo = e => this.setActive(+e.target.dataset.index);
render() {
const { length } = items;
const {active} = this.state;
return (
<div>
<div className="slideshow-container">
<div className="mySlides fade">
<div className="numbertext">{active + 1} / {length}</div>
<img src={slide.img} />
<div className="text">{slide.title}</div>
</div>
<a className="prev" onClick={this.next} data-step={-1}>❮</a>
<a className="next" onClick={this.next} data-step={+1}>❯</a>
</div>
<div className="dots">
{items.map((n, i) => (
<span
key={n.id}
className={`dot ${i === active ? 'active' : ''}`}
onClick={this.goTo}
data-index={i}
></span>
))}
</div>
</div>
);
}
}
const items = [
{ title: 'One', img: 'https://upload.wikimedia.org/wikipedia/commons/1/1f/Purity_of_nature.jpg' },
{ title: 'Two', img: 'https://mairie-balma.fr/wp-content/uploads/2016/06/Lhers.jpg' },
{ title: 'Three', img: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRt-b1iBqHQ_emkm1wFmkM7KQskzIqg7YQPZWW85Sa7k2nNLwgjMw' },
].map((n, i) => ({ ...n, id: i + 1 }));
ReactDOM.render(<Slider items={items} />, document.getElementById('app'));

Resources