align expandable panels

This commit is contained in:
saml33 2022-02-24 12:37:31 +11:00
parent 835c1a11ea
commit cbf7047bb7
6 changed files with 546 additions and 614 deletions

View File

@ -1,7 +1,5 @@
import { FunctionComponent, useEffect, useMemo, useState } from 'react'
import { ExternalLinkIcon, EyeOffIcon } from '@heroicons/react/outline'
import { ChevronDownIcon } from '@heroicons/react/solid'
import { Disclosure } from '@headlessui/react'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { AreaChart, Area, XAxis, YAxis, Tooltip } from 'recharts'
@ -9,6 +7,7 @@ import useDimensions from 'react-cool-dimensions'
import { IconButton } from './Button'
import { LineChartIcon } from './icons'
import { useTranslation } from 'next-i18next'
import { ExpandableRow } from './TableElements'
dayjs.extend(relativeTime)
@ -326,270 +325,247 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
{t('swap:chart-not-available')}
</div>
)}
{inputTokenInfo && outputTokenInfo && baseTokenId ? (
<div className="w-full">
<Disclosure>
{({ open }) => (
<>
<Disclosure.Button
className={`border border-th-bkg-4 default-transition flex items-center justify-between mt-6 p-3 rounded-md w-full hover:bg-th-bkg-2 ${
open
? 'border-b-transparent rounded-b-none'
: 'transform rotate-360'
}`}
>
<div className="flex items-center">
{inputTokenInfo.image?.small ? (
<img
className="rounded-full"
src={inputTokenInfo.image?.small}
width="32"
height="32"
alt={inputTokenInfo.name}
/>
) : null}
<div className="ml-2.5 text-left">
<h2 className="font-bold text-base text-th-fgd-1">
{inputTokenInfo?.symbol?.toUpperCase()}
</h2>
<div className="font-normal text-th-fgd-3 text-xs">
{inputTokenInfo.name}
</div>
<div className="pt-8">
{inputTokenInfo && outputTokenInfo && baseTokenId ? (
<ExpandableRow
buttonTemplate={
<div className="flex items-center justify-between text-fgd-1 w-full">
<div className="flex items-center">
{inputTokenInfo.image?.small ? (
<img
className="rounded-full"
src={inputTokenInfo.image?.small}
width="32"
height="32"
alt={inputTokenInfo.name}
/>
) : null}
<div className="ml-2.5 text-left">
<h2 className="font-bold text-base text-th-fgd-1">
{inputTokenInfo?.symbol?.toUpperCase()}
</h2>
<div className="font-normal text-th-fgd-3 text-xs">
{inputTokenInfo.name}
</div>
</div>
<div className="flex items-center">
<div className="flex items-center space-x-3">
{inputTokenInfo.market_data?.current_price?.usd ? (
<div className="font-normal text-th-fgd-1">
</div>
<div className="flex items-center">
<div className="flex items-center space-x-3">
{inputTokenInfo.market_data?.current_price?.usd ? (
<div className="font-normal text-th-fgd-1">
$
{numberFormatter.format(
inputTokenInfo.market_data?.current_price.usd
)}
</div>
) : null}
{inputTokenInfo.market_data?.price_change_percentage_24h ? (
<div
className={`font-normal text-th-fgd-1 ${
inputTokenInfo.market_data
.price_change_percentage_24h >= 0
? 'text-th-green'
: 'text-th-red'
}`}
>
{inputTokenInfo.market_data.price_change_percentage_24h.toFixed(
2
)}
%
</div>
) : null}
</div>
</div>
</div>
}
panelTemplate={
<div>
<div className="font-bold m-1 mt-0 pb-2 text-th-fgd-1 text-base">
{t('market-data')}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2 xl:grid-cols-3 grid-flow-row">
{inputTokenInfo.market_cap_rank ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:market-cap-rank')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
#{inputTokenInfo.market_cap_rank}
</div>
</div>
) : null}
{inputTokenInfo.market_data?.market_cap &&
inputTokenInfo.market_data?.market_cap?.usd !== 0 ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:market-cap')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberCompacter.format(
inputTokenInfo.market_data?.market_cap?.usd
)}
</div>
</div>
) : null}
{inputTokenInfo.market_data?.total_volume?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('daily-volume')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberCompacter.format(
inputTokenInfo.market_data?.total_volume?.usd
)}
</div>
</div>
) : null}
{inputTokenInfo.market_data?.circulating_supply ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:token-supply')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
{numberCompacter.format(
inputTokenInfo.market_data.circulating_supply
)}
</div>
{inputTokenInfo.market_data?.max_supply ? (
<div className="text-th-fgd-2 text-xs">
{t('swap:max-supply')}:{' '}
{numberCompacter.format(
inputTokenInfo.market_data.max_supply
)}
</div>
) : null}
</div>
) : null}
{inputTokenInfo.market_data?.ath?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:ath')}
</div>
<div className="flex">
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberFormatter.format(
inputTokenInfo.market_data?.current_price.usd
inputTokenInfo.market_data.ath.usd
)}
</div>
) : null}
{inputTokenInfo.market_data
?.price_change_percentage_24h ? (
<div
className={`font-normal text-th-fgd-1 ${
inputTokenInfo.market_data
.price_change_percentage_24h >= 0
? 'text-th-green'
: 'text-th-red'
}`}
>
{inputTokenInfo.market_data.price_change_percentage_24h.toFixed(
2
)}
%
</div>
) : null}
</div>
<ChevronDownIcon
className={`default-transition h-6 ml-2 w-6 text-th-fgd-3 ${
open ? 'transform rotate-180' : 'transform rotate-360'
}`}
/>
</div>
</Disclosure.Button>
<Disclosure.Panel>
<div className="border border-th-bkg-4 border-t-0 p-3 rounded-b-md">
<div className="font-bold m-1 mt-0 pb-2 text-th-fgd-1 text-base">
{t('market-data')}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2 xl:grid-cols-3 grid-flow-row">
{inputTokenInfo.market_cap_rank ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:market-cap-rank')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
#{inputTokenInfo.market_cap_rank}
</div>
</div>
) : null}
{inputTokenInfo.market_data?.market_cap &&
inputTokenInfo.market_data?.market_cap?.usd !== 0 ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:market-cap')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberCompacter.format(
inputTokenInfo.market_data?.market_cap?.usd
)}
</div>
</div>
) : null}
{inputTokenInfo.market_data?.total_volume?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('daily-volume')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberCompacter.format(
inputTokenInfo.market_data?.total_volume?.usd
)}
</div>
</div>
) : null}
{inputTokenInfo.market_data?.circulating_supply ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:token-supply')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
{numberCompacter.format(
inputTokenInfo.market_data.circulating_supply
)}
</div>
{inputTokenInfo.market_data?.max_supply ? (
<div className="text-th-fgd-2 text-xs">
{t('swap:max-supply')}:{' '}
{numberCompacter.format(
inputTokenInfo.market_data.max_supply
)}
</div>
) : null}
</div>
) : null}
{inputTokenInfo.market_data?.ath?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:ath')}
</div>
<div className="flex">
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberFormatter.format(
inputTokenInfo.market_data.ath.usd
)}
</div>
{inputTokenInfo.market_data?.ath_change_percentage
?.usd ? (
<div
className={`ml-1.5 mt-2 text-xs ${
inputTokenInfo.market_data
?.ath_change_percentage?.usd >= 0
? 'text-th-green'
: 'text-th-red'
}`}
>
{(inputTokenInfo.market_data?.ath_change_percentage?.usd).toFixed(
2
)}
%
</div>
) : null}
</div>
{inputTokenInfo.market_data?.ath_date?.usd ? (
<div className="text-th-fgd-2 text-xs">
{dayjs(
inputTokenInfo.market_data.ath_date.usd
).fromNow()}
</div>
) : null}
</div>
) : null}
{inputTokenInfo.market_data?.atl?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:atl')}
</div>
<div className="flex">
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberFormatter.format(
inputTokenInfo.market_data.atl.usd
)}
</div>
{inputTokenInfo.market_data?.atl_change_percentage
?.usd ? (
<div
className={`ml-1.5 mt-2 text-xs ${
inputTokenInfo.market_data
?.atl_change_percentage?.usd >= 0
? 'text-th-green'
: 'text-th-red'
}`}
>
{(inputTokenInfo.market_data?.atl_change_percentage?.usd).toLocaleString(
undefined,
{
minimumFractionDigits: 0,
maximumFractionDigits: 2,
}
)}
%
</div>
) : null}
</div>
{inputTokenInfo.market_data?.atl_date?.usd ? (
<div className="text-th-fgd-2 text-xs">
{dayjs(
inputTokenInfo.market_data.atl_date.usd
).fromNow()}
</div>
) : null}
</div>
) : null}
</div>
{topHolders?.inputHolders ? (
<div className="pt-4">
<div className="font-bold m-1 pb-3 text-th-fgd-1 text-base">
{t('swap:top-ten')}
</div>
{topHolders.inputHolders.map((holder) => (
<a
className="border-t border-th-bkg-4 default transition flex justify-between mx-1 px-2 py-2.5 text-th-fgd-3 hover:bg-th-bkg-2"
href={`https://explorer.solana.com/address/${holder.owner}`}
target="_blank"
rel="noopener noreferrer"
key={holder.owner}
{inputTokenInfo.market_data?.ath_change_percentage
?.usd ? (
<div
className={`ml-1.5 mt-2 text-xs ${
inputTokenInfo.market_data?.ath_change_percentage
?.usd >= 0
? 'text-th-green'
: 'text-th-red'
}`}
>
<div className="text-th-fgd-3">
{holder.owner.slice(0, 5) +
'…' +
holder.owner.slice(-5)}
</div>
<div className="flex items-center">
<div className="text-th-fgd-1">
{numberFormatter.format(
holder.amount / Math.pow(10, holder.decimals)
)}
</div>
<ExternalLinkIcon className="h-4 ml-2 w-4" />
</div>
</a>
))}
{(inputTokenInfo.market_data?.ath_change_percentage?.usd).toFixed(
2
)}
%
</div>
) : null}
</div>
) : null}
{inputTokenInfo.market_data?.ath_date?.usd ? (
<div className="text-th-fgd-2 text-xs">
{dayjs(
inputTokenInfo.market_data.ath_date.usd
).fromNow()}
</div>
) : null}
</div>
) : null}
{inputTokenInfo.market_data?.atl?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:atl')}
</div>
<div className="flex">
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberFormatter.format(
inputTokenInfo.market_data.atl.usd
)}
</div>
{inputTokenInfo.market_data?.atl_change_percentage
?.usd ? (
<div
className={`ml-1.5 mt-2 text-xs ${
inputTokenInfo.market_data?.atl_change_percentage
?.usd >= 0
? 'text-th-green'
: 'text-th-red'
}`}
>
{(inputTokenInfo.market_data?.atl_change_percentage?.usd).toLocaleString(
undefined,
{
minimumFractionDigits: 0,
maximumFractionDigits: 2,
}
)}
%
</div>
) : null}
</div>
{inputTokenInfo.market_data?.atl_date?.usd ? (
<div className="text-th-fgd-2 text-xs">
{dayjs(
inputTokenInfo.market_data.atl_date.usd
).fromNow()}
</div>
) : null}
</div>
) : null}
</div>
{topHolders?.inputHolders ? (
<div className="pt-4">
<div className="font-bold m-1 pb-3 text-th-fgd-1 text-base">
{t('swap:top-ten')}
</div>
{topHolders.inputHolders.map((holder) => (
<a
className="border-t border-th-bkg-4 default transition flex justify-between mx-1 px-2 py-2.5 text-th-fgd-3 hover:bg-th-bkg-2"
href={`https://explorer.solana.com/address/${holder.owner}`}
target="_blank"
rel="noopener noreferrer"
key={holder.owner}
>
<div className="text-th-fgd-3">
{holder.owner.slice(0, 5) +
'…' +
holder.owner.slice(-5)}
</div>
<div className="flex items-center">
<div className="text-th-fgd-1">
{numberFormatter.format(
holder.amount / Math.pow(10, holder.decimals)
)}
</div>
<ExternalLinkIcon className="h-4 ml-2 w-4" />
</div>
</a>
))}
</div>
</Disclosure.Panel>
</>
)}
</Disclosure>
</div>
) : (
<div className="bg-th-bkg-3 mt-3 p-4 rounded-md text-center text-th-fgd-3">
{t('swap:input-info-unavailable')}
</div>
)}
) : null}
</div>
}
/>
) : (
<div className="bg-th-bkg-3 mt-3 p-4 rounded-md text-center text-th-fgd-3">
{t('swap:input-info-unavailable')}
</div>
)}
{outputTokenInfo && quoteTokenId ? (
<div className="w-full">
<Disclosure>
{({ open }) => (
<>
<Disclosure.Button
className={`border border-th-bkg-4 default-transition flex items-center justify-between mt-3 p-3 rounded-md w-full hover:bg-th-bkg-2 ${
open
? 'border-b-transparent rounded-b-none'
: 'transform rotate-360'
}`}
>
{outputTokenInfo && quoteTokenId ? (
<div className="border-b border-th-bkg-4 w-full">
<ExpandableRow
buttonTemplate={
<div className="flex items-center justify-between text-fgd-1 w-full">
<div className="flex items-center">
{outputTokenInfo.image?.small ? (
<img
@ -615,7 +591,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
<div className="font-normal text-th-fgd-1">
$
{numberFormatter.format(
outputTokenInfo.market_data.current_price.usd
outputTokenInfo.market_data?.current_price.usd
)}
</div>
) : null}
@ -636,198 +612,193 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
</div>
) : null}
</div>
<ChevronDownIcon
className={`default-transition h-6 ml-2 w-6 text-th-fgd-3 ${
open ? 'transform rotate-180' : 'transform rotate-360'
}`}
/>
</div>
</Disclosure.Button>
<Disclosure.Panel>
<div className="border border-th-bkg-4 border-t-0 p-3 rounded-b-md">
<div className="font-bold m-1 mt-0 pb-2 text-th-fgd-1 text-base">
{t('market-data')}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2 xl:grid-cols-3 grid-flow-row">
{outputTokenInfo.market_cap_rank ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:market-cap-rank')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
#{outputTokenInfo.market_cap_rank}
</div>
</div>
}
panelTemplate={
<div>
<div className="font-bold m-1 mt-0 pb-2 text-th-fgd-1 text-base">
{t('market-data')}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2 xl:grid-cols-3 grid-flow-row">
{outputTokenInfo.market_cap_rank ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:market-cap-rank')}
</div>
) : null}
{outputTokenInfo.market_data?.market_cap &&
outputTokenInfo.market_data?.market_cap?.usd !== 0 ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:market-cap')}
<div className="font-bold text-th-fgd-1 text-lg">
#{outputTokenInfo.market_cap_rank}
</div>
</div>
) : null}
{outputTokenInfo.market_data?.market_cap &&
outputTokenInfo.market_data?.market_cap?.usd !== 0 ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:market-cap')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberCompacter.format(
outputTokenInfo.market_data?.market_cap?.usd
)}
</div>
</div>
) : null}
{outputTokenInfo.market_data?.total_volume?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('daily-volume')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberCompacter.format(
outputTokenInfo.market_data?.total_volume?.usd
)}
</div>
</div>
) : null}
{outputTokenInfo.market_data?.circulating_supply ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:token-supply')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
{numberCompacter.format(
outputTokenInfo.market_data.circulating_supply
)}
</div>
{outputTokenInfo.market_data?.max_supply ? (
<div className="text-th-fgd-2 text-xs">
{t('swap:max-supply')}:{' '}
{numberCompacter.format(
outputTokenInfo.market_data.max_supply
)}
</div>
) : null}
</div>
) : null}
{outputTokenInfo.market_data?.ath?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:ath')}
</div>
<div className="flex">
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberCompacter.format(
outputTokenInfo.market_data?.market_cap?.usd
{numberFormatter.format(
outputTokenInfo.market_data.ath.usd
)}
</div>
{outputTokenInfo.market_data?.ath_change_percentage
?.usd ? (
<div
className={`ml-1.5 mt-2 text-xs ${
outputTokenInfo.market_data
?.ath_change_percentage?.usd >= 0
? 'text-th-green'
: 'text-th-red'
}`}
>
{(outputTokenInfo.market_data?.ath_change_percentage?.usd).toFixed(
2
)}
%
</div>
) : null}
</div>
) : null}
{outputTokenInfo.market_data?.total_volume?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('daily-volume')}
{outputTokenInfo.market_data?.ath_date?.usd ? (
<div className="text-th-fgd-2 text-xs">
{dayjs(
outputTokenInfo.market_data.ath_date.usd
).fromNow()}
</div>
) : null}
</div>
) : null}
{outputTokenInfo.market_data?.atl?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:atl')}
</div>
<div className="flex">
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberCompacter.format(
outputTokenInfo.market_data?.total_volume?.usd
{numberFormatter.format(
outputTokenInfo.market_data.atl.usd
)}
</div>
</div>
) : null}
{outputTokenInfo.market_data?.circulating_supply ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:token-supply')}
</div>
<div className="font-bold text-th-fgd-1 text-lg">
{numberCompacter.format(
outputTokenInfo.market_data.circulating_supply
)}
</div>
{outputTokenInfo.market_data?.max_supply ? (
<div className="text-th-fgd-2 text-xs">
{t('swap:max-supply')}:{' '}
{numberCompacter.format(
outputTokenInfo.market_data.max_supply
{outputTokenInfo.market_data?.atl_change_percentage
?.usd ? (
<div
className={`ml-1.5 mt-2 text-xs ${
outputTokenInfo.market_data
?.atl_change_percentage?.usd >= 0
? 'text-th-green'
: 'text-th-red'
}`}
>
{(outputTokenInfo.market_data?.atl_change_percentage?.usd).toLocaleString(
undefined,
{
minimumFractionDigits: 0,
maximumFractionDigits: 2,
}
)}
%
</div>
) : null}
</div>
) : null}
{outputTokenInfo.market_data?.ath?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:ath')}
{outputTokenInfo.market_data?.atl_date?.usd ? (
<div className="text-th-fgd-2 text-xs">
{dayjs(
outputTokenInfo.market_data.atl_date.usd
).fromNow()}
</div>
<div className="flex">
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberFormatter.format(
outputTokenInfo.market_data.ath.usd
)}
</div>
{outputTokenInfo.market_data?.ath_change_percentage
?.usd ? (
<div
className={`ml-1.5 mt-2 text-xs ${
outputTokenInfo.market_data
?.ath_change_percentage?.usd >= 0
? 'text-th-green'
: 'text-th-red'
}`}
>
{(outputTokenInfo.market_data?.ath_change_percentage?.usd).toFixed(
2
)}
%
</div>
) : null}
</div>
{outputTokenInfo.market_data?.ath_date?.usd ? (
<div className="text-th-fgd-2 text-xs">
{dayjs(
outputTokenInfo.market_data.ath_date.usd
).fromNow()}
</div>
) : null}
</div>
) : null}
{outputTokenInfo.market_data?.atl?.usd ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
<div className="text-th-fgd-3 text-xs">
{t('swap:atl')}
</div>
<div className="flex">
<div className="font-bold text-th-fgd-1 text-lg">
$
{numberFormatter.format(
outputTokenInfo.market_data.atl.usd
)}
</div>
{outputTokenInfo.market_data?.atl_change_percentage
?.usd ? (
<div
className={`ml-1.5 mt-2 text-xs ${
outputTokenInfo.market_data
?.atl_change_percentage?.usd >= 0
? 'text-th-green'
: 'text-th-red'
}`}
>
{(outputTokenInfo.market_data?.atl_change_percentage?.usd).toLocaleString(
undefined,
{
minimumFractionDigits: 0,
maximumFractionDigits: 2,
}
)}
%
</div>
) : null}
</div>
{outputTokenInfo.market_data?.atl_date?.usd ? (
<div className="text-th-fgd-2 text-xs">
{dayjs(
outputTokenInfo.market_data.atl_date.usd
).fromNow()}
</div>
) : null}
</div>
) : null}
</div>
{topHolders?.outputHolders ? (
<div className="pt-4">
<div className="font-bold m-1 pb-3 text-th-fgd-1 text-base">
{t('swap:top-ten')}
</div>
{topHolders.outputHolders.map((holder) => (
<a
className="border-t border-th-bkg-4 default transition flex justify-between mx-1 px-2 py-2.5 text-th-fgd-3 hover:bg-th-bkg-2"
href={`https://explorer.solana.com/address/${holder.owner}`}
target="_blank"
rel="noopener noreferrer"
key={holder.owner}
>
<div className="text-th-fgd-3">
{holder.owner.slice(0, 5) +
'…' +
holder.owner.slice(-5)}
</div>
<div className="flex items-center">
<div className="text-th-fgd-1">
{numberFormatter.format(
holder.amount / Math.pow(10, holder.decimals)
)}
</div>
<ExternalLinkIcon className="h-4 ml-2 w-4" />
</div>
</a>
))}
) : null}
</div>
) : null}
</div>
</Disclosure.Panel>
</>
)}
</Disclosure>
</div>
) : (
<div className="bg-th-bkg-3 mt-3 p-4 rounded-md text-center text-th-fgd-3">
{t('swap:output-info-unavailable')}
</div>
)}
{topHolders?.inputHolders ? (
<div className="pt-4">
<div className="font-bold m-1 pb-3 text-th-fgd-1 text-base">
{t('swap:top-ten')}
</div>
{topHolders.inputHolders.map((holder) => (
<a
className="border-t border-th-bkg-4 default transition flex justify-between mx-1 px-2 py-2.5 text-th-fgd-3 hover:bg-th-bkg-2"
href={`https://explorer.solana.com/address/${holder.owner}`}
target="_blank"
rel="noopener noreferrer"
key={holder.owner}
>
<div className="text-th-fgd-3">
{holder.owner.slice(0, 5) +
'…' +
holder.owner.slice(-5)}
</div>
<div className="flex items-center">
<div className="text-th-fgd-1">
{numberFormatter.format(
holder.amount / Math.pow(10, holder.decimals)
)}
</div>
<ExternalLinkIcon className="h-4 ml-2 w-4" />
</div>
</a>
))}
</div>
) : null}
</div>
}
/>
</div>
) : (
<div className="bg-th-bkg-3 mt-3 p-4 rounded-md text-center text-th-fgd-3">
{t('swap:output-info-unavailable')}
</div>
)}
</div>
</div>
)
}

View File

@ -1,15 +1,14 @@
import { useEffect, useState } from 'react'
import { PublicKey } from '@solana/web3.js'
import { Disclosure } from '@headlessui/react'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { ChevronDownIcon } from '@heroicons/react/solid'
import ButtonGroup from './ButtonGroup'
import { numberCompacter, numberFormatter } from './SwapTokenInfo'
import Button, { IconButton } from './Button'
import Input from './Input'
import { SearchIcon, XIcon } from '@heroicons/react/outline'
import { useTranslation } from 'next-i18next'
import { ExpandableRow } from './TableElements'
const filterByVals = ['change-percent', '24h-volume']
const timeFrameVals = ['24h', '7d', '30d']
@ -155,26 +154,17 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
<div className="animate-pulse bg-th-bkg-3 h-12 rounded-md w-full" />
</div>
) : filteredTokenInsights.length > 0 ? (
filteredTokenInsights.map((insight) => {
const jupToken = jupiterTokens.find(
(t) => t?.extensions?.coingeckoId === insight.id
)
return (
<Disclosure key={insight.id}>
{({ open }) => (
<>
<div
className={`border-b default-transition flex items-center p-2 hover:bg-th-bkg-2 ${
open
? 'bg-th-bkg-2 border-transparent'
: 'border-th-bkg-4'
}`}
>
<Disclosure.Button
className="flex font-normal items-center justify-between text-th-fgd-1 w-full"
key={insight.symbol}
>
<div className="flex items-center space-x-3">
<div className="border-b border-th-bkg-4">
{filteredTokenInsights.map((insight) => {
const jupToken = jupiterTokens.find(
(t) => t?.extensions?.coingeckoId === insight.id
)
return (
<>
<ExpandableRow
buttonTemplate={
<div className="flex items-center w-full">
<div className="flex items-center space-x-3 w-1/2">
<div
className={`min-w-[48px] text-xs ${
timeframe === timeFrameVals[0] //timeframe 24h
@ -233,7 +223,7 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
</div>
</div>
</div>
<div className="flex items-center pl-2 space-x-3 text-right text-xs">
<div className="flex items-center justify-end pl-2 space-x-3 text-right text-xs w-1/2">
<div>
<div className="mb-[4px] text-th-fgd-4">
{t('price')}
@ -259,28 +249,21 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
: '?'}
</div>
</div>
<ChevronDownIcon
className={`default-transition h-5 text-th-fgd-3 w-5 ${
open
? 'transform rotate-180'
: 'transform rotate-360'
}`}
/>
</div>
</Disclosure.Button>
<Button
className="hidden lg:block ml-3 pl-3 pr-3 text-xs"
onClick={() =>
setOutputToken({
...formState,
outputMint: new PublicKey(jupToken.address),
})
}
>
{t('buy')}
</Button>
</div>
<Disclosure.Panel className="bg-th-bkg-2 border-b border-th-bkg-4 px-2 pb-2">
<Button
className="hidden lg:block ml-3 pl-3 pr-3 text-xs"
onClick={() =>
setOutputToken({
...formState,
outputMint: new PublicKey(jupToken.address),
})
}
>
{t('buy')}
</Button>
</div>
}
panelTemplate={
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 grid-flow-row">
{insight.market_cap_rank ? (
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
@ -383,23 +366,12 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
</div>
) : null}
</div>
<Button
className="block lg:hidden my-2 text-xs w-full"
onClick={() =>
setOutputToken({
...formState,
outputMint: new PublicKey(jupToken.address),
})
}
>
{t('buy')}
</Button>
</Disclosure.Panel>
</>
)}
</Disclosure>
)
})
}
/>
</>
)
})}
</div>
) : (
<div className="bg-th-bkg-3 mt-3 p-4 rounded-md text-center text-th-fgd-3">
{t('swap:no-tokens-found')}

View File

@ -1,6 +1,6 @@
import { Disclosure } from '@headlessui/react'
import { Disclosure, Transition } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/solid'
import { ReactNode } from 'react'
import { Fragment, ReactNode } from 'react'
export const Table = ({ children }) => (
<table className="min-w-full">{children}</table>
@ -32,14 +32,12 @@ export const Td = ({
type ExpandableRowProps = {
buttonTemplate: React.ReactNode
index: number
panelTemplate: React.ReactNode
rounded?: boolean
}
export const ExpandableRow = ({
buttonTemplate,
index,
panelTemplate,
rounded,
}: ExpandableRowProps) => {
@ -48,9 +46,7 @@ export const ExpandableRow = ({
{({ open }) => (
<>
<Disclosure.Button
className={`${
index % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-4`
} default-transition flex items-center justify-between font-normal p-4 text-th-fgd-1 w-full hover:filter hover:brightness-90 focus:outline-none ${
className={`border-t border-th-bkg-4 default-transition flex items-center justify-between font-normal p-4 text-th-fgd-1 w-full hover:bg-th-bkg-4 focus:outline-none ${
rounded
? open
? 'rounded-b-none'
@ -59,29 +55,29 @@ export const ExpandableRow = ({
}`}
>
{buttonTemplate}
<div className="flex items-center justify-end pl-5">
<div className="flex items-center justify-end pl-4">
<ChevronDownIcon
className={`${
open ? 'transform rotate-180' : 'transform rotate-360'
} default-transition h-5 flex-shrink-0 w-5 text-th-primary`}
} default-transition h-5 flex-shrink-0 w-5 text-th-fgd-1`}
/>
</div>
</Disclosure.Button>
<Disclosure.Panel
className={`${
index % 2 === 0
? `bg-[rgba(255,255,255,0.03)]`
: `bg-[rgba(255,255,255,0.07)]`
} px-4 ${
rounded
? open
? 'rounded-b-md'
: 'rounded-none'
: 'rounded-none'
}`}
<Transition
appear={true}
show={open}
as={Fragment}
enter="transition-all ease-in duration-200"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition ease-out"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="py-4">{panelTemplate}</div>
</Disclosure.Panel>
<Disclosure.Panel>
<div className="pb-4 pt-2 px-4">{panelTemplate}</div>
</Disclosure.Panel>
</Transition>
</>
)}
</Disclosure>

View File

@ -12,8 +12,6 @@ import {
ExclamationCircleIcon,
InformationCircleIcon,
} from '@heroicons/react/outline'
import { ChevronDownIcon } from '@heroicons/react/solid'
import { Disclosure } from '@headlessui/react'
import Select from './Select'
import { withdraw } from '../utils/mango'
import {
@ -24,6 +22,7 @@ import {
} from '@blockworks-foundation/mango-client'
import { notify } from '../utils/notifications'
import { useTranslation } from 'next-i18next'
import { ExpandableRow } from './TableElements'
interface WithdrawModalProps {
onClose: () => void
@ -450,85 +449,79 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
)} ${withdrawTokenSymbol}`}</div>
) : null}
</div>
<Disclosure>
{({ open }) => (
<>
<Disclosure.Button
className={`border border-th-bkg-4 default-transition font-normal mt-4 pl-3 pr-2 py-2.5 ${
open ? 'rounded-b-none' : 'rounded-md'
} text-th-fgd-1 w-full hover:border-th-fgd-4 focus:outline-none`}
>
<div className="flex items-center justify-between">
<div className="flex items-center">
<span className="flex h-2 w-2 mr-2.5 relative">
<span
className={`animate-ping absolute inline-flex h-full w-full rounded-full ${getAccountStatusColor(
simulation.initHealthRatio,
false,
true
)} opacity-75`}
></span>
<span
className={`relative inline-flex rounded-full h-2 w-2 ${getAccountStatusColor(
simulation.initHealthRatio,
false,
true
)}`}
></span>
</span>
{t('health-check')}
<Tooltip content={t('tooltip-after-withdrawal')}>
<InformationCircleIcon
className={`h-5 w-5 ml-2 text-th-primary cursor-help`}
/>
</Tooltip>
</div>
<ChevronDownIcon
className={`default-transition h-5 w-5 mr-1 ${
open ? 'transform rotate-180' : 'transform rotate-360'
}`}
/>
<div className="border-b border-th-bkg-4 pt-4">
<ExpandableRow
buttonTemplate={
<div className="flex items-center justify-between">
<div className="flex items-center">
<span className="flex h-2 w-2 mr-2.5 relative">
<span
className={`animate-ping absolute inline-flex h-full w-full rounded-full ${getAccountStatusColor(
simulation.initHealthRatio,
false,
true
)} opacity-75`}
></span>
<span
className={`relative inline-flex rounded-full h-2 w-2 ${getAccountStatusColor(
simulation.initHealthRatio,
false,
true
)}`}
></span>
</span>
{t('health-check')}
<Tooltip content={t('tooltip-after-withdrawal')}>
<InformationCircleIcon
className={`h-5 w-5 ml-2 text-th-primary cursor-help`}
/>
</Tooltip>
</div>
</Disclosure.Button>
<Disclosure.Panel
className={`border border-th-bkg-4 border-t-0 p-4 rounded-b-md`}
>
{simulation ? (
<div>
<div className="flex justify-between pb-2">
<p className="mb-0">{t('account-value')}</p>
<div className="text-th-fgd-1">
${simulation.equity.toFixed(2)}
</div>
</div>
<div className="flex justify-between pb-2">
<p className="mb-0">{t('account-risk')}</p>
<div className="text-th-fgd-1">
{getAccountStatusColor(
simulation.initHealthRatio,
true
)}
</div>
</div>
<div className="flex justify-between pb-2">
<p className="mb-0">{t('leverage')}</p>
<div className="text-th-fgd-1">
{simulation.leverage.toFixed(2)}x
</div>
</div>
<div className="flex justify-between">
<p className="mb-0">{t('borrow-value')}</p>
<div className="text-th-fgd-1">
${simulation.liabsVal.toFixed(2)}
</div>
</div>
}
panelTemplate={
simulation ? (
<div>
<div className="flex justify-between pb-2">
<p className="mb-0">{t('account-value')}</p>
<div className="text-th-fgd-1">
$
{simulation.equity.toLocaleString(undefined, {
maximumFractionDigits: 2,
})}
</div>
</div>
) : null}
</Disclosure.Panel>
</>
)}
</Disclosure>
<div className="flex justify-between pb-2">
<p className="mb-0">{t('account-risk')}</p>
<div className="text-th-fgd-1">
{getAccountStatusColor(
simulation.initHealthRatio,
true
)}
</div>
</div>
<div className="flex justify-between pb-2">
<p className="mb-0">{t('leverage')}</p>
<div className="text-th-fgd-1">
{simulation.leverage.toFixed(2)}x
</div>
</div>
<div className="flex justify-between">
<p className="mb-0">{t('borrow-value')}</p>
<div className="text-th-fgd-1">
$
{simulation.liabsVal.toLocaleString(undefined, {
maximumFractionDigits: 2,
})}
</div>
</div>
</div>
) : null
}
/>
</div>
<div className={`mt-6 flex flex-col items-center`}>
<Button
onClick={handleWithdraw}

View File

@ -212,7 +212,6 @@ export default function AccountBorrows() {
</div>
}
key={`${asset.symbol}${i}`}
index={i}
panelTemplate={
<>
<div className="grid grid-cols-2 grid-flow-row gap-4 pb-4">
@ -454,7 +453,6 @@ export default function AccountBorrows() {
</div>
}
key={`${token.symbol}${i}`}
index={i}
panelTemplate={
<div className="grid grid-cols-2 grid-flow-row gap-4">
<div className="text-left">
@ -467,27 +465,29 @@ export default function AccountBorrows() {
.toNumber()
)}
</div>
<div className="text-left">
<div className="pb-0.5 text-th-fgd-3 text-xs">
{t('max-borrow')}
{mangoAccount ? (
<div className="text-left">
<div className="pb-0.5 text-th-fgd-3 text-xs">
{t('max-borrow')}
</div>
{mangoAccount
? mangoAccount
.getMaxWithBorrowForToken(
mangoGroup,
mangoCache,
tokenIndex
)
.mul(I80F48.fromString('0.995'))
.toNumber()
.toLocaleString(undefined, {
minimumFractionDigits:
tokenPrecision[token.symbol],
maximumFractionDigits:
tokenPrecision[token.symbol],
})
: null}
</div>
{mangoAccount
? mangoAccount
.getMaxWithBorrowForToken(
mangoGroup,
mangoCache,
tokenIndex
)
.mul(I80F48.fromString('0.995'))
.toNumber()
.toLocaleString(undefined, {
minimumFractionDigits:
tokenPrecision[token.symbol],
maximumFractionDigits:
tokenPrecision[token.symbol],
})
: null}
</div>
) : null}
<div className="text-left">
<div className="pb-0.5 text-th-fgd-3 text-xs">
{t('liquidity')}
@ -505,7 +505,7 @@ export default function AccountBorrows() {
</div>
<Button
onClick={() => handleShowBorrow(token.symbol)}
className="text-xs pt-0 pb-0 h-8 w-full"
className="col-span-2 text-xs pt-0 pb-0 h-8"
disabled={
!connected || loadingMangoAccount || !canWithdraw
}

View File

@ -10,7 +10,7 @@ const MobileTableHeader = ({
colThreeHeader = null,
}: MobileTableHeaderProps) => {
return (
<div className="flex justify-between pb-2 pl-4 pr-14 text-th-fgd-3 text-xs">
<div className="flex justify-between pb-2 pl-4 pr-12 text-th-fgd-3 text-xs">
<div>{colOneHeader}</div>
<div>{colTwoHeader}</div>
{colThreeHeader ? <div>{colThreeHeader}</div> : null}