mango-v4-ui/components/shared/DetailedAreaChart.tsx

270 lines
9.0 KiB
TypeScript
Raw Normal View History

2022-10-23 18:43:07 -07:00
import { FunctionComponent, useMemo, useState } from 'react'
2022-08-03 02:49:31 -07:00
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import {
AreaChart,
Area,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
} from 'recharts'
import FlipNumbers from 'react-flip-numbers'
import LineChartIcon from '../icons/LineChartIcon'
import ContentBox from '../shared/ContentBox'
import SheenLoader from '../shared/SheenLoader'
import { COLORS } from '../../styles/colors'
import { useTheme } from 'next-themes'
import { IconButton } from './Button'
2022-09-06 21:36:35 -07:00
import { ArrowLeftIcon } from '@heroicons/react/20/solid'
2022-08-03 02:49:31 -07:00
import { FadeInFadeOut } from './Transitions'
2022-09-07 23:25:32 -07:00
import ChartRangeButtons from './ChartRangeButtons'
2022-10-05 22:11:28 -07:00
import Change from './Change'
2022-08-03 02:49:31 -07:00
dayjs.extend(relativeTime)
interface DetailedAreaChartProps {
data: any[]
2022-08-10 21:09:58 -07:00
daysToShow: number
2022-08-13 03:44:13 -07:00
hideChange?: boolean
2022-08-03 02:49:31 -07:00
hideChart?: () => void
loading?: boolean
2022-08-10 21:09:58 -07:00
setDaysToShow: (x: number) => void
tickFormat?: (x: any) => string
2022-08-03 02:49:31 -07:00
title?: string
xKey: string
yKey: string
}
export const formatDateAxis = (date: string, days: number) => {
2022-10-21 05:54:18 -07:00
if (days === 1) {
return dayjs(date).format('h:mma')
} else {
return dayjs(date).format('D MMM')
}
}
2022-08-03 02:49:31 -07:00
const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
data,
2022-08-10 21:09:58 -07:00
daysToShow = 1,
2022-08-13 03:44:13 -07:00
hideChange,
2022-08-03 02:49:31 -07:00
hideChart,
loading,
2022-08-10 21:09:58 -07:00
setDaysToShow,
tickFormat,
2022-08-03 02:49:31 -07:00
title,
xKey,
yKey,
}) => {
const [mouseData, setMouseData] = useState<any>(null)
const { theme } = useTheme()
const handleMouseMove = (coords: any) => {
if (coords.activePayload) {
setMouseData(coords.activePayload[0].payload)
}
}
const handleMouseLeave = () => {
setMouseData(null)
}
const calculateChartChange = () => {
if (data.length) {
if (mouseData) {
const index = data.findIndex((d: any) => d[xKey] === mouseData[xKey])
2022-11-20 03:23:23 -08:00
const change = data[index][yKey] - data[0][yKey]
2022-08-10 21:09:58 -07:00
return isNaN(change) ? 0 : change
2022-11-20 03:23:23 -08:00
} else return data[data.length - 1][yKey] - data[0][yKey]
2022-08-03 02:49:31 -07:00
}
return 0
}
const flipGradientCoords = useMemo(
() => data[0][yKey] <= 0 && data[data.length - 1][yKey] < data[0][yKey],
[data]
)
2022-08-03 02:49:31 -07:00
return (
<FadeInFadeOut show={true}>
<ContentBox hideBorder hidePadding>
{loading ? (
2022-09-08 18:00:47 -07:00
<SheenLoader className="flex flex-1">
<div className="h-[448px] w-full rounded-lg bg-th-bkg-2" />
2022-08-03 02:49:31 -07:00
</SheenLoader>
) : data.length ? (
<div className="relative">
<div className="flex items-start justify-between">
<div className="flex flex-col md:flex-row md:items-start md:space-x-6">
<IconButton className="mb-6" onClick={hideChart}>
<ArrowLeftIcon className="h-5 w-5" />
</IconButton>
<div>
<p className="mb-0.5 text-base text-th-fgd-3">{title}</p>
{mouseData ? (
<div>
<div className="mb-1 flex items-end text-4xl font-bold text-th-fgd-1">
2022-08-10 21:09:58 -07:00
$
2022-08-03 02:49:31 -07:00
<FlipNumbers
height={40}
width={26}
play
2022-08-10 21:09:58 -07:00
numbers={mouseData[yKey].toFixed(2)}
2022-08-03 02:49:31 -07:00
/>
2022-08-13 03:44:13 -07:00
{!hideChange ? (
2022-09-08 18:00:47 -07:00
<span className="ml-3">
2022-11-20 03:23:23 -08:00
<Change
change={calculateChartChange()}
isCurrency
/>
2022-08-03 02:49:31 -07:00
</span>
2022-08-13 03:44:13 -07:00
) : null}
2022-08-03 02:49:31 -07:00
</div>
<p className="text-sm text-th-fgd-4">
{dayjs(mouseData[xKey]).format('DD MMM YY, h:mma')}
</p>
</div>
) : (
<div>
<div className="mb-1 flex items-end text-4xl font-bold text-th-fgd-1">
2022-08-10 21:09:58 -07:00
$
2022-08-03 02:49:31 -07:00
<FlipNumbers
height={40}
width={26}
play
2022-08-10 21:09:58 -07:00
numbers={data[data.length - 1][yKey].toFixed(2)}
2022-08-03 02:49:31 -07:00
/>
2022-08-13 03:44:13 -07:00
{!hideChange ? (
2022-09-08 18:00:47 -07:00
<span className="ml-3">
2022-11-20 03:23:23 -08:00
<Change
change={calculateChartChange()}
isCurrency
/>
2022-08-03 02:49:31 -07:00
</span>
2022-08-13 03:44:13 -07:00
) : null}
2022-08-03 02:49:31 -07:00
</div>
<p className="text-sm text-th-fgd-4">
{dayjs(data[data.length - 1][xKey]).format(
'DD MMM YY, h:mma'
)}
</p>
</div>
)}
</div>
</div>
</div>
<div className="-mt-1 h-96 w-auto">
<div className="absolute -top-1 right-0 -mb-2 flex justify-end">
2022-09-07 23:25:32 -07:00
<ChartRangeButtons
activeValue={daysToShow}
names={['24H', '7D', '30D']}
values={[1, 7, 30]}
onChange={(v) => setDaysToShow(v)}
/>
2022-08-03 02:49:31 -07:00
</div>
<div className="-mx-6 mt-6 h-full">
<ResponsiveContainer width="100%" height="100%">
<AreaChart
data={data}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
>
<Tooltip
cursor={{
strokeOpacity: 0.09,
}}
content={<></>}
/>
<defs>
<linearGradient
id="gradientArea"
x1="0"
y1={flipGradientCoords ? '1' : '0'}
2022-08-03 02:49:31 -07:00
x2="0"
y2={flipGradientCoords ? '0' : '1'}
2022-08-03 02:49:31 -07:00
>
<stop
offset="0%"
stopColor={
calculateChartChange() >= 0
? COLORS.GREEN[theme]
: COLORS.RED[theme]
}
stopOpacity={0.15}
/>
<stop
offset="99%"
stopColor={
calculateChartChange() >= 0
? COLORS.GREEN[theme]
: COLORS.RED[theme]
}
stopOpacity={0}
/>
</linearGradient>
</defs>
<Area
isAnimationActive={false}
type="monotone"
dataKey={yKey}
stroke={
calculateChartChange() >= 0
? COLORS.GREEN[theme]
: COLORS.RED[theme]
}
strokeWidth={1.5}
fill="url(#gradientArea)"
/>
<XAxis
axisLine={false}
dataKey={xKey}
padding={{ left: 20, right: 20 }}
tick={{
fill:
theme === 'Light'
? 'rgba(0,0,0,0.4)'
: 'rgba(255,255,255,0.6)',
fontSize: 10,
}}
tickLine={false}
2022-10-21 05:54:18 -07:00
tickFormatter={(d) => formatDateAxis(d, daysToShow)}
2022-08-03 02:49:31 -07:00
/>
<YAxis
axisLine={false}
dataKey={yKey}
type="number"
domain={['dataMin', 'dataMax']}
padding={{ top: 20, bottom: 20 }}
tick={{
fill:
theme === 'Light'
? 'rgba(0,0,0,0.4)'
: 'rgba(255,255,255,0.6)',
fontSize: 10,
}}
2022-08-10 21:09:58 -07:00
tickFormatter={
tickFormat ? (v) => tickFormat(v) : undefined
}
2022-08-03 02:49:31 -07:00
tickLine={false}
/>
</AreaChart>
</ResponsiveContainer>
</div>
</div>
</div>
) : (
<div className="flex h-96 items-center justify-center rounded-lg bg-th-bkg-2 p-4 text-th-fgd-3">
<div className="">
<LineChartIcon className="mx-auto h-10 w-10 text-th-fgd-4" />
<p className="text-th-fgd-4">Chart not available</p>
</div>
</div>
)}
</ContentBox>
</FadeInFadeOut>
)
}
export default DetailedAreaChart