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'
|
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} />
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
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 => {
|
export const devideByPotentiallyZero = (numerator: number, denominator: number): number => {
|
||||||
if (denominator === 0) return 0
|
if (denominator === 0) return 0
|
||||||
|
@ -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.`
|
||||||
|
Loading…
Reference in New Issue
Block a user