import { AxisBottom, AxisLeft } from "@visx/axis"; import { bottomTickLabelProps } from "@visx/axis/lib/axis/AxisBottom"; import { leftTickLabelProps } from "@visx/axis/lib/axis/AxisLeft"; import { GridColumns, GridRows } from "@visx/grid"; import { Group } from "@visx/group"; import { scaleBand, scaleLinear } from "@visx/scale"; import { Bar } from "@visx/shape"; import { Text } from "@visx/text"; import React, { useState } from "react"; // TODO: refine props for styles interface BarGraphProps { data: BarGraphData[]; width: number; height: number; margin: { top: number; bottom: number; left: number; right: number; }; className?: string; } interface BarGraphData { category: string; value: number; } const COLOURS = { salmon: "#ffcad0", navy: "#2c3651", white: "#ffffff", pink: "#ef83b1", darkpink: "#cc5773", }; // TODO: styling, possibly refactor all into one component export function BarGraphVertical(props: BarGraphProps) { const { width, height, margin, data, className } = props; const categoryMax = width - margin.left - margin.right; const valueMax = height - margin.top - margin.bottom; const getCategory = (d: BarGraphData) => d.category; const getValue = (d: BarGraphData) => d.value; const categoryScale = scaleBand({ range: [0, categoryMax], domain: data.map(getCategory), padding: 0.1, }); const valueScale = scaleLinear({ range: [valueMax, 0], nice: true, domain: [0, Math.max(...data.map(getValue))], }); const categoryPoint = (d: BarGraphData) => categoryScale(getCategory(d)); const valuePoint = (d: BarGraphData) => valueScale(getValue(d)); return ( {data.map((d) => { const barHeight = valueMax - valuePoint(d); return ( ); })} ); } export function BarGraphHorizontal(props: BarGraphProps) { const { width, height, margin, data, className } = props; const barPadding = 0.4; const categoryMax = height - margin.top - margin.bottom; const valueMax = width - margin.left - margin.right; const getCategory = (d: BarGraphData) => d.category; const getValue = (d: BarGraphData) => d.value; const [isBarHovered, setIsBarHovered] = useState( Object.fromEntries( data.map((d, idx) => [`${getCategory(d)}-${idx}`, false]) ) ); const categoryScale = scaleBand({ range: [0, categoryMax], domain: data.map(getCategory), padding: barPadding, }); const valueScale = scaleLinear({ range: [0, valueMax], nice: true, domain: [0, Math.max(...data.map(getValue))], }); const categoryPoint = (d: BarGraphData) => categoryScale(getCategory(d)); const valuePoint = (d: BarGraphData) => valueScale(getValue(d)); return ( {data.map((d, idx) => { const barName = `${getCategory(d)}-${idx}`; const barWidth = categoryScale.bandwidth(); const backgroundBarWidth = barWidth / (1 - barPadding); return idx % 2 === 0 ? ( ) : null; })} {data.map((d, idx) => { const barName = `${getCategory(d)}-${idx}`; const barLength = valuePoint(d); const barWidth = categoryScale.bandwidth(); return ( { setIsBarHovered({ ...isBarHovered, [barName]: true }); }} onMouseLeave={() => { setIsBarHovered({ ...isBarHovered, [barName]: false }); }} > {isBarHovered[barName] ? ( {getValue(d)} ) : null} ); })} { return { ...leftTickLabelProps(), dx: "-0.5rem", dy: "0.25rem", fill: COLOURS.white, fontSize: "1rem", fontFamily: "Inconsolata", fontWeight: 800, }; }} /> { return { ...bottomTickLabelProps(), dy: "0.25rem", fill: COLOURS.white, fontSize: "1rem", fontFamily: "Inconsolata", fontWeight: 800, }; }} /> ); }