CryptoDashboard.tsx 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. 'use client';
  2. import { useRef, useState, useCallback, useEffect } from 'react';
  3. import { useCryptoContext } from '@/contexts/cryptoProvider';
  4. import useMarketData from '@/hooks/useMarketData';
  5. import useTradeSound from '@/hooks/useTradeSound';
  6. import MarketHeader from './MarketHeader';
  7. import TradingChart from './TradingChart';
  8. import Orderbook from './Orderbook';
  9. import TradeHistory from './TradeHistory';
  10. import './dashboard.scss';
  11. export default function CryptoDashboard() {
  12. const { selectedMarket } = useCryptoContext();
  13. useMarketData(selectedMarket);
  14. const tradeSound = useTradeSound();
  15. const [chartHeight, setChartHeight] = useState(500);
  16. const [orderbookRatio, setOrderbookRatio] = useState(50); // %
  17. const dashboardRef = useRef<HTMLDivElement>(null);
  18. const draggingRef = useRef<'chart' | 'split' | null>(null);
  19. const startRef = useRef({ y: 0, x: 0, value: 0 });
  20. const handleMouseDown = useCallback((type: 'chart' | 'split', e: React.MouseEvent) => {
  21. e.preventDefault();
  22. draggingRef.current = type;
  23. if (type === 'chart') {
  24. startRef.current = { y: e.clientY, x: 0, value: chartHeight };
  25. } else {
  26. startRef.current = { y: 0, x: e.clientX, value: orderbookRatio };
  27. }
  28. document.body.style.cursor = type === 'chart' ? 'row-resize' : 'col-resize';
  29. document.body.style.userSelect = 'none';
  30. }, [chartHeight, orderbookRatio]);
  31. useEffect(() => {
  32. const handleMouseMove = (e: MouseEvent) => {
  33. if (!draggingRef.current) return;
  34. if (draggingRef.current === 'chart') {
  35. const delta = e.clientY - startRef.current.y;
  36. const newHeight = Math.min(800, Math.max(200, startRef.current.value + delta));
  37. setChartHeight(newHeight);
  38. } else if (draggingRef.current === 'split' && dashboardRef.current) {
  39. const bottomEl = dashboardRef.current.querySelector('.dashboard-bottom') as HTMLElement;
  40. if (!bottomEl) return;
  41. const rect = bottomEl.getBoundingClientRect();
  42. const totalWidth = rect.width;
  43. const relX = e.clientX - rect.left;
  44. const ratio = Math.min(80, Math.max(20, (relX / totalWidth) * 100));
  45. setOrderbookRatio(ratio);
  46. }
  47. };
  48. const handleMouseUp = () => {
  49. if (draggingRef.current) {
  50. draggingRef.current = null;
  51. document.body.style.cursor = '';
  52. document.body.style.userSelect = '';
  53. }
  54. };
  55. document.addEventListener('mousemove', handleMouseMove);
  56. document.addEventListener('mouseup', handleMouseUp);
  57. return () => {
  58. document.removeEventListener('mousemove', handleMouseMove);
  59. document.removeEventListener('mouseup', handleMouseUp);
  60. };
  61. }, []);
  62. return (
  63. <div className='crypto-dashboard' ref={dashboardRef}>
  64. <MarketHeader />
  65. <div style={{ height: chartHeight }}>
  66. <TradingChart />
  67. </div>
  68. <div
  69. className='resize-handle-h'
  70. onMouseDown={(e) => handleMouseDown('chart', e)}
  71. />
  72. <div className='dashboard-bottom'>
  73. <div className='dashboard-orderbook' style={{ width: `${orderbookRatio}%` }}>
  74. <Orderbook playOrderbookSound={tradeSound.playOrderbookSound} />
  75. </div>
  76. <div
  77. className='resize-handle-v'
  78. onMouseDown={(e) => handleMouseDown('split', e)}
  79. />
  80. <div className='dashboard-trades' style={{ width: `${100 - orderbookRatio}%` }}>
  81. <TradeHistory tradeSound={tradeSound} />
  82. </div>
  83. </div>
  84. </div>
  85. );
  86. }