convert trade form inputs to tailwind and continue theming

This commit is contained in:
Tyler Shipe 2021-04-12 16:01:55 -04:00
parent 932b52f649
commit 6605cbdd48
12 changed files with 170 additions and 167 deletions

View File

@ -70,6 +70,7 @@ const AccountSelect = ({
</Listbox.Button>
<Transition
show={open}
appear={true}
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"

View File

@ -13,17 +13,39 @@ const Input = ({
onChange,
className,
disabled,
prefix,
suffix,
...props
}: InputProps) => {
return (
<input
type={type}
value={value}
onChange={onChange}
className={`bg-th-bkg-1 ${className}`}
disabled={disabled}
{...props}
/>
<div
className={`flex items-center rounded ${
disabled ? 'bg-th-bkg-3' : 'bg-th-bkg-1'
} ${className}`}
>
{prefix ? (
<div className="border-r border-th-fgd-4 bg-th-bkg-2 p-2 rounded rounded-r-none">
{prefix}
</div>
) : null}
<div className="flex h-full">
<input
type={type}
value={value}
onChange={onChange}
className={`bg-transparent w-full font-light focus:outline-none ${
disabled && 'opacity-20 cursor-not-allowed'
} ${type === 'number' ? 'text-right' : ''}`}
disabled={disabled}
{...props}
/>
</div>
{suffix ? (
<span className="text-xs px-2 bg-transparent text-th-fgd-4">
{suffix}
</span>
) : null}
</div>
)
}

View File

@ -22,11 +22,11 @@ const MarketSelect = () => {
>
{Object.entries(spotMarkets).map(([name, address]) => (
<div
className={`cursor-pointer
className={`px-3 py-1 cursor-pointer text-sm font-normal
${
selectedMarket.name === name
? `px-3 py-1 text-th-primary text-xs font-normal`
: `px-3 py-1 text-th-fgd-3 hover:text-th-fgd-1 text-xs font-normal`
? `text-th-primary`
: `text-th-fgd-3 hover:text-th-fgd-1`
}
`}
onClick={() => handleChange(name)}

View File

@ -9,7 +9,6 @@ import useMangoStore from '../stores/useMangoStore'
const NotificationList = () => {
const setMangoStore = useMangoStore((s) => s.set)
const notifications = useMangoStore((s) => s.notifications)
console.log('notifications', notifications)
useEffect(() => {
if (notifications.length > 0) {
@ -70,7 +69,13 @@ const Notification = ({ type, message, description, txid }) => {
<p className={`text-lg font-medium text-gray-900`}>{message}</p>
<p className={`mt-0.5 text-base text-gray-500`}>{description}</p>
{txid ? (
<p className={`mt-0.5 text-sm text-gray-500`}>{txid}</p>
<a
href={'https://explorer.solana.com/tx/' + txid}
className="text-th-blue-400"
>
View transaction {txid.slice(0, 8)}...
{txid.slice(txid.length - 8)}
</a>
) : null}
</div>
<div className={`ml-4 flex-shrink-0 flex`}>

View File

@ -1,5 +1,5 @@
import { useState, useEffect, useRef } from 'react'
import { Input, Radio, Switch, Select } from 'antd'
import { Switch } from 'antd'
import useMarket from '../hooks/useMarket'
import useIpAddress from '../hooks/useIpAddress'
import useConnection from '../hooks/useConnection'
@ -12,7 +12,8 @@ import FloatingElement from './FloatingElement'
import { roundToDecimal } from '../utils/index'
import useMangoStore from '../stores/useMangoStore'
import Button from './Button'
// import TradeType from './TradeType'
import TradeType from './TradeType'
import NewInput from './Input'
export default function TradeForm({
setChangeOrderRef,
@ -21,12 +22,17 @@ export default function TradeForm({
ref: ({ size, price }: { size?: number; price?: number }) => void
) => void
}) {
const [side, setSide] = useState<'buy' | 'sell'>('buy')
const { baseCurrency, quoteCurrency, market, marketAddress } = useMarket()
const set = useMangoStore((s) => s.set)
const { connected } = useMangoStore((s) => s.wallet)
const { connection, cluster } = useConnection()
const tradeForm = useMangoStore((s) => s.tradeForm)
const { side, baseSize, quoteSize, price, tradeType } = useMangoStore(
(s) => s.tradeForm
)
const { ipAllowed } = useIpAddress()
const [postOnly, setPostOnly] = useState(false)
const [ioc, setIoc] = useState(false)
const [submitting, setSubmitting] = useState(false)
const orderBookRef = useRef(useMangoStore.getState().market.orderBook)
const orderbook = orderBookRef.current[0]
@ -39,6 +45,31 @@ export default function TradeForm({
[]
)
const setSide = (side) =>
set((s) => {
s.tradeForm.side = side
})
const setBaseSize = (baseSize) =>
set((s) => {
s.tradeForm.baseSize = parseFloat(baseSize)
})
const setQuoteSize = (quoteSize) =>
set((s) => {
s.tradeForm.quoteSize = parseFloat(quoteSize)
})
const setPrice = (price) =>
set((s) => {
s.tradeForm.price = parseFloat(price)
})
const setTradeType = (type) =>
set((s) => {
s.tradeForm.tradeType = type
})
const markPriceRef = useRef(useMangoStore.getState().market.markPrice)
const markPrice = markPriceRef.current
useEffect(
@ -50,16 +81,6 @@ export default function TradeForm({
[]
)
const { ipAllowed } = useIpAddress()
const [postOnly, setPostOnly] = useState(false)
const [ioc, setIoc] = useState(false)
const [baseSize, setBaseSize] = useState<number | undefined>(undefined)
const [quoteSize, setQuoteSize] = useState<number | undefined>(undefined)
const [price, setPrice] = useState<number | undefined>(undefined)
const [submitting, setSubmitting] = useState(false)
const [tradeType, setTradeType] = useState('Limit')
const sizeDecimalCount =
market?.minOrderSize && getDecimalCount(market.minOrderSize)
const priceDecimalCount = market?.tickSize && getDecimalCount(market.tickSize)
@ -69,25 +90,6 @@ export default function TradeForm({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setChangeOrderRef])
useEffect(() => {
if (!price && markPrice && tradeType !== 'Market') {
setPrice(markPrice)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [price, baseSize, quoteSize])
// Set the price from the balance comp
useEffect(() => {
if (tradeForm.currency) {
if (tradeForm.currency === baseCurrency) {
// onSetBaseSize(size.size);
} else {
// onSetQuoteSize(size.size);
}
}
}, [tradeForm])
const onSetBaseSize = (baseSize: number | undefined) => {
setBaseSize(baseSize)
if (!baseSize) {
@ -175,8 +177,8 @@ export default function TradeForm({
if (tradeType === 'Market') {
calculatedPrice =
side === 'buy'
? calculateMarketPrice(orderbook.asks, tradeForm.size, side)
: calculateMarketPrice(orderbook.bids, tradeForm.size, side)
? calculateMarketPrice(orderbook.asks, baseSize, side)
: calculateMarketPrice(orderbook.bids, baseSize, side)
}
await placeAndSettle(
@ -210,7 +212,7 @@ export default function TradeForm({
setTradeType(tradeType)
if (tradeType === 'Market') {
setIoc(true)
setPrice(undefined)
setPrice('')
} else {
const limitPrice =
side === 'buy' ? orderbook.asks[0][0] : orderbook.bids[0][0]
@ -222,94 +224,74 @@ export default function TradeForm({
return (
<FloatingElement>
<div>
<Radio.Group
onChange={(e) => setSide(e.target.value)}
value={side}
buttonStyle="solid"
style={{
marginBottom: 8,
width: '100%',
}}
>
<Radio.Button
value="buy"
style={{
width: '50%',
textAlign: 'center',
color: side === 'buy' ? '#141026' : '',
background: side === 'buy' ? '#AFD803' : '',
borderColor: side === 'buy' ? '#AFD803' : '',
}}
<div className={`flex text-base text-th-fgd-4`}>
<button
onClick={() => setSide('buy')}
className={`flex-1 outline-none focus:outline-none`}
>
BUY
</Radio.Button>
<Radio.Button
className="sell-button"
value="sell"
style={{
width: '50%',
textAlign: 'center',
background: side === 'sell' ? '#E54033' : '',
borderColor: side === 'sell' ? '#E54033' : '',
}}
<div
className={`hover:text-th-primary pb-1
${
side === 'buy' &&
`text-th-green hover:text-th-green border-b-2 border-th-green`
}`}
>
Buy
</div>
</button>
<button
onClick={() => setSide('sell')}
className={`flex-1 outline-none focus:outline-none`}
>
SELL
</Radio.Button>
</Radio.Group>
<Input.Group compact style={{ paddingBottom: 8 }}>
<Input
style={{
width: 'calc(50% + 30px)',
textAlign: 'right',
paddingBottom: 8,
}}
addonBefore={<div style={{ width: '30px' }}>Price</div>}
suffix={
<span style={{ fontSize: 10, opacity: 0.5 }}>
{quoteCurrency}
</span>
}
value={price}
<div
className={`hover:text-th-primary pb-1
${
side === 'sell' &&
`text-th-red hover:text-th-red border-b-2 border-th-red`
}
`}
>
Sell
</div>
</button>
</div>
<NewInput.Group className="mt-4">
<NewInput
type="number"
step={market?.tickSize || 1}
onChange={(e) => setPrice(parseFloat(e.target.value))}
value={price}
disabled={tradeType === 'Market'}
prefix={'Price'}
suffix={quoteCurrency}
className="w-3/5"
/>
{/* <TradeType onChange={handleTradeTypeChange} value={tradeType} /> */}
<Select
style={{ width: 'calc(50% - 30px)' }}
<TradeType
onChange={handleTradeTypeChange}
value={tradeType}
>
<Select.Option value="Limit">Limit</Select.Option>
<Select.Option value="Market">Market</Select.Option>
</Select>
</Input.Group>
<Input.Group compact style={{ paddingBottom: 8 }}>
<Input
style={{ width: 'calc(50% + 30px)', textAlign: 'right' }}
addonBefore={<div style={{ width: '30px' }}>Size</div>}
suffix={
<span style={{ fontSize: 10, opacity: 0.5 }}>{baseCurrency}</span>
}
value={baseSize}
className="w-2/5"
/>
</NewInput.Group>
<NewInput.Group className="mt-4">
<NewInput
type="number"
step={market?.minOrderSize || 1}
onChange={(e) => onSetBaseSize(parseFloat(e.target.value))}
value={baseSize}
className="text-right flex-grow w-3/5"
prefix={'Size'}
suffix={baseCurrency}
/>
<Input
style={{ width: 'calc(50% - 30px)', textAlign: 'right' }}
suffix={
<span style={{ fontSize: 10, opacity: 0.5 }}>
{quoteCurrency}
</span>
}
value={quoteSize}
<NewInput
type="number"
step={market?.minOrderSize || 1}
onChange={(e) => onSetQuoteSize(parseFloat(e.target.value))}
value={quoteSize}
className="text-right border-l border-th-fgd-4 rounded-l-none w-2/5"
suffix={quoteCurrency}
/>
</Input.Group>
</NewInput.Group>
{tradeType !== 'Market' ? (
<div style={{ paddingTop: 18 }}>
{'POST '}
@ -317,14 +299,9 @@ export default function TradeForm({
checked={postOnly}
onChange={postOnChange}
style={{ marginRight: 40 }}
disabled={tradeType === 'Market'}
/>
{'IOC '}
<Switch
checked={ioc}
onChange={iocOnChange}
disabled={tradeType === 'Market'}
/>
<Switch checked={ioc} onChange={iocOnChange} />
</div>
) : null}
</div>
@ -340,7 +317,7 @@ export default function TradeForm({
}
grow={true}
onClick={onSubmit}
className={`text-lg font-light bg-mango-green text-mango-dark hover:bg-mango-yellow flex-grow`}
className={`rounded text-lg font-light bg-th-green text-th-bkg-1 hover:bg-th-primary flex-grow`}
>
{connected ? `Buy ${baseCurrency}` : 'CONNECT WALLET TO TRADE'}
</Button>
@ -354,7 +331,7 @@ export default function TradeForm({
}
grow={true}
onClick={onSubmit}
className={`text-lg font-light bg-mango-red text-white hover:bg-mango-yellow flex-grow`}
className={`rounded text-lg font-light bg-th-red text-white hover:bg-th-primary flex-grow`}
>
{connected ? `Sell ${baseCurrency}` : 'CONNECT WALLET TO TRADE'}
</Button>

View File

@ -27,8 +27,8 @@ const layouts = {
],
lg: [
{ i: 'tvChart', x: 0, y: 0, w: 2, h: 24 },
{ i: 'balanceInfo', x: 2, y: 0, w: 1, h: 12 },
{ i: 'marginStats', x: 2, y: 1, w: 1, h: 12 },
{ i: 'balanceInfo', x: 2, y: 0, w: 1, h: 13 },
{ i: 'marginStats', x: 2, y: 1, w: 1, h: 11 },
{ i: 'orderbook', x: 0, y: 2, w: 1, h: 17 },
{ i: 'tradeForm', x: 1, y: 2, w: 1, h: 17 },
{ i: 'marketTrades', x: 2, y: 2, w: 1, h: 17 },

View File

@ -1,19 +1,21 @@
import { Listbox, Transition } from '@headlessui/react'
import { Listbox } from '@headlessui/react'
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid'
const TradeType = ({ value, onChange }) => {
const TRADE_TYPES = ['Limit', 'Market']
const TradeType = ({ value, onChange, className = '' }) => {
return (
<div className={`ml-4 relative inline-block -mb-1`}>
<div className={`relative ${className}`}>
<Listbox value={value} onChange={onChange}>
{({ open }) => (
<>
<Listbox.Button
className={`border border-mango-dark-lighter focus:outline-none focus:ring-1 focus:ring-mango-yellow p-2 w-56`}
className={`h-full w-full border-l border-th-fgd-4 focus:outline-none focus:ring-1 focus:ring-th-primary`}
>
<div
className={`flex items-center text-lg justify-between font-light`}
className={`flex items-center justify-between space-x-4 font-light pl-2`}
>
{value}
<span>{value}</span>
{open ? (
<ChevronUpIcon className={`h-5 w-5 mr-1`} />
) : (
@ -21,35 +23,26 @@ const TradeType = ({ value, onChange }) => {
)}
</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"
>
{open ? (
<Listbox.Options
static
className={`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`}
className={`z-20 w-full p-1 absolute left-0 mt-1 bg-th-bkg-3 origin-top-left divide-y divide-th-fgd-4 shadow-lg outline-none`}
>
<div className={`opacity-50 p-2`}>Markets</div>
{['Limit', 'Market'].map((type) => (
<Listbox.Option key={type} value={name}>
{TRADE_TYPES.map((type) => (
<Listbox.Option key={type} value={type}>
{({ selected }) => (
<div
className={`p-2 text-base hover:bg-mango-dark-lighter hover:cursor-pointer tracking-wider font-light ${
selected && 'text-mango-yellow bg-mango-dark-lighter'
className={`p-2 text-base hover:bg-th-fgd-4 hover:cursor-pointer tracking-wider font-light ${
selected && `text-th-primary`
}`}
>
{name}
{type}
</div>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
) : null}
</>
)}
</Listbox>

View File

@ -5,13 +5,9 @@ import useSerumStore from '../stores/useSerumStore'
import useOrderbook from './useOrderbook'
import useConnection from './useConnection'
// const _VERY_SLOW_REFRESH_INTERVAL = 5000 * 1000;
// For things that don't really change
const _SLOW_REFRESH_INTERVAL = 5 * 1000
// For things that change frequently
// const _FAST_REFRESH_INTERVAL = 1000;
const _SLOW_REFRESH_INTERVAL = 5 * 1000
// const _VERY_SLOW_REFRESH_INTERVAL = 5000 * 1000;
export function _useUnfilteredTrades(limit = 10000) {
console.log('fetching unfiltered trades')

View File

@ -39,6 +39,8 @@ export default function useWallet() {
useEffect(() => {
if (!wallet) return
wallet.on('connect', () => {
console.log('connected')
setMangoStore((state) => {
state.wallet.connected = true
})

View File

@ -11,7 +11,7 @@ const Index = () => {
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all`}>
<TopBar />
<MarketSelect />
<div className={`min-h-screen p-1 sm:p-2 md:p-6 md:pt-4`}>
<div className={`min-h-screen p-1 sm:px-2 sm:py-1 md:px-6 md:py-1`}>
<TradePageGrid />
</div>
<Notifications />

View File

@ -76,9 +76,11 @@ interface MangoStore extends State {
current: MarginAccount | null
}
tradeForm: {
side: string
currency: string
size: number
side: 'buy' | 'sell'
price: number | ''
baseSize: number | ''
quoteSize: number | ''
tradeType: 'Market' | 'Limit'
}
wallet: {
connected: boolean
@ -127,8 +129,10 @@ const useMangoStore = create<MangoStore>(
},
tradeForm: {
side: 'buy',
size: 0,
currency: 'BTC',
baseSize: '',
quoteSize: '',
tradeType: 'Limit',
price: '',
},
wallet: {
connected: false,

View File

@ -7,10 +7,13 @@ export function notify(newNotification: {
txid?: string
}) {
const setMangoStore = useMangoStore.getState().set
const notifications = useMangoStore.getState().notifications
setMangoStore((state) => {
console.log('original', state.notifications)
state.notifications = [
...state.notifications,
...notifications,
{ type: 'success', ...newNotification },
]
})