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 from "react"; import { Color } from "utils/Color"; import styles from "./BarGraph.module.css"; interface BarGraphProps { data: BarGraphData[]; /** Width of the entire graph, in pixels. */ width: number; /** Height of the entire graph, in pixels. */ height: number; /** Distance between the edge of the graph and the area where the bars are drawn, in pixels. */ margin: { top: number; bottom: number; left: number; right: number; }; className?: string; /** Font size of the category tick labels, in pixels. Default is 16px. */ categoryTickLabelSize?: number; /** Font size of the value tick labels, in pixels. Default is 16px. */ valueTickLabelSize?: number; /** Font size of the value that appears when hovering over a bar, in pixels. */ hoverLabelSize?: number; /** Label text for the category axis. */ categoryAxisLabel?: string; /** Font size of the label for the cateogry axis, in pixels. */ categoryAxisLabelSize?: number; /** Controls the distance between the category axis label and the category axis. */ categoryAxisLabelOffset?: number; /** Label text for the value axis. */ valueAxisLabel?: string; /** Font size of the label for the value axis, in pixels. */ valueAxisLabelSize?: number; /** Controls the distance between the value axis label and the value axis. */ valueAxisLabelOffset?: number; } interface BarGraphData { category: string; value: number; } const DEFAULT_LABEL_SIZE = 16; export function BarGraphHorizontal(props: BarGraphProps) { const { width, height, margin, data, className, categoryTickLabelSize = DEFAULT_LABEL_SIZE, valueTickLabelSize = DEFAULT_LABEL_SIZE, hoverLabelSize, categoryAxisLabel, categoryAxisLabelSize = DEFAULT_LABEL_SIZE, categoryAxisLabelOffset = 0, valueAxisLabel, valueAxisLabelSize = DEFAULT_LABEL_SIZE, valueAxisLabelOffset = 0, } = 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 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 ( {getValue(d)} ); })} { return { ...leftTickLabelProps(), className: styles.tickLabel, dx: "-0.5rem", dy: "0.25rem", fontSize: `${categoryTickLabelSize / 16}rem`, }; }} label={categoryAxisLabel} labelClassName={styles.axisLabel} labelOffset={categoryAxisLabelOffset} labelProps={{ fontSize: `${categoryAxisLabelSize / 16}rem`, }} /> { return { ...bottomTickLabelProps(), className: styles.tickLabel, dy: "0.25rem", fontSize: `${valueTickLabelSize / 16}rem`, }; }} label={valueAxisLabel} labelClassName={styles.axisLabel} labelOffset={valueAxisLabelOffset} labelProps={{ fontSize: `${valueAxisLabelSize / 16}rem`, }} /> ); } export function BarGraphVertical(props: BarGraphProps) { const { width, height, margin, data, className, categoryTickLabelSize = DEFAULT_LABEL_SIZE, valueTickLabelSize = DEFAULT_LABEL_SIZE, hoverLabelSize, categoryAxisLabel, categoryAxisLabelSize = DEFAULT_LABEL_SIZE, categoryAxisLabelOffset = 0, valueAxisLabel, valueAxisLabelSize = DEFAULT_LABEL_SIZE, valueAxisLabelOffset = 0, } = props; const barPadding = 0.4; 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: barPadding, }); 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, 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 barHeight = valueMax - valuePoint(d); const barWidth = categoryScale.bandwidth(); return ( {getValue(d)} ); })} { return { ...bottomTickLabelProps(), className: styles.tickLabel, dy: "-0.25rem", fontSize: `${categoryTickLabelSize / 16}rem`, width: categoryScale.bandwidth(), verticalAnchor: "start", }; }} label={categoryAxisLabel} labelClassName={styles.axisLabel} labelOffset={categoryAxisLabelOffset} labelProps={{ fontSize: `${categoryAxisLabelSize / 16}rem`, }} /> { return { ...leftTickLabelProps(), className: styles.tickLabel, dx: "-0.5rem", dy: "0.25rem", fontSize: `${valueTickLabelSize / 16}rem`, }; }} label={valueAxisLabel} labelClassName={styles.axisLabel} labelOffset={valueAxisLabelOffset} labelProps={{ fontSize: `${valueAxisLabelSize / 16}rem`, }} /> ); }