use disclosure for mobile expanding rows

This commit is contained in:
saml33 2023-03-18 23:15:01 +11:00
parent 91f530c197
commit ed157d7d09
3 changed files with 326 additions and 315 deletions

View File

@ -1,5 +1,5 @@
import { Bank, MangoAccount } from '@blockworks-foundation/mango-v4'
import { Transition } from '@headlessui/react'
import { Disclosure, Transition } from '@headlessui/react'
import {
ChevronDownIcon,
EllipsisHorizontalIcon,
@ -9,7 +9,7 @@ import { useWallet } from '@solana/wallet-adapter-react'
import { useTranslation } from 'next-i18next'
import Image from 'next/legacy/image'
import { useRouter } from 'next/router'
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useViewport } from '../hooks/useViewport'
import mangoStore from '@store/mangoStore'
import { breakpoints } from '../utils/theme'
@ -215,7 +215,7 @@ const TokenList = () => {
</tbody>
</Table>
) : (
<div>
<div className="border-b border-th-bkg-3">
{filteredBanks.map((b) => {
return <MobileTokenListItem key={b.bank.name} bank={b} />
})}
@ -229,7 +229,6 @@ export default TokenList
const MobileTokenListItem = ({ bank }: { bank: BankWithBalance }) => {
const { t } = useTranslation(['common', 'token'])
const [showTokenDetails, setShowTokenDetails] = useState(false)
const { mangoTokens } = useJupiterMints()
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
const { mangoAccount } = useMangoAccount()
@ -240,7 +239,7 @@ const MobileTokenListItem = ({ bank }: { bank: BankWithBalance }) => {
const mint = tokenBank.mint
const symbol = tokenBank.name
let logoURI
let logoURI: string | undefined
if (mangoTokens?.length) {
logoURI = mangoTokens.find(
(t) => t.address === tokenBank.mint.toString()
@ -266,98 +265,106 @@ const MobileTokenListItem = ({ bank }: { bank: BankWithBalance }) => {
const unsettled = spotBalances[mint.toString()]?.unsettled || 0
return (
<button
key={symbol}
className="w-full border-b border-th-bkg-3 px-6 py-4 text-left focus:outline-none"
onClick={() => setShowTokenDetails((prev) => !prev)}
>
<div className="flex items-center justify-between">
<div className="flex items-start">
<div className="mr-2.5 mt-0.5 flex flex-shrink-0 items-center">
{logoURI ? (
<Image alt="" width="24" height="24" src={logoURI} />
) : (
<QuestionMarkCircleIcon className="h-7 w-7 text-th-fgd-3" />
)}
</div>
<div>
<p className="mb-0.5 leading-none text-th-fgd-1">{symbol}</p>
<p className="font-mono text-sm text-th-fgd-2">
<FormatNumericValue
value={tokenBalance}
decimals={tokenBank.mintDecimals}
/>
<p className="mt-0.5 text-sm leading-none text-th-fgd-4">
<FormatNumericValue
value={mangoAccount ? tokenBalance * tokenBank.uiPrice : 0}
decimals={2}
isUsd
<Disclosure>
{({ open }) => (
<>
<Disclosure.Button
className={`w-full border-t border-th-bkg-3 px-6 py-4 text-left first:border-t-0 focus:outline-none`}
>
<div className="flex items-center justify-between">
<div className="flex items-start">
<div className="mr-2.5 mt-0.5 flex flex-shrink-0 items-center">
{logoURI ? (
<Image alt="" width="24" height="24" src={logoURI} />
) : (
<QuestionMarkCircleIcon className="h-7 w-7 text-th-fgd-3" />
)}
</div>
<div>
<p className="mb-0.5 leading-none text-th-fgd-1">{symbol}</p>
<p className="font-mono text-sm text-th-fgd-2">
<FormatNumericValue
value={tokenBalance}
decimals={tokenBank.mintDecimals}
/>
<span className="mt-0.5 block text-sm leading-none text-th-fgd-4">
<FormatNumericValue
value={
mangoAccount ? tokenBalance * tokenBank.uiPrice : 0
}
decimals={2}
isUsd
/>
</span>
</p>
</div>
</div>
<div className="flex items-center space-x-3">
<ActionsMenu bank={tokenBank} mangoAccount={mangoAccount} />
<ChevronDownIcon
className={`${
open ? 'rotate-180' : 'rotate-360'
} h-6 w-6 flex-shrink-0 text-th-fgd-1`}
/>
</p>
</p>
</div>
</div>
<div className="flex items-center space-x-3">
<ActionsMenu bank={tokenBank} mangoAccount={mangoAccount} />
<ChevronDownIcon
className={`${
showTokenDetails ? 'rotate-180' : 'rotate-360'
} h-6 w-6 flex-shrink-0 text-th-fgd-1`}
/>
</div>
</div>
<Transition
appear={true}
show={showTokenDetails}
as={Fragment}
enter="transition ease-in duration-200"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition ease-out"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="mt-4 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pt-4">
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('trade:in-orders')}</p>
<BankAmountWithValue amount={inOrders} bank={tokenBank} />
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('trade:unsettled')}</p>
<BankAmountWithValue amount={unsettled} bank={tokenBank} />
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('interest-earned-paid')}</p>
<BankAmountWithValue
amount={interestAmount}
bank={tokenBank}
value={interestValue}
/>
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('rates')}</p>
<p className="space-x-2 font-mono">
<span className="text-th-up">
<FormatNumericValue
value={tokenBank.getDepositRateUi()}
decimals={2}
/>
%
</span>
<span className="font-normal text-th-fgd-4">|</span>
<span className="text-th-down">
<FormatNumericValue
value={tokenBank.getBorrowRateUi()}
decimals={2}
roundUp
/>
%
</span>
</p>
</div>
</div>
</Transition>
</button>
</div>
</div>
</Disclosure.Button>
<Transition
enter="transition ease-in duration-200"
enterFrom="opacity-0"
enterTo="opacity-100"
>
<Disclosure.Panel>
<div className="mx-6 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pt-4 pb-4">
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">
{t('trade:in-orders')}
</p>
<BankAmountWithValue amount={inOrders} bank={tokenBank} />
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">
{t('trade:unsettled')}
</p>
<BankAmountWithValue amount={unsettled} bank={tokenBank} />
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">
{t('interest-earned-paid')}
</p>
<BankAmountWithValue
amount={interestAmount}
bank={tokenBank}
value={interestValue}
/>
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('rates')}</p>
<p className="space-x-2 font-mono">
<span className="text-th-up">
<FormatNumericValue
value={tokenBank.getDepositRateUi()}
decimals={2}
/>
%
</span>
<span className="font-normal text-th-fgd-4">|</span>
<span className="text-th-down">
<FormatNumericValue
value={tokenBank.getBorrowRateUi()}
decimals={2}
roundUp
/>
%
</span>
</p>
</div>
</div>
</Disclosure.Panel>
</Transition>
</>
)}
</Disclosure>
)
}

View File

@ -11,7 +11,7 @@ import { useViewport } from 'hooks/useViewport'
import { useTranslation } from 'next-i18next'
import Image from 'next/legacy/image'
import { useRouter } from 'next/router'
import { Fragment, useCallback, useMemo, useState } from 'react'
import { useCallback, useMemo } from 'react'
import {
floorToDecimal,
formatNumericValue,
@ -30,11 +30,10 @@ import useBanksWithBalances, {
BankWithBalance,
} from 'hooks/useBanksWithBalances'
import useUnownedAccount from 'hooks/useUnownedAccount'
import { Transition } from '@headlessui/react'
import { Disclosure, Transition } from '@headlessui/react'
const BalancesTable = () => {
const { t } = useTranslation(['common', 'trade'])
const [showTokenDetails, setShowTokenDetails] = useState('')
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
const { mangoTokens } = useJupiterMints()
@ -57,14 +56,6 @@ const BalancesTable = () => {
return []
}, [banks])
const toggleTokenDetails = (token: string) => {
if (showTokenDetails === token) {
setShowTokenDetails('')
} else {
setShowTokenDetails(token)
}
}
return filteredBanks.length ? (
showTableView ? (
<Table>
@ -127,11 +118,11 @@ const BalancesTable = () => {
</tbody>
</Table>
) : (
<>
<div className="border-b border-th-bkg-3">
{filteredBanks.map((b, i) => {
const bank = b.bank
let logoURI
let logoURI: string | undefined
if (mangoTokens.length) {
logoURI = mangoTokens.find(
(t) => t.address === bank.mint.toString()
@ -142,69 +133,78 @@ const BalancesTable = () => {
const unsettled = spotBalances[bank.mint.toString()]?.unsettled || 0
return (
<button
className="w-full border-b border-th-bkg-3 px-6 py-4 text-left focus:outline-none"
key={bank.name}
onClick={() => toggleTokenDetails(bank.name)}
>
<div className="flex items-center justify-between">
<div className="flex items-start">
<div className="mr-2.5 mt-0.5 flex flex-shrink-0 items-center">
{logoURI ? (
<Image alt="" width="24" height="24" src={logoURI} />
) : (
<QuestionMarkCircleIcon className="h-7 w-7 text-th-fgd-3" />
)}
</div>
<div>
<p className="mb-0.5 leading-none text-th-fgd-1">
{bank.name}
</p>
<Balance bank={b} />
<p className="mt-0.5 text-sm leading-none text-th-fgd-4">
<FormatNumericValue
value={mangoAccount ? b.balance * bank.uiPrice : 0}
isUsd
<Disclosure key={bank.name}>
{({ open }) => (
<>
<Disclosure.Button
className={`w-full border-t border-th-bkg-3 px-6 py-4 text-left focus:outline-none ${
i === 0 ? 'border-t-0' : ''
}`}
>
<div className="flex items-center justify-between">
<div className="flex items-start">
<div className="mr-2.5 mt-0.5 flex flex-shrink-0 items-center">
{logoURI ? (
<Image
alt=""
width="24"
height="24"
src={logoURI}
/>
) : (
<QuestionMarkCircleIcon className="h-7 w-7 text-th-fgd-3" />
)}
</div>
<div>
<p className="mb-0.5 leading-none text-th-fgd-1">
{bank.name}
</p>
<Balance bank={b} />
<p className="mt-0.5 text-sm leading-none text-th-fgd-4">
<FormatNumericValue
value={
mangoAccount ? b.balance * bank.uiPrice : 0
}
isUsd
/>
</p>
</div>
</div>
<ChevronDownIcon
className={`${
open ? 'rotate-180' : 'rotate-360'
} h-6 w-6 flex-shrink-0 text-th-fgd-1`}
/>
</p>
</div>
</div>
<ChevronDownIcon
className={`${
showTokenDetails === bank.name ? 'rotate-180' : 'rotate-360'
} h-6 w-6 flex-shrink-0 text-th-fgd-1`}
/>
</div>
<Transition
appear={true}
show={showTokenDetails === filteredBanks[i].bank.name}
as={Fragment}
enter="transition ease-in duration-200"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition ease-out"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="mt-4 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pt-4">
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">
{t('trade:in-orders')}
</p>
<BankAmountWithValue amount={inOrders} bank={bank} />
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">
{t('trade:unsettled')}
</p>
<BankAmountWithValue amount={unsettled} bank={bank} />
</div>
</div>
</Transition>
</button>
</div>
</Disclosure.Button>
<Transition
enter="transition ease-in duration-200"
enterFrom="opacity-0"
enterTo="opacity-100"
>
<Disclosure.Panel>
<div className="mx-6 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pt-4 pb-4">
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">
{t('trade:in-orders')}
</p>
<BankAmountWithValue amount={inOrders} bank={bank} />
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">
{t('trade:unsettled')}
</p>
<BankAmountWithValue amount={unsettled} bank={bank} />
</div>
</div>
</Disclosure.Panel>
</Transition>
</>
)}
</Disclosure>
)
})}
</>
</div>
)
) : mangoAccountAddress || connected ? (
<div className="flex flex-col items-center p-8">

View File

@ -1,4 +1,4 @@
import { Transition } from '@headlessui/react'
import { Disclosure, Transition } from '@headlessui/react'
import {
ChevronDownIcon,
ChevronRightIcon,
@ -6,7 +6,7 @@ import {
} from '@heroicons/react/20/solid'
import { useTranslation } from 'next-i18next'
import Image from 'next/legacy/image'
import { Fragment, useEffect, useState } from 'react'
import { useEffect } from 'react'
import { useViewport } from '../../hooks/useViewport'
import { breakpoints } from '../../utils/theme'
import { LinkButton } from '../shared/Button'
@ -27,7 +27,6 @@ const TokenStats = () => {
const { t } = useTranslation(['common', 'token'])
const actions = mangoStore.getState().actions
const initialStatsLoad = mangoStore((s) => s.tokenStats.initialLoad)
const [showTokenDetails, setShowTokenDetails] = useState('')
const { group } = useMangoGroup()
const { mangoTokens } = useJupiterMints()
const { width } = useViewport()
@ -41,12 +40,6 @@ const TokenStats = () => {
}
}, [group])
const handleShowTokenDetails = (bank: Bank) => {
showTokenDetails === bank.name
? setShowTokenDetails('')
: setShowTokenDetails(bank.name)
}
const goToTokenPage = (bank: Bank) => {
router.push(`/token/${bank.name}`, undefined, { shallow: true })
}
@ -244,9 +237,9 @@ const TokenStats = () => {
</div>
) : (
<div className="border-b border-th-bkg-3">
{banks.map((b) => {
{banks.map((b, i) => {
const bank = b.bank
let logoURI
let logoURI: string | undefined
if (mangoTokens?.length) {
logoURI = mangoTokens.find(
(t) => t.address === bank.mint.toString()
@ -266,142 +259,153 @@ const TokenStats = () => {
bank.mintDecimals
)
return (
<>
<button
key={bank.name}
className="w-full border-t border-th-bkg-3 px-6 py-4 text-left first:border-t-0"
onClick={() => handleShowTokenDetails(bank)}
>
<div className="flex items-center justify-between">
<div className="flex items-center">
<div className="mr-2.5 flex flex-shrink-0 items-center">
{logoURI ? (
<Image alt="" width="24" height="24" src={logoURI} />
) : (
<QuestionMarkCircleIcon className="h-7 w-7 text-th-fgd-3" />
)}
<Disclosure key={bank.name}>
{({ open }) => (
<>
<Disclosure.Button
className={`w-full border-t border-th-bkg-3 px-6 py-4 text-left focus:outline-none ${
i === 0 ? 'border-t-0' : ''
}`}
>
<div className="flex items-center justify-between">
<div className="flex items-center">
<div className="mr-2.5 flex flex-shrink-0 items-center">
{logoURI ? (
<Image
alt=""
width="24"
height="24"
src={logoURI}
/>
) : (
<QuestionMarkCircleIcon className="h-7 w-7 text-th-fgd-3" />
)}
</div>
<p className="text-th-fgd-1">{bank.name}</p>
</div>
<ChevronDownIcon
className={`${
open ? 'rotate-180' : 'rotate-360'
} h-6 w-6 flex-shrink-0 text-th-fgd-3`}
/>
</div>
<p className="text-th-fgd-1">{bank.name}</p>
</div>
<ChevronDownIcon
className={`${
showTokenDetails === bank.name
? 'rotate-180'
: 'rotate-360'
} h-6 w-6 flex-shrink-0 text-th-fgd-3`}
/>
</div>
</button>
<Transition
appear={true}
show={showTokenDetails === bank.name}
as={Fragment}
enter="transition ease-in duration-200"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition ease-out"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="mx-6 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pb-4 pt-4">
<div className="col-span-1">
<p className="mb-0.5 text-xs">{t('total-deposits')}</p>
<BankAmountWithValue
amount={deposits.toFixed(4)}
bank={bank}
fixDecimals={false}
/>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-xs">{t('total-borrows')}</p>
<BankAmountWithValue
amount={borrows.toFixed(4)}
bank={bank}
fixDecimals={false}
/>
</div>
<div className="col-span-1">
<Tooltip content="The amount available to borrow">
<p className="tooltip-underline text-xs">
{t('available')}
</p>
</Tooltip>
<BankAmountWithValue
amount={available}
bank={bank}
fixDecimals={false}
/>
</div>
<div className="col-span-1">
<Tooltip content="Fees collected for loan originations">
<p className="tooltip-underline text-xs">{t('fees')}</p>
</Tooltip>
<BankAmountWithValue
amount={feesEarned}
bank={bank}
fixDecimals={false}
/>
</div>
<div className="col-span-1">
<p className="text-xs">{t('rates')}</p>
<p className="space-x-2">
<span className="font-mono text-th-up">
<FormatNumericValue
value={bank.getDepositRateUi()}
decimals={2}
/>
%
</span>
<span className="font-normal text-th-fgd-4">|</span>
<span className="font-mono text-th-down">
<FormatNumericValue
value={bank.getBorrowRateUi()}
decimals={2}
/>
%
</span>
</p>
</div>
<div className="col-span-1">
<p className="text-xs">{t('utilization')}</p>
<p className="font-mono text-th-fgd-1">
{bank.uiDeposits() > 0
? (
(bank.uiBorrows() / bank.uiDeposits()) *
100
).toFixed(1)
: '0.0'}
%
</p>
</div>
<div className="col-span-1">
<Tooltip content={t('asset-liability-weight-desc')}>
<p className="tooltip-underline text-xs text-th-fgd-3">
{t('asset-liability-weight')}
</p>
</Tooltip>
<div className="flex space-x-1.5 text-right font-mono">
<p className="text-th-fgd-1">
{bank.initAssetWeight.toFixed(2)}
</p>
<span className="text-th-fgd-4">|</span>
<p className="text-th-fgd-1">
{bank.initLiabWeight.toFixed(2)}
</p>
</div>
</div>
<div className="col-span-1">
<LinkButton
className="flex items-center"
onClick={() => goToTokenPage(bank)}
>
{t('token:token-details')}
<ChevronRightIcon className="ml-2 h-5 w-5" />
</LinkButton>
</div>
</div>
</Transition>
</>
</Disclosure.Button>
<Transition
enter="transition ease-in duration-200"
enterFrom="opacity-0"
enterTo="opacity-100"
>
<Disclosure.Panel>
<div className="mx-6 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pt-4 pb-4">
<div className="col-span-1">
<p className="mb-0.5 text-xs">
{t('total-deposits')}
</p>
<BankAmountWithValue
amount={deposits.toFixed(4)}
bank={bank}
fixDecimals={false}
/>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-xs">
{t('total-borrows')}
</p>
<BankAmountWithValue
amount={borrows.toFixed(4)}
bank={bank}
fixDecimals={false}
/>
</div>
<div className="col-span-1">
<Tooltip content="The amount available to borrow">
<p className="tooltip-underline text-xs">
{t('available')}
</p>
</Tooltip>
<BankAmountWithValue
amount={available}
bank={bank}
fixDecimals={false}
/>
</div>
<div className="col-span-1">
<Tooltip content="Fees collected for loan originations">
<p className="tooltip-underline text-xs">
{t('fees')}
</p>
</Tooltip>
<BankAmountWithValue
amount={feesEarned}
bank={bank}
fixDecimals={false}
/>
</div>
<div className="col-span-1">
<p className="text-xs">{t('rates')}</p>
<p className="space-x-2">
<span className="font-mono text-th-up">
<FormatNumericValue
value={bank.getDepositRateUi()}
decimals={2}
/>
%
</span>
<span className="font-normal text-th-fgd-4">
|
</span>
<span className="font-mono text-th-down">
<FormatNumericValue
value={bank.getBorrowRateUi()}
decimals={2}
/>
%
</span>
</p>
</div>
<div className="col-span-1">
<p className="text-xs">{t('utilization')}</p>
<p className="font-mono text-th-fgd-1">
{bank.uiDeposits() > 0
? (
(bank.uiBorrows() / bank.uiDeposits()) *
100
).toFixed(1)
: '0.0'}
%
</p>
</div>
<div className="col-span-1">
<Tooltip content={t('asset-liability-weight-desc')}>
<p className="tooltip-underline text-xs text-th-fgd-3">
{t('asset-liability-weight')}
</p>
</Tooltip>
<div className="flex space-x-1.5 text-right font-mono">
<p className="text-th-fgd-1">
{bank.initAssetWeight.toFixed(2)}
</p>
<span className="text-th-fgd-4">|</span>
<p className="text-th-fgd-1">
{bank.initLiabWeight.toFixed(2)}
</p>
</div>
</div>
<div className="col-span-1">
<LinkButton
className="flex items-center"
onClick={() => goToTokenPage(bank)}
>
{t('token:token-details')}
<ChevronRightIcon className="ml-2 h-5 w-5" />
</LinkButton>
</div>
</div>
</Disclosure.Panel>
</Transition>
</>
)}
</Disclosure>
)
})}
</div>