callback()} dependencies={['foo']} />, container); });
+
+ expect(callback).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/src/lib/GaugeChart/customHooks.js b/src/lib/GaugeChart/customHooks.js
deleted file mode 100644
index 0c2b9a1..0000000
--- a/src/lib/GaugeChart/customHooks.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import _ from "lodash";
-import { useEffect, useRef } from "react";
-
-const isDeepEquals = (toCompare, reference) => {
- return _.isEqual(toCompare, reference);
-};
-
-const useDeepCompareMemo = (dependencies) => {
- const ref = useRef(null);
- if (isDeepEquals(dependencies, ref.current)) {
- ref.current = dependencies;
- }
- return ref.current;
-};
-
-// this function compares deeply new dependencies with old one
-// It works like useEffect but we are using isEqual from lodash to compares deeply
-const useDeepCompareEffect = (callback, dependencies) => {
- useEffect(callback, [useDeepCompareMemo(dependencies), callback]);
-};
-
-export default useDeepCompareEffect;
diff --git a/src/lib/GaugeChart/hooks.js b/src/lib/GaugeChart/hooks.js
new file mode 100644
index 0000000..4f89698
--- /dev/null
+++ b/src/lib/GaugeChart/hooks.js
@@ -0,0 +1,8 @@
+import { useEffect } from "react";
+
+// A simple useEffect alternative for dependencies with arrays/objects by serialising to JSON string.
+// As recommended by Facebook guru Dan Abramov (gaearon) for small objects:
+// https://github.com/facebook/react/issues/14476#issuecomment-471199055
+export const useDeepCompareEffect = (effect, deps) => {
+ useEffect(effect, [JSON.stringify(deps)]);
+};
diff --git a/src/lib/GaugeChart/index.js b/src/lib/GaugeChart/index.js
index a5e50e8..30a5039 100644
--- a/src/lib/GaugeChart/index.js
+++ b/src/lib/GaugeChart/index.js
@@ -1,16 +1,10 @@
-import React, { useCallback, useEffect, useRef, useLayoutEffect } from "react";
-import {
- arc,
- pie,
- select,
- easeElastic,
- scaleLinear,
- interpolateHsl,
- interpolateNumber,
-} from "d3";
+import React, { useCallback, useRef, useLayoutEffect } from "react";
+import { select } from "d3";
import PropTypes from "prop-types";
-import useDeepCompareEffect from "./customHooks";
+import { useDeepCompareEffect } from "./hooks";
+import { renderChart } from './utils'
+
/*
GaugeChart creates a gauge chart using D3
The chart is responsive and will have the same width as the "container"
@@ -21,21 +15,6 @@ The svg element surrounding the gauge will always be square
*/
//Constants
-const startAngle = -Math.PI / 2; //Negative x-axis
-const endAngle = Math.PI / 2; //Positive x-axis
-
-const defaultStyle = {
- width: "100%",
-};
-
-// Props that should cause an animation on update
-const animateNeedleProps = [
- "marginInPercent",
- "arcPadding",
- "percent",
- "nrOfLevels",
- "animDelay",
-];
const GaugeChart = (props) => {
const svg = useRef({});
@@ -47,97 +26,37 @@ const GaugeChart = (props) => {
const outerRadius = useRef({});
const margin = useRef({}); // = {top: 20, right: 50, bottom: 50, left: 50},
const container = useRef({});
- const nbArcsToDisplay = useRef(0);
- const colorArray = useRef([]);
- const arcChart = useRef(arc());
- const arcData = useRef([]);
- const pieChart = useRef(pie());
- const prevProps = useRef(props);
- let selectedRef = useRef({});
-
- const initChart = useCallback(
- (update, resize = false, prevProps) => {
- if (update) {
- renderChart(
- resize,
- prevProps,
- width,
- margin,
- height,
- outerRadius,
- g,
- doughnut,
- arcChart,
- needle,
- pieChart,
- svg,
- props,
- container,
- arcData
- );
- return;
- }
-
- container.current.select("svg").remove();
- svg.current = container.current.append("svg");
- g.current = svg.current.append("g"); //Used for margins
- doughnut.current = g.current.append("g").attr("class", "doughnut");
-
- //Set up the pie generator
- //Each arc should be of equal length (or should they?)
- pieChart.current
- .value(function (d) {
- return d.value;
- })
- //.padAngle(arcPadding)
- .startAngle(startAngle)
- .endAngle(endAngle)
- .sort(null);
- //Add the needle element
- needle.current = g.current.append("g").attr("class", "needle");
-
- renderChart(
- resize,
- prevProps,
- width,
- margin,
- height,
- outerRadius,
- g,
- doughnut,
- arcChart,
- needle,
- pieChart,
- svg,
- props,
- container,
- arcData
- );
- },
- [props]
- );
+ const prevProps = useRef();
+ const selectedRef = useRef();
+
+ const initChart = useCallback(() => {
+ container.current = select(selectedRef.current);
+ container.current.select("svg").remove();
+ svg.current = container.current.append("svg");
+ g.current = svg.current.append("g"); //Used for margins
+ doughnut.current = g.current.append("g").attr("class", "doughnut");
+ needle.current = g.current.append("g").attr("class", "needle");
+ }, [])
useLayoutEffect(() => {
- setArcData(props, nbArcsToDisplay, colorArray, arcData);
- container.current = select(selectedRef);
- //Initialize chart
initChart();
- }, [props, initChart]);
+ }, [initChart]);
useDeepCompareEffect(() => {
- if (
- props.nrOfLevels ||
- prevProps.current.arcsLength.every((a) => props.arcsLength.includes(a)) ||
- prevProps.current.colors.every((a) => props.colors.includes(a))
- ) {
- setArcData(props, nbArcsToDisplay, colorArray, arcData);
- }
- //Initialize chart
- // Always redraw the chart, but potentially do not animate it
- const resize = !animateNeedleProps.some(
- (key) => prevProps.current[key] !== props[key]
+ renderChart(
+ prevProps.current,
+ width,
+ margin,
+ height,
+ outerRadius,
+ g,
+ doughnut,
+ needle,
+ svg,
+ props,
+ container,
);
- initChart(true, resize, prevProps.current);
+
prevProps.current = props;
}, [
props.nrOfLevels,
@@ -148,50 +67,18 @@ const GaugeChart = (props) => {
props.needleBaseColor,
]);
- useEffect(() => {
- const handleResize = () => {
- var resize = true;
-
- renderChart(
- resize,
- prevProps,
- width,
- margin,
- height,
- outerRadius,
- g,
- doughnut,
- arcChart,
- needle,
- pieChart,
- svg,
- props,
- container,
- arcData
- );
- };
- //Set up resize event listener to re-render the chart everytime the window is resized
- window.addEventListener("resize", handleResize);
- return () => {
- window.removeEventListener("resize", handleResize);
- };
- }, [props]);
-
const { id, style, className } = props;
return (
(selectedRef = svg)}
+ ref={selectedRef}
/>
);
};
-export default GaugeChart;
-
GaugeChart.defaultProps = {
- style: defaultStyle,
marginInPercent: 0.05,
cornerRadius: 6,
nrOfLevels: 3,
@@ -233,283 +120,4 @@ GaugeChart.propTypes = {
animDelay: PropTypes.number,
};
-// This function update arc's datas when component is mounting or when one of arc's props is updated
-const setArcData = (props, nbArcsToDisplay, colorArray, arcData) => {
- // We have to make a decision about number of arcs to display
- // If arcsLength is setted, we choose arcsLength length instead of nrOfLevels
- nbArcsToDisplay.current = props.arcsLength
- ? props.arcsLength.length
- : props.nrOfLevels;
-
- //Check if the number of colors equals the number of levels
- //Otherwise make an interpolation
- if (nbArcsToDisplay.current === props.colors.length) {
- colorArray.current = props.colors;
- } else {
- colorArray.current = getColors(props, nbArcsToDisplay);
- }
- //The data that is used to create the arc
- // Each arc could have hiw own value width arcsLength prop
- arcData.current = [];
- for (var i = 0; i < nbArcsToDisplay.current; i++) {
- var arcDatum = {
- value:
- props.arcsLength && props.arcsLength.length > i
- ? props.arcsLength[i]
- : 1,
- color: colorArray.current[i],
- };
- arcData.current.push(arcDatum);
- }
-};
-
-//Renders the chart, should be called every time the window is resized
-const renderChart = (
- resize,
- prevProps,
- width,
- margin,
- height,
- outerRadius,
- g,
- doughnut,
- arcChart,
- needle,
- pieChart,
- svg,
- props,
- container,
- arcData
-) => {
- updateDimensions(props, container, margin, width, height);
- //Set dimensions of svg element and translations
- svg.current
- .attr("width", width.current + margin.current.left + margin.current.right)
- .attr(
- "height",
- height.current + margin.current.top + margin.current.bottom
- );
- g.current.attr(
- "transform",
- "translate(" + margin.current.left + ", " + margin.current.top + ")"
- );
- //Set the radius to lesser of width or height and remove the margins
- //Calculate the new radius
- calculateRadius(width, height, outerRadius, margin, g);
- doughnut.current.attr(
- "transform",
- "translate(" + outerRadius.current + ", " + outerRadius.current + ")"
- );
- //Setup the arc
- arcChart.current
- .outerRadius(outerRadius.current)
- .innerRadius(outerRadius.current * (1 - props.arcWidth))
- .cornerRadius(props.cornerRadius)
- .padAngle(props.arcPadding);
- //Remove the old stuff
- doughnut.current.selectAll(".arc").remove();
- needle.current.selectAll("*").remove();
- g.current.selectAll(".text-group").remove();
- //Draw the arc
- var arcPaths = doughnut.current
- .selectAll(".arc")
- .data(pieChart.current(arcData.current))
- .enter()
- .append("g")
- .attr("class", "arc");
- arcPaths
- .append("path")
- .attr("d", arcChart.current)
- .style("fill", function (d) {
- return d.data.color;
- });
-
- drawNeedle(
- resize,
- prevProps,
- props,
- width,
- needle,
- container,
- outerRadius,
- g
- );
- //Translate the needle starting point to the middle of the arc
- needle.current.attr(
- "transform",
- "translate(" + outerRadius.current + ", " + outerRadius.current + ")"
- );
-};
-
-//Depending on the number of levels in the chart
-//This function returns the same number of colors
-const getColors = (props, nbArcsToDisplay) => {
- const { colors } = props;
- var colorScale = scaleLinear()
- .domain([1, nbArcsToDisplay.current])
- .range([colors[0], colors[colors.length - 1]]) //Use the first and the last color as range
- .interpolate(interpolateHsl);
- var colorArray = [];
- for (var i = 1; i <= nbArcsToDisplay.current; i++) {
- colorArray.push(colorScale(i));
- }
- return colorArray;
-};
-
-//If 'resize' is true then the animation does not play
-const drawNeedle = (
- resize,
- prevProps,
- props,
- width,
- needle,
- container,
- outerRadius,
- g
-) => {
- const { percent, needleColor, needleBaseColor, hideText, animate } = props;
- var needleRadius = 15 * (width.current / 500), // Make the needle radius responsive
- centerPoint = [0, -needleRadius / 2];
- //Draw the triangle
- //var pathStr = `M ${leftPoint[0]} ${leftPoint[1]} L ${topPoint[0]} ${topPoint[1]} L ${rightPoint[0]} ${rightPoint[1]}`;
- const prevPercent = prevProps ? prevProps.percent : 0;
- var pathStr = calculateRotation(prevPercent || percent, outerRadius, width);
- needle.current.append("path").attr("d", pathStr).attr("fill", needleColor);
- //Add a circle at the bottom of needle
- needle.current
- .append("circle")
- .attr("cx", centerPoint[0])
- .attr("cy", centerPoint[1])
- .attr("r", needleRadius)
- .attr("fill", needleBaseColor);
- if (!hideText) {
- addText(percent, props, outerRadius, width, g);
- }
- //Rotate the needle
- if (!resize && animate) {
- needle.current
- .transition()
- .delay(props.animDelay)
- .ease(easeElastic)
- .duration(props.animateDuration)
- .tween("progress", function () {
- const currentPercent = interpolateNumber(prevPercent, percent);
- return function (percentOfPercent) {
- const progress = currentPercent(percentOfPercent);
- return container.current
- .select(`.needle path`)
- .attr("d", calculateRotation(progress, outerRadius, width));
- };
- });
- } else {
- container.current
- .select(`.needle path`)
- .attr("d", calculateRotation(percent, outerRadius, width));
- }
-};
-
-const calculateRotation = (percent, outerRadius, width) => {
- var needleLength = outerRadius.current * 0.55, //TODO: Maybe it should be specified as a percentage of the arc radius?
- needleRadius = 15 * (width.current / 500),
- theta = percentToRad(percent),
- centerPoint = [0, -needleRadius / 2],
- topPoint = [
- centerPoint[0] - needleLength * Math.cos(theta),
- centerPoint[1] - needleLength * Math.sin(theta),
- ],
- leftPoint = [
- centerPoint[0] - needleRadius * Math.cos(theta - Math.PI / 2),
- centerPoint[1] - needleRadius * Math.sin(theta - Math.PI / 2),
- ],
- rightPoint = [
- centerPoint[0] - needleRadius * Math.cos(theta + Math.PI / 2),
- centerPoint[1] - needleRadius * Math.sin(theta + Math.PI / 2),
- ];
- var pathStr = `M ${leftPoint[0]} ${leftPoint[1]} L ${topPoint[0]} ${topPoint[1]} L ${rightPoint[0]} ${rightPoint[1]}`;
- return pathStr;
-};
-
-//Returns the angle (in rad) for the given 'percent' value where percent = 1 means 100% and is 180 degree angle
-const percentToRad = (percent) => {
- return percent * Math.PI;
-};
-
-//Adds text undeneath the graft to display which percentage is the current one
-const addText = (percentage, props, outerRadius, width, g) => {
- const { formatTextValue, fontSize } = props;
- var textPadding = 20;
- const text = formatTextValue
- ? formatTextValue(floatingNumber(percentage))
- : floatingNumber(percentage) + "%";
- g.current
- .append("g")
- .attr("class", "text-group")
- .attr(
- "transform",
- `translate(${outerRadius.current}, ${
- outerRadius.current / 2 + textPadding
- })`
- )
- .append("text")
- .text(text)
- // this computation avoid text overflow. When formatted value is over 10 characters, we should reduce font size
- .style("font-size", () =>
- fontSize
- ? fontSize
- : `${width.current / 11 / (text.length > 10 ? text.length / 10 : 1)}px`
- )
- .style("fill", props.textColor)
- .style("text-anchor", "middle");
-};
-
-const floatingNumber = (value, maxDigits = 2) => {
- return Math.round(value * 100 * 10 ** maxDigits) / 10 ** maxDigits;
-};
-
-const calculateRadius = (width, height, outerRadius, margin, g) => {
- //The radius needs to be constrained by the containing div
- //Since it is a half circle we are dealing with the height of the div
- //Only needs to be half of the width, because the width needs to be 2 * radius
- //For the whole arc to fit
-
- //First check if it is the width or the height that is the "limiting" dimension
- if (width.current < 2 * height.current) {
- //Then the width limits the size of the chart
- //Set the radius to the width - the horizontal margins
- outerRadius.current =
- (width.current - margin.current.left - margin.current.right) / 2;
- } else {
- outerRadius.current =
- height.current - margin.current.top - margin.current.bottom;
- }
- centerGraph(width, g, outerRadius, margin);
-};
-
-//Calculates new margins to make the graph centered
-const centerGraph = (width, g, outerRadius, margin) => {
- margin.current.left =
- width.current / 2 - outerRadius.current + margin.current.right;
- g.current.attr(
- "transform",
- "translate(" + margin.current.left + ", " + margin.current.top + ")"
- );
-};
-
-const updateDimensions = (props, container, margin, width, height) => {
- //TODO: Fix so that the container is included in the component
- const { marginInPercent } = props;
- var divDimensions = container.current.node().getBoundingClientRect(),
- divWidth = divDimensions.width,
- divHeight = divDimensions.height;
-
- //Set the new width and horizontal margins
- margin.current.left = divWidth * marginInPercent;
- margin.current.right = divWidth * marginInPercent;
- width.current = divWidth - margin.current.left - margin.current.right;
-
- margin.current.top = divHeight * marginInPercent;
- margin.current.bottom = divHeight * marginInPercent;
- height.current =
- width.current / 2 - margin.current.top - margin.current.bottom;
- //height.current = divHeight - margin.current.top - margin.current.bottom;
-};
+export default GaugeChart;
diff --git a/src/lib/GaugeChart/utils.js b/src/lib/GaugeChart/utils.js
new file mode 100644
index 0000000..6d539c3
--- /dev/null
+++ b/src/lib/GaugeChart/utils.js
@@ -0,0 +1,282 @@
+import {
+ arc,
+ pie,
+ select,
+ easeElastic,
+ interpolateHsl,
+ interpolateNumber,
+} from "d3";
+
+// Helpers
+const first = (array) => array[0]
+const last = (array) => array[array.length - 1]
+
+// Constants
+const startAngle = -Math.PI / 2; //Negative x-axis
+const endAngle = Math.PI / 2; //Positive x-axis
+
+// Returns array of arc data (length and color) for pie and arc generator
+const getArcData = ({ nrOfLevels, arcsLength, colors }) => {
+ // We have to make a decision about number of arcs to display
+ // If arcsLength is setted, we choose arcsLength length instead of nrOfLevels
+ const nbArcsToDisplay = arcsLength ? arcsLength.length : nrOfLevels;
+
+ //Check if the number of colors equals the number of levels
+ //Otherwise make an interpolation
+ const arcColors = (nbArcsToDisplay === colors.length)
+ ? colors
+ : interpolateColors(first(colors), last(colors), nbArcsToDisplay);
+
+ //The data that is used to create the arc
+ // Each arc could have hiw own value width arcsLength prop
+ return arcColors.map((color, index) => ({
+ value: arcsLength ? arcsLength[index] : 1,
+ color,
+ }))
+};
+
+//Renders the chart
+export const renderChart = (
+ prevProps,
+ width,
+ margin,
+ height,
+ outerRadius,
+ g,
+ doughnut,
+ needle,
+ svg,
+ props,
+ container,
+) => {
+ updateDimensions(props, container, margin, width, height);
+
+ //Set dimensions of svg element and translations
+ svg.current
+ .attr("width", width.current + margin.current.left + margin.current.right)
+ .attr(
+ "height",
+ height.current + margin.current.top + margin.current.bottom
+ );
+ g.current.attr(
+ "transform",
+ "translate(" + margin.current.left + ", " + margin.current.top + ")"
+ );
+ //Set the radius to lesser of width or height and remove the margins
+ //Calculate the new radius
+ calculateRadius(width, height, outerRadius, margin, g);
+ doughnut.current.attr(
+ "transform",
+ "translate(" + outerRadius.current + ", " + outerRadius.current + ")"
+ );
+ //Setup the arc
+ const arcChart = arc()
+ .outerRadius(outerRadius.current)
+ .innerRadius(outerRadius.current * (1 - props.arcWidth))
+ .cornerRadius(props.cornerRadius)
+ .padAngle(props.arcPadding);
+ //Remove the old stuff
+ doughnut.current.selectAll(".arc").remove();
+ needle.current.selectAll("*").remove();
+ g.current.selectAll(".text-group").remove();
+ //Set up the pie generator
+ const pieChart = pie()
+ .value((d) => d.value)
+ .startAngle(startAngle)
+ .endAngle(endAngle)
+ .sort(null);
+ //Draw the arc
+ var arcPaths = doughnut.current
+ .selectAll(".arc")
+ .data(pieChart(getArcData(props)))
+ .enter()
+ .append("g")
+ .attr("class", "arc");
+ arcPaths
+ .append("path")
+ .attr("d", arcChart)
+ .style("fill", function (d) {
+ return d.data.color;
+ });
+
+ drawNeedle(
+ prevProps,
+ props,
+ width,
+ needle,
+ container,
+ outerRadius,
+ g
+ );
+ //Translate the needle starting point to the middle of the arc
+ needle.current.attr(
+ "transform",
+ "translate(" + outerRadius.current + ", " + outerRadius.current + ")"
+ );
+};
+
+const interpolateColors = (startColor, endColor, length) => {
+ const interpolator = interpolateHsl(startColor, endColor)
+
+ return length > 1
+ ? Array.from({ length }, (_, i) => interpolator(i / (length - 1)))
+ : [startColor]
+}
+
+const drawNeedle = (
+ prevProps,
+ props,
+ width,
+ needle,
+ container,
+ outerRadius,
+ g
+) => {
+ const { percent, needleColor, needleBaseColor, hideText, animate } = props;
+ var needleRadius = 15 * (width.current / 500), // Make the needle radius responsive
+ centerPoint = [0, -needleRadius / 2];
+ //Draw the triangle
+ //var pathStr = `M ${leftPoint[0]} ${leftPoint[1]} L ${topPoint[0]} ${topPoint[1]} L ${rightPoint[0]} ${rightPoint[1]}`;
+ const prevPercent = prevProps ? prevProps.percent : 0;
+ var pathStr = calculateRotation(prevPercent || percent, outerRadius, width);
+ needle.current.append("path").attr("d", pathStr).attr("fill", needleColor);
+ //Add a circle at the bottom of needle
+ needle.current
+ .append("circle")
+ .attr("cx", centerPoint[0])
+ .attr("cy", centerPoint[1])
+ .attr("r", needleRadius)
+ .attr("fill", needleBaseColor);
+ if (!hideText) {
+ addText(percent, props, outerRadius, width, g);
+ }
+ //Rotate the needle
+ if (animate) {
+ needle.current
+ .transition()
+ .delay(props.animDelay)
+ .ease(easeElastic)
+ .duration(props.animateDuration)
+ .tween("progress", function () {
+ const currentPercent = interpolateNumber(prevPercent, percent);
+ return function (percentOfPercent) {
+ const progress = currentPercent(percentOfPercent);
+ return container.current
+ .select(`.needle path`)
+ .attr("d", calculateRotation(progress, outerRadius, width));
+ };
+ });
+ } else {
+ container.current
+ .select(`.needle path`)
+ .attr("d", calculateRotation(percent, outerRadius, width));
+ }
+};
+
+const calculateRotation = (percent, outerRadius, width) => {
+ var needleLength = outerRadius.current * 0.55, //TODO: Maybe it should be specified as a percentage of the arc radius?
+ needleRadius = 15 * (width.current / 500),
+ theta = percentToRad(percent),
+ centerPoint = [0, -needleRadius / 2],
+ topPoint = [
+ centerPoint[0] - needleLength * Math.cos(theta),
+ centerPoint[1] - needleLength * Math.sin(theta),
+ ],
+ leftPoint = [
+ centerPoint[0] - needleRadius * Math.cos(theta - Math.PI / 2),
+ centerPoint[1] - needleRadius * Math.sin(theta - Math.PI / 2),
+ ],
+ rightPoint = [
+ centerPoint[0] - needleRadius * Math.cos(theta + Math.PI / 2),
+ centerPoint[1] - needleRadius * Math.sin(theta + Math.PI / 2),
+ ];
+ var pathStr = `M ${leftPoint[0]} ${leftPoint[1]} L ${topPoint[0]} ${topPoint[1]} L ${rightPoint[0]} ${rightPoint[1]}`;
+ return pathStr;
+};
+
+//Returns the angle (in rad) for the given 'percent' value where percent = 1 means 100% and is 180 degree angle
+const percentToRad = (percent) => {
+ return percent * Math.PI;
+};
+
+//Adds text undeneath the graft to display which percentage is the current one
+const addText = (percentage, props, outerRadius, width, g) => {
+ const { formatTextValue, fontSize } = props;
+ var textPadding = 20;
+ const text = formatTextValue
+ ? formatTextValue(floatingNumber(percentage))
+ : floatingNumber(percentage) + "%";
+ g.current
+ .append("g")
+ .attr("class", "text-group")
+ .attr(
+ "transform",
+ `translate(${outerRadius.current}, ${
+ outerRadius.current / 2 + textPadding
+ })`
+ )
+ .append("text")
+ .text(text)
+ // this computation avoid text overflow. When formatted value is over 10 characters, we should reduce font size
+ .style("font-size", () =>
+ fontSize
+ ? fontSize
+ : `${width.current / 11 / (text.length > 10 ? text.length / 10 : 1)}px`
+ )
+ .style("fill", props.textColor)
+ .style("text-anchor", "middle");
+};
+
+const floatingNumber = (value, maxDigits = 2) => {
+ return Math.round(value * 100 * 10 ** maxDigits) / 10 ** maxDigits;
+};
+
+const calculateRadius = (width, height, outerRadius, margin, g) => {
+ //The radius needs to be constrained by the containing div
+ //Since it is a half circle we are dealing with the height of the div
+ //Only needs to be half of the width, because the width needs to be 2 * radius
+ //For the whole arc to fit
+
+ //First check if it is the width or the height that is the "limiting" dimension
+ if (width.current < 2 * height.current) {
+ //Then the width limits the size of the chart
+ //Set the radius to the width - the horizontal margins
+ outerRadius.current =
+ (width.current - margin.current.left - margin.current.right) / 2;
+ } else {
+ outerRadius.current =
+ height.current - margin.current.top - margin.current.bottom;
+ }
+ centerGraph(width, g, outerRadius, margin);
+};
+
+//Calculates new margins to make the graph centered
+const centerGraph = (width, g, outerRadius, margin) => {
+ margin.current.left =
+ width.current / 2 - outerRadius.current + margin.current.right;
+ g.current.attr(
+ "transform",
+ "translate(" + margin.current.left + ", " + margin.current.top + ")"
+ );
+};
+
+const updateDimensions = (props, container, margin, width, height) => {
+ //TODO: Fix so that the container is included in the component
+ const { marginInPercent } = props;
+ var divDimensions = process.env.NODE_ENV === 'test'
+ ? { width: 500, height: 250 }
+ : container.current.node().getBoundingClientRect();
+ var divWidth = divDimensions.width;
+ var divHeight = divDimensions.height;
+
+ //Set the new width and horizontal margins
+ margin.current.left = divWidth * marginInPercent;
+ margin.current.right = divWidth * marginInPercent;
+ width.current = divWidth - margin.current.left - margin.current.right;
+
+ margin.current.top = divHeight * marginInPercent;
+ margin.current.bottom = divHeight * marginInPercent;
+ height.current =
+ width.current / 2 - margin.current.top - margin.current.bottom;
+ //height.current = divHeight - margin.current.top - margin.current.bottom;
+};
diff --git a/yarn.lock b/yarn.lock
index 70903b4..7bf7708 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8525,16 +8525,7 @@ lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
-"lodash@>=3.5 <5", lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.5:
- version "4.17.11"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
-
-lodash@^4.17.13:
- version "4.17.15"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
- integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
-
-lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
+"lodash@>=3.5 <5", lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5, lodash@^4.7.0:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -10369,6 +10360,11 @@ prepend-http@^1.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
+prettier@^2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032"
+ integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==
+
pretty-bytes@^5.3.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"