Add chart navigation controls with keyboard shortcuts
- Navigation buttons (<, >, >>) appear when mouse within 30px of bottom - Left/Right arrows shift timeline 0.8 window width - Up arrow jumps to most recent candle - Buttons: < move left, > move right, >> go to recent
This commit is contained in:
@ -567,6 +567,51 @@
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Navigation Controls */
|
||||||
|
.nav-controls {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 4px;
|
||||||
|
z-index: 10;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-wrapper.show-nav .nav-controls {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
background: rgba(42, 46, 57, 0.9);
|
||||||
|
border: 1px solid #363c4e;
|
||||||
|
color: #d1d4dc;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:hover {
|
||||||
|
background: #363c4e;
|
||||||
|
border-color: #4a4f5e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:active {
|
||||||
|
background: #2962ff;
|
||||||
|
border-color: #2962ff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.ha-header {
|
.ha-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -995,12 +1040,17 @@
|
|||||||
|
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="chart-area">
|
<div class="chart-area">
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper" id="chartWrapper">
|
||||||
<div id="chart"></div>
|
<div id="chart"></div>
|
||||||
<div class="price-scale-controls" id="priceScaleControls">
|
<div class="price-scale-controls" id="priceScaleControls">
|
||||||
<button class="ps-control-btn auto-scale active" id="btnAutoScale" title="Auto Scale (A)">A</button>
|
<button class="ps-control-btn auto-scale active" id="btnAutoScale" title="Auto Scale (A)">A</button>
|
||||||
<button class="ps-control-btn log-scale" id="btnLogScale" title="Logarithmic Scale">L</button>
|
<button class="ps-control-btn log-scale" id="btnLogScale" title="Logarithmic Scale">L</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="nav-controls" id="navControls">
|
||||||
|
<button class="nav-btn" id="navLeft" title="Navigate Left (←)">‹</button>
|
||||||
|
<button class="nav-btn" id="navRight" title="Navigate Right (→)">›</button>
|
||||||
|
<button class="nav-btn" id="navRecent" title="Go to Recent (↑)">»</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ta-panel" id="taPanel">
|
<div class="ta-panel" id="taPanel">
|
||||||
|
|||||||
@ -105,6 +105,7 @@ export class TradingDashboard {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.initPriceScaleControls();
|
this.initPriceScaleControls();
|
||||||
|
this.initNavigationControls();
|
||||||
|
|
||||||
this.chart.timeScale().subscribeVisibleLogicalRangeChange(this.onVisibleRangeChange.bind(this));
|
this.chart.timeScale().subscribeVisibleLogicalRangeChange(this.onVisibleRangeChange.bind(this));
|
||||||
|
|
||||||
@ -204,6 +205,57 @@ export class TradingDashboard {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initNavigationControls() {
|
||||||
|
const chartWrapper = document.getElementById('chartWrapper');
|
||||||
|
const navLeft = document.getElementById('navLeft');
|
||||||
|
const navRight = document.getElementById('navRight');
|
||||||
|
const navRecent = document.getElementById('navRecent');
|
||||||
|
|
||||||
|
if (!chartWrapper || !navLeft || !navRight || !navRecent) return;
|
||||||
|
|
||||||
|
chartWrapper.addEventListener('mousemove', (e) => {
|
||||||
|
const rect = chartWrapper.getBoundingClientRect();
|
||||||
|
const distanceFromBottom = rect.bottom - e.clientY;
|
||||||
|
chartWrapper.classList.toggle('show-nav', distanceFromBottom < 30);
|
||||||
|
});
|
||||||
|
|
||||||
|
chartWrapper.addEventListener('mouseleave', () => {
|
||||||
|
chartWrapper.classList.remove('show-nav');
|
||||||
|
});
|
||||||
|
|
||||||
|
navLeft.addEventListener('click', () => this.navigateLeft());
|
||||||
|
navRight.addEventListener('click', () => this.navigateRight());
|
||||||
|
navRecent.addEventListener('click', () => this.navigateToRecent());
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateLeft() {
|
||||||
|
const visibleRange = this.chart.timeScale().getVisibleLogicalRange();
|
||||||
|
if (!visibleRange) return;
|
||||||
|
|
||||||
|
const visibleBars = visibleRange.to - visibleRange.from;
|
||||||
|
const shift = visibleBars * 0.8;
|
||||||
|
const newFrom = visibleRange.from - shift;
|
||||||
|
const newTo = visibleRange.to - shift;
|
||||||
|
|
||||||
|
this.chart.timeScale().setVisibleLogicalRange({ from: newFrom, to: newTo });
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateRight() {
|
||||||
|
const visibleRange = this.chart.timeScale().getVisibleLogicalRange();
|
||||||
|
if (!visibleRange) return;
|
||||||
|
|
||||||
|
const visibleBars = visibleRange.to - visibleRange.from;
|
||||||
|
const shift = visibleBars * 0.8;
|
||||||
|
const newFrom = visibleRange.from + shift;
|
||||||
|
const newTo = visibleRange.to + shift;
|
||||||
|
|
||||||
|
this.chart.timeScale().setVisibleLogicalRange({ from: newFrom, to: newTo });
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToRecent() {
|
||||||
|
this.chart.timeScale().scrollToRealTime();
|
||||||
|
}
|
||||||
|
|
||||||
initEventListeners() {
|
initEventListeners() {
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON') return;
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON') return;
|
||||||
@ -217,6 +269,14 @@ export class TradingDashboard {
|
|||||||
if (shortcuts[e.key]) {
|
if (shortcuts[e.key]) {
|
||||||
this.switchTimeframe(shortcuts[e.key]);
|
this.switchTimeframe(shortcuts[e.key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e.key === 'ArrowLeft') {
|
||||||
|
this.navigateLeft();
|
||||||
|
} else if (e.key === 'ArrowRight') {
|
||||||
|
this.navigateRight();
|
||||||
|
} else if (e.key === 'ArrowUp') {
|
||||||
|
this.navigateToRecent();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user