When I try to update the state on the hover event, the actual state value is getting changed but the graph is not re-rendering.
in the console, I am able to see the node label is changed to sample. but the graph is not rerendering.
Here is my react function based component.
import React, { useEffect, useState } from 'react';
import Graph from 'react-graph-vis';
import './vis-network.css';
function RelationGraph1() {
const [graph, setGraph] = useState({
nodes: [
{
id: 1,
label: 'Node 1',
title: '',
},
{ id: 2, label: 'Node 2', title: '' },
{ id: 3, label: 'Node 3', title: '' },
{ id: 4, label: 'Node 4', title: '' },
{ id: 5, label: 'Node 5', title: '' },
],
edges: [
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 2, to: 4 },
{ from: 2, to: 5 },
],
});
const options = {
layout: {
hierarchical: false,
},
edges: {
color: '#1D1D1D',
},
interaction: {
hover: true,
navigationButtons: true,
tooltipDelay: 0,
},
nodes: {
borderWidth: 0,
borderWidthSelected: 0,
color: '#0262C4',
shape: 'circle',
size: 1,
shadow: {
enabled: true,
color: 'rgba(0,0,0,0.5)',
size: 10,
x: 5,
y: 5,
},
font: {
color: '#fff',
size: 13,
bold: {
mod: 'bold',
},
},
},
};
const events = {
select: function (event) {
var { nodes, edges } = event;
console.log('Selected nodes:');
console.log(nodes);
console.log('Selected edges:');
console.log(edges);
},
showPopup: (id) => { // node id
const data = graph.nodes.map((el) => {
if (el.id === id) {
el.label = `sample node name`;
}
return el;
});
setGraph({ ...graph, nodes: data });
},
};
return (
<Graph
graph={graph}
options={options}
events={events}
style={{ height: '450px' }}
/>
);
}
export default RelationGraph1;
Really Appriciate for the help. Thanks !
I was able to update the label in hoverNode event like this:
hoverNode: (e) => {
const data = graph.nodes.map((el) => {
if (el.id === e.node) return { ...el, label: "sample node name" };
else return el;
});
const temp = { ...graph };
temp.nodes = data;
setGraph(temp);
},
Sample: https://codesandbox.io/s/long-bird-4h444?file=/src/App.js:1235-1501
Related
I am trying to make a force directed graph with amcharts 5 where the nodes are images.
I was able to make images as nodes but was not really able to customize it. I want it to be rounded and has an onClick handler, which returns the node which is being clicked.
import React, { useLayoutEffect } from "react";
import "./App.css";
import * as am5 from "#amcharts/amcharts5";
import * as am5hierarchy from "#amcharts/amcharts5/hierarchy";
import am5themes_Animated from "#amcharts/amcharts5/themes/Animated";
function App(props) {
useLayoutEffect(() => {
let root = am5.Root.new("chartdiv");
root.setThemes([am5themes_Animated.new(root)]);
let chart = root.container.children.push(
am5.Container.new(root, {
width: am5.percent(100),
height: am5.percent(100),
layout: root.verticalLayout,
})
);
let series = chart.children.push(
am5hierarchy.ForceDirected.new(root, {
downDepth: 1,
initialDepth: 1,
topDepth: 0,
valueField: "value",
categoryField: "name",
childDataField: "children",
xField: "x",
yField: "y",
minRadius: 30,
manyBodyStrength: -40,
})
);
// series.circles.template.set("forceHidden", true);
// series.outerCircles.template.set("forceHidden", true);
series.circles.template.events.on("click", function (ev) {
console.log(ev);
console.log("Clicked on");
});
// Use template.setup function to prep up node with an image
series.nodes.template.setup = function (target) {
target.events.on("dataitemchanged", function (ev) {
target.children.push(
am5.Picture.new(root, {
width: 90,
height: 90,
centerX: am5.percent(50),
centerY: am5.percent(50),
src: ev.target.dataItem.dataContext.image,
})
);
});
};
series.bullets.push(function (root) {
return am5.Bullet.new(root, {
sprite: am5.Picture.new(root, {
radius: 4,
fill: series.get("fill"),
}),
});
});
series.data.setAll([
{
name: "Chrome",
value: 1,
image: "https://picsum.photos/202",
children: [
{ name: "Google", value: 1, image: "https://picsum.photos/203" },
{
name: "Firefox",
value: 1,
image: "https://picsum.photos/204",
},
{
name: "IE",
value: 1,
image: "https://picsum.photos/203",
},
{
name: "Safari",
value: 1,
image: "https://picsum.photos/205",
},
{
name: "Opera",
value: 1,
image: "https://picsum.photos/206",
},
],
},
]);
series.set("selectedDataItem", series.dataItems[0]);
return () => {
root.dispose();
};
}, []);
return <div id="chartdiv" style={{ width: "100%", height: "500px" }}></div>;
}
export default App;
I was able to find some workaround using pinBullets in amhcarts 4 but I'm trying to get it working on amcharts 5.
So I have react-datepicker to choose the dates of my data, the data that is returned looks like this:
{clicks: 6, date: "2022-05-15", link: "instagram"},
{clicks: 5, date: "2022-05-15", link: "google"}...
On load and every change in the datePicker, I have this function which sets the data variables, the date will be moment():
const onChangeClick = (dates: any) => {
const [start, end] = dates;
setStartDateClick(start);
setEndDateClick(end);
if (start & end) {
let dates = {
dateFrom: moment(start).format('YYYY-MM-DD'),
dateTo: moment(end).format("YYYY-MM-DD")
}
_statisticsService.getClickStats(dates).then((response: any) => {
response.data.map((e: any) => {
clicksData[e?.link].push(e)
})
setInstaDataClick(clicksData['instagram'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setFacebookDataClick(clicksData['facebook'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setGoogleDataClick(clicksData['google'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setTripadvisorDataClick(clicksData['tripadvisor'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setWebsiteDataClick(clicksData['website'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
})
setLabelsClick(getDaysArray(clicksData[0]?.date, clicksData[clicksData.length - 1].date))
}
};
And later I use this values in the datasets:
const data = {
labels: labelsClick,
datasets: [
{
label: 'Instagram',
data: instaDataClick,
borderColor: '#c32aa3',
backgroundColor: '#c32aa3',
},
{
label: 'Facebook',
data: facebookDataClick,
borderColor: '#1877f2',
backgroundColor: '#1877f2',
},
{
label: 'Google',
data: googleDataClick,
borderColor: '#ea4335',
backgroundColor: '#ea4335',
},
{
label: 'Tripadvisor',
data: tripadvisorDataClick,
borderColor: '#34e0a1',
backgroundColor: '#34e0a1',
},
{
label: 'Website',
data: websiteDataClick,
borderColor: '#010101',
backgroundColor: '#010101',
},
],
};
And at the end this is how I preview the Line chart
<div className='datepicker-container'>
<DatePicker
onChange={onChangeClick}
startDate={startDateClick}
endDate={endDateClick}
selectsRange
placeholderText='Select Date'
isClearable
/>
</div>
<div className='chart'>
<Line options={options} data={data} />
</div>
The options for this line look like this, x axis is the date and y is the clicks:
const options: ChartOptions<'line'> = {
responsive: true,
scales: {
y: {
title: {
display: true,
text: 'Date'
},
ticks: {
precision: 0,
}
},
x: {
type: 'time',
title: {
display: true,
text: "Date"
},
time: {
unit: 'day',
stepSize: 1,
displayFormats: {
day: "MMM DD"
}
}
},
},
parsing: {
xAxisKey: 'date',
yAxisKey: 'clicks',
},
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Click statistics',
},
},
};
And now THE PROBLEM:
On first load, the first line in the dataset, in this case Instagram, shows like this:
The instagram line has a different animation, what could be the problem?
[edit]: Changing the animation in options won't affect the instagram line, it will still come from the left, depending on the animation, it will come either slower or faster, but always from left.
For anyone who has the same issue in the future, here is the solution:
The problem with my code was that the first state of the instaDataClick variable was an empty array, it should have a date property, because the x axis is the date.
so the variable should look something like this:
const initialStartDate = new Date(moment().subtract(10, 'd').format('YYYY-MM-DD'))
const initialEndDate = new Date(moment().format('YYYY-MM-DD'))
const [instaDataClick, setInstaDataClick] = useState(
getDaysArray(initialStartDate.toString(), initialEndDate.toString()).map((e)=>{
return {clicks: null, date: e}
})
)
the function getDaysArray:
const getDaysArray = function (start: any, end: any) {
for (var arr = [], dt = new Date(start); dt <= new Date(end); dt.setDate(dt.getDate() + 1)) {
arr.push(new Date(dt).toString().substr(4, 6));
}
return arr;
};
And by doing this, we're setting the initial position of the line.
I have a problem regarding highcharts organization in reactjs/nextjs. I installed the npm package of highcharts-react-official and highcharts and import it in my component, still it gives me error that the organization is a missing module.
Here is the error:
Error: Highcharts error #17: www.highcharts.com/errors/17/?missingModuleFor=organization
- missingModuleFor: organization
Here is what I've tried .
import { useState } from "react";
import Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";
const HighChartsRender = () => {
const options = {
title: {
text: "My chart",
},
accessibility: {
point: {
descriptionFormatter: function (point) {
var nodeName = point.toNode.name,
nodeId = point.toNode.id,
nodeDesc = nodeName === nodeId ? nodeName : nodeName + ', ' + nodeId,
parentDesc = point.fromNode.id;
return point.index + '. ' + nodeDesc + ', reports to ' + parentDesc + '.';
}
}
},
series: [{
type: 'organization',
name: 'Highsoft',
keys: ['from', 'to'],
data: [
['Shareholders', 'Board'],
['Board', 'CEO'],
['CEO', 'CTO'],
['CEO', 'CPO'],
['CEO', 'CSO'],
['CEO', 'HR'],
['CTO', 'Product'],
['CTO', 'Web'],
['CSO', 'Sales'],
['HR', 'Market'],
['CSO', 'Market'],
['HR', 'Market'],
['CTO', 'Market']
],
levels: [{
level: 0,
color: 'silver',
dataLabels: {
color: 'black'
},
height: 25
}, {
level: 1,
color: 'silver',
dataLabels: {
color: 'black'
},
height: 25
}, {
level: 2,
color: '#980104'
}, {
level: 4,
color: '#359154'
}],
nodes: [{
id: 'Shareholders'
}, {
id: 'Board'
}, {
id: 'CEO',
title: 'CEO',
name: 'Grethe Hjetland',
image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131126/Highsoft_03862_.jpg'
}, {
id: 'HR',
title: 'HR/CFO',
name: 'Anne Jorunn Fjærestad',
color: '#007ad0',
image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131210/Highsoft_04045_.jpg'
}, {
id: 'CTO',
title: 'CTO',
name: 'Christer Vasseng',
image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131120/Highsoft_04074_.jpg'
}, {
id: 'CPO',
title: 'CPO',
name: 'Torstein Hønsi',
image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131213/Highsoft_03998_.jpg'
}, {
id: 'CSO',
title: 'CSO',
name: 'Anita Nesse',
image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131156/Highsoft_03834_.jpg'
}, {
id: 'Product',
name: 'Product developers'
}, {
id: 'Web',
name: 'Web devs, sys admin'
}, {
id: 'Sales',
name: 'Sales team'
}, {
id: 'Market',
name: 'Marketing team',
column: 5
}],
colorByPoint: false,
color: '#007ad0',
dataLabels: {
color: 'white'
},
borderColor: 'white',
nodeWidth: 65
}],
tooltip: {
outside: true
},
exporting: {
allowHTML: true,
sourceWidth: 800,
sourceHeight: 600
}
};
return (
<div id="wrapper">
<div className="content-area">
<HighchartsReact highcharts={Highcharts} options={options} />
</div>
</div>
);
};
export default HighChartsRender;
anyone tried to use the organization chart from highcharts in reactjs/nextjs ? .
Notice that the organization series requires two additional modules:
<script src="https://code.highcharts.com/modules/sankey.js"></script>
<script src="https://code.highcharts.com/modules/organization.js"></script>
API: https://api.highcharts.com/highcharts/series.organization
You need to import those modules and initialize them: https://stackblitz.com/edit/react-3gu5bv?file=index.js
API: https://github.com/highcharts/highcharts-react
I'd like to be able to render a React Component inside of a HighCharts Graph. I'd like to render the component DatePicker inside my HighCharts Graph, rather than being above it like it currently is? I'm using the HighCharts Official React Wrapper, and this is what my code looks like:
function Graph(props) {
const [startDate, setStartDate] = useState(new Date('1969-11-19 PST'));
const [bx, setBx] = useState([]);
const [by, setBy] = useState([]);
const [bz, setBz] = useState([]);
async function getData(table, year, month, day) {
...
}
useEffect(() => {
...
}, [startDate])
var options = {
chart: {
events: {
load: function() {
this.showLoading();
setTimeout(this.hideLoading.bind(this), 2000);
}
}
},
lang: {
noData: "Awaiting for data"
},
rangeSelector: {
inputEnabled: false,
buttons: [
{
type: 'all',
text: '1d',
title: 'View 1 Day'
},
{
type: 'minute',
count: 10,
text: '10m',
title: 'View 10 minutes'
},
{
type: 'minute',
count: 1,
text: '1m',
title: 'View 1 minutes'
},
{
type: 'second',
count: 30,
text: '30s',
title: 'View 30 seconds'
},
]
},
chart: {
zoomType: 'x'
},
title: {
text: props.title
},
xAxis: {
type: 'datetime'
},
time: {
timezoneOffset: 8*60
},
series: [
{
marker: {
enabled: true,
radius: 2
},
color: 'red',
name: 'Bx',
pointStart: startDate.getTime(),
pointInterval: 1000,
data: bx // data has to be [x, y] pairs
},
{
marker: {
enabled: true,
radius: 2
},
color: 'blue',
name: 'By',
pointStart: startDate.getTime(),
pointInterval: 1000,
data: by
},
{
marker: {
enabled: true,
radius: 2
},
color: 'green',
name: 'Bz',
pointStart: startDate.getTime(),
pointInterval: 1000,
data: bz
}
]
}
const isValidDate = (date) => {
date.getTime();
const origDate = new Date('1969-11-19 PST');
const finalDate = new Date('1970-04-03 PST');
console.log("GET DATE", date.getTime()) // getting number day of month
const bool = origDate.getTime() <= date.getTime() && date.getTime() <= finalDate.getTime();
return bool;
}
return (
<React.Fragment>
<DatePicker
selected={startDate}
filterDate={isValidDate}
onChange={(date) => setStartDate(date)}
/>
<HighchartsReact
highcharts={Highcharts}
constructorType={'stockChart'}
options={options}
/>
</React.Fragment>
);
}
I see this example from the HighCharts React Official Wrapper Github, but I'm still a bit confused by it and am wondering if I could simply do the same thing with a functional component. https://codesandbox.io/s/1o5y7r31k3
I've made a codesandbox of my code here https://codesandbox.io/s/friendly-moon-y8guc
I'm using React Particle JS. I want to get image to be shown among particles (like here - https://rpj.bembi.dev/#images), but can't see images, only bubbles are shown. Paths are correct. I'm including pathseg.js script through useEffect hook.
import Particles from "react-particles-js";
export default function Banner() {
useEffect(() => {
const script = document.createElement("script");
script.src = "https://cdn.rawgit.com/progers/pathseg/master/pathseg.js";
script.async = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, []);
return (
<Particles
params={{
particles: {
number: {
value: 8,
density: {
enable: true,
value_area: 800,
},
},
line_linked: {
enable: false,
},
move: {
speed: 1,
out_mode: "out",
},
shape: {
type: ["image", "circle"],
image: [
{
src: "https://image.flaticon.com/icons/png/512/29/29495.png",
height: 20,
width: 20,
},
],
},
color: {
value: "#CCC",
},
size: {
value: 30,
random: false,
anim: {
enable: true,
speed: 4,
size_min: 10,
sync: false,
},
},
},
retina_detect: false,
}}
/>
);
}
I think there's a bug using array with image shape type, if you use images with array it works, image is working with single object only but I'll check it out
import { useEffect } from "react";
import Particles from "react-particles-js";
export default function Banner() {
useEffect(() => {
const script = document.createElement("script");
script.src = "https://cdn.rawgit.com/progers/pathseg/master/pathseg.js";
script.async = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, []);
return (
<Particles
params={{
particles: {
number: {
value: 8,
density: {
enable: true,
value_area: 800,
},
},
line_linked: {
enable: false,
},
move: {
speed: 1,
out_mode: "out",
},
shape: {
type: ["image", "circle"],
image: {
src: "https://image.flaticon.com/icons/png/512/29/29495.png",
height: 20,
width: 20,
},
},
color: {
value: "#CCC",
},
size: {
value: 30,
random: false,
anim: {
enable: true,
speed: 4,
size_min: 10,
sync: false,
},
},
},
retina_detect: false,
}}
/>
);
}
This is working, if you need multiple images change image to images