feat: added ChartLoading component
This commit is contained in:
parent
160165aa22
commit
51916683ac
@ -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>
|
||||
)
|
||||
}
|
117
src/components/Chart/ChartBody.tsx
Normal file
117
src/components/Chart/ChartBody.tsx
Normal 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>
|
||||
)
|
||||
}
|
93
src/components/Chart/ChartLoading.tsx
Normal file
93
src/components/Chart/ChartLoading.tsx
Normal 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>
|
||||
)
|
||||
}
|
16
src/components/Chart/index.tsx
Normal file
16
src/components/Chart/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
@ -1,36 +1,44 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import Chart from 'components/Chart'
|
||||
|
||||
export default function StatsTrading() {
|
||||
const totalSwapVolume = [
|
||||
{
|
||||
date: '2023-11-15',
|
||||
value: 2501271,
|
||||
},
|
||||
{
|
||||
date: '2023-11-16',
|
||||
value: 2804718,
|
||||
},
|
||||
{
|
||||
date: '2023-11-17',
|
||||
value: 4901520,
|
||||
},
|
||||
{
|
||||
date: '2023-11-18',
|
||||
value: 6500000,
|
||||
},
|
||||
{
|
||||
date: '2023-11-19',
|
||||
value: 7486720,
|
||||
},
|
||||
{
|
||||
date: '2023-11-20',
|
||||
value: 8412721,
|
||||
},
|
||||
{
|
||||
date: '2023-11-21',
|
||||
value: 10432321,
|
||||
},
|
||||
]
|
||||
const [totalSwapVolume, setTotalSwapVolume] = useState<ChartData | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setTotalSwapVolume([
|
||||
{
|
||||
date: '2023-11-15',
|
||||
value: 2501271,
|
||||
},
|
||||
{
|
||||
date: '2023-11-16',
|
||||
value: 2804718,
|
||||
},
|
||||
{
|
||||
date: '2023-11-17',
|
||||
value: 4901520,
|
||||
},
|
||||
{
|
||||
date: '2023-11-18',
|
||||
value: 6500000,
|
||||
},
|
||||
{
|
||||
date: '2023-11-19',
|
||||
value: 7486720,
|
||||
},
|
||||
{
|
||||
date: '2023-11-20',
|
||||
value: 8412721,
|
||||
},
|
||||
{
|
||||
date: '2023-11-21',
|
||||
value: 10432321,
|
||||
},
|
||||
])
|
||||
}, 6000)
|
||||
})
|
||||
|
||||
return <Chart title='Total Swap Volume' data={totalSwapVolume} />
|
||||
}
|
||||
|
@ -61,3 +61,9 @@
|
||||
table tr:last-child td {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.recharts-dot {
|
||||
stroke: theme('colors.body');
|
||||
fill: theme('colors.martian-red');
|
||||
r: 5px;
|
||||
}
|
||||
|
3
src/types/interfaces/components/Chart.d.ts
vendored
Normal file
3
src/types/interfaces/components/Chart.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
type ChartDataItem = { date: string; value: number }
|
||||
|
||||
type ChartData = ChartDataItem[]
|
@ -1,4 +1,4 @@
|
||||
import { BN } from './helpers'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export const devideByPotentiallyZero = (numerator: number, denominator: number): number => {
|
||||
if (denominator === 0) return 0
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { formatAmountWithSymbol } from './formatters'
|
||||
import { formatAmountWithSymbol } from 'utils/formatters'
|
||||
|
||||
export function getNoBalanceMessage(symbol: string) {
|
||||
return `You don't have an ${symbol} balance in your account.`
|
||||
|
Loading…
Reference in New Issue
Block a user