From 1b520f60fd81db2a23dbf0f8023da6c7472baea7 Mon Sep 17 00:00:00 2001 From: rufinomec-afk Date: Mon, 8 Jun 2026 11:44:40 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20toolbar=20do=20gr=C3=A1fico=20com=20eng?= =?UTF-8?q?renagem,=20alinhar=20ao=20pre=C3=A7o=20e=20inputs=20edit=C3=A1v?= =?UTF-8?q?eis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Toolbar flutuante no canto superior esquerdo do canvas - Ícone de mira: centraliza o gráfico no preço atual (reset scroll Y) - Ícone de engrenagem: abre painel de configurações flutuante sobre o gráfico - Painel de config: modo de exibição, passo do preço, modo de fechamento, delta máx - Passo do preço e delta máx agora têm input de texto digitável + botões −/+ - zoomH/zoomV separados: centerTrigger prop para alinhar do App.jsx Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/App.jsx | 124 ++++++++++++++++++++++++++++++- frontend/src/FootprintCanvas.jsx | 9 ++- 2 files changed, 130 insertions(+), 3 deletions(-) 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(() => {