vertically center orderbook as window is resized

This commit is contained in:
tjs 2022-09-20 14:14:31 -04:00
parent 2b9ada9ebc
commit da13e3a0b5
4 changed files with 231 additions and 222 deletions

View File

@ -42,82 +42,83 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
return ( return (
<div <div
className={`flex flex-col justify-between transition-all duration-500 ${ className={`transition-all duration-500 ${
collapsed ? 'w-[64px]' : 'w-44 lg:w-48 xl:w-52' collapsed ? 'w-[64px]' : 'w-44 lg:w-48 xl:w-52'
} min-h-screen border-r border-th-bkg-3 bg-th-bkg-1`} } border-r border-th-bkg-3 bg-th-bkg-1`}
> >
<div className="my-2"> <div className="flex min-h-screen flex-col justify-between">
<Link href={'/'} shallow={true} passHref> <div className="my-2">
<div <Link href={'/'} shallow={true} passHref>
className={`h-14 items-center transition-all duration-500 ease-in-out ${ <div
collapsed ? '' : 'justify-start' className={`h-14 items-center transition-all duration-500 ease-in-out ${
} pb-1 pt-2 pl-4`} collapsed ? '' : 'justify-start'
> } pb-1 pt-2 pl-4`}
<div className={`flex flex-shrink-0 cursor-pointer items-center`}> >
<img <div className={`flex flex-shrink-0 cursor-pointer items-center`}>
className={`h-8 w-8 flex-shrink-0`} <img
src="/logos/logo-mark.svg" className={`h-8 w-8 flex-shrink-0`}
alt="next" src="/logos/logo-mark.svg"
/> alt="next"
<Transition />
show={!collapsed} <Transition
as={Fragment} show={!collapsed}
enter="transition ease-in duration-200" as={Fragment}
enterFrom="opacity-50" enter="transition ease-in duration-200"
enterTo="opacity-100" enterFrom="opacity-50"
leave="transition ease-out duration-200" enterTo="opacity-100"
leaveFrom="opacity-100" leave="transition ease-out duration-200"
leaveTo="opacity-0" leaveFrom="opacity-100"
> leaveTo="opacity-0"
<span className="ml-3 text-lg font-bold text-th-fgd-1"> >
Mango <span className="ml-3 text-lg font-bold text-th-fgd-1">
</span> Mango
</Transition> </span>
</Transition>
</div>
</div> </div>
</div> </Link>
</Link> <div className="flex flex-col items-start">
<div className="flex flex-col items-start"> <MenuItem
<MenuItem active={pathname === '/'}
active={pathname === '/'} collapsed={collapsed}
collapsed={collapsed} icon={<CurrencyDollarIcon className="h-5 w-5" />}
icon={<CurrencyDollarIcon className="h-5 w-5" />} title={t('account')}
title={t('account')} pagePath="/"
pagePath="/" />
/> <MenuItem
<MenuItem active={pathname === '/swap'}
active={pathname === '/swap'} collapsed={collapsed}
collapsed={collapsed} icon={<ArrowsRightLeftIcon className="h-5 w-5" />}
icon={<ArrowsRightLeftIcon className="h-5 w-5" />} title={t('swap')}
title={t('swap')} pagePath="/swap"
pagePath="/swap" />
/> <MenuItem
<MenuItem active={pathname === '/trade'}
active={pathname === '/trade'} collapsed={collapsed}
collapsed={collapsed} icon={<TradeIcon className="h-5 w-5" />}
icon={<TradeIcon className="h-5 w-5" />} title={t('trade')}
title={t('trade')} pagePath="/trade"
pagePath="/trade" />
/> <MenuItem
<MenuItem active={pathname === '/stats'}
active={pathname === '/stats'} collapsed={collapsed}
collapsed={collapsed} icon={<ChartBarIcon className="h-5 w-5" />}
icon={<ChartBarIcon className="h-5 w-5" />} title={t('stats')}
title={t('stats')} pagePath="/stats"
pagePath="/stats" />
/> <MenuItem
<MenuItem active={pathname === '/settings'}
active={pathname === '/settings'} collapsed={collapsed}
collapsed={collapsed} icon={<Cog8ToothIcon className="h-5 w-5" />}
icon={<Cog8ToothIcon className="h-5 w-5" />} title={t('settings')}
title={t('settings')} pagePath="/settings"
pagePath="/settings" />
/> <ExpandableMenuItem
<ExpandableMenuItem collapsed={collapsed}
collapsed={collapsed} icon={<EllipsisHorizontalIcon className="h-5 w-5" />}
icon={<EllipsisHorizontalIcon className="h-5 w-5" />} title={t('more')}
title={t('more')} >
> {/* <MenuItem
{/* <MenuItem
active={pathname === '/fees'} active={pathname === '/fees'}
collapsed={false} collapsed={false}
icon={<ReceiptTaxIcon className="h-5 w-5" />} icon={<ReceiptTaxIcon className="h-5 w-5" />}
@ -125,69 +126,70 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
pagePath="/fees" pagePath="/fees"
hideIconBg hideIconBg
/> */} /> */}
<MenuItem <MenuItem
collapsed={false} collapsed={false}
icon={<LightBulbIcon className="h-5 w-5" />} icon={<LightBulbIcon className="h-5 w-5" />}
title={t('learn')} title={t('learn')}
pagePath="https://docs.mango.markets" pagePath="https://docs.mango.markets"
hideIconBg hideIconBg
isExternal isExternal
showTooltip={false} showTooltip={false}
/> />
<MenuItem <MenuItem
collapsed={false} collapsed={false}
icon={<BuildingLibraryIcon className="h-5 w-5" />} icon={<BuildingLibraryIcon className="h-5 w-5" />}
title={t('governance')} title={t('governance')}
pagePath="https://dao.mango.markets" pagePath="https://dao.mango.markets"
hideIconBg hideIconBg
isExternal isExternal
showTooltip={false} showTooltip={false}
/> />
{connected ? ( {connected ? (
<button <button
className="default-transition mt-1 flex items-center px-4 text-th-fgd-2 md:hover:text-th-primary" className="default-transition mt-1 flex items-center px-4 text-th-fgd-2 md:hover:text-th-primary"
onClick={handleTakeTour} onClick={handleTakeTour}
> >
<InformationCircleIcon className="mr-3 h-5 w-5" /> <InformationCircleIcon className="mr-3 h-5 w-5" />
<span className="text-base">Take UI Tour</span> <span className="text-base">Take UI Tour</span>
</button> </button>
) : null} ) : null}
</ExpandableMenuItem>
</div>
</div>
<div className="border-t border-th-bkg-3">
<ExpandableMenuItem
collapsed={collapsed}
icon={
<HealthHeart
health={
mangoAccount
? mangoAccount.getHealthRatioUi(HealthType.maint)
: undefined
}
size={32}
/>
}
title={
<div className="text-left">
<p className="mb-0.5 whitespace-nowrap text-xs">Health Check</p>
<p className="whitespace-nowrap text-sm font-bold text-th-fgd-1">
{mangoAccount
? mangoAccount.name
: connected
? 'No Account'
: 'Connect'}
</p>
</div>
}
alignBottom
hideIconBg
>
<div className="px-4 pb-4 pt-2">
<MangoAccountSummary collapsed={collapsed} />
</div>
</ExpandableMenuItem> </ExpandableMenuItem>
</div> </div>
</div> </div>
<div className="border-t border-th-bkg-3">
<ExpandableMenuItem
collapsed={collapsed}
icon={
<HealthHeart
health={
mangoAccount
? mangoAccount.getHealthRatioUi(HealthType.maint)
: undefined
}
size={32}
/>
}
title={
<div className="text-left">
<p className="mb-0.5 whitespace-nowrap text-xs">Health Check</p>
<p className="whitespace-nowrap text-sm font-bold text-th-fgd-1">
{mangoAccount
? mangoAccount.name
: connected
? 'No Account'
: 'Connect'}
</p>
</div>
}
alignBottom
hideIconBg
>
<div className="px-4 pb-4 pt-2">
<MangoAccountSummary collapsed={collapsed} />
</div>
</ExpandableMenuItem>
</div>
</div> </div>
) )
} }

View File

@ -1,7 +1,6 @@
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { Listbox } from '@headlessui/react' import { Listbox } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/20/solid' import { ChevronDownIcon } from '@heroicons/react/20/solid'
import { isEqual } from '../../utils'
const GroupSize = ({ const GroupSize = ({
tickSize, tickSize,
@ -72,6 +71,4 @@ const GroupSize = ({
) )
} }
export default React.memo(GroupSize, (prevProps, nextProps) => export default React.memo(GroupSize)
isEqual(prevProps, nextProps, ['tickSize', 'value'])
)

View File

@ -1,7 +1,7 @@
import { AccountInfo } from '@solana/web3.js' import { AccountInfo } from '@solana/web3.js'
import Big from 'big.js' import Big from 'big.js'
import mangoStore from '@store/mangoStore' import mangoStore from '@store/mangoStore'
import React, { useEffect, useMemo, useRef, useState } from 'react' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Market, Orderbook as SpotOrderBook } from '@project-serum/serum' import { Market, Orderbook as SpotOrderBook } from '@project-serum/serum'
import useInterval from '@components/shared/useInterval' import useInterval from '@components/shared/useInterval'
import isEqualLodash from 'lodash/isEqual' import isEqualLodash from 'lodash/isEqual'
@ -152,7 +152,8 @@ const Orderbook = ({ depth = 12 }) => {
const { t } = useTranslation('common') const { t } = useTranslation('common')
const selectedMarket = mangoStore((s) => s.selectedMarket.current) const selectedMarket = mangoStore((s) => s.selectedMarket.current)
const [openOrderPrices, setOpenOrderPrices] = useState<any[]>([]) // const [openOrderPrices, setOpenOrderPrices] = useState<any[]>([])
const [isScrolled, setIsScrolled] = useState(false)
const [orderbookData, setOrderbookData] = useState<any | null>(null) const [orderbookData, setOrderbookData] = useState<any | null>(null)
const [defaultLayout, setDefaultLayout] = useState(true) const [defaultLayout, setDefaultLayout] = useState(true)
const [displayCumulativeSize, setDisplayCumulativeSize] = useState(false) const [displayCumulativeSize, setDisplayCumulativeSize] = useState(false)
@ -162,9 +163,14 @@ const Orderbook = ({ depth = 12 }) => {
const currentOrderbookData = useRef<any>(null) const currentOrderbookData = useRef<any>(null)
const nextOrderbookData = useRef<any>(null) const nextOrderbookData = useRef<any>(null)
const orderbookElRef = useRef<HTMLDivElement>(null)
const previousDepth = usePrevious(depth) const previousDepth = usePrevious(depth)
const previousGrouping = usePrevious(grouping) const previousGrouping = usePrevious(grouping)
const depthArray = useMemo(() => {
return Array(depth).fill(0)
}, [depth])
const serum3MarketExternal = useMemo(() => { const serum3MarketExternal = useMemo(() => {
const group = mangoStore.getState().group const group = mangoStore.getState().group
if (!group || !selectedMarket) return if (!group || !selectedMarket) return
@ -188,6 +194,13 @@ const Orderbook = ({ depth = 12 }) => {
[] []
) )
const verticallyCenterOrderbook = useCallback(() => {
const element = orderbookElRef.current
if (element) {
element.scrollTop = (element.scrollHeight - element.offsetHeight) / 2
}
}, [])
useInterval(() => { useInterval(() => {
const orderbook = mangoStore.getState().selectedMarket.orderbook const orderbook = mangoStore.getState().selectedMarket.orderbook
const group = mangoStore.getState().group const group = mangoStore.getState().group
@ -273,6 +286,9 @@ const Orderbook = ({ depth = 12 }) => {
spread, spread,
spreadPercentage, spreadPercentage,
}) })
if (!isScrolled) {
verticallyCenterOrderbook()
}
} else { } else {
setOrderbookData(null) setOrderbookData(null)
} }
@ -337,9 +353,17 @@ const Orderbook = ({ depth = 12 }) => {
} }
}, [serum3MarketExternal]) }, [serum3MarketExternal])
const onGroupSizeChange = (groupSize: number) => { useEffect(() => {
function handleResize() {
verticallyCenterOrderbook()
}
window.addEventListener('resize', handleResize)
}, [verticallyCenterOrderbook])
const onGroupSizeChange = useCallback((groupSize: number) => {
setGrouping(groupSize) setGrouping(groupSize)
} }, [])
if (!serum3MarketExternal) return null if (!serum3MarketExternal) return null
@ -409,35 +433,43 @@ const Orderbook = ({ depth = 12 }) => {
<div className="col-span-1 text-right">Size</div> <div className="col-span-1 text-right">Size</div>
<div className="col-span-1 text-right">Price</div> <div className="col-span-1 text-right">Price</div>
</div> </div>
<div className="hide-scroll relative h-full overflow-y-scroll"> <div
{showSells className="hide-scroll relative h-full overflow-y-scroll"
? orderbookData?.asks.map( ref={orderbookElRef}
({ onScroll={() => setIsScrolled(true)}
price, >
size, {showSells && orderbookData?.asks?.length
cumulativeSize, ? depthArray.map((_x, index) => {
sizePercent, return (
maxSizePercent, <div key={index}>
}: cumOrderbookSide) => ( {orderbookData?.asks[index] ? (
<MemoizedOrderbookRow <MemoizedOrderbookRow
minOrderSize={serum3MarketExternal.minOrderSize} minOrderSize={serum3MarketExternal.minOrderSize}
tickSize={serum3MarketExternal.tickSize} tickSize={serum3MarketExternal.tickSize}
// hasOpenOrder={hasOpenOrderForPriceGroup( // hasOpenOrder={hasOpenOrderForPriceGroup(
// openOrderPrices, // openOrderPrices,
// price, // price,
// grouping // grouping
// )} // )}
key={price + ''} key={orderbookData?.asks[index].price}
price={price} price={orderbookData?.asks[index].price}
size={displayCumulativeSize ? cumulativeSize : size} size={
side="sell" displayCumulativeSize
sizePercent={ ? orderbookData?.asks[index].cumulativeSize
displayCumulativeSize ? maxSizePercent : sizePercent : orderbookData?.asks[index].size
} }
grouping={grouping} side="sell"
/> sizePercent={
displayCumulativeSize
? orderbookData?.asks[index].maxSizePercent
: orderbookData?.asks[index].sizePercent
}
grouping={grouping}
/>
) : null}
</div>
) )
) })
: null} : null}
{showBuys && showSells ? ( {showBuys && showSells ? (
<div className="my-2 grid grid-cols-2 border-y border-th-bkg-3 py-2 px-4 text-xs text-th-fgd-4"> <div className="my-2 grid grid-cols-2 border-y border-th-bkg-3 py-2 px-4 text-xs text-th-fgd-4">
@ -452,34 +484,35 @@ const Orderbook = ({ depth = 12 }) => {
</div> </div>
</div> </div>
) : null} ) : null}
{showBuys {showBuys && orderbookData?.bids?.length
? orderbookData?.bids.map( ? depthArray.map((_x, index) => (
({ <div key={index}>
price, {orderbookData?.bids[index] ? (
size, <MemoizedOrderbookRow
cumulativeSize, minOrderSize={serum3MarketExternal.minOrderSize}
sizePercent, tickSize={serum3MarketExternal.tickSize}
maxSizePercent, // hasOpenOrder={hasOpenOrderForPriceGroup(
}: cumOrderbookSide) => ( // openOrderPrices,
<MemoizedOrderbookRow // price,
minOrderSize={serum3MarketExternal.minOrderSize} // grouping
tickSize={serum3MarketExternal.tickSize} // )}
// hasOpenOrder={hasOpenOrderForPriceGroup( price={orderbookData?.bids[index].price}
// openOrderPrices, size={
// price, displayCumulativeSize
// grouping ? orderbookData?.bids[index].cumulativeSize
// )} : orderbookData?.bids[index].size
key={price} }
price={price} side="buy"
size={displayCumulativeSize ? cumulativeSize : size} sizePercent={
side="buy" displayCumulativeSize
sizePercent={ ? orderbookData?.bids[index].maxSizePercent
displayCumulativeSize ? maxSizePercent : sizePercent : orderbookData?.bids[index].sizePercent
} }
grouping={grouping} grouping={grouping}
/> />
) ) : null}
) </div>
))
: null} : null}
</div> </div>
</div> </div>
@ -561,7 +594,7 @@ const OrderbookRow = ({
return ( return (
<div <div
className={`relative flex cursor-pointer justify-between border-b border-b-th-bkg-1 text-sm`} className={`relative flex h-[24px] cursor-pointer justify-between border-b border-b-th-bkg-1 text-sm`}
ref={element} ref={element}
> >
<> <>

View File

@ -49,7 +49,7 @@ const getHeight = (
const TradeAdvancedPage = () => { const TradeAdvancedPage = () => {
const { height, width } = useViewport() const { height, width } = useViewport()
const [currentBreakpoint, setCurrentBreakpoint] = useState<string>() const [currentBreakpoint, setCurrentBreakpoint] = useState<string>()
const [orderbookDepth, setOrderbookDepth] = useState(6) const [orderbookDepth, setOrderbookDepth] = useState(60)
const { uiLocked } = mangoStore((s) => s.settings) const { uiLocked } = mangoStore((s) => s.settings)
const showMobileView = width <= breakpoints.md const showMobileView = width <= breakpoints.md
@ -150,29 +150,6 @@ const TradeAdvancedPage = () => {
defaultLayouts defaultLayouts
) )
useEffect(() => {
const adjustOrderBook = (
layouts: ReactGridLayout.Layouts,
breakpoint?: string | null
) => {
const bp = breakpoint ? breakpoint : getCurrentBreakpoint()
if (bp) {
const orderbookLayout = layouts[bp].find((obj) => {
return obj.i === 'orderbook'
})
let depth = (orderbookLayout!.h / 24 / 2) * 1.25
const maxNum = Math.max(1, depth)
if (typeof maxNum === 'number') {
depth = Math.round(maxNum)
}
setOrderbookDepth(depth)
}
}
adjustOrderBook(defaultLayouts, currentBreakpoint)
}, [currentBreakpoint, defaultLayouts])
const onLayoutChange = useCallback((layouts: ReactGridLayout.Layouts) => { const onLayoutChange = useCallback((layouts: ReactGridLayout.Layouts) => {
if (layouts) { if (layouts) {
setSavedLayouts(layouts) setSavedLayouts(layouts)