diff --git a/src/constants/orderbook.ts b/src/constants/orderbook.ts index 2b6805fd5..7e2cfb535 100644 --- a/src/constants/orderbook.ts +++ b/src/constants/orderbook.ts @@ -1,7 +1,9 @@ /** * @description Orderbook display constants */ +export const ORDERBOOK_MIN_ROWS_PER_SIDE = 2; export const ORDERBOOK_MAX_ROWS_PER_SIDE = 30; +export const ORDERBOOK_PAGE_HEIGHT_RATIO = 11 / 25; export const ORDERBOOK_ANIMATION_DURATION = 100; /** diff --git a/src/hooks/Orderbook/useOrderbookValues.ts b/src/hooks/Orderbook/useOrderbookValues.ts index bcd8e0385..30a8eb5a9 100644 --- a/src/hooks/Orderbook/useOrderbookValues.ts +++ b/src/hooks/Orderbook/useOrderbookValues.ts @@ -14,7 +14,7 @@ import { MustBigNumber } from '@/lib/numbers'; import { safeAssign } from '@/lib/objectHelpers'; import { orEmptyRecord } from '@/lib/typeUtils'; -export const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number }) => { +export const useCalculateOrderbookData = ({ rowsPerSide }: { rowsPerSide: number }) => { const orderbook = useAppSelector(getCurrentMarketOrderbook, shallowEqual); const subaccountOrderSizeBySideAndPrice = orEmptyRecord( @@ -37,7 +37,7 @@ export const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: row ) ) - .slice(0, maxRowsPerSide); + .slice(0, rowsPerSide); const bids: Array = ( orderbook?.bids?.toArray() ?? [] @@ -54,7 +54,7 @@ export const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: row ) ) - .slice(0, maxRowsPerSide); + .slice(0, rowsPerSide); const spread = orderbook?.spread; const spreadPercent = orderbook?.spreadPercent; @@ -73,7 +73,7 @@ export const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: hasOrderbook: !!orderbook, currentGrouping: orderbook?.grouping, }; - }, [maxRowsPerSide, orderbook, subaccountOrderSizeBySideAndPrice]); + }, [rowsPerSide, orderbook, subaccountOrderSizeBySideAndPrice]); }; export const useOrderbookValuesForDepthChart = () => { diff --git a/src/pages/trade/Trade.tsx b/src/pages/trade/Trade.tsx index d7040f6a5..d07e735c5 100644 --- a/src/pages/trade/Trade.tsx +++ b/src/pages/trade/Trade.tsx @@ -43,77 +43,80 @@ const TradePage = () => { usePageTitlePriceUpdates(); useTradeFormInputs(); + const header = ( + <$Header> + + + ); + + // Depending on the screensize, we either render the market selector header as a header across the whole page, or just as a header to the markets chart + const maybePageHeader = !isDesktopMedium && header; + const maybeChartSectionHeader = isDesktopMedium && header; + + const sidebar = ( + <$ParentSection> + + <$TradeBox /> + + ); + const tradeSection = ( + <$TradeSection> + + + ); + const chartSection = ( + <$ChartSection> + {maybeChartSectionHeader} + + + ); + const horizontalSection = ( + <$HorizontalSection> + + + ); + const desktopLayout = () => { - const top = ( - <$Top> - - - ); - const sidebar = ( - <$SideSection gridArea="Side"> - - <$TradeBox /> - - ); - const orderbookTradePanel = ( - <$TradeSection> - - - ); - const tradingChart = ( - <$InnerSection> - {isDesktopMedium && top} - - - ); - const horizontalPane = ( - <$HorizontalSection> - - - ); switch (tradeLayout) { case TradeLayouts.Alternative: return ( - <$TradeLayout ref={tradePageRef} isHorizontalPanelOpen={isHorizontalPanelOpen}> - {!isDesktopMedium && top} - <$MainSection gridArea="Main"> - <$TestSection> - {orderbookTradePanel} - {tradingChart} - - {horizontalPane} + <> + <$MainSection> + <$BorderedContainer> + {tradeSection} + {chartSection} + + {horizontalSection} {sidebar} - + ); case TradeLayouts.Reverse: return ( - <$TradeLayout ref={tradePageRef} isHorizontalPanelOpen={isHorizontalPanelOpen}> - {!isDesktopMedium && top} - <$MainSection gridArea="Main"> - <$TestSection> - {tradingChart} - {orderbookTradePanel} - - {horizontalPane} + <> + <$MainSection> + <$BorderedContainer> + {chartSection} + {tradeSection} + + {horizontalSection} {sidebar} - + ); case TradeLayouts.Default: default: return ( - <$TradeLayout ref={tradePageRef} isHorizontalPanelOpen={isHorizontalPanelOpen}> - {!isDesktopMedium && top} + <> {sidebar} - <$MainSection gridArea="Main"> - <$TestSection> - {orderbookTradePanel} - {tradingChart} - - {horizontalPane} + <$MainSection> + <$BorderedContainer> + {tradeSection} + {chartSection} + + {horizontalSection} - + ); } }; @@ -139,27 +142,15 @@ const TradePage = () => { {canAccountTrade && } ) : ( - desktopLayout() + <$TradeLayoutDesktop ref={tradePageRef}> + <> + {maybePageHeader} {desktopLayout()} + + ); - // <$TradeLayout ref={tradePageRef} isHorizontalPanelOpen={isHorizontalPanelOpen}> - /* */ }; export default TradePage; -const $TradeLayout = styled.article<{ - isHorizontalPanelOpen: boolean; -}>` - // Rules - width: 0; - min-width: 100%; - height: 0; - min-height: 100%; - - display: flex; - flex-wrap: wrap; - - ${layoutMixins.withOuterAndInnerBorders}; -`; const $TradeLayoutMobile = styled.article` ${layoutMixins.contentContainerPage} @@ -179,17 +170,23 @@ const $TradeLayoutMobile = styled.article` } `; -const $Top = styled.header` - box-shadow: none; +const $TradeLayoutDesktop = styled.article` + width: 0; + min-width: 100%; + height: 0; + min-height: 100%; + + display: flex; + flex-wrap: wrap; + + ${layoutMixins.withOuterAndInnerBorders}; `; -const $GridSection = styled.section<{ gridArea: string }>` - grid-area: ${({ gridArea }) => gridArea}; +const $Header = styled.header` + box-shadow: none; `; -const $SideSection = styled($GridSection)` - grid-template-rows: auto minmax(0, 1fr); - height: 100%; +const $ParentSection = styled.section` display: flex; flex-direction: column; @@ -200,44 +197,35 @@ const $SideSection = styled($GridSection)` } `; -const $MainSection = styled($GridSection)` - display: flex; - flex-direction: column; - height: calc(100% - var(--market-info-row-height)); - - @media ${breakpoints.desktopMedium} { - height: 100%; - } +const $MainSection = styled($ParentSection)` + flex: 1 1 1px; ${layoutMixins.withOuterAndInnerBorders}; +`; - flex: 1 1 1px; +const $TradeSection = styled.section` + width: var(--orderbook-trades-width); `; -const $TestSection = styled.div` +const $ChartSection = styled.section` display: flex; flex-grow: 1; - ${layoutMixins.withOuterAndInnerBorders}; + flex-direction: column; `; -const $TradeSection = styled.div` - width: var(--orderbook-trades-width); +const $HorizontalSection = styled.section` + overflow: auto; `; -const $InnerSection = styled.div` - flex: 1; +const $BorderedContainer = styled.div` display: flex; - flex-direction: column; -`; + flex-grow: 1; -const $HorizontalSection = styled.div` - overflow: hidden; - width: 100%; - display: grid; + ${layoutMixins.withOuterAndInnerBorders}; `; const $TradeBox = styled(TradeBox)` - overflow-y: auto; height: 100%; + overflow-y: auto; padding-top: var(--border-width); `; diff --git a/src/pages/trade/VerticalPanel.tsx b/src/pages/trade/VerticalPanel.tsx index 23cfce577..3cd893da3 100644 --- a/src/pages/trade/VerticalPanel.tsx +++ b/src/pages/trade/VerticalPanel.tsx @@ -2,7 +2,12 @@ import { useEffect, useRef, useState } from 'react'; import { TradeLayouts } from '@/constants/layout'; import { STRING_KEYS } from '@/constants/localization'; -import { ORDERBOOK_MAX_ROWS_PER_SIDE, ORDERBOOK_ROW_HEIGHT } from '@/constants/orderbook'; +import { + ORDERBOOK_MAX_ROWS_PER_SIDE, + ORDERBOOK_MIN_ROWS_PER_SIDE, + ORDERBOOK_PAGE_HEIGHT_RATIO, + ORDERBOOK_ROW_HEIGHT, +} from '@/constants/orderbook'; import { useStringGetter } from '@/hooks/useStringGetter'; @@ -21,46 +26,50 @@ const HISTOGRAM_SIDES_BY_LAYOUT = { [TradeLayouts.Reverse]: 'right', } as const; -// xcxc -const MIN_NUMBER_ROWS = 2; -const MAX_NUMBER_ROWS = 30; - export const VerticalPanel = ({ tradeLayout }: { tradeLayout: TradeLayouts }) => { const stringGetter = useStringGetter(); const [value, setValue] = useState(Tab.Orderbook); - const [maxNumRowsToShow, setMaxNumRowsToShow] = useState(ORDERBOOK_MAX_ROWS_PER_SIDE); + const [numRowsToShow, setNumRowsToShow] = useState(ORDERBOOK_MAX_ROWS_PER_SIDE); const [prevHeight, setPrevHeight] = useState(undefined); const canvasOrderbookRef = useRef(null); useEffect(() => { const calculateNumRows = () => { + // Calculates the number of rows the orderbook should render on each side const viewportHeight = window.innerHeight; - const verticalPanelHeight = Math.floor((viewportHeight / 51) * 19); - const numRows = Math.floor( - (verticalPanelHeight - 2 * ORDERBOOK_ROW_HEIGHT) / (2 * ORDERBOOK_ROW_HEIGHT) - ); - let secondRowsCalculation = numRows; + // The initial calculation calculates the number of rows we should render based on the current window height. + const verticalPanelHeight = Math.floor(viewportHeight * ORDERBOOK_PAGE_HEIGHT_RATIO); + const minNumRowsToRender = Math.floor( + (verticalPanelHeight - ORDERBOOK_ROW_HEIGHT) / (2 * ORDERBOOK_ROW_HEIGHT) + ); - if (canvasOrderbookRef.current && canvasOrderbookRef.current.clientHeight - 66 > 0) { - secondRowsCalculation = Math.floor( - (canvasOrderbookRef.current.clientHeight - 66 - ORDERBOOK_ROW_HEIGHT) / - (2 * ORDERBOOK_ROW_HEIGHT) + // The second calculation checks if there is extra vertical space (relevant to large windows) that is not being + // otherwise used (the parent will flex-grow to fill the space); if so, it calculates the updated number of rows we + // can now render + let maxNumRowsToRender = minNumRowsToRender; + if (canvasOrderbookRef.current) { + maxNumRowsToRender = Math.max( + Math.floor( + (canvasOrderbookRef.current.clientHeight - 66 - ORDERBOOK_ROW_HEIGHT) / + (2 * ORDERBOOK_ROW_HEIGHT) + ), + maxNumRowsToRender ); } - const newRows = Math.max( + const numRows = Math.max( Math.min( prevHeight && viewportHeight >= prevHeight - ? Math.max(numRows, secondRowsCalculation) - : Math.min(numRows, secondRowsCalculation), - MAX_NUMBER_ROWS + ? Math.max(minNumRowsToRender, maxNumRowsToRender) + : Math.min(minNumRowsToRender, maxNumRowsToRender), + ORDERBOOK_MAX_ROWS_PER_SIDE ), - MIN_NUMBER_ROWS + ORDERBOOK_MIN_ROWS_PER_SIDE ); - setMaxNumRowsToShow(newRows); + setNumRowsToShow(numRows); setPrevHeight(viewportHeight); }; @@ -68,7 +77,7 @@ export const VerticalPanel = ({ tradeLayout }: { tradeLayout: TradeLayouts }) => window.addEventListener('resize', calculateNumRows); return () => window.removeEventListener('resize', calculateNumRows); - }, [canvasOrderbookRef, prevHeight]); + }, [canvasOrderbookRef, prevHeight, canvasOrderbookRef.current?.clientHeight]); return ( asChild: true, content: ( ), diff --git a/src/views/CanvasOrderbook/CanvasOrderbook.tsx b/src/views/CanvasOrderbook/CanvasOrderbook.tsx index 07f713640..5e993356f 100644 --- a/src/views/CanvasOrderbook/CanvasOrderbook.tsx +++ b/src/views/CanvasOrderbook/CanvasOrderbook.tsx @@ -33,7 +33,7 @@ import { OrderbookControls } from './OrderbookControls'; import { OrderbookMiddleRow, OrderbookRow } from './OrderbookRow'; type ElementProps = { - maxRowsPerSide?: number; + rowsPerSide?: number; layout?: 'vertical' | 'horizontal'; }; @@ -48,13 +48,13 @@ export const CanvasOrderbook = forwardRef( histogramSide = 'right', hideHeader = false, layout = 'vertical', - maxRowsPerSide = ORDERBOOK_MAX_ROWS_PER_SIDE, + rowsPerSide = ORDERBOOK_MAX_ROWS_PER_SIDE, }: ElementProps & StyleProps, ref: React.ForwardedRef ) => { const { asks, bids, hasOrderbook, histogramRange, currentGrouping } = useCalculateOrderbookData( { - maxRowsPerSide, + rowsPerSide, } ); @@ -66,12 +66,12 @@ export const CanvasOrderbook = forwardRef( const { tickSizeDecimals = USD_DECIMALS } = currentMarketConfig ?? {}; /** - * Slice asks and bids to maxRowsPerSide using empty rows + * Slice asks and bids to rowsPerSide using empty rows */ const { asksSlice, bidsSlice } = useMemo(() => { const emptyAskRows = - asks.length < maxRowsPerSide - ? new Array(maxRowsPerSide - asks.length).fill(undefined) + asks.length < rowsPerSide + ? new Array(rowsPerSide - asks.length).fill(undefined) : []; const newAsksSlice: Array = [ @@ -80,8 +80,8 @@ export const CanvasOrderbook = forwardRef( ]; const emptyBidRows = - bids.length < maxRowsPerSide - ? new Array(maxRowsPerSide - bids.length).fill(undefined) + bids.length < rowsPerSide + ? new Array(rowsPerSide - bids.length).fill(undefined) : []; const newBidsSlice: Array = [ ...bids, @@ -92,7 +92,7 @@ export const CanvasOrderbook = forwardRef( asksSlice: layout === 'horizontal' ? newAsksSlice : newAsksSlice.reverse(), bidsSlice: newBidsSlice, }; - }, [asks, bids, layout, maxRowsPerSide]); + }, [asks, bids, layout, rowsPerSide]); const orderbookRef = useRef(null); useCenterOrderbook({ @@ -151,7 +151,7 @@ export const CanvasOrderbook = forwardRef( const usdTag = USD; const assetTag = id ? {id} : undefined; const asksOrderbook = ( - <$OrderbookSideContainer $side="asks" $rows={maxRowsPerSide}> + <$OrderbookSideContainer $side="asks" $rows={rowsPerSide}> <$HoverRows $bottom={layout !== 'horizontal'}> {[...asksSlice].reverse().map((row: PerpetualMarketOrderbookLevel | undefined, idx) => row ? ( @@ -173,7 +173,7 @@ export const CanvasOrderbook = forwardRef( ); const bidsOrderbook = ( - <$OrderbookSideContainer $side="bids" $rows={maxRowsPerSide}> + <$OrderbookSideContainer $side="bids" $rows={rowsPerSide}> <$HoverRows> {bidsSlice.map((row: PerpetualMarketOrderbookLevel | undefined, idx) => row ? ( @@ -261,7 +261,6 @@ export const CanvasOrderbook = forwardRef( const $OrderbookContainer = styled.div` display: flex; - // flex: 1 1 0%; flex-direction: column; overflow: hidden; `; diff --git a/src/views/forms/TradeForm.tsx b/src/views/forms/TradeForm.tsx index bfc56e69e..6cdb3726c 100644 --- a/src/views/forms/TradeForm.tsx +++ b/src/views/forms/TradeForm.tsx @@ -236,7 +236,7 @@ export const TradeForm = ({ const orderbookAndInputs = ( <$OrderbookAndInputs showOrderbook={showOrderbook}> - {isTablet && showOrderbook && <$Orderbook maxRowsPerSide={5} hideHeader />} + {isTablet && showOrderbook && <$Orderbook rowsPerSide={5} hideHeader />} <$InputsColumn>