Components/Charts/LineChart/SessionChartD3.jsx

import React, { useEffect, useState } from 'react';
import "./SessionChart.scss";
import * as d3 from "d3";
import PropTypes from 'prop-types';

/**
 * @component
 * @memberOf Dashboard
 * @description Component who show the sport sessions length of the user for each day, on a line chart
 * @param  {object} props Activities datas of the user
 */
function SessionChartD3(props) {

	// console.log(props)
	const userDatas = props.data.sessions.sessions
	// eslint-disable-next-line no-unused-vars
	const [userData, setUserData] = useState(userDatas)

	// console.log(userData)
	const sessData = props.sessions

	useEffect(() => {
		draw()
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [])

	function draw() {

		// Création des diemnsions nécessaires dans des variableses dimensions 
		let margin = { top: 20, right: 20, bottom: 0, left: 20 };
		// eslint-disable-next-line no-unused-vars
		let width = 258 - margin.left - margin.right;
		let height = 263 - margin.top - margin.bottom;
		// Create the chart
		let chart = d3.select(".lineChart")
			.append("svg")
			.attr("width", "100%")
			.attr("height", "100%")
		// Clean the svg chart if the component re-render
		chart.selectAll(".lineChart .d3").remove()
		let min = d3.min(sessData)
		let max = d3.max(sessData)
		let xScale = d3.scaleLinear()
			.domain([0.5, 7.5])
			.range([-50, 200])
		// console.log(d3.extent(userData, d => d.day))
		let yScale = d3.scaleLinear()
			.domain([min, max + 30])
			.range([height - 40, 0])
		// Register the line
		let line = d3.line()
			.x(d => d.x || xScale(d.day))
			.y(d => d.y || yScale(d.sessionLength))
			.curve(d3.curveMonotoneX)

		const linePath = line(userDatas);

		// Draw the path
		chart.append("path")
			.datum(getPathCoordinates([-15, 0, 15, 30, 45, 60, 75, 90, 100, 115]))
			.attr("d", linePath)
			.attr("class", "d3")
			.attr("stroke", "white")
			.attr("stroke-width", "3")
			.attr("fill", "none")
			// launch the transition for the line chart
			.transition()
			.duration(750)
			.call(lineTween)
		// Add coordinates for the points to draw info box, transparent div...
		// eslint-disable-next-line array-callback-return
		getPathCoordinates([...userData]).map((coordinates, index) => {
			let group = chart.append("g")
				.attr("id", "session" + index)
				.attr("class", "d3")
			group.append("rect")
				.attr("x", coordinates.x + 41)
				.attr("y", 0)
				.attr("width", "100%")
				.attr("height", "100%")
				.attr("class", "d3")
				.attr("fill", "rgba(17, 24, 39, 0.3)")
				.attr("opacity", "0")
			group.append("rect")
				.attr("x", getBubbleXCoordinate(coordinates.x) + 51)
				.attr("y", coordinates.y - 25)
				.attr("class", "d3")
				.attr("width", "50")
				.attr("height", 30)
				.attr("fill", "white")
				.attr("opacity", "0")
			group.append("text")
				.attr("x", getBubbleXCoordinate(coordinates.x) + 76)
				.attr("y", coordinates.y - 7)
				.style("text-anchor", "middle")
				.attr("class", "d3")
				.text(userData[index].sessionLength + "min")
				.attr("opacity", "0")
			group.append("circle")
				.attr("class", "d3")
				.attr("cx", coordinates.x + 37)
				.attr("cy", coordinates.y)
				.attr("r", 4)
				.attr("opacity", "0")
				.attr("fill", "white")
			// hitbox
			chart.append("rect")
				.attr("x", coordinates.x + 21)
				.attr("y", 0)
				.attr("width", 41)
				.attr("height", 300)
				.attr("class", "d3")
				.attr("opacity", "0")
				// Make appear informations on hover
				.on("mouseover", function () {
					d3.selectAll(`#session${index} > *`).transition()
						.attr("opacity", "1")
				})
				.on("mouseout", function () {
					d3.selectAll(`#session${index} > *`).transition()
						.attr("opacity", "0")
				})

		})
		// Register the transition
		function lineTween(transition) {
			transition.attrTween("d", function (d) {
				let interpolateEnd = d3.interpolate(d, getPathCoordinates([20, ...userData, 75]))
				return function (t) {
					d = interpolateEnd(t)
					return line(d)
				}
			})
		}
		// Function to get the coordiantes of the points to draw
		function getPathCoordinates(dataPoints) {
			let coordinates = dataPoints.map((point, index) => (
				{
					x: index * 37 - 23,
					y: (215 - 215 * (point / 144)) || yScale(point.sessionLength)
				}
			))
			// console.log(coordinates)
			return coordinates
		}
		// Function to make the last day box appear on the chart and not outside
		function getBubbleXCoordinate(x) {
			if (x <= 170) return x
			else return 125
		}
	}

	return (
		<div className="lineChart" >
			<p className="lineChart-title">Durée moyenne des sessions</p>

			<div className="days">
				<p>L</p>
				<p>M</p>
				<p>M</p>
				<p>J</p>
				<p>V</p>
				<p>S</p>
				<p>D</p>
			</div>
		</div>
	)
}

SessionChartD3.propTypes = {
	/**
	 * User's datas, length of a sport session on minutes
	 */
	data: PropTypes.object.isRequired,
}

export default SessionChartD3;