add more ant styling to trade form

This commit is contained in:
Tyler Shipe 2021-04-10 17:12:15 -04:00
parent 07df84b64b
commit 3c75f8e90d
10 changed files with 376 additions and 40 deletions

View File

@ -7,7 +7,12 @@ import { nativeToUi } from '@blockworks-foundation/mango-client/lib/utils'
import useMangoStore from '../stores/useMangoStore'
import { tokenPrecision } from '../utils/index'
const AccountSelect = ({ accounts, selectedAccount, onSelectAccount }) => {
const AccountSelect = ({
accounts,
selectedAccount,
onSelectAccount,
hideBalance = false,
}) => {
const { getTokenIndex } = useMarketList()
const mintDecimals = useMangoStore((s) => s.selectedMangoGroup.mintDecimals)
const handleChange = (value) => {
@ -51,9 +56,11 @@ const AccountSelect = ({ accounts, selectedAccount, onSelectAccount }) => {
css={xw`mr-4`}
/>
{abbreviateAddress(selectedAccount?.publicKey)}
<div css={xw`ml-4 text-sm text-right flex-grow`}>
({getBalanceForAccount(selectedAccount)})
</div>
{!hideBalance ? (
<div css={xw`ml-4 text-sm text-right flex-grow`}>
({getBalanceForAccount(selectedAccount)})
</div>
) : null}
</div>
{open ? (
<ChevronUpIcon css={xw`h-5 w-5 ml-2`} />
@ -107,8 +114,10 @@ const AccountSelect = ({ accounts, selectedAccount, onSelectAccount }) => {
{abbreviateAddress(account?.publicKey)}
</div>
<div css={xw`text-sm`}>
{getBalanceForAccount(account)} (
{symbolForAccount})
{!hideBalance
? getBalanceForAccount(account)
: null}{' '}
({symbolForAccount})
</div>
</div>
</div>

View File

@ -1,14 +1,61 @@
import xw from 'xwind'
import { useBalances } from '../hooks/useBalances'
import useMangoStore from '../stores/useMangoStore'
import { settleAll } from '../utils/mango'
import useConnection from '../hooks/useConnection'
import Button from '../components/Button'
const BalancesTable = () => {
const balances = useBalances()
console.log('balances', balances)
const { programId, connection } = useConnection()
async function handleSettleAll() {
const markets = Object.values(
useMangoStore.getState().selectedMangoGroup.markets
)
const marginAccount = useMangoStore.getState().selectedMarginAccount.current
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
const wallet = useMangoStore.getState().wallet.current
try {
await settleAll(
connection,
programId,
mangoGroup,
marginAccount,
markets,
wallet
)
// notify({
// message: 'Successfully settled funds',
// type: 'info',
// });
} catch (e) {
console.warn('Error settling all:', e)
if (e.message === 'No unsettled funds') {
// notify({
// message: 'There are no unsettled funds',
// type: 'error',
// });
} else {
// notify({
// message: 'Error settling funds',
// description: e.message,
// type: 'error',
// });
}
}
}
return (
<div css={xw`flex flex-col py-6`}>
<div css={xw`-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8`}>
<div css={xw`align-middle inline-block min-w-full sm:px-6 lg:px-8`}>
{balances.length ? (
<div css={xw`text-right`}>
<Button onClick={handleSettleAll}>Settle All</Button>
</div>
) : null}
{balances.length ? (
<div
css={xw`shadow overflow-hidden border-b border-mango-dark-light sm:rounded-md`}

View File

@ -5,6 +5,7 @@ interface ButtonProps {
onClick?: (x?) => void
disabled?: boolean
className?: string
grow?: boolean
}
const Button: FunctionComponent<ButtonProps> = ({
@ -12,14 +13,17 @@ const Button: FunctionComponent<ButtonProps> = ({
onClick,
disabled = false,
className,
grow = false,
...props
}) => {
if (disabled) {
console.log('grow', grow)
return (
<button
css={[
xw`w-full px-8 py-2 border border-mango-dark-lighter bg-mango-dark-light
xw`px-8 py-2 border border-mango-dark-lighter bg-mango-dark-light
focus:outline-none`,
grow && xw`flex-grow`,
disabled && xw`cursor-not-allowed text-mango-med`,
]}
disabled={disabled}

View File

@ -145,7 +145,8 @@ const DepositModal = ({ isOpen, onClose }) => {
css={xw`ml-3`}
/>
<input
type="text"
type="number"
min="0"
css={xw`outline-none bg-mango-dark-light w-full py-4 mx-3 text-2xl text-gray-300 flex-grow`}
placeholder="0.00"
value={inputAmount}

View File

@ -11,6 +11,7 @@ import useMangoStore from '../stores/useMangoStore'
import useMarketList from '../hooks/useMarketList'
import { tokenPrecision } from '../utils/index'
import DepositModal from './DepositModal'
import WithdrawModal from './WithdrawModal'
import Button from './Button'
export default function MarginStats() {
@ -68,7 +69,7 @@ export default function MarginStats() {
Borrows
</th>
<th scope="col" css={xw`flex-auto`}>
Interest
Interest (APY)
</th>
</tr>
</thead>
@ -144,7 +145,7 @@ export default function MarginStats() {
<DepositModal isOpen={showDepositModal} onClose={handleCloseDeposit} />
)}
{showWithdrawModal && (
<DepositModal
<WithdrawModal
isOpen={showWithdrawModal}
onClose={handleCloseWithdraw}
/>

View File

@ -1,7 +1,6 @@
import { useState, useEffect, useRef } from 'react'
import { Input, Radio, Switch, Select } from 'antd'
import xw from 'xwind'
import styled from '@emotion/styled'
import useMarket from '../hooks/useMarket'
import useIpAddress from '../hooks/useIpAddress'
import useConnection from '../hooks/useConnection'
@ -12,9 +11,9 @@ import { placeAndSettle } from '../utils/mango'
import { calculateMarketPrice, getDecimalCount } from '../utils'
import FloatingElement from './FloatingElement'
import { roundToDecimal } from '../utils/index'
import useMarginAccount from '../hooks/useMarginAcccount'
import useMangoStore from '../stores/useMangoStore'
import Button from './Button'
// import TradeType from './TradeType'
export default function TradeForm({
setChangeOrderRef,
@ -24,12 +23,10 @@ export default function TradeForm({
) => void
}) {
const [side, setSide] = useState<'buy' | 'sell'>('buy')
const { baseCurrency, quoteCurrency, market } = useMarket()
const address = market?.publicKey
const { current: wallet, connected } = useMangoStore((s) => s.wallet)
const { baseCurrency, quoteCurrency, market, marketAddress } = useMarket()
const { connected } = useMangoStore((s) => s.wallet)
const { connection, cluster } = useConnection()
const { marginAccount, mangoGroup } = useMarginAccount()
const tradeForm = useMangoStore((s) => s.tradeForm)
const orderBookRef = useRef(useMangoStore.getState().market.orderBook)
@ -166,14 +163,12 @@ export default function TradeForm({
})
return
}
console.log('checking if we can call place order', {
mangoGroup,
address,
marginAccount,
market,
})
if (!mangoGroup || !address || !marginAccount || !market) return
const marginAccount = useMangoStore.getState().selectedMarginAccount.current
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
const wallet = useMangoStore.getState().wallet.current
if (!mangoGroup || !marketAddress || !marginAccount || !market) return
setSubmitting(true)
try {
@ -189,8 +184,6 @@ export default function TradeForm({
connection,
new PublicKey(IDS[cluster].mango_program_id),
mangoGroup,
// TODO:
// @ts-ignore
marginAccount,
market,
wallet,
@ -199,17 +192,17 @@ export default function TradeForm({
baseSize,
ioc ? 'ioc' : postOnly ? 'postOnly' : 'limit'
)
console.log('Successfully placed trade!')
// refreshCache(tuple('getTokenAccounts', wallet, connected))
setPrice(undefined)
onSetBaseSize(undefined)
} catch (e) {
console.warn(e)
notify({
message: 'Error placing order',
description: e.message,
type: 'error',
})
console.warn('Error placing trade:', e)
// notify({
// message: 'Error placing order',
// description: e.message,
// type: 'error',
// })
} finally {
setSubmitting(false)
}
@ -272,11 +265,7 @@ export default function TradeForm({
textAlign: 'right',
paddingBottom: 8,
}}
addonBefore={
<div style={{ width: '30px' }} css={xw`bg-mango-dark-light`}>
Price
</div>
}
addonBefore={<div style={{ width: '30px' }}>Price</div>}
suffix={
<span style={{ fontSize: 10, opacity: 0.5 }}>
{quoteCurrency}
@ -288,6 +277,7 @@ export default function TradeForm({
onChange={(e) => setPrice(parseFloat(e.target.value))}
disabled={tradeType === 'Market'}
/>
{/* <TradeType onChange={handleTradeTypeChange} value={tradeType} /> */}
<Select
style={{ width: 'calc(50% - 30px)' }}
onChange={handleTradeTypeChange}
@ -350,8 +340,11 @@ export default function TradeForm({
!connected ||
submitting
}
grow={true}
onClick={onSubmit}
css={[xw`bg-mango-green flex-grow text-mango-dark`]}
css={[
xw`text-lg font-light bg-mango-green text-mango-dark hover:bg-mango-yellow flex-grow`,
]}
>
{connected ? `Buy ${baseCurrency}` : 'CONNECT WALLET TO TRADE'}
</Button>
@ -363,8 +356,11 @@ export default function TradeForm({
!connected ||
submitting
}
grow={true}
onClick={onSubmit}
css={[xw`bg-mango-red flex-grow text-white`]}
css={[
xw`text-lg font-light bg-mango-red text-white hover:bg-mango-yellow flex-grow`,
]}
>
{connected ? `Sell ${baseCurrency}` : 'CONNECT WALLET TO TRADE'}
</Button>

63
components/TradeType.jsx Normal file
View File

@ -0,0 +1,63 @@
import xw from 'xwind'
import { Listbox, Transition } from '@headlessui/react'
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid'
const TradeType = ({ value, onChange }) => {
return (
<div css={xw`ml-4 relative inline-block -mb-1`}>
<Listbox value={value} onChange={onChange}>
{({ open }) => (
<>
<Listbox.Button
css={xw`border border-mango-dark-lighter focus:outline-none focus:ring-1 focus:ring-mango-yellow p-2 w-56`}
>
<div
css={xw`flex items-center text-lg justify-between font-light`}
>
{value}
{open ? (
<ChevronUpIcon css={xw`h-5 w-5 mr-1`} />
) : (
<ChevronDownIcon css={xw`h-5 w-5 mr-1`} />
)}
</div>
</Listbox.Button>
<Transition
show={open}
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
<Listbox.Options
static
css={xw`z-20 p-1 absolute left-0 w-56 mt-1 bg-mango-dark-light origin-top-left divide-y divide-mango-dark-lighter shadow-lg outline-none`}
>
<div css={xw`opacity-50 p-2`}>Markets</div>
{['Limit', 'Market'].map((type) => (
<Listbox.Option key={type} value={name}>
{({ selected }) => (
<div
css={[
xw`p-2 text-base hover:bg-mango-dark-lighter hover:cursor-pointer tracking-wider font-light`,
selected &&
xw`text-mango-yellow bg-mango-dark-lighter`,
]}
>
{name}
</div>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</>
)}
</Listbox>
</div>
)
}
export default TradeType

View File

@ -0,0 +1,150 @@
import React, { useMemo, useState } from 'react'
import xw from 'xwind'
// import { nativeToUi } from '@blockworks-foundation/mango-client/lib/utils'
import Modal from './Modal'
import AccountSelect from './AccountSelect'
import useMangoStore from '../stores/useMangoStore'
import useMarketList from '../hooks/useMarketList'
import { getSymbolForTokenMintAddress, tokenPrecision } from '../utils/index'
import useConnection from '../hooks/useConnection'
import { withdraw } from '../utils/mango'
import Loading from './Loading'
import Button from './Button'
const WithdrawModal = ({ isOpen, onClose }) => {
const [inputAmount, setInputAmount] = useState('')
const [submitting, setSubmitting] = useState(false)
const { symbols } = useMarketList()
const { getTokenIndex } = useMarketList()
const { connection, programId } = useConnection()
const walletAccounts = useMangoStore((s) => s.wallet.balances)
const actions = useMangoStore((s) => s.actions)
const depositAccounts = useMemo(
() =>
walletAccounts.filter((acc) =>
Object.values(symbols).includes(acc.account.mint.toString())
),
[symbols, walletAccounts]
)
const [selectedAccount, setSelectedAccount] = useState(depositAccounts[0])
const withdrawDisabled = Number(inputAmount) <= 0
const setMaxForSelectedAccount = () => {
const marginAccount = useMangoStore.getState().selectedMarginAccount.current
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
const mintAddress = selectedAccount?.account.mint.toString()
const tokenIndex = getTokenIndex(mintAddress)
const symbol = getSymbolForTokenMintAddress(mintAddress)
const max = marginAccount.getUiDeposit(mangoGroup, tokenIndex)
setInputAmount(max.toFixed(tokenPrecision[symbol]))
}
const handleWithdraw = () => {
setSubmitting(true)
const marginAccount = useMangoStore.getState().selectedMarginAccount.current
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
const wallet = useMangoStore.getState().wallet.current
if (marginAccount && mangoGroup) {
console.log('withdrawing')
withdraw(
connection,
programId,
mangoGroup,
marginAccount,
wallet,
selectedAccount.account.mint,
selectedAccount.publicKey,
Number(inputAmount)
)
.then((transSig: string) => {
actions.fetchWalletBalances()
setSubmitting(false)
console.log('Successfull withrawal:', transSig)
onClose()
})
.catch((err) => {
setSubmitting(false)
console.warn('Error withdrawing:', err)
onClose()
})
}
}
return (
<Modal isOpen={isOpen} onClose={onClose}>
<Modal.Header>
<div css={xw`text-mango-med-light flex-shrink invisible`}>X</div>
<div
css={xw`text-mango-med-light flex-grow text-center flex items-center justify-center`}
>
<div css={xw`flex-initial`}>Select: </div>
<div css={xw`ml-4 flex-grow`}>
<AccountSelect
hideBalance
accounts={depositAccounts}
selectedAccount={selectedAccount}
onSelectAccount={setSelectedAccount}
/>
</div>
</div>
<div css={xw`text-mango-med-light flex-shrink ml-6 mr-2 text-lg`}>
<button onClick={onClose} css={xw`hover:text-mango-yellow`}>
<svg
viewBox="64 64 896 896"
focusable="false"
data-icon="close"
width="1em"
height="1em"
fill="currentColor"
aria-hidden="true"
>
<path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
</svg>
</button>
</div>
</Modal.Header>
<div css={xw`pb-6 px-8`}>
<div css={xw`mt-3 text-center sm:mt-5`}>
<div css={xw`mt-6 bg-mango-dark-light rounded-md flex items-center`}>
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${getSymbolForTokenMintAddress(
selectedAccount?.account?.mint.toString()
).toLowerCase()}.svg`}
css={xw`ml-3`}
/>
<input
type="number"
min="0"
css={xw`outline-none bg-mango-dark-light w-full py-4 mx-3 text-2xl text-gray-300 flex-grow`}
placeholder="0.00"
value={inputAmount}
onChange={(e) => setInputAmount(e.target.value)}
></input>
<Button
onClick={setMaxForSelectedAccount}
css={xw`m-2 rounded py-1`}
>
Max
</Button>
</div>
</div>
<div css={xw`mt-5 sm:mt-6 flex justify-center`}>
<Button onClick={handleWithdraw} disabled={withdrawDisabled}>
<div css={xw`flex items-center`}>
{submitting && <Loading />}
Withdraw
</div>
</Button>
</div>
</div>
</Modal>
)
}
export default React.memo(WithdrawModal)

View File

@ -31,6 +31,9 @@ const ENDPOINT_URL = ENDPOINTS.find((e) => e.name === CLUSTER).endpoint
const DEFAULT_CONNECTION = new Connection(ENDPOINT_URL, 'recent')
const DEFAULT_MANGO_GROUP_NAME = 'BTC_ETH_USDT'
// an object with keys of Solana account addresses that we are
// subscribing to with connection.onAccountChange() in the
// useHydrateStore hook
interface AccountInfoList {
[key: string]: AccountInfo<Buffer>
}

View File

@ -18,6 +18,19 @@
user-select: none;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
/* display: none; <- Crashes Chrome on hover */
-webkit-appearance: none;
margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
}
input[type='number'] {
-moz-appearance: textfield; /* Firefox */
}
/* TODO: remove. styling for old ant components below */
.ant-input-group-addon {
background-color: transparent !important;
}
@ -33,3 +46,52 @@
.ant-switch-handle::before {
background-color: #ffffff !important;
}
.ant-input-affix-wrapper-disabled {
background-color: #262337 !important;
color: #ffffff !important;
}
.ant-select-arrow {
color: #817f8a !important;
}
.ant-select-item-option-selected:not(.ant-select-item-option-disabled) {
color: #f2c94c !important;
font-weight: 600 !important;
background-color: #141026 !important;
}
.ant-select-item-option-active:not(.ant-select-item-option-disabled) {
background-color: #584f81 !important;
}
.ant-input-group::after {
border-style: none !important;
}
.ant-input-group::before {
border-style: none !important;
}
.ant-input-group-wrapper {
border-style: none !important;
}
.ant-input-group:first-child .ant-input-affix-wrapper:not(:first-child) {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.ant-input-group.ant-input-group-compact > *:last-child,
.ant-input-group.ant-input-group-compact
> .ant-select:last-child
> .ant-select-selector,
.ant-input-group.ant-input-group-compact
> .ant-cascader-picker:last-child
.ant-input,
.ant-input-group.ant-input-group-compact
> .ant-cascader-picker-focused:last-child
.ant-input {
border-color: #524a79 !important;
}