import { useRef, useEffect, useState, useCallback } from 'react';
import { TradeGroup } from './TradeGroup';
interface Trade {
id: number;
symbol: string;
trade_date: string;
trade_type: 'buy' | 'sell';
quantity: number;
price: number;
exchange: string;
order_id: string;
}
interface TradeGroupType {
symbol: string;
accountId: number;
accountName: string;
totalBuyQuantity: number;
totalSellQuantity: number;
netQuantity: number;
totalBuyValue: number;
totalSellValue: number;
avgBuyPrice: number;
avgSellPrice: number;
currentPrice: number;
currentValue: number;
realizedPnL: number;
realizedPnLPercent: number;
unrealizedPnL: number;
unrealizedPnLPercent: number;
totalPnL: number;
status: 'active' | 'sold';
xirr: number | null;
trades: Trade[];
firstTradeDate: string;
lastTradeDate: string;
// For sold positions only
currentValueIfHeld?: number;
opportunityCost?: number;
opportunityCostPercent?: number;
}
interface VirtualTradeListProps {
groups: TradeGroupType[];
expandedGroups: Set<string>;
editingTrade: number | null;
editValues: { symbol?: string; quantity?: string; price?: string };
onToggleGroup: (key: string) => void;
onStartEdit: (trade: Trade) => void;
onSave: (tradeId: number) => void;
onCancelEdit: () => void;
onEditChange: (values: { symbol?: string; quantity?: string; price?: string }) => void;
formatDate: (date: string) => string;
formatCurrency: (amount: number) => string;
}
// Virtual scrolling for large lists (only renders visible items + buffer)
export function VirtualTradeList({
groups,
expandedGroups,
editingTrade,
editValues,
onToggleGroup,
onStartEdit,
onSave,
onCancelEdit,
onEditChange,
formatDate,
formatCurrency,
}: VirtualTradeListProps) {
const containerRef = useRef<HTMLDivElement>(null);
const [visibleRange, setVisibleRange] = useState({ start: 0, end: 20 });
// Estimated height per item (collapsed)
const ITEM_HEIGHT = 150;
const BUFFER_SIZE = 5; // Render extra items above and below viewport
const updateVisibleRange = useCallback(() => {
if (!containerRef.current) return;
const scrollTop = window.scrollY;
const viewportHeight = window.innerHeight;
const startIndex = Math.max(0, Math.floor(scrollTop / ITEM_HEIGHT) - BUFFER_SIZE);
const endIndex = Math.min(
groups.length,
Math.ceil((scrollTop + viewportHeight) / ITEM_HEIGHT) + BUFFER_SIZE
);
setVisibleRange({ start: startIndex, end: endIndex });
}, [groups.length]);
useEffect(() => {
// For small lists, don't use virtual scrolling
if (groups.length <= 50) {
setVisibleRange({ start: 0, end: groups.length });
return;
}
updateVisibleRange();
// Throttle scroll events for better performance
let timeoutId: NodeJS.Timeout;
const handleScroll = () => {
clearTimeout(timeoutId);
timeoutId = setTimeout(updateVisibleRange, 50);
};
window.addEventListener('scroll', handleScroll, { passive: true });
return () => {
window.removeEventListener('scroll', handleScroll);
clearTimeout(timeoutId);
};
}, [groups.length, updateVisibleRange]);
// If list is small, render all items
if (groups.length <= 50) {
return (
<div ref={containerRef} className="space-y-2">
{groups.map((group) => {
const key = `${group.symbol}_${group.accountId}`;
const isExpanded = expandedGroups.has(key);
return (
<TradeGroup
key={key}
group={group}
isExpanded={isExpanded}
onToggle={() => onToggleGroup(key)}
editingTrade={editingTrade}
editValues={editValues}
onStartEdit={onStartEdit}
onSave={onSave}
onCancelEdit={onCancelEdit}
onEditChange={onEditChange}
formatDate={formatDate}
formatCurrency={formatCurrency}
/>
);
})}
</div>
);
}
// Virtual scrolling for large lists
const visibleGroups = groups.slice(visibleRange.start, visibleRange.end);
const offsetTop = visibleRange.start * ITEM_HEIGHT;
const totalHeight = groups.length * ITEM_HEIGHT;
return (
<div ref={containerRef} style={{ position: 'relative', minHeight: `${totalHeight}px` }}>
<div style={{ transform: `translateY(${offsetTop}px)` }} className="space-y-2">
{visibleGroups.map((group, index) => {
const actualIndex = visibleRange.start + index;
const key = `${group.symbol}_${group.accountId}`;
const isExpanded = expandedGroups.has(key);
return (
<TradeGroup
key={key}
group={group}
isExpanded={isExpanded}
onToggle={() => onToggleGroup(key)}
editingTrade={editingTrade}
editValues={editValues}
onStartEdit={onStartEdit}
onSave={onSave}
onCancelEdit={onCancelEdit}
onEditChange={onEditChange}
formatDate={formatDate}
formatCurrency={formatCurrency}
/>
);
})}
</div>
</div>
);
}