diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index a824e2a..18a3ca4 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -35,6 +35,10 @@ export default function App() { // WebSocket Data States (Phase 5) const [domData, setDomData] = useState(null); const [bigTrades, setBigTrades] = useState([]); + + // Chart toolbar + const [showChartSettings, setShowChartSettings] = useState(false); + const [centerTrigger, setCenterTrigger] = useState(0); // Toasts const [toasts, setToasts] = useState([]); @@ -263,6 +267,115 @@ export default function App() { )} + {/* Chart Toolbar */} +
+ {/* Align to current price */} + + +
+ + {/* Settings gear */} + +
+ + {/* Floating Settings Panel */} + {showChartSettings && ( +
+
+ Configurações + +
+ + {/* View Mode */} +
+ MODO DE EXIBIÇÃO +
+ + +
+
+ + {/* Price Step */} +
+ PASSO DO PREÇO (ticks) +
+ + setStepMultiplier(Math.max(1, Number(e.target.value) || 1))} + className="flex-1 bg-transparent text-center text-sm font-bold text-white outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> + +
+
+ + {/* Close Mode */} +
+ MODO DE FECHAMENTO + +
+ + {/* Delta Max */} + {closeMode === 'delta' && ( +
+ DELTA MÁX +
+ + { setDeltaMax(Math.max(100, Number(e.target.value) || 100)); setConfigDirty(true); }} + className="flex-1 bg-transparent text-center text-sm font-bold text-white outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> + +
+
+ )} + + {/* Apply */} + +
+ )} +
@@ -310,8 +424,14 @@ export default function App() {
PRICE STEP
- -
{(stepMultiplier * dynamicTickSize).toFixed(2)} pts
+ + setStepMultiplier(Math.max(1, Number(e.target.value) || 1))} + className="px-1 py-1 text-sm font-bold text-white min-w-[52px] text-center bg-transparent outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + />
diff --git a/frontend/src/FootprintCanvas.jsx b/frontend/src/FootprintCanvas.jsx index 5ec39ea..0dcc267 100644 --- a/frontend/src/FootprintCanvas.jsx +++ b/frontend/src/FootprintCanvas.jsx @@ -1,6 +1,6 @@ import React, { useRef, useEffect, useState } from 'react'; -export default function FootprintCanvas({ clusters, tickSize = 1.0, stepMultiplier = 1, viewMode = 'bidask', imbalanceRatio = 300, onStepChange, bid = 0, ask = 0, domData = null, bigTrades = [] }) { +export default function FootprintCanvas({ clusters, tickSize = 1.0, stepMultiplier = 1, viewMode = 'bidask', imbalanceRatio = 300, onStepChange, bid = 0, ask = 0, domData = null, bigTrades = [], centerTrigger = 0 }) { const canvasRef = useRef(null); // Navigation & Scale State @@ -28,6 +28,13 @@ export default function FootprintCanvas({ clusters, tickSize = 1.0, stepMultipli const rowHeight = 26 * zoomV; // height of each price cell (vertical only) const axisWidth = 70; // width of the vertical price axis on the right + // Center on current price when triggered from toolbar + useEffect(() => { + if (centerTrigger > 0) { + setScrollOffset(prev => ({ ...prev, y: 0 })); + } + }, [centerTrigger]); + // Auto-scroll to keep latest cluster visible whenever clusters or horizontal zoom changes const lastClusterCount = useRef(0); useEffect(() => {