feat(trading): update layout on mobile

This commit is contained in:
Madalina Raicu 2024-02-01 16:11:04 +00:00
parent 18a3786c98
commit f5406941e7
No known key found for this signature in database
GPG Key ID: 688B7B31149C1DCD
3 changed files with 149 additions and 81 deletions

View File

@ -3,7 +3,6 @@ import { type Market } from '@vegaprotocol/markets';
// TODO: handle oracle banner // TODO: handle oracle banner
// import { OracleBanner } from '@vegaprotocol/markets'; // import { OracleBanner } from '@vegaprotocol/markets';
import { useState } from 'react'; import { useState } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import classNames from 'classnames'; import classNames from 'classnames';
import { import {
Popover, Popover,
@ -23,10 +22,12 @@ interface TradePanelsProps {
} }
export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => { export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
const [view, setView] = useState<TradingView>('chart'); const [view1, setView1] = useState<TradingView>('chart');
const viewCfg = TradingViews[view]; const viewCfg1 = TradingViews[view1];
const [view2, setView2] = useState<TradingView>('positions');
const viewCfg2 = TradingViews[view2];
const renderView = () => { const renderView = (view: TradingView) => {
const Component = TradingViews[view].component; const Component = TradingViews[view].component;
if (!Component) { if (!Component) {
@ -44,7 +45,8 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
); );
}; };
const renderMenu = () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any
const renderMenu = (viewCfg: any) => {
if ('menu' in viewCfg || 'settings' in viewCfg) { if ('menu' in viewCfg || 'settings' in viewCfg) {
return ( return (
<div className="flex items-center justify-end gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default"> <div className="flex items-center justify-end gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
@ -69,56 +71,86 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
}; };
return ( return (
<div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]"> <div className="h-full flex flex-col lg:grid grid-rows-[min-content_min-content_1fr_min-content]">
<div> <div>
<MarketBanner market={market} /> <MarketBanner market={market} />
</div> </div>
<div>{renderMenu()}</div> <div className="flex flex-col w-full overflow-hidden">
<div className="h-full relative"> <div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
<AutoSizer> {['chart', 'orderbook', 'trades', 'liquidity', 'fundingPayments']
{({ width, height }) => ( // filter to control available views for the current market
<div style={{ width, height }} className="overflow-auto"> // e.g. only perpetuals should get the funding views
{renderView()} .filter((_key) => {
</div> const key = _key as TradingView;
)} const perpOnlyViews = ['funding', 'fundingPayments'];
</AutoSizer>
</div> if (
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default"> market?.tradableInstrument.instrument.product.__typename ===
{Object.keys(TradingViews) 'Perpetual'
// filter to control available views for the current market ) {
// eg only perps should get the funding views return true;
.filter((_key) => { }
const key = _key as TradingView;
const perpOnlyViews = ['funding', 'fundingPayments']; if (perpOnlyViews.includes(key)) {
return false;
}
if (
market?.tradableInstrument.instrument.product.__typename ===
'Perpetual'
) {
return true; return true;
} })
.map((_key) => {
if (perpOnlyViews.includes(key)) { const key = _key as TradingView;
return false; const isActive = view1 === key;
} return (
<ViewButton
return true; key={key}
}) view={key}
.map((_key) => { isActive={isActive}
const key = _key as TradingView; onClick={() => {
const isActive = view === key; setView1(key);
return ( }}
<ViewButton />
key={key} );
view={key} })}
isActive={isActive} </div>
onClick={() => { <div className="h-[376px] sm:h-[460px] lg:h-full relative">
setView(key); <div>{renderMenu(viewCfg1)}</div>
}} <div className="overflow-auto h-full">{renderView(view1)}</div>
/> </div>
);
})}
</div> </div>
{
<div className="flex flex-col w-full grow">
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
{[
'positions',
'activeOrders',
'closedOrders',
'rejectedOrders',
'orders',
'stopOrders',
'collateral',
'fills',
].map((_key) => {
const key = _key as TradingView;
const isActive = view2 === key;
return (
<ViewButton
key={key}
view={key}
isActive={isActive}
onClick={() => {
setView2(key);
}}
/>
);
})}
</div>
<div className="relative grow">
<div className="flex flex-col">{renderMenu(viewCfg2)}</div>
<div className="overflow-auto h-full">{renderView(view2)}</div>
</div>
</div>
}
</div> </div>
); );
}; };
@ -157,7 +189,7 @@ const useViewLabel = (view: TradingView) => {
depth: t('Depth'), depth: t('Depth'),
liquidity: t('Liquidity'), liquidity: t('Liquidity'),
funding: t('Funding'), funding: t('Funding'),
fundingPayments: t('Funding Payments'), fundingPayments: t('Funding'),
orderbook: t('Orderbook'), orderbook: t('Orderbook'),
trades: t('Trades'), trades: t('Trades'),
positions: t('Positions'), positions: t('Positions'),

View File

@ -27,10 +27,13 @@ export const LayoutWithSidebar = ({
<div className={gridClasses}> <div className={gridClasses}>
<div className="col-span-full">{header}</div> <div className="col-span-full">{header}</div>
<main <main
className={classNames('col-start-1 col-end-1 overflow-y-auto', { className={classNames(
'lg:col-end-3': !sidebarOpen, 'col-start-1 col-end-1 overflow-y-auto grow lg:grow-0',
'hidden lg:block lg:col-end-2': sidebarOpen, {
})} 'lg:col-end-3': !sidebarOpen,
'hidden lg:block lg:col-end-2': sidebarOpen,
}
)}
> >
<Outlet /> <Outlet />
</main> </main>

View File

@ -14,7 +14,8 @@ export const NavHeader = () => {
const t = useT(); const t = useT();
const { marketId } = useParams(); const { marketId } = useParams();
const { data } = useMarket(marketId); const { data } = useMarket(marketId);
const [open, setOpen] = useState(false); const [openMarket, setOpenMarket] = useState(false);
const [openPrice, setOpenPrice] = useState(false);
// Ensure that markets are kept cached so opening the list // Ensure that markets are kept cached so opening the list
// shows all markets instantly // shows all markets instantly
@ -23,32 +24,64 @@ export const NavHeader = () => {
if (!marketId) return null; if (!marketId) return null;
return ( return (
<FullScreenPopover <div className="flex items-center gap-2">
open={open} <FullScreenPopover
onOpenChange={(x) => { open={openMarket}
setOpen(x); onOpenChange={(x) => {
}} setOpenMarket(x);
trigger={ }}
<h1 className="flex gap-1 sm:gap-2 md:gap-4 items-center text-default text-lg whitespace-nowrap xl:pr-4 xl:border-r border-default"> trigger={
{data ? data.tradableInstrument.instrument.code : t('Select market')} <h1 className="flex gap-1 sm:gap-2 md:gap-4 items-center text-default text-sm md:text-lg whitespace-nowrap xl:pr-4 xl:border-r border-default">
<span {data
className={classNames( ? data.tradableInstrument.instrument.code
'transition-transform ease-in-out duration-300', : t('Select market')}
{ <span
'rotate-180': open, className={classNames(
} 'transition-transform ease-in-out duration-300',
)} {
> 'rotate-180': openMarket,
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} /> }
</span> )}
</h1> >
} <VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={12} />
> </span>
<MarketSelector </h1>
currentMarketId={marketId} }
onSelect={() => setOpen(false)} >
/> <MarketSelector
</FullScreenPopover> currentMarketId={marketId}
onSelect={() => setOpenMarket(false)}
/>
</FullScreenPopover>
{/* // TODO MOBILE - price popover with market header content */}
<FullScreenPopover
open={openPrice}
onOpenChange={(x) => {
setOpenPrice(x);
}}
trigger={
<h1 className="flex gap-1 sm:gap-2 md:gap-4 items-center text-default text-xs md:text-md whitespace-nowrap xl:pr-4 xl:border-r border-default">
44,500
<span
className={classNames(
'transition-transform ease-in-out duration-300',
{
'rotate-180': openPrice,
}
)}
>
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={12} />
</span>
</h1>
}
>
<MarketSelector
currentMarketId={marketId}
onSelect={() => setOpenMarket(false)}
/>
{/* <MarketHeader /> */}
</FullScreenPopover>
</div>
); );
}; };