I'm new to React and trying to learn. My personal project is homebrewing display site.
I want to show some of the details in radial bar. I followed tutorial and made array data file, and index file where details show. Now I'm stuck getting single values to display in radial bar.
Also values need to be calculated from percent to numeric. I got that working. I tried some push functions but did not get it to work way I wanted. My coding skills are beginner level.
display example
data.js
import product1 from '../../images/product-1.png';
import product2 from '../../images/product-1.png';
export const productData = [
{
id: 1,
img: product1,
alt: 'Beer',
name: 'Lager',
desc: 'Saaz',
ibu: '35',
ebc: '40',
abv: '8.5',
},
{
id: 2,
img: product2,
alt: 'Beer',
name: 'IPA',
desc: 'Mango, Citrus',
ibu: '85',
ebc: '25',
abv: '5.5',
},
];
index.js
import React, { Component } from 'react';
import Chart from 'react-apexcharts';
import {
ProductsContainer,
ProductWrapper,
ProductsHeading,
ProductTitle,
ProductCard,
ProductImg,
ProductInfo,
ProductDesc,
ProductIbu,
ProductEbc,
ProductAbv,
} from './ProductsElements';
const max = 119;
function valueToPercent(value) {
return (value * 100) / max;
}
class IBU extends Component {
constructor(props) {
super(props);
this.state = {
series: [valueToPercent(15)],
options: {
plotOptions: {
radialBar: {
startAngle: -90,
endAngle: 90,
hollow: {
margin: 10,
size: '70%',
background: '#222222',
image: undefined,
imageOffsetX: 0,
imageOffsetY: 0,
position: 'front',
dropShadow: {
enabled: true,
top: 5,
left: 0,
blur: 4,
opacity: 0.9,
},
},
track: {
background: '#d42d2d',
strokeWidth: '67%',
margin: 0, // margin is in pixels
dropShadow: {
enabled: true,
top: 0,
left: 0,
blur: 4,
color: '#000',
opacity: 0.6,
},
},
dataLabels: {
show: true,
name: {
offsetY: -10,
show: true,
color: '#fff',
fontSize: '17px',
},
value: {
formatter: function (value) {
return (parseFloat(value) * max) / 100;
},
color: '#dadada',
fontSize: '36px',
show: true,
},
},
},
},
fill: {
colors: [
function ({ value, seriesIndex, w }) {
if (value < 55) {
return '#7E36AF';
} else if (value >= 55 && value < 80) {
return '#164666';
} else {
return '#D9534F';
}
},
],
},
stroke: {
lineCap: 'round',
},
labels: ['IBU'],
},
};
}
render() {
return <Chart options={this.state.options} series={this.state.series} type="radialBar" height={250} />;
}
}
const Products = ({ data }) => {
return (
<ProductsContainer>
<ProductsHeading>heading</ProductsHeading>
<ProductWrapper>
{data.map((product, index) => {
return (
<ProductCard key={index}>
<ProductImg src={product.img} alt={product.alt} />
<ProductInfo>
<ProductTitle>{product.name}</ProductTitle>
<ProductDesc>{product.desc}</ProductDesc>
<ProductIbu>{product.ibu}</ProductIbu>
<IBU></IBU>
<ProductEbc>{product.ebc}</ProductEbc>
<ProductAbv>{product.abv}</ProductAbv>
</ProductInfo>
</ProductCard>
);
})}
</ProductWrapper>
</ProductsContainer>
);
};
export default Products;
Related
i want to do effect with particlejs i want to contain my particle in div with (width 250px height 250px) but i cant i dont know why the all particle are in all page
i tried to this contain the div in div and with position absolute/relative
import { useCallback } from "react";
import Particles from "react-tsparticles";
import type { Container, Engine } from "tsparticles-engine";
import { loadFull } from "tsparticles";
import '../style/Particules.css'
const Particule = () => {
const particlesInit = useCallback(async (engine: Engine) => {
console.log(engine);
// you can initialize the tsParticles instance (engine) here, adding custom shapes or presets
// this loads the tsparticles package bundle, it's the easiest method for getting everything ready
// starting from v2 you can add only the features you need reducing the bundle size
await loadFull(engine);
}, []);
const particlesLoaded = useCallback(async (container: Container | undefined) => {
await console.log(container);
}, []);
return (
<div className="container">
<div>
<h2>first</h2>
</div>
<div> <h2>second</h2>
<Particles
id="tsparticles"
init={particlesInit}
loaded={particlesLoaded}
options={{
background: {
color: {
value: "#0d47a1",
}
},
fullScreen: {
enable: true,
zIndex: -1,
},
fpsLimit: 60,
interactivity: {
events: {
onClick: {
enable: true,
mode: "push",
},
onHover: {
enable: true,
mode: "repulse",
},
resize: true,
},
modes: {
push: {
quantity: 1,
},
repulse: {
distance: 100,
duration: 0.04,
},
},
},
particles: {
color: {
value: "#fff",
},
links: {
color: "#ffffff",
distance: 15,
enable: true,
opacity: 0.1,
},
collisions: {
enable: false,
},
move: {
direction: "none",
enable: true,
outModes: {
default: "bounce",
},
random: false,
speed: 0.5,
straight: false,
},
number: {
density: {
enable: true,
area: 800,
},
value: 30,
},
opacity: {
value: 2,
},
style: {
position: "absolute"
},
shape: {
type: "images",
"images":
[{
"src": 'https://media.wuerth.com/stmedia/modyf/eshop/products/std.lang.all/resolutions/category/png-546x410px/56314216.png',
}, {
"src": "https://media.wuerth.com/stmedia/modyf/eshop/products/std.lang.all/resolutions/category/png-546x410px/56597539.png"
},
{
"src": "https://media.wuerth.com/stmedia/modyf/eshop/products/std.lang.all/resolutions/category/png-546x410px/4042942.png"
},
{
"src": ""
}
]
},
size: {
random: true,
value: 50,
},
},
detectRetina: true,
}}
/>
</div>
<div>
<h2>third</h2>
</div>
</div>
);
};
export default Particule;
css
.container{
display: flex;
position: absolute;
width: 100vw;
height : 100%;
top: 0;
left: 0;
}
.container > div {
position: relative;
width: 100%;
}
i have this result in the image your see me i want this
but me i want to contain my particle in the second div only [enter image description here](https://i.stack.imgur.com/Yxteq.png)
i try to add the style in the the position absolute , but nothing
<Particles
id="tsparticles"
init={particlesInit}
loaded={particlesLoaded}
options={{
background: {
color: {
value: "#0d47a1",
}
},
style:{
position:"absolute"
},
thanks for the help
i want to add the particle in the second div with heigth and width i dont want on the all page
I need to create a graph in real time getting data from Firebase. I'm using ReactJs with typescript along with Highcharts.
I've already managed to simulate Highcharts in real time, but now I'm wondering how I can connect it to get the data that will be added to Firebase.
Global Graph Component:
import * as Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import HighchartsMore from 'highcharts/highcharts-more';
import HighchartsAccessibility from 'highcharts/modules/accessibility';
import HighchartsData from 'highcharts/modules/data';
import HighchartsExporting from 'highcharts/modules/exporting';
import HighchartsHeatmap from 'highcharts/modules/heatmap';
import HighchartsTreeChart from 'highcharts/modules/treemap';
import { defaultTheme } from '../../../styles/themes';
HighchartsAccessibility(Highcharts);
HighchartsMore(Highcharts);
HighchartsData(Highcharts);
HighchartsHeatmap(Highcharts);
HighchartsTreeChart(Highcharts);
HighchartsExporting(Highcharts);
interface IChartProps {
options: Highcharts.Options;
}
export const labelsStyle = (
fontSize = 20,
color = defaultTheme.palette.grey[900]
) => ({
color,
fontSize: `${fontSize / 16}rem`,
fontFamily: 'Poppins',
margin: '0px',
fontWeight: 400,
lineHeight: `${(fontSize + 10) / 16}rem`,
});
export const Chart: React.FC<IChartProps> = ({ options }) => {
Highcharts.setOptions({
// rangeSelector: {
// enabled: false,
// },
// navigator: {
// enabled: false,
// },
xAxis: {
labels: {
style: labelsStyle(),
},
},
yAxis: {
labels: {
style: labelsStyle(18, defaultTheme.palette.grey[200]),
},
},
credits: {
enabled: false,
},
exporting: {
enabled: false,
},
});
return <HighchartsReact highcharts={Highcharts} options={options} />;
};
Realtime Graph Component:
import { TooltipFormatterContextObject } from 'highcharts';
import {
Chart,
labelsStyle,
} from '../../../../shared/components/DataDisplay/Chart';
import { defaultTheme } from '../../../../shared/styles/themes';
import { formatDate } from '../../../../shared/utils/date';
import { getRandomIntInclusive } from '../../../../shared/utils/math';
export const GraphHeartRateMonitor: React.FC = () => {
const analyticsDataState = {
dates: [],
data: [],
};
const options: Highcharts.Options = {
chart: {
borderWidth: 0,
height: '176px',
plotBackgroundColor: defaultTheme.palette.background.default,
type: 'line',
marginRight: 10,
events: {
load() {
const series = this.series[0];
setInterval(function () {
const x = new Date().getTime(), // current time
y = getRandomIntInclusive(80, 120);
series.addPoint([x, y], true, true);
}, 1000);
},
},
},
title: {
text: '',
},
xAxis: {
type: 'datetime',
tickPixelInterval: 300,
labels: {
enabled: false,
},
},
yAxis: {
title: {
text: '',
},
gridLineColor: defaultTheme.palette.background.default,
tickInterval: 10,
labels: {
enabled: false,
},
plotLines: [
{
value: 120,
color: defaultTheme.palette.red[300],
width: 2,
label: {
text: '120',
useHTML: true,
verticalAlign: 'middle',
align: 'left',
style: {
background: defaultTheme.palette.red[300],
borderRadius: '24px',
height: '18px',
padding: '2px 12px',
color: defaultTheme.palette.common.white,
fontFamily: 'Roboto Mono',
fontSize: '12px',
fontWeight: '400',
},
},
},
],
// lineColor: defaultTheme.palette.background.default,
},
legend: {
enabled: false,
},
exporting: {
enabled: false,
},
series: [
{
name: 'Random data',
color: defaultTheme.palette.grey[900],
data: (function () {
// generate an array of random data
let data = [],
time = new Date().getTime(),
i;
for (i = -19; i <= 0; i++) {
data.push({
x: time + i * 1000,
y: getRandomIntInclusive(80, 120),
});
}
return data;
})(),
},
],
tooltip: {
useHTML: true,
backgroundColor: defaultTheme.palette.background.paper,
borderRadius: 4,
shadow: {
color: defaultTheme.palette.common.black,
},
padding: 12,
style: labelsStyle(20, defaultTheme.palette.grey[300]),
formatter() {
const self: TooltipFormatterContextObject = this;
return `
<span> Time:</span>
<strong>${formatDate(new Date(self.x), 'HH:mm:ss')}</strong>
</br>
<span>Heart Rate:</span>
<strong >${self.y} bpm</strong>
`;
},
},
};
return <Chart options={options} />;
};
To call the component just call as a normal component <GraphHeartRateMonitor /> .
I am building an app for gym-goers to record and track their progress. For each exercise they input, it should render a chart showing their previous track record. This is working fine.
Users can then add another entry to this track record, but it does not update the chart unless you refresh the page. I can't work out why or how to fix it.
There are a number of different components involved - a parent Exercise.js one, then an ExerciseFooter.js one, which contains the buttons to adjust the target or add a new entry to the exercise, and then AddHistory.js and SetTarget.js components which contain modals and the logic to update the exercise via Redux and MongoDB.
A minimal version of the Exercise.js page is here (I've collapsed the stuff that's mainly styling into single lines as much as possible):
import React, { useState, useEffect } from "react";
import { ExerciseFooter } from "./ExerciseFooter";
import { Line } from "react-chartjs-2";
import { useLocation } from "react-router-dom";
import { useSelector } from "react-redux";
export const Exercise = (props) => {
const location = useLocation();
const users = useSelector((state) => state.auth);
const localUser = JSON.parse(localStorage.getItem("profile"));
const [user, setUser] = useState("");
const [exerciseProp, setExerciseProp] = useState({
history: [""],
target: 0,
});
useEffect(() => {
localUser &&
localUser?.result &&
users.length > 0 &&
setUser(
users.filter(
(filteredUser) => filteredUser._id == props.match.params.userId
)[0]
);
if (!localUser) setUser("");
setExerciseProp(
user?.exercises?.filter(
(exercise) => exercise._id == props.match.params.exerciseId
)[0]
);
}, [users, location]);
//styling for chart
const [barData, setBarData] = useState({
labels: [""],
datasets: [
{ label: "Target", fill: false, radius: 0, data: [""], borderColor: ["rgba(35, 53, 89)"], borderWidth: [3], },
{ label: "You did", data: [""], tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3, },
],
});
//updating chart data
var weightArr = [];
var dateArr = [];
var targetArr = [];
if (exerciseProp) {
exerciseProp.history.map((hist) =>
weightArr.push(parseInt(hist.weight) || 0)
);
exerciseProp.history.map((hist) => dateArr.push(hist.date));
for (let i = 0; i < exerciseProp.history.length; i++) {
targetArr.push(exerciseProp.target);
}
}
useEffect(() => {
if (exerciseProp) {
setBarData({
labels: dateArr,
datasets: [
{
label: "Target",
fill: false,
radius: 0,
data: targetArr,
borderColor: ["rgba(35, 53, 89)"], borderWidth: [3],
},
{
label: "Weight",
data: weightArr,
tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3,
},
],
});
}
}, [users]);
//render chart ones exerciseProp is populated
if (exerciseProp) {
return (
<div style={{ marginTop: "200px" }}>
<Line
data={barData}
options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}
/>
{exerciseProp && <ExerciseFooter user={user} exercise={exerciseProp} />}
</div>
);
} else {
return <>Loading...</>;
}
};
I've tried doing a few different things but nothing has worked. I tried adding an 'update' state variable which was updated by a function passed down to the the various dispatches, and then added it to the dependencies of the useEffects, but that didn't seem to make any difference.
Any help much appreciated! As I say, if I just force a refresh then it works fine but know that's bad practice so trying to work out why it isn't re-rendering correctly.
Thanks!
You just have to enable redraw prop
like this
<Line
redraw={true}
data={barData}
options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}/>
this all you have to do
redraw={true}
So I've been using react ChartJS to build some graphs, but eventually I ran into scroll problems that I couldn't solve with chartjs, so I found Apex that has Zoom built-in and I could make it scrollable.
But now since they are two completely different libs, so I can't copy it 100% the way it was.
I did not find anything on the docs talking about multiple labels in one value, so if anyone could help me with this one.
And if you see close enough there are some line running on the chart below, are those possible to do with Apex? (I'm using a customPlugin in chartJS to do it)
Ok, in the end I changed back to ChartJS and used the chartjs-plugin-zoom plugin to do the same thing I was doing with ApexChart, I'm placing my code in this answer in case that anyone faces the same problem.
React Bar Component:
import React from "react";
import { Bar, Chart } from "react-chartjs-2";
import ChartDataLabels from "chartjs-plugin-datalabels";
import Zoom from "chartjs-plugin-zoom";
const ChartBar = ({
data,
text,
noLabel,
stacked,
newPlugin,
labelPosition,
// test,
}) => {
Chart.register(Zoom);
return (
<div
className="graphics"
style={{
display: "flex",
alignItems: "center",
flexDirection: "column",
}}
>
{/* <h1>Gráficos</h1> */}
<div style={{ width: "65%", height: "350px" }}>
<Bar
data={data}
plugins={[
ChartDataLabels,
newPlugin ? newPlugin : "",
// test ? test : "",
]}
options={{
categoryPercentage: stacked ? 1.0 : 0.9,
barPercentage: 1.0,
// layout: {
// padding: {
// left: 0,
// right: 0,
// top: 60,
// bottom: 0,
// },
// },
responsive: true,
maintainAspectRatio: false,
plugins: {
zoom: {
// limits: { y: { min: "original", max: "original" } },
// pan: { enabled: true, mode: "xy", threshold: 10 },
// zoom: {
// wheel: {
// enabled: true,
// mode: "xy",
// },
// },
limits: { y: { min: "original", max: "original" } },
pan: { enabled: true, mode: "x", threshold: 10 },
zoom: {
mode: "x",
drag: {
enabled: true,
backgroundColor: "rgba(225,0,225,0.3)",
},
wheel: {
enabled: true,
modifierKey: "alt",
},
},
},
tooltip: { enabled: false },
legend: {
display: noLabel ? false : true,
position: labelPosition ? labelPosition : "bottom",
title: { padding: 40 },
},
title: { text: text, display: true, padding: 30 },
},
scales: {
// scaleLabel: { display: true },
x: {
stacked: stacked ? true : false,
// ticks: {
// display: false,
// autoSkip: true,
// maxTicksLimit: 10,
// beginAtZero: true,
// },
// gridLines: {
// display: false,
// },
},
y: { stacked: stacked ? true : false, ticks: { display: false } },
// xAxes: [{ scaleLabel: { display: true } }],
},
}}
/>
</div>
</div>
);
};
export default ChartBar;
One data object that is being used with this component(eg. the one in the main question):
{
customPlugin: {
id: "customValue",
afterDraw: (chart, args, opts) => {
const {
ctx,
data: { datasets },
_metasets,
} = chart;
datasets[1].data.forEach((dp, i) => {
let increasePercent =
(dp * 100) / datasets[0].data[i] >= 100
? Math.round(
((dp * 100) / datasets[0].data[i] - 100) * 100
) / 100
: (Math.round(
(100 - (dp * 100) / datasets[0].data[i]) * 100
) /
100) *
-1;
let barValue = `${increasePercent}%`;
const lineHeight = ctx.measureText("M").width;
const offset = opts.offset || 0;
const dash = opts.dash || [];
ctx.textAlign = "center";
ctx.fillText(
barValue,
_metasets[1].data[i].x,
_metasets[1].data[i].y - lineHeight * 1.5,
_metasets[1].data[i].width
);
if (_metasets[0].data[i].y >= _metasets[1].data[i].y) {
ctx.beginPath();
ctx.setLineDash(dash);
ctx.moveTo(_metasets[0].data[i].x, _metasets[0].data[i].y);
ctx.lineTo(
_metasets[0].data[i].x,
_metasets[1].data[i].y - offset
);
ctx.lineTo(
_metasets[1].data[i].x,
_metasets[1].data[i].y - offset
);
ctx.stroke();
} else {
ctx.beginPath();
ctx.setLineDash(dash);
ctx.moveTo(
_metasets[0].data[i].x,
_metasets[0].data[i].y - offset
);
ctx.lineTo(
_metasets[1].data[i].x,
_metasets[0].data[i].y - offset
);
ctx.lineTo(
_metasets[1].data[i].x,
_metasets[1].data[i].y - offset - lineHeight * 2
);
ctx.stroke();
}
});
},
},
text: "Evolução da Receita Líquida por Produto",
type: "bar",
// labels: values?.estimatedProducts?.map((v, i) => {
// return `Rec Líq - Prod ${++i}`;
// }),
labels: addNewArrayValue(values?.estimatedProducts, true),
datasets: [
{
type: "bar",
label: values?.monthsLabels?.mesBaseLabel,
data: values?.productsValues?.receitaLiquidaBase,
backgroundColor: ["rgba(42,62,176, 1)"],
datalabels: {
font: {
size: 10,
},
rotation: -90,
color: "white",
formatter: (value, context) => {
if (value !== 0) {
return value
?.toFixed()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
// ?.toFixed(0)
// .replace(/\d(?=(\d{3})+\.)/g, "$&,");
} else {
return 0;
}
},
},
},
{
type: "bar",
label: values?.monthsLabels?.mesOrcadoLabel,
data: values?.productsValues?.receitaLiquidaOrcado,
backgroundColor: "orange",
datalabels: {
font: {
size: 10,
},
rotation: -90,
color: "black",
formatter: (value, context) => {
if (value !== 0) {
return value
?.toFixed()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
} else {
return 0;
}
},
},
},
],
},
Yeah, it's not the best code, but it's a starter, I've been struggling for 1h30m just because there's little to no actual examples/doc on this case.
I am trying to find a way in the apex chart. By which if user zoom on the year graph then the user should be able to get month data when they zoom on month data this should show the day data.
But I am not able to figure out if its possible in apex chart or not.
This is how my graph look like right now.
import React from "react";
import ReactApexChart from "react-apexcharts";
interface StackedGraphProps {}
type SeriesType = {
name: string;
data: number[];
};
interface StackedGraphState {
series: SeriesType[];
options: any;
}
class StackedBarGraph extends React.Component<
StackedGraphProps,
StackedGraphState
> {
constructor(props: any) {
super(props);
this.state = {
series: [
{
name: "Marine Sprite",
data: [44, 55, 41, 37, 22, 43, 21],
},
{
name: "Striking Calf",
data: [53, 32, 33, 52, 13, 43, 32],
},
{
name: "Tank Picture",
data: [12, 17, 11, 9, 15, 11, 20],
},
],
options: {
chart: {
events: {
zoomed: function (chartContext: any, { xaxis, yaxis }) {
console.log("xAxis", xaxis, yaxis);
},
selection: function (chartContext: any, { xaxis, yaxis }) {
console.log("Selecton", xaxis, yaxis);
},
dataPointSelection: (
event: any,
chartContext: any,
config: any
) => {
console.log("datapoint", chartContext, config);
},
},
zoom: {
enabled: true,
type: "x",
autoScaleYaxis: false,
// zoomedArea: {
// fill: {
// color: "#90CAF9",
// opacity: 0.4,
// },
// stroke: {
// color: "#0D47A1",
// opacity: 0.4,
// width: 1,
// },
// },
},
type: "bar",
height: 350,
stacked: true,
},
toolbar: {
show: true,
},
plotOptions: {
bar: {
horizontal: false,
},
},
stroke: {
width: 1,
colors: ["#fff"],
},
grid: {
row: {
colors: ["#fff", "#f2f2f2f2"],
},
},
title: {
text: "",
},
xaxis: {
tickPlacement: "on",
categories: [2008, 2009, 2010, 2011, 2012, 2013, 2014],
labels: {
formatter: function (val: any) {
return val + "K";
},
},
},
yaxis: {
title: {
text: undefined,
},
},
tooltip: {
y: {
formatter: function (val: any) {
return val + "K";
},
},
},
fill: {
opacity: 1,
},
legend: {
position: "top",
horizontalAlign: "left",
offsetX: 40,
},
},
};
}
render() {
return (
<div id="chart">
<ReactApexChart
zoomEnabled={true}
options={this.state.options}
series={this.state.series}
type="bar"
height={350}
/>
</div>
);
}
}
export default StackedBarGraph;