import { max } from 'd3-array'; import { ScaleBand, scaleBand, ScaleLinear, scaleLinear } from 'd3-scale'; import { select } from 'd3-selection'; import React, { CSSProperties, Fragment, MutableRefObject, useEffect, useRef } from 'react'; import ChartTooltip from '../chart-tooltip/chart-tooltip'; import './bar-chart.scss'; export type barChartDataType = { label?: string; value: number; date: string; borderColor?: string; borderWidth?: number; backgroundColor?: string; }; /* eslint-disable-next-line */ export interface BarChartProps { data: barChartDataType[]; // data is collection of array size: number[]; // size represents here width and height of the bar chart paddingInner?: number; // its consider to distance between bars style?: CSSProperties; // its consider to add style to change svg } const barChartDataTypeDefault: barChartDataType[] = [ { label: '12/12/2002', value: 1, borderColor: '#fff', borderWidth: 2, backgroundColor: '#555', date: '10/10/2002', }, ]; /** * * @param BarChartProps; * @component BarChart; * @description its dynamic and responsive bar chart * @created by Md. Rakibul Alam */ export const BarChart = ({ data = barChartDataTypeDefault, size = [200, 200], paddingInner = 0.1, style, }: BarChartProps) => { const chartRef: MutableRefObject = useRef(); const tooltipRef: MutableRefObject = useRef(); const [width, height] = size; useEffect(() => { const node = chartRef.current; // current ref dom as here is node const tooltipNode = tooltipRef.current; // current ref dom as here is tooltip const sortDataByDate = data.sort( (a: barChartDataType, b: barChartDataType): any => (new Date(a.date) as any) - (new Date(b.date) as any) ); // const dataLenght = sortDataByDate.length; // take the data length for the optimize performance const valueMax: number = max(sortDataByDate.map(({ value }) => value)); // const dateMax: Date = max(data.map(({ date }) => new Date(date))); // const dateMin: Date = min(data.map(({ date }) => new Date(date))); const xScaleBand: ScaleBand = scaleBand() .domain(sortDataByDate.map((d) => d.date)) .rangeRound([0, width]) .paddingInner(paddingInner) .paddingOuter(0); const yScale: ScaleLinear = scaleLinear() .domain([0, valueMax]) .range([0, height]); select(node).selectAll('rect').data(sortDataByDate).enter().append('rect'); select(node).selectAll('rect').data(sortDataByDate).exit().remove(); // const deltaWidth: number = (dataLenght - 1) * barGap; // its consider to fix the bar witdh removing extra width from the current width. its always be considered total width minus one // const barWidth: number = (width - deltaWidth) / dataLenght; // find the exact bar witdh const tooltip = select(tooltipNode) // .style("position", "absolute") // .style("z-index", "10") // .style("visibility", "hidden"); select(node) .selectAll('rect') .data(sortDataByDate) .style('fill', ({ backgroundColor }) => backgroundColor || '#000') .attr('x', ({ date }) => xScaleBand(date)) // added barWidth for change x position from the current position first time i=0, so there is no need to change first positions .attr('y', ({ value }) => height - yScale(value)) .attr('height', ({ value }) => yScale(value)) .attr('width', xScaleBand.bandwidth()) .on("mouseover", () => tooltip.style("visibility", "visible")) .on("mousemove", (event, { label }) => tooltip.style("top", (event.pageY - 10) + "px").style("left", (event.pageX + 10) + "px").text(label)) .on("mouseout", () => tooltip.style("visibility", "hidden")); }, []); return }; export default BarChart;