/* eslint-disable @typescript-eslint/no-explicit-any */ import { FunctionComponent, useMemo, useState } from 'react' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import { AreaChart, Area, XAxis, YAxis, Tooltip as RechartsTooltip, ResponsiveContainer, ReferenceLine, BarChart, Bar, Cell, } from 'recharts' import FlipNumbers from 'react-flip-numbers' import ContentBox from './ContentBox' import SheenLoader from './SheenLoader' import { COLORS } from '../../styles/colors' import { useTheme } from 'next-themes' import { IconButton } from './Button' import { ArrowLeftIcon, NoSymbolIcon } from '@heroicons/react/20/solid' import { FadeInFadeOut } from './Transitions' import ChartRangeButtons from './ChartRangeButtons' import Change from './Change' import useLocalStorageState from 'hooks/useLocalStorageState' import { ANIMATION_SETTINGS_KEY, DAILY_MILLISECONDS } from 'utils/constants' import { formatNumericValue } from 'utils/numbers' import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings' import { AxisDomain } from 'recharts/types/util/types' import { useTranslation } from 'next-i18next' import FormatNumericValue from './FormatNumericValue' import { ContentType } from 'recharts/types/component/Tooltip' import Tooltip from './Tooltip' dayjs.extend(relativeTime) interface DetailedAreaOrBarChartProps { chartType?: 'area' | 'bar' customTooltip?: ContentType data: any[] daysToShow?: string domain?: AxisDomain heightClass?: string hideChange?: boolean hideChart?: () => void loaderHeightClass?: string loading?: boolean prefix?: string setDaysToShow?: (x: string) => void small?: boolean suffix?: string tickFormat?: (x: number) => string title?: string tooltipContent?: string xKey: string yDecimals?: number yKey: string showZeroLine?: boolean tooltipDateFormat?: string } export const formatDateAxis = (date: string, days: number) => { if (days === 1) { return dayjs(date).format('h:mma') } else { return dayjs(date).format('D MMM') } } const DetailedAreaOrBarChart: FunctionComponent< DetailedAreaOrBarChartProps > = ({ chartType = 'area', customTooltip, data, daysToShow = '1', domain, heightClass, hideChange, hideChart, loaderHeightClass, loading, prefix = '', setDaysToShow, small, suffix = '', tickFormat, title, tooltipContent, xKey, yDecimals, yKey, showZeroLine, tooltipDateFormat, }) => { const { t } = useTranslation('common') const [mouseData, setMouseData] = useState(null) const { theme } = useTheme() const [animationSettings] = useLocalStorageState( ANIMATION_SETTINGS_KEY, INITIAL_ANIMATION_SETTINGS ) const handleMouseMove = (coords: any) => { if (coords.activePayload) { setMouseData(coords.activePayload[0].payload) } } const handleMouseLeave = () => { setMouseData(null) } const flipGradientCoords = useMemo(() => { if (!data.length) return return data[0][yKey] <= 0 && data[data.length - 1][yKey] < 0 }, [data]) const filteredData = useMemo(() => { if (!data.length) return [] const start = Number(daysToShow) * DAILY_MILLISECONDS const filtered = data.filter((d: any) => { const dataTime = new Date(d[xKey]).getTime() const now = new Date().getTime() const limit = now - start return dataTime >= limit }) return filtered }, [data, daysToShow]) const calculateChartChange = () => { if (filteredData.length) { if (mouseData) { const index = filteredData.findIndex( (d: any) => d[xKey] === mouseData[xKey] ) const change = index >= 0 ? filteredData[index][yKey] - filteredData[0][yKey] : 0 return isNaN(change) ? 0 : change } else return ( filteredData[filteredData.length - 1][yKey] - filteredData[0][yKey] ) } return 0 } const titleClasses = `${small ? 'text-sm' : 'mb-0.5 text-base'} text-th-fgd-3` return ( {loading ? (
) : filteredData.length ? (
{setDaysToShow ? (
setDaysToShow(v)} />
) : null}
{hideChart ? ( ) : null}
{title ? ( tooltipContent ? (

{title}

) : (

{title}

) ) : null} {mouseData ? (
{animationSettings['number-scroll'] ? ( ) : ( {mouseData[yKey] < 0 ? '-' : ''} {prefix} {suffix} )} {!hideChange ? ( ) : null}

{dayjs(mouseData[xKey]).format( tooltipDateFormat ? tooltipDateFormat : 'DD MMM YY, h:mma' )}

) : (
{animationSettings['number-scroll'] ? ( ) : ( {filteredData[filteredData.length - 1][yKey] < 0 ? '-' : ''} {prefix} {suffix} )} {!hideChange ? ( ) : null}

{dayjs( filteredData[filteredData.length - 1][xKey] ).format( tooltipDateFormat ? tooltipDateFormat : 'DD MMM YY, h:mma' )}

)}
{chartType === 'area' ? ( } /> = 0 ? COLORS.UP[theme] : COLORS.DOWN[theme] } stopOpacity={0.15} /> = 0 ? COLORS.UP[theme] : COLORS.DOWN[theme] } stopOpacity={0} /> = 0 ? COLORS.UP[theme] : COLORS.DOWN[theme] } strokeWidth={1.5} fill={`url(#gradientArea-${title?.replace( /[^a-zA-Z]/g, '' )})`} /> formatDateAxis(d, parseInt(daysToShow)) } /> { const difference = dataMax - dataMin if (difference < 0.01) { return [dataMin - 0.001, dataMax + 0.001] } else if (difference < 0.1) { return [dataMin - 0.01, dataMax + 0.01] } else if (difference < 1) { return [dataMin - 0.1, dataMax + 0.11] } else if (difference < 10) { return [dataMin - 1, dataMax + 1] } else { return [dataMin, dataMax] } } } padding={{ top: 20, bottom: 20 }} tick={{ fill: 'var(--fgd-4)', fontSize: 10, }} tickFormatter={ tickFormat ? (v) => tickFormat(v) : undefined } tickLine={false} /> {showZeroLine ? ( ) : null} ) : ( } /> {filteredData.map((entry, index) => { return ( 0 ? 'url(#greenGradientBar)' : 'url(#redGradientBar)' } /> ) })} formatDateAxis(v, parseInt(daysToShow)) } /> tickFormat(v) : undefined } type="number" /> )}
) : (

{t('chart-unavailable')}

)} ) } export default DetailedAreaOrBarChart