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 (
<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'
} 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">
<Link href={'/'} shallow={true} passHref>
<div
className={`h-14 items-center transition-all duration-500 ease-in-out ${
collapsed ? '' : 'justify-start'
} pb-1 pt-2 pl-4`}
>
<div className={`flex flex-shrink-0 cursor-pointer items-center`}>
<img
className={`h-8 w-8 flex-shrink-0`}
src="/logos/logo-mark.svg"
alt="next"
/>
<Transition
show={!collapsed}
as={Fragment}
enter="transition ease-in duration-200"
enterFrom="opacity-50"
enterTo="opacity-100"
leave="transition ease-out duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<span className="ml-3 text-lg font-bold text-th-fgd-1">
Mango
</span>
</Transition>
<div className="flex min-h-screen flex-col justify-between">
<div className="my-2">
<Link href={'/'} shallow={true} passHref>
<div
className={`h-14 items-center transition-all duration-500 ease-in-out ${
collapsed ? '' : 'justify-start'
} pb-1 pt-2 pl-4`}
>
<div className={`flex flex-shrink-0 cursor-pointer items-center`}>
<img
className={`h-8 w-8 flex-shrink-0`}
src="/logos/logo-mark.svg"
alt="next"
/>
<Transition
show={!collapsed}
as={Fragment}
enter="transition ease-in duration-200"
enterFrom="opacity-50"
enterTo="opacity-100"
leave="transition ease-out duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<span className="ml-3 text-lg font-bold text-th-fgd-1">
Mango
</span>
</Transition>
</div>
</div>
</div>
</Link>
<div className="flex flex-col items-start">
<MenuItem
active={pathname === '/'}
collapsed={collapsed}
icon={<CurrencyDollarIcon className="h-5 w-5" />}
title={t('account')}
pagePath="/"
/>
<MenuItem
active={pathname === '/swap'}
collapsed={collapsed}
icon={<ArrowsRightLeftIcon className="h-5 w-5" />}
title={t('swap')}
pagePath="/swap"
/>
<MenuItem
active={pathname === '/trade'}
collapsed={collapsed}
icon={<TradeIcon className="h-5 w-5" />}
title={t('trade')}
pagePath="/trade"
/>
<MenuItem
active={pathname === '/stats'}
collapsed={collapsed}
icon={<ChartBarIcon className="h-5 w-5" />}
title={t('stats')}
pagePath="/stats"
/>
<MenuItem
active={pathname === '/settings'}
collapsed={collapsed}
icon={<Cog8ToothIcon className="h-5 w-5" />}
title={t('settings')}
pagePath="/settings"
/>
<ExpandableMenuItem
collapsed={collapsed}
icon={<EllipsisHorizontalIcon className="h-5 w-5" />}
title={t('more')}
>
{/* <MenuItem
</Link>
<div className="flex flex-col items-start">
<MenuItem
active={pathname === '/'}
collapsed={collapsed}
icon={<CurrencyDollarIcon className="h-5 w-5" />}
title={t('account')}
pagePath="/"
/>
<MenuItem
active={pathname === '/swap'}
collapsed={collapsed}
icon={<ArrowsRightLeftIcon className="h-5 w-5" />}
title={t('swap')}
pagePath="/swap"
/>
<MenuItem
active={pathname === '/trade'}
collapsed={collapsed}
icon={<TradeIcon className="h-5 w-5" />}
title={t('trade')}
pagePath="/trade"
/>
<MenuItem
active={pathname === '/stats'}
collapsed={collapsed}
icon={<ChartBarIcon className="h-5 w-5" />}
title={t('stats')}
pagePath="/stats"
/>
<MenuItem
active={pathname === '/settings'}
collapsed={collapsed}
icon={<Cog8ToothIcon className="h-5 w-5" />}
title={t('settings')}
pagePath="/settings"
/>
<ExpandableMenuItem
collapsed={collapsed}
icon={<EllipsisHorizontalIcon className="h-5 w-5" />}
title={t('more')}
>
{/* <MenuItem
active={pathname === '/fees'}
collapsed={false}
icon={<ReceiptTaxIcon className="h-5 w-5" />}
@ -125,69 +126,70 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
pagePath="/fees"
hideIconBg
/> */}
<MenuItem
collapsed={false}
icon={<LightBulbIcon className="h-5 w-5" />}
title={t('learn')}
pagePath="https://docs.mango.markets"
hideIconBg
isExternal
showTooltip={false}
/>
<MenuItem
collapsed={false}
icon={<BuildingLibraryIcon className="h-5 w-5" />}
title={t('governance')}
pagePath="https://dao.mango.markets"
hideIconBg
isExternal
showTooltip={false}
/>
{connected ? (
<button
className="default-transition mt-1 flex items-center px-4 text-th-fgd-2 md:hover:text-th-primary"
onClick={handleTakeTour}
>
<InformationCircleIcon className="mr-3 h-5 w-5" />
<span className="text-base">Take UI Tour</span>
</button>
) : null}
<MenuItem
collapsed={false}
icon={<LightBulbIcon className="h-5 w-5" />}
title={t('learn')}
pagePath="https://docs.mango.markets"
hideIconBg
isExternal
showTooltip={false}
/>
<MenuItem
collapsed={false}
icon={<BuildingLibraryIcon className="h-5 w-5" />}
title={t('governance')}
pagePath="https://dao.mango.markets"
hideIconBg
isExternal
showTooltip={false}
/>
{connected ? (
<button
className="default-transition mt-1 flex items-center px-4 text-th-fgd-2 md:hover:text-th-primary"
onClick={handleTakeTour}
>
<InformationCircleIcon className="mr-3 h-5 w-5" />
<span className="text-base">Take UI Tour</span>
</button>
) : 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>
</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>
)
}

View File

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

View File

@ -1,7 +1,7 @@
import { AccountInfo } from '@solana/web3.js'
import Big from 'big.js'
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 useInterval from '@components/shared/useInterval'
import isEqualLodash from 'lodash/isEqual'
@ -152,7 +152,8 @@ const Orderbook = ({ depth = 12 }) => {
const { t } = useTranslation('common')
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 [defaultLayout, setDefaultLayout] = useState(true)
const [displayCumulativeSize, setDisplayCumulativeSize] = useState(false)
@ -162,9 +163,14 @@ const Orderbook = ({ depth = 12 }) => {
const currentOrderbookData = useRef<any>(null)
const nextOrderbookData = useRef<any>(null)
const orderbookElRef = useRef<HTMLDivElement>(null)
const previousDepth = usePrevious(depth)
const previousGrouping = usePrevious(grouping)
const depthArray = useMemo(() => {
return Array(depth).fill(0)
}, [depth])
const serum3MarketExternal = useMemo(() => {
const group = mangoStore.getState().group
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(() => {
const orderbook = mangoStore.getState().selectedMarket.orderbook
const group = mangoStore.getState().group
@ -273,6 +286,9 @@ const Orderbook = ({ depth = 12 }) => {
spread,
spreadPercentage,
})
if (!isScrolled) {
verticallyCenterOrderbook()
}
} else {
setOrderbookData(null)
}
@ -337,9 +353,17 @@ const Orderbook = ({ depth = 12 }) => {
}
}, [serum3MarketExternal])
const onGroupSizeChange = (groupSize: number) => {
useEffect(() => {
function handleResize() {
verticallyCenterOrderbook()
}
window.addEventListener('resize', handleResize)
}, [verticallyCenterOrderbook])
const onGroupSizeChange = useCallback((groupSize: number) => {
setGrouping(groupSize)
}
}, [])
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">Price</div>
</div>
<div className="hide-scroll relative h-full overflow-y-scroll">
{showSells
? orderbookData?.asks.map(
({
price,
size,
cumulativeSize,
sizePercent,
maxSizePercent,
}: cumOrderbookSide) => (
<MemoizedOrderbookRow
minOrderSize={serum3MarketExternal.minOrderSize}
tickSize={serum3MarketExternal.tickSize}
// hasOpenOrder={hasOpenOrderForPriceGroup(
// openOrderPrices,
// price,
// grouping
// )}
key={price + ''}
price={price}
size={displayCumulativeSize ? cumulativeSize : size}
side="sell"
sizePercent={
displayCumulativeSize ? maxSizePercent : sizePercent
}
grouping={grouping}
/>
<div
className="hide-scroll relative h-full overflow-y-scroll"
ref={orderbookElRef}
onScroll={() => setIsScrolled(true)}
>
{showSells && orderbookData?.asks?.length
? depthArray.map((_x, index) => {
return (
<div key={index}>
{orderbookData?.asks[index] ? (
<MemoizedOrderbookRow
minOrderSize={serum3MarketExternal.minOrderSize}
tickSize={serum3MarketExternal.tickSize}
// hasOpenOrder={hasOpenOrderForPriceGroup(
// openOrderPrices,
// price,
// grouping
// )}
key={orderbookData?.asks[index].price}
price={orderbookData?.asks[index].price}
size={
displayCumulativeSize
? orderbookData?.asks[index].cumulativeSize
: orderbookData?.asks[index].size
}
side="sell"
sizePercent={
displayCumulativeSize
? orderbookData?.asks[index].maxSizePercent
: orderbookData?.asks[index].sizePercent
}
grouping={grouping}
/>
) : null}
</div>
)
)
})
: null}
{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">
@ -452,34 +484,35 @@ const Orderbook = ({ depth = 12 }) => {
</div>
</div>
) : null}
{showBuys
? orderbookData?.bids.map(
({
price,
size,
cumulativeSize,
sizePercent,
maxSizePercent,
}: cumOrderbookSide) => (
<MemoizedOrderbookRow
minOrderSize={serum3MarketExternal.minOrderSize}
tickSize={serum3MarketExternal.tickSize}
// hasOpenOrder={hasOpenOrderForPriceGroup(
// openOrderPrices,
// price,
// grouping
// )}
key={price}
price={price}
size={displayCumulativeSize ? cumulativeSize : size}
side="buy"
sizePercent={
displayCumulativeSize ? maxSizePercent : sizePercent
}
grouping={grouping}
/>
)
)
{showBuys && orderbookData?.bids?.length
? depthArray.map((_x, index) => (
<div key={index}>
{orderbookData?.bids[index] ? (
<MemoizedOrderbookRow
minOrderSize={serum3MarketExternal.minOrderSize}
tickSize={serum3MarketExternal.tickSize}
// hasOpenOrder={hasOpenOrderForPriceGroup(
// openOrderPrices,
// price,
// grouping
// )}
price={orderbookData?.bids[index].price}
size={
displayCumulativeSize
? orderbookData?.bids[index].cumulativeSize
: orderbookData?.bids[index].size
}
side="buy"
sizePercent={
displayCumulativeSize
? orderbookData?.bids[index].maxSizePercent
: orderbookData?.bids[index].sizePercent
}
grouping={grouping}
/>
) : null}
</div>
))
: null}
</div>
</div>
@ -561,7 +594,7 @@ const OrderbookRow = ({
return (
<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}
>
<>

View File

@ -49,7 +49,7 @@ const getHeight = (
const TradeAdvancedPage = () => {
const { height, width } = useViewport()
const [currentBreakpoint, setCurrentBreakpoint] = useState<string>()
const [orderbookDepth, setOrderbookDepth] = useState(6)
const [orderbookDepth, setOrderbookDepth] = useState(60)
const { uiLocked } = mangoStore((s) => s.settings)
const showMobileView = width <= breakpoints.md
@ -150,29 +150,6 @@ const TradeAdvancedPage = () => {
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) => {
if (layouts) {
setSavedLayouts(layouts)