import type { CSSProperties } from 'react'; import { useMemo } from 'react'; import type { DlobDepthBandRow } from './useDlobDepthBands'; function formatUsd(v: number | null | undefined): string { if (v == null || !Number.isFinite(v)) return '—'; if (v >= 1_000_000) return `$${(v / 1_000_000).toFixed(2)}M`; if (v >= 1000) return `$${(v / 1000).toFixed(0)}K`; if (v >= 1) return `$${v.toFixed(2)}`; return `$${v.toPrecision(4)}`; } function formatPct(v: number | null | undefined): string { if (v == null || !Number.isFinite(v)) return '—'; return `${v.toFixed(0)}%`; } function bandRowStyle(askScale: number, bidScale: number): CSSProperties { const a = Number.isFinite(askScale) && askScale > 0 ? Math.min(1, askScale) : 0; const b = Number.isFinite(bidScale) && bidScale > 0 ? Math.min(1, bidScale) : 0; return { ['--ask-scale' as any]: a, ['--bid-scale' as any]: b } as CSSProperties; } export default function DlobDepthBandsPanel({ rows }: { rows: DlobDepthBandRow[] }) { const sorted = useMemo(() => rows.slice().sort((a, b) => a.bandBps - b.bandBps), [rows]); const maxUsd = useMemo(() => { let max = 0; for (const r of sorted) { if (r.askUsd != null && Number.isFinite(r.askUsd)) max = Math.max(max, r.askUsd); if (r.bidUsd != null && Number.isFinite(r.bidUsd)) max = Math.max(max, r.bidUsd); } return max; }, [sorted]); return (
Depth (bands)
±bps around mid
Band Ask USD Bid USD Bid %
{sorted.length ? ( sorted.map((r) => (
0 ? (r.askUsd || 0) / maxUsd : 0, maxUsd > 0 ? (r.bidUsd || 0) / maxUsd : 0)} title={`band=${r.bandBps}bps bid=${r.bidUsd ?? '—'} ask=${r.askUsd ?? '—'} imbalance=${r.imbalance ?? '—'}`} > {r.bandBps} bps {formatUsd(r.askUsd)} {formatUsd(r.bidUsd)} {r.imbalance == null || !Number.isFinite(r.imbalance) ? '—' : formatPct(((r.imbalance + 1) / 2) * 100)}
)) ) : (
No depth band rows yet.
)}
); }