import { classnames, PropTypes, React } from '@gravityforms/libraries';
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import Box from '../../../elements/Box';
import Checkbox from '../../../elements/Checkbox';
import DataDot from '../common/DataDot';
import CustomTooltip from '../common/Tooltip';
const { forwardRef, useState, useEffect } = React;
/**
* @module GravityAreaChart
* @description The AreaChart component. Loaded by the Chart renderer and displayed by passing type "area".
*
* @since 4.4.5
*
* @param {number} animationDuration The duration of the reveal animation in milliseconds.
* @param {object} areaProps The props to pass to each Area (can also override the presets we use). Check Recharts docs for all available.
* @param {object} cartesianGridProps The props to pass to the CartesianGrid (can also override the presets we use). Check Recharts docs for all available.
* @param {object} checkboxProps The props to pass to each Checkbox. Check our docs for all available.
* @param {node} children Any additional content to display below the chart.
* @param {string} cursorColor The color of the cursor line in the chart that appears when the tooltip is hovered.
* @param {object} customAttributes Custom attributes to apply to the chart wrapper.
* @param {string|array|object} customClasses Custom classes to apply to the chart wrapper.
* @param {array} data The data to display in the chart. Check Recharts docs for formats.
* @param {string} gridColor The color of the grid lines and the x and y axis.
* @param {number|string} height The height of the chart.
* @param {object} legendProps The props to pass to the Legend (can also override the presets we use). Check Recharts docs for all available.
* @param {array} options The options to display as checkboxes to toggle the visibility of each data set.
* @param {boolean} showCheckboxes Whether to show the checkboxes to toggle the visibility of each data set.
* @param {boolean} showLegend Whether to show the legend.
* @param {object} tooltipProps The props to pass to the Tooltip (can also override the presets we use). Check Recharts docs for all available.
* @param {number|string} width The width of the chart.
* @param {object} xAxisProps The props to pass to the XAxis (can also override the presets we use). Check Recharts docs for all available.
* @param {object} yAxisProps The props to pass to the YAxis (can also override the presets we use). Check Recharts docs for all available.
* @param {object} ref The reference to the chart component.
*
* @return {JSX.Element|null} The AreaChart component.
*/
const GravityAreaChart = forwardRef( ( {
animationDuration = 1000,
areaProps = {},
cartesianGridProps = {},
checkboxProps = {},
children = null,
cursorColor = '#9092b2',
customAttributes = {},
customClasses = {},
customInterval = null,
data = [],
gridColor = '#ecedf8',
height = 400,
legendProps = {},
options = [],
showCheckboxes = true,
showLegend = false,
tooltipProps = {},
width = '100%',
xAxisProps = {},
yAxisProps = {},
}, ref ) => {
const componentProps = {
className: classnames( {
'gform-chart--area': true,
'gform-chart__wrapper': true,
}, customClasses ),
...customAttributes,
ref,
};
const initialCheckboxState = options.reduce( ( acc, option ) => {
acc[ option.dataKey ] = option.defaultChecked || true; // Set default visibility, default to true
return acc;
}, {} );
const [ checkboxState, setCheckboxState ] = useState( initialCheckboxState );
const [ animationActive, setAnimationActive ] = useState( true );
const [ interval, setInterval ] = useState( Math.floor( data.length / 10 ) );
const updateInterval = () => {
const screenWidth = window.innerWidth;
setInterval( Math.floor( data.length / ( screenWidth / 180 ) ) );
};
useEffect( () => {
// Disable animation after the first render
const timer = setTimeout( () => {
setAnimationActive( false );
}, animationDuration );
return () => clearTimeout( timer );
}, [] );
useEffect( () => {
if ( customInterval ) {
customInterval( setInterval, data.length );
} else {
updateInterval();
window.addEventListener( 'resize', updateInterval );
return () => {
window.removeEventListener( 'resize', updateInterval );
};
}
}, [ data.length, customInterval ] );
const handleCheckboxChange = ( dataKey ) => {
setCheckboxState( ( prevState ) => ( {
...prevState,
[ dataKey ]: ! prevState[ dataKey ],
} ) );
};
const cartesianGridAttributes = {
stroke: gridColor,
strokeDasharray: '0',
vertical: false,
...cartesianGridProps,
};
const yAxisAttributes = {
axisLine: { stroke: gridColor },
padding: { top: 10 },
tickLine: { stroke: gridColor },
...yAxisProps,
};
const xAxisAttributes = {
axisLine: { stroke: gridColor },
tickLine: { stroke: gridColor },
interval,
...xAxisProps,
};
const tooltipAttributes = {
content: <CustomTooltip />,
cursor: { stroke: cursorColor },
...tooltipProps,
};
const legendAttributes = {
...legendProps,
};
return (
<div { ...componentProps }>
{ showCheckboxes && (
<div className="gform-chart__checkboxes">
<Box display="flex" spacing={ [ 0, 5, 0, 0 ] }>
{ options.map( ( option ) => {
const checkboxAttributes = {
externalChecked: checkboxState[ option.dataKey ],
externalControl: true,
labelAttributes: {
label: option.label,
weight: 'normal',
},
onChange: () => handleCheckboxChange( option.dataKey ),
spacing: [ 0, 0, 4, 3 ],
...checkboxProps,
};
return (
<Checkbox key={ option.dataKey } { ...checkboxAttributes } />
);
} ) }
</Box>
</div>
) }
<ResponsiveContainer width={ width } height={ height }>
<AreaChart data={ data } margin={ { top: 0, right: 20, left: 0, bottom: 0 } }>
<defs>
{ options.map( ( option ) => (
<linearGradient key={ option.dataKey } id={ `color${ option.dataKey }` } x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={ option.color } stopOpacity={ 0.1 } />
<stop offset="95%" stopColor={ option.color } stopOpacity={ 0 } />
</linearGradient>
) ) }
</defs>
<CartesianGrid { ...cartesianGridAttributes } />
<XAxis { ...xAxisAttributes } />
<YAxis { ...yAxisAttributes } />
<Tooltip { ...tooltipAttributes } />
{ showLegend && <Legend { ...legendAttributes } /> }
{ options.map(
( option ) => {
const areaAttributes = {
type: 'monotone',
dataKey: option.dataKey,
stroke: option.color,
fillOpacity: 1,
fill: `url(#color${ option.dataKey })`,
strokeWidth: 2,
dot: false,
activeDot: <DataDot stroke={ option.color } />,
isAnimationActive: animationActive,
animationBegin: 0,
animationDuration,
...areaProps,
};
return checkboxState[ option.dataKey ] ? (
<Area key={ option.dataKey } { ...areaAttributes } />
) : null;
}
) }
</AreaChart>
</ResponsiveContainer>
{ children }
</div>
);
} );
GravityAreaChart.propTypes = {
animationDuration: PropTypes.number,
areaProps: PropTypes.object,
cartesianGridProps: PropTypes.object,
checkboxProps: PropTypes.object,
children: PropTypes.oneOfType( [
PropTypes.arrayOf( PropTypes.node ),
PropTypes.node,
] ),
cursorColor: PropTypes.string,
customAttributes: PropTypes.object,
customClasses: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.array,
PropTypes.object,
] ),
data: PropTypes.array,
gridColor: PropTypes.string,
height: PropTypes.oneOfType( [ PropTypes.string, PropTypes.number ] ),
legendProps: PropTypes.object,
options: PropTypes.array,
showCheckboxes: PropTypes.bool,
showLegend: PropTypes.bool,
tooltipProps: PropTypes.object,
width: PropTypes.oneOfType( [ PropTypes.string, PropTypes.number ] ),
xAxisProps: PropTypes.object,
yAxisProps: PropTypes.object,
};
GravityAreaChart.displayName = 'AreaChart';
export default GravityAreaChart;