fix: chart settings functionality, layout, and persistence

This commit is contained in:
DiTus
2026-03-20 23:30:14 +01:00
parent 62eeffaf1d
commit 785792fa6e
2 changed files with 113 additions and 28 deletions

View File

@ -99,22 +99,38 @@
<button class="w-8 h-8 bg-[#1e222d] border border-[#2d3a4f] text-gray-300 flex items-center justify-center rounded hover:bg-[#2d3a4f] transition-colors shadow-lg" id="btnLogScale" title="Log Scale">L</button> <button class="w-8 h-8 bg-[#1e222d] border border-[#2d3a4f] text-gray-300 flex items-center justify-center rounded hover:bg-[#2d3a4f] transition-colors shadow-lg" id="btnLogScale" title="Log Scale">L</button>
<!-- Settings Popup --> <!-- Settings Popup -->
<div class="hidden absolute bottom-10 right-0 bg-[#1e222d] border border-[#2d3a4f] rounded-lg p-3 z-50 w-48 shadow-xl" id="settingsPopup"> <div class="hidden absolute bottom-10 right-0 bg-[#1e222d] border border-[#2d3a4f] rounded-lg p-3 z-50 w-56 shadow-xl" id="settingsPopup">
<div class="mb-3"> <div class="mb-3">
<label class="block text-[10px] text-[#8fa2b3] mb-1 uppercase tracking-wider">Timezone</label> <label class="block text-[10px] text-[#8fa2b3] mb-1 uppercase tracking-wider">Candle Colors</label>
<select class="w-full bg-[#0d1421] border border-[#2d3a4f] text-gray-300 text-xs rounded p-1.5 focus:ring-1 focus:ring-blue-500 focus:border-blue-500" id="timezoneSelect"> <div class="flex gap-2">
<div class="flex-1">
<label class="text-[9px] text-[#8fa2b3] block mb-0.5">Up</label>
<div class="h-6 w-full rounded border border-[#2d3a4f] relative overflow-hidden">
<input type="color" id="candleUpColor" value="#ff9800" class="absolute -top-2 -left-2 w-[200%] h-[200%] cursor-pointer p-0 m-0 border-0">
</div>
</div>
<div class="flex-1">
<label class="text-[9px] text-[#8fa2b3] block mb-0.5">Down</label>
<div class="h-6 w-full rounded border border-[#2d3a4f] relative overflow-hidden">
<input type="color" id="candleDownColor" value="#ff9800" class="absolute -top-2 -left-2 w-[200%] h-[200%] cursor-pointer p-0 m-0 border-0">
</div>
</div>
</div>
</div>
<div class="grid grid-cols-[1fr_auto] items-center gap-2 mb-3">
<label class="text-[10px] text-[#8fa2b3] uppercase tracking-wider">Decimals</label>
<input type="number" id="priceFormatInput" min="0" max="8" value="2" class="w-16 bg-[#0d1421] border border-[#2d3a4f] text-gray-300 text-xs rounded p-1.5 text-center focus:ring-1 focus:ring-blue-500 focus:border-blue-500">
</div>
<div class="grid grid-cols-[auto_1fr] items-center gap-2 mb-0">
<label class="text-[10px] text-[#8fa2b3] uppercase tracking-wider whitespace-nowrap">Timezone</label>
<select class="w-full bg-[#0d1421] border border-[#2d3a4f] text-gray-300 text-[10px] rounded p-1.5 focus:ring-1 focus:ring-blue-500 focus:border-blue-500" id="timezoneSelect">
<option value="UTC">UTC</option> <option value="UTC">UTC</option>
<option value="Europe/Warsaw" selected>Warsaw</option> <option value="Europe/Warsaw" selected>Warsaw</option>
<option value="America/New_York">New York</option> <option value="America/New_York">New York</option>
</select> </select>
</div> </div>
<div class="mb-0">
<label class="block text-[10px] text-[#8fa2b3] mb-1 uppercase tracking-wider">Price Format</label>
<select class="w-full bg-[#0d1421] border border-[#2d3a4f] text-gray-300 text-xs rounded p-1.5 focus:ring-1 focus:ring-blue-500 focus:border-blue-500" id="priceFormatSelect">
<option value="0">Integer</option>
<option value="2">2 Decimals</option>
</select>
</div>
</div> </div>
</div> </div>

View File

@ -342,27 +342,80 @@ constructor() {
}, },
}); });
// Setup price format selector change handler // Setup price format selector change handler
const priceSelect = document.getElementById("priceFormatSelect"); const priceInput = document.getElementById("priceFormatInput");
if (priceSelect) {
priceSelect.addEventListener("change", (e) => { // Load saved precision
const precision = parseInt(e.target.value); let savedPrecision = parseInt(localStorage.getItem('winterfail_price_precision'));
this.chart.priceScale().applyOptions({ if (isNaN(savedPrecision)) savedPrecision = 2;
priceFormat: { type: "price", precision: precision, minMove: precision===0 ? 1 : 0.0001 }
if (priceInput) priceInput.value = savedPrecision;
if (priceInput) {
priceInput.addEventListener("input", (e) => {
let precision = parseInt(e.target.value);
if (isNaN(precision)) precision = 2;
if (precision < 0) precision = 0;
if (precision > 8) precision = 8;
localStorage.setItem('winterfail_price_precision', precision);
const minMove = precision === 0 ? 1 : Number((1 / Math.pow(10, precision)).toFixed(precision));
this.candleSeries.applyOptions({
priceFormat: { type: "price", precision: precision, minMove: minMove }
}); });
}); });
} }
// Load candle colors from storage or default
const savedUpColor = localStorage.getItem('winterfail_candle_up') || '#ff9800';
const savedDownColor = localStorage.getItem('winterfail_candle_down') || '#ff9800';
const candleUpInput = document.getElementById('candleUpColor');
const candleDownInput = document.getElementById('candleDownColor');
if (candleUpInput) candleUpInput.value = savedUpColor;
if (candleDownInput) candleDownInput.value = savedDownColor;
// Calculate initial minMove based on saved precision
const initialMinMove = savedPrecision === 0 ? 1 : Number((1 / Math.pow(10, savedPrecision)).toFixed(savedPrecision));
this.candleSeries = this.chart.addSeries(LightweightCharts.CandlestickSeries, { this.candleSeries = this.chart.addSeries(LightweightCharts.CandlestickSeries, {
upColor: '#f0b90b', upColor: savedUpColor,
downColor: '#f0b90b', downColor: savedDownColor,
borderUpColor: '#f0b90b', borderUpColor: savedUpColor,
borderDownColor: '#f0b90b', borderDownColor: savedDownColor,
wickUpColor: '#f0b90b', wickUpColor: savedUpColor,
wickDownColor: '#f0b90b', wickDownColor: savedDownColor,
lastValueVisible: false, lastValueVisible: false,
priceLineVisible: false, priceLineVisible: false,
priceFormat: { type: 'price', precision: 0, minMove: 1 } priceFormat: { type: 'price', precision: savedPrecision, minMove: initialMinMove }
}, 0); }, 0);
// Color change listeners
if (candleUpInput) {
candleUpInput.addEventListener('input', (e) => {
const color = e.target.value;
localStorage.setItem('winterfail_candle_up', color);
this.candleSeries.applyOptions({
upColor: color,
borderUpColor: color,
wickUpColor: color
});
});
}
if (candleDownInput) {
candleDownInput.addEventListener('input', (e) => {
const color = e.target.value;
localStorage.setItem('winterfail_candle_down', color);
this.candleSeries.applyOptions({
downColor: color,
borderDownColor: color,
wickDownColor: color
});
});
}
this.avgPriceSeries = this.chart.addSeries(LightweightCharts.LineSeries, { this.avgPriceSeries = this.chart.addSeries(LightweightCharts.LineSeries, {
color: '#00bcd4', color: '#00bcd4',
@ -372,7 +425,7 @@ constructor() {
priceLineVisible: false, priceLineVisible: false,
crosshairMarkerVisible: false, crosshairMarkerVisible: false,
title: '', title: '',
priceFormat: { type: 'price', precision: 0, minMove: 1 } priceFormat: { type: 'price', precision: savedPrecision, minMove: initialMinMove }
}); });
this.currentPriceLine = this.candleSeries.createPriceLine({ this.currentPriceLine = this.candleSeries.createPriceLine({
@ -432,9 +485,24 @@ constructor() {
} }
initPriceScaleControls() { initPriceScaleControls() {
const btnSettings = document.getElementById('btnSettings');
const settingsPopup = document.getElementById('settingsPopup');
const btnAutoScale = document.getElementById('btnAutoScale'); const btnAutoScale = document.getElementById('btnAutoScale');
const btnLogScale = document.getElementById('btnLogScale'); const btnLogScale = document.getElementById('btnLogScale');
if (btnSettings && settingsPopup) {
btnSettings.addEventListener('click', (e) => {
e.stopPropagation();
settingsPopup.classList.toggle('hidden');
});
document.addEventListener('click', (e) => {
if (!settingsPopup.contains(e.target) && e.target !== btnSettings && !btnSettings.contains(e.target)) {
settingsPopup.classList.add('hidden');
}
});
}
if (!btnAutoScale || !btnLogScale) return; if (!btnAutoScale || !btnLogScale) return;
this.priceScaleState = { this.priceScaleState = {
@ -1045,16 +1113,17 @@ async loadSignals() {
}); });
} }
document.getElementById('currentPrice').textContent = price.toFixed(2); const savedPrecision = parseInt(localStorage.getItem('winterfail_price_precision')) || 2;
document.getElementById('currentPrice').textContent = price.toFixed(savedPrecision);
if (this.statsData) { if (this.statsData) {
const change = this.statsData.change_24h; const change = this.statsData.change_24h;
document.getElementById('currentPrice').className = 'stat-value ' + (change >= 0 ? 'positive' : 'negative'); document.getElementById('currentPrice').className = 'stat-value ' + (change >= 0 ? 'positive' : 'negative');
document.getElementById('priceChange').textContent = (change >= 0 ? '+' : '') + change.toFixed(2) + '%'; document.getElementById('priceChange').textContent = (change >= 0 ? '+' : '') + change.toFixed(2) + '%';
document.getElementById('priceChange').className = 'stat-value ' + (change >= 0 ? 'positive' : 'negative'); document.getElementById('priceChange').className = 'stat-value ' + (change >= 0 ? 'positive' : 'negative');
document.getElementById('dailyHigh').textContent = this.statsData.high_24h.toFixed(2); document.getElementById('dailyHigh').textContent = this.statsData.high_24h.toFixed(savedPrecision);
document.getElementById('dailyLow').textContent = this.statsData.low_24h.toFixed(2); document.getElementById('dailyLow').textContent = this.statsData.low_24h.toFixed(savedPrecision);
} }
} }
switchTimeframe(interval) { switchTimeframe(interval) {