diff --git a/src/hooks/orderbook/useDrawOrderbookHistograms.ts b/src/hooks/orderbook/useDrawOrderbookHistograms.ts index fba9a32..ed9979c 100644 --- a/src/hooks/orderbook/useDrawOrderbookHistograms.ts +++ b/src/hooks/orderbook/useDrawOrderbookHistograms.ts @@ -29,6 +29,134 @@ export const useDrawOrderbookHistograms = ({ const canvasRef = useRef(null); const canvas = canvasRef.current; + /** + * to === 'right' to === 'left' + * |===================| |===================| + * bar: a b c d + * a = 0 + * b = depthBarWidth + * c = canvasWidth - depthBarWidth + * d = canvasWidth + * + */ + const getXYValues = ({ + barWidth, + canvasWidth, + gradientMultiplier, + }: { + barWidth: number; + canvasWidth: number; + gradientMultiplier: number; + }) => { + const gradient = { + x1: to === 'right' ? barWidth : canvasWidth - barWidth, + x2: + to === 'right' + ? 0 - (canvasWidth * gradientMultiplier - canvasWidth) + : canvasWidth * gradientMultiplier, + }; + const bar = { + x1: to === 'right' ? 0 : canvasWidth - barWidth, + x2: to === 'right' ? Math.min(barWidth, canvasWidth - 2) : canvasWidth - 2, + }; + + return { + bar, + gradient, + }; + }; + + const drawBars = ({ + value, + canvasWidth, + ctx, + gradientMultiplier, + histogramAccentColor, + idx, + to, + }: { + value: Nullable; + canvasWidth: number; + ctx: CanvasRenderingContext2D; + gradientMultiplier: number; + histogramAccentColor: string; + idx: number; + to: 'left' | 'right'; + }) => { + const histogramBarWidth = ctx.canvas.width - 2; + const barWidth = value ? (value / histogramRange) * histogramBarWidth : 0; + const { gradient, bar } = getXYValues({ barWidth, canvasWidth, gradientMultiplier }); + + const linearGradient = ctx.createLinearGradient(gradient.x1, 10, gradient.x2, 10); + linearGradient.addColorStop(0, histogramAccentColor); + linearGradient.addColorStop(1, 'rgba(0, 0, 0, 0)'); + ctx.fillStyle = linearGradient; + + ctx.beginPath(); + + ctx.roundRect + ? ctx.roundRect?.( + bar.x1, + idx * 20 + 1, + bar.x2, + 18, + to === 'left' ? [2, 0, 0, 2] : [0, 2, 2, 0] + ) + : ctx.rect(bar.x1, idx * 20 + 1, bar.x2, 18); + ctx.fill(); + }; + + const drawText = ({ + ctx, + idx, + size, + price, + mine, + x, + }: { + ctx: CanvasRenderingContext2D; + idx: number; + size?: Nullable; + price?: Nullable; + mine?: Nullable; + x: number; + }) => { + ctx.font = `12.5px Satoshi`; + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.imageSmoothingQuality = 'high'; + + // Size text + if (size) { + ctx.fillStyle = 'white'; + ctx.fillText( + MustBigNumber(size).toFixed(stepSizeDecimals ?? TOKEN_DECIMALS), + x - 8, + idx * 20 + 10 + ); + } + + // Price text + if (price) { + ctx.fillStyle = 'white'; + ctx.fillText( + MustBigNumber(price).toFixed(tickSizeDecimals ?? SMALL_USD_DECIMALS), + x * 2 - 8, + idx * 20 + 10 + ); + } + + // Mine text + if (mine) { + ctx.fillStyle = 'white'; + ctx.fillText( + MustBigNumber(mine).toFixed(stepSizeDecimals ?? TOKEN_DECIMALS), + x * 3 - 8, + idx * 20 + 10 + ); + } + }; + useEffect(() => { const ctx = canvas?.getContext('2d'); @@ -73,119 +201,42 @@ export const useDrawOrderbookHistograms = ({ histogramAlpha = 0.75; } - const histogramBarWidth = ctx.canvas.width - 2; const histogramAccentColor = side === 'bid' ? `hsla(159, 67%, 39%, ${histogramAlpha})` : `hsla(360, 73%, 61%, ${histogramAlpha})`; - /** - * to === 'right' to === 'left' - * |===================| |===================| - * bar: a b c d - * a = 0 - * b = depthBarWidth - * c = canvasWidth - depthBarWidth - * d = canvasWidth - * - */ - const getXYValues = ({ - barWidth, - gradientMultiplier, - }: { - barWidth: number; - gradientMultiplier: number; - }) => { - const gradient = { - x1: to === 'right' ? barWidth : canvasWidth - barWidth, - x2: - to === 'right' - ? 0 - (canvasWidth * gradientMultiplier - canvasWidth) - : canvasWidth * gradientMultiplier, - }; - const bar = { - x1: to === 'right' ? 0 : canvasWidth - barWidth, - x2: to === 'right' ? Math.min(barWidth, canvasWidth - 2) : canvasWidth - 2, - }; - - return { - bar, - gradient, - }; - }; - - const drawBars = ({ - barWidth, - gradientMultiplier, - }: { - barWidth: number; - gradientMultiplier: number; - }) => { - const { gradient, bar } = getXYValues({ barWidth, gradientMultiplier }); - const linearGradient = ctx.createLinearGradient(gradient.x1, 10, gradient.x2, 10); - linearGradient.addColorStop(0, histogramAccentColor); - linearGradient.addColorStop(1, 'rgba(0, 0, 0, 0)'); - ctx.fillStyle = linearGradient; - ctx.beginPath(); - - ctx.roundRect - ? ctx.roundRect?.( - bar.x1, - idx * 20 + 1, - bar.x2, - 18, - to === 'left' ? [2, 0, 0, 2] : [0, 2, 2, 0] - ) - : ctx.rect(bar.x1, idx * 20 + 1, bar.x2, 18); - ctx.fill(); - }; - // Depth Bar - const depthBarWidth = depth ? (depth / histogramRange) * histogramBarWidth : 0; - drawBars({ barWidth: depthBarWidth, gradientMultiplier: 2 }); + drawBars({ + value: depth, + canvasWidth, + ctx, + gradientMultiplier: 2, + histogramAccentColor, + idx, + to, + }); // Size Bar - const sizeBarWidth = size ? (size / histogramRange) * histogramBarWidth : 0; - drawBars({ barWidth: sizeBarWidth, gradientMultiplier: 5 }); + drawBars({ + value: size, + canvasWidth, + ctx, + gradientMultiplier: 5, + histogramAccentColor, + idx, + to, + }); - const drawText = () => { - ctx.font = `12.5px Satoshi`; - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.imageSmoothingQuality = 'high'; - - // Size text - if (size) { - ctx.fillStyle = 'white'; - ctx.fillText( - MustBigNumber(size).toFixed(stepSizeDecimals ?? TOKEN_DECIMALS), - columnX - 8, - idx * 20 + 10 - ); - } - - // Price text - if (price) { - ctx.fillStyle = 'white'; - ctx.fillText( - MustBigNumber(price).toFixed(tickSizeDecimals ?? SMALL_USD_DECIMALS), - columnX * 2 - 8, - idx * 20 + 10 - ); - } - - // Mine text - if (mine) { - ctx.fillStyle = 'white'; - ctx.fillText( - MustBigNumber(mine).toFixed(stepSizeDecimals ?? TOKEN_DECIMALS), - columnX * 3 - 8, - idx * 20 + 10 - ); - } - }; - - drawText(); + // Size, Price, Mine + drawText({ + ctx, + idx, + size, + price, + mine, + x: columnX, + }); }); }, [data, histogramRange, hoveredRow, selectedTheme, stepSizeDecimals, tickSizeDecimals, to]); diff --git a/src/hooks/orderbook/useSpreadRowScrollListener.ts b/src/hooks/orderbook/useSpreadRowScrollListener.ts index 50712e6..38fb9bf 100644 --- a/src/hooks/orderbook/useSpreadRowScrollListener.ts +++ b/src/hooks/orderbook/useSpreadRowScrollListener.ts @@ -1,4 +1,4 @@ -import { type RefObject, useEffect, useState } from 'react'; +import { type RefObject, useEffect, useState, Dispatch } from 'react'; export const useSpreadRowScrollListener = ({ orderbookRef, @@ -11,18 +11,17 @@ export const useSpreadRowScrollListener = ({ useEffect(() => { const onScroll = () => { - if (spreadRowRef.current) { - const { top } = spreadRowRef.current.getBoundingClientRect(); - const { scrollHeight, scrollTop } = orderbookRef.current || {}; + if (spreadRowRef.current && orderbookRef.current) { + const { clientHeight } = orderbookRef.current; + const parent = orderbookRef.current.getBoundingClientRect(); + const spread = spreadRowRef.current.getBoundingClientRect(); + const spreadTop = spread.top - parent.top; + const spreadBottom = spread.bottom - parent.top; - if (scrollHeight !== undefined && scrollTop !== undefined) { - if (top > scrollHeight / 2) { - setDisplaySide('bottom'); - } else if (scrollTop > scrollHeight / 2) { - setDisplaySide('top'); - } else { - setDisplaySide(undefined); - } + if (spreadBottom > clientHeight) { + setDisplaySide('bottom'); + } else if (spreadTop < 0) { + setDisplaySide('top'); } else { setDisplaySide(undefined); } @@ -34,7 +33,7 @@ export const useSpreadRowScrollListener = ({ return () => { orderbookRef.current?.removeEventListener('scroll', onScroll, false); }; - }, [orderbookRef.current]); + }, [orderbookRef.current, spreadRowRef.current]); - return { displaySide }; + return displaySide; }; diff --git a/src/views/Orderbook.tsx b/src/views/Orderbook.tsx index a02d055..49903aa 100644 --- a/src/views/Orderbook.tsx +++ b/src/views/Orderbook.tsx @@ -92,7 +92,8 @@ export const Orderbook = ({ * Display top or bottom spreadRow when center spreadRow is off screen */ const spreadRowRef = useRef(null); - const { displaySide } = useSpreadRowScrollListener({ + + const displaySide = useSpreadRowScrollListener({ orderbookRef, spreadRowRef, }); @@ -143,7 +144,7 @@ export const Orderbook = ({ {showMineColumn && stringGetter({ key: STRING_KEYS.MINE })} {displaySide === 'top' && ( - {displaySide === 'bottom' && ( - void }>` ${({ onClick }) => (onClick ? 'cursor: pointer;' : 'cursor: default;')} `; + +const $SpreadRow = styled(SpreadRow)` + position: absolute; +`; diff --git a/src/views/Orderbook/OrderbookRow.tsx b/src/views/Orderbook/OrderbookRow.tsx index 0b418b6..0ad8189 100644 --- a/src/views/Orderbook/OrderbookRow.tsx +++ b/src/views/Orderbook/OrderbookRow.tsx @@ -17,7 +17,6 @@ export const Row = styled.div` min-height: ${ROW_HEIGHT}px; font: var(--font-mini-book); - cursor: pointer; position: relative; padding-right: 0.5rem;