feat: added ChartLoading component

This commit is contained in:
Linkie Link 2023-11-24 10:32:54 +01:00
parent 160165aa22
commit 51916683ac
No known key found for this signature in database
GPG Key ID: 5318B0F2564D38EA
9 changed files with 275 additions and 151 deletions

View File

@ -1,119 +0,0 @@
import classNames from 'classnames'
import moment from 'moment'
import {
Area,
AreaChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts'
import Card from 'components/Card'
import Text from 'components/Text'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/useLocalStorage'
import { formatValue } from 'utils/formatters'
interface Props {
data: { date: string; value: number }[]
title: string
}
export default function Chart(props: Props) {
const [reduceMotion] = useLocalStorage<boolean>(
LocalStorageKeys.REDUCE_MOTION,
DEFAULT_SETTINGS.reduceMotion,
)
return (
<Card className='w-full' title={props.title} contentClassName='p-4 pr-0'>
<div className='-ml-6 h-[400px] w-full'>
<ResponsiveContainer width='100%' height='100%'>
<AreaChart
data={props.data}
margin={{
top: 0,
right: 0,
left: 0,
bottom: 0,
}}
>
<defs>
<linearGradient id='chartGradient' x1='0' y1='0' x2='0' y2='1'>
<stop offset='0%' stopColor={'#AB47BC'} stopOpacity={0.3} />
<stop offset='100%' stopColor={'#AB47BC'} stopOpacity={0.02} />
</linearGradient>
</defs>
<CartesianGrid
horizontal={false}
stroke='rgba(255,255,255,0.1)'
strokeDasharray='6 3'
syncWithTicks={true}
/>
<XAxis
stroke='rgba(255, 255, 255, 0.4)'
tickFormatter={(value) => {
return moment(value).format('DD MMM')
}}
padding={{ left: 10, right: 20 }}
axisLine={false}
tickLine={false}
fontSize={12}
dataKey='date'
dy={10}
/>
<YAxis
axisLine={false}
tickLine={false}
fontSize={12}
stroke='rgba(255, 255, 255, 0.4)'
tickFormatter={(value) => {
return formatValue(value, {
minDecimals: 0,
maxDecimals: 0,
prefix: '$',
abbreviated: true,
})
}}
/>
<Tooltip
cursor={false}
isAnimationActive={!reduceMotion}
wrapperStyle={{ outline: 'none' }}
content={({ payload, label }) => {
if (payload && payload.length) {
const value = Number(payload[0].value) ?? 0
return (
<div
className={classNames(
'max-w-[320px] rounded-lg px-4 py-2 isolate bg-black/5 backdrop-blur',
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-sm before:p-[1px] before:border-glas',
)}
>
<Text size='sm' className='text-white/60'>
{moment(label).format('DD MMM YYYY')}
</Text>
<Text size='sm'>
{formatValue(value, { minDecimals: 0, maxDecimals: 0, prefix: '$' })}
</Text>
</div>
)
}
}}
/>
<Area
type='monotone'
dataKey='value'
stroke='#AB47BC'
fill='url(#chartGradient)'
isAnimationActive={!reduceMotion}
/>
</AreaChart>
</ResponsiveContainer>
</div>
</Card>
)
}

View File

@ -0,0 +1,117 @@
import classNames from 'classnames'
import moment from 'moment'
import {
Area,
AreaChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts'
import Text from 'components/Text'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/useLocalStorage'
import { formatValue } from 'utils/formatters'
interface Props {
data: ChartData
height?: number
}
export default function ChartBody(props: Props) {
const [reduceMotion] = useLocalStorage<boolean>(
LocalStorageKeys.REDUCE_MOTION,
DEFAULT_SETTINGS.reduceMotion,
)
const height = props.height ?? 400
return (
<div className={`-ml-6 h-[${height}px] w-full`}>
<ResponsiveContainer width='100%' height='100%'>
<AreaChart
data={props.data}
margin={{
top: 0,
right: 0,
left: 0,
bottom: 0,
}}
>
<defs>
<linearGradient id='chartGradient' x1='0' y1='0' x2='0' y2='1'>
<stop offset='0%' stopColor={'#AB47BC'} stopOpacity={0.3} />
<stop offset='100%' stopColor={'#AB47BC'} stopOpacity={0.02} />
</linearGradient>
</defs>
<CartesianGrid
horizontal={false}
stroke='rgba(255,255,255,0.1)'
strokeDasharray='6 3'
syncWithTicks={true}
/>
<XAxis
stroke='rgba(255, 255, 255, 0.4)'
tickFormatter={(value) => {
return moment(value).format('DD MMM')
}}
padding={{ left: 10, right: 20 }}
axisLine={false}
tickLine={false}
fontSize={12}
dataKey='date'
dy={10}
/>
<YAxis
axisLine={false}
tickLine={false}
fontSize={12}
stroke='rgba(255, 255, 255, 0.4)'
tickFormatter={(value) => {
return formatValue(value, {
minDecimals: 0,
maxDecimals: 0,
prefix: '$',
abbreviated: true,
})
}}
/>
<Tooltip
cursor={false}
isAnimationActive={!reduceMotion}
wrapperStyle={{ outline: 'none' }}
content={({ payload, label }) => {
if (payload && payload.length) {
const value = Number(payload[0].value) ?? 0
return (
<div
className={classNames(
'max-w-[320px] rounded-lg px-4 py-2 isolate bg-black/5 backdrop-blur',
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-sm before:p-[1px] before:border-glas',
)}
>
<Text size='sm' className='text-white/60'>
{moment(label).format('DD MMM YYYY')}
</Text>
<Text size='sm'>
{formatValue(value, { minDecimals: 0, maxDecimals: 0, prefix: '$' })}
</Text>
</div>
)
}
}}
/>
<Area
type='monotone'
dataKey='value'
stroke='#AB47BC'
fill='url(#chartGradient)'
isAnimationActive={!reduceMotion}
/>
</AreaChart>
</ResponsiveContainer>
</div>
)
}

View File

@ -0,0 +1,93 @@
import moment from 'moment'
import { Area, AreaChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis } from 'recharts'
import classNames from 'classnames'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/useLocalStorage'
interface Props {
height?: number
}
function createLoadingData() {
const data = []
const dataValues = [0, 20, 40, 30, 60, 50, 100]
const startDate = moment().subtract(7, 'days')
const endDate = moment()
const days = endDate.diff(startDate, 'days')
for (let i = 0; i < days; i++) {
const date = moment(startDate).add(i, 'days')
data.push({
date: date.format('YYYY-MM-DD'),
value: dataValues[i],
})
}
return data
}
export default function Chart(props: Props) {
const [reduceMotion] = useLocalStorage<boolean>(
LocalStorageKeys.REDUCE_MOTION,
DEFAULT_SETTINGS.reduceMotion,
)
const height = props.height ?? 400
const loadingData = createLoadingData()
return (
<div className={classNames(`-ml-6 h-[${height}px] w-full`, !reduceMotion && 'animate-pulse')}>
<ResponsiveContainer width='100%' height='100%'>
<AreaChart
data={loadingData}
margin={{
top: 0,
right: 0,
left: 0,
bottom: 0,
}}
>
<defs>
<linearGradient id='chartGradient' x1='0' y1='0' x2='0' y2='1'>
<stop offset='0%' stopColor={'#FFF'} stopOpacity={0.3} />
<stop offset='100%' stopColor={'#FFF'} stopOpacity={0.02} />
</linearGradient>
</defs>
<CartesianGrid
horizontal={false}
stroke='rgba(255,255,255,0.2)'
strokeDasharray='6 3'
syncWithTicks={true}
/>
<XAxis
stroke='rgba(255, 255, 255, 0.3)'
tickFormatter={() => {
return '...'
}}
padding={{ left: 10, right: 20 }}
axisLine={false}
tickLine={false}
fontSize={12}
dataKey='date'
dy={10}
/>
<YAxis
axisLine={false}
tickLine={false}
fontSize={12}
stroke='rgba(255, 255, 255, 0.5)'
tickFormatter={() => {
return '...'
}}
/>
<Area
type='monotone'
dataKey='value'
stroke='rgba(255, 255, 255, 0.3)'
fill='url(#chartGradient)'
isAnimationActive={!reduceMotion}
/>
</AreaChart>
</ResponsiveContainer>
</div>
)
}

View File

@ -0,0 +1,16 @@
import Card from 'components/Card'
import ChartBody from 'components/Chart/ChartBody'
import ChartLoading from 'components/Chart/ChartLoading'
interface Props {
data: ChartData | null
title: string
}
export default function Chart(props: Props) {
return (
<Card className='w-full' title={props.title} contentClassName='p-4 pr-0'>
{props.data === null ? <ChartLoading /> : <ChartBody data={props.data} />}
</Card>
)
}

View File

@ -1,36 +1,44 @@
import { useEffect, useState } from 'react'
import Chart from 'components/Chart' import Chart from 'components/Chart'
export default function StatsTrading() { export default function StatsTrading() {
const totalSwapVolume = [ const [totalSwapVolume, setTotalSwapVolume] = useState<ChartData | null>(null)
{
date: '2023-11-15', useEffect(() => {
value: 2501271, setTimeout(() => {
}, setTotalSwapVolume([
{ {
date: '2023-11-16', date: '2023-11-15',
value: 2804718, value: 2501271,
}, },
{ {
date: '2023-11-17', date: '2023-11-16',
value: 4901520, value: 2804718,
}, },
{ {
date: '2023-11-18', date: '2023-11-17',
value: 6500000, value: 4901520,
}, },
{ {
date: '2023-11-19', date: '2023-11-18',
value: 7486720, value: 6500000,
}, },
{ {
date: '2023-11-20', date: '2023-11-19',
value: 8412721, value: 7486720,
}, },
{ {
date: '2023-11-21', date: '2023-11-20',
value: 10432321, value: 8412721,
}, },
] {
date: '2023-11-21',
value: 10432321,
},
])
}, 6000)
})
return <Chart title='Total Swap Volume' data={totalSwapVolume} /> return <Chart title='Total Swap Volume' data={totalSwapVolume} />
} }

View File

@ -61,3 +61,9 @@
table tr:last-child td { table tr:last-child td {
border-bottom-width: 0; border-bottom-width: 0;
} }
.recharts-dot {
stroke: theme('colors.body');
fill: theme('colors.martian-red');
r: 5px;
}

View File

@ -0,0 +1,3 @@
type ChartDataItem = { date: string; value: number }
type ChartData = ChartDataItem[]

View File

@ -1,4 +1,4 @@
import { BN } from './helpers' import { BN } from 'utils/helpers'
export const devideByPotentiallyZero = (numerator: number, denominator: number): number => { export const devideByPotentiallyZero = (numerator: number, denominator: number): number => {
if (denominator === 0) return 0 if (denominator === 0) return 0

View File

@ -1,4 +1,4 @@
import { formatAmountWithSymbol } from './formatters' import { formatAmountWithSymbol } from 'utils/formatters'
export function getNoBalanceMessage(symbol: string) { export function getNoBalanceMessage(symbol: string) {
return `You don't have an ${symbol} balance in your account.` return `You don't have an ${symbol} balance in your account.`