import React from 'react'; import { BarStack } from '@visx/shape'; import { SeriesPoint } from '@visx/shape/lib/types'; import { Group } from '@visx/group'; import { Grid } from '@visx/grid'; import { AxisBottom } from '@visx/axis'; import cityTemperature, { CityTemperature } from '@visx/mock-data/lib/mocks/cityTemperature'; import { scaleBand, scaleLinear, scaleOrdinal } from '@visx/scale'; import { timeParse, timeFormat } from '@visx/vendor/d3-time-format'; import { useTooltip, useTooltipInPortal, defaultStyles } from '@visx/tooltip'; import { LegendOrdinal } from '@visx/legend'; import { localPoint } from '@visx/event'; type CityName = 'New York' | 'San Francisco' | 'Austin'; type TooltipData = { bar: SeriesPoint; key: CityName; index: number; height: number; width: number; x: number; y: number; color: string; }; export type BarStackProps = { width: number; height: number; margin?: { top: number; right: number; bottom: number; left: number }; events?: boolean; }; const purple1 = '#6c5efb'; const purple2 = '#c998ff'; export const purple3 = '#a44afe'; export const background = 'transparent'; const defaultMargin = { top: 40, right: 0, bottom: 0, left: 0 }; const tooltipStyles = { ...defaultStyles, minWidth: 60, backgroundColor: 'rgba(0,0,0,0.9)', color: 'white', }; const data = cityTemperature.slice(0, 12); const keys = Object.keys(data[0]).filter((d) => d !== 'date') as CityName[]; const temperatureTotals = data.reduce((allTotals, currentDate) => { const totalTemperature = keys.reduce((dailyTotal, k) => { dailyTotal += Number(currentDate[k]); return dailyTotal; }, 0); allTotals.push(totalTemperature); return allTotals; }, [] as number[]); const parseDate = timeParse('%Y-%m-%d'); const format = timeFormat('%b %d'); const formatDate = (date: string) => format(parseDate(date) as Date); // accessors const getDate = (d: CityTemperature) => d.date; // scales const dateScale = scaleBand({ domain: data.map(getDate), padding: 0.2, }); const temperatureScale = scaleLinear({ domain: [0, Math.max(...temperatureTotals)], nice: true, }); const colorScale = scaleOrdinal({ domain: keys, range: [purple1, purple2, purple3], }); let tooltipTimeout: number; console.log(cityTemperature); export default function BarStackChart({ width, height, events = false, margin = defaultMargin, }: BarStackProps) { const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = useTooltip(); const { containerRef, TooltipInPortal } = useTooltipInPortal({ // TooltipInPortal is rendered in a separate child of and positioned // with page coordinates which should be updated on scroll. consider using // Tooltip or TooltipWithBounds if you don't need to render inside a Portal scroll: true, }); if (width < 10) return null; // bounds const xMax = width; const yMax = height - margin.top - 100; dateScale.rangeRound([0, xMax]); temperatureScale.range([yMax, 0]); return width < 10 ? null : (
data={data} keys={keys} x={getDate} xScale={dateScale} yScale={temperatureScale} color={colorScale} > {(barStacks) => barStacks.map((barStack) => barStack.bars.map((bar) => ( { if (events) alert(`clicked: ${JSON.stringify(bar)}`); }} onMouseLeave={() => { tooltipTimeout = window.setTimeout(() => { hideTooltip(); }, 300); }} onMouseMove={(event) => { if (tooltipTimeout) clearTimeout(tooltipTimeout); // TooltipInPortal expects coordinates to be relative to containerRef // localPoint returns coordinates relative to the nearest SVG, which // is what containerRef is set to in this example. const eventSvgCoords = localPoint(event); const left = bar.x + bar.width / 2; showTooltip({ tooltipData: bar, tooltipTop: eventSvgCoords?.y, tooltipLeft: left, }); }} /> )), ) }
{tooltipOpen && tooltipData && (
{tooltipData.key}
{tooltipData.bar.data[tooltipData.key]}℉
{formatDate(getDate(tooltipData.bar.data))}
)}
); }