fix: chart settings functionality, layout, and persistence
This commit is contained in:
36
index.html
36
index.html
@ -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>
|
||||
|
||||
<!-- 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">
|
||||
<label class="block text-[10px] text-[#8fa2b3] mb-1 uppercase tracking-wider">Timezone</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">
|
||||
<label class="block text-[10px] text-[#8fa2b3] mb-1 uppercase tracking-wider">Candle Colors</label>
|
||||
<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="Europe/Warsaw" selected>Warsaw</option>
|
||||
<option value="America/New_York">New York</option>
|
||||
</select>
|
||||
</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>
|
||||
|
||||
|
||||
103
js/ui/chart.js
103
js/ui/chart.js
@ -342,28 +342,81 @@ constructor() {
|
||||
},
|
||||
});
|
||||
// Setup price format selector change handler
|
||||
const priceSelect = document.getElementById("priceFormatSelect");
|
||||
if (priceSelect) {
|
||||
priceSelect.addEventListener("change", (e) => {
|
||||
const precision = parseInt(e.target.value);
|
||||
this.chart.priceScale().applyOptions({
|
||||
priceFormat: { type: "price", precision: precision, minMove: precision===0 ? 1 : 0.0001 }
|
||||
const priceInput = document.getElementById("priceFormatInput");
|
||||
|
||||
// Load saved precision
|
||||
let savedPrecision = parseInt(localStorage.getItem('winterfail_price_precision'));
|
||||
if (isNaN(savedPrecision)) savedPrecision = 2;
|
||||
|
||||
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, {
|
||||
upColor: '#f0b90b',
|
||||
downColor: '#f0b90b',
|
||||
borderUpColor: '#f0b90b',
|
||||
borderDownColor: '#f0b90b',
|
||||
wickUpColor: '#f0b90b',
|
||||
wickDownColor: '#f0b90b',
|
||||
upColor: savedUpColor,
|
||||
downColor: savedDownColor,
|
||||
borderUpColor: savedUpColor,
|
||||
borderDownColor: savedDownColor,
|
||||
wickUpColor: savedUpColor,
|
||||
wickDownColor: savedDownColor,
|
||||
lastValueVisible: false,
|
||||
priceLineVisible: false,
|
||||
priceFormat: { type: 'price', precision: 0, minMove: 1 }
|
||||
priceFormat: { type: 'price', precision: savedPrecision, minMove: initialMinMove }
|
||||
}, 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, {
|
||||
color: '#00bcd4',
|
||||
lineWidth: 1,
|
||||
@ -372,7 +425,7 @@ constructor() {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
title: '',
|
||||
priceFormat: { type: 'price', precision: 0, minMove: 1 }
|
||||
priceFormat: { type: 'price', precision: savedPrecision, minMove: initialMinMove }
|
||||
});
|
||||
|
||||
this.currentPriceLine = this.candleSeries.createPriceLine({
|
||||
@ -432,9 +485,24 @@ constructor() {
|
||||
}
|
||||
|
||||
initPriceScaleControls() {
|
||||
const btnSettings = document.getElementById('btnSettings');
|
||||
const settingsPopup = document.getElementById('settingsPopup');
|
||||
const btnAutoScale = document.getElementById('btnAutoScale');
|
||||
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;
|
||||
|
||||
this.priceScaleState = {
|
||||
@ -1045,15 +1113,16 @@ 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) {
|
||||
const change = this.statsData.change_24h;
|
||||
document.getElementById('currentPrice').className = 'stat-value ' + (change >= 0 ? 'positive' : 'negative');
|
||||
document.getElementById('priceChange').textContent = (change >= 0 ? '+' : '') + change.toFixed(2) + '%';
|
||||
document.getElementById('priceChange').className = 'stat-value ' + (change >= 0 ? 'positive' : 'negative');
|
||||
document.getElementById('dailyHigh').textContent = this.statsData.high_24h.toFixed(2);
|
||||
document.getElementById('dailyLow').textContent = this.statsData.low_24h.toFixed(2);
|
||||
document.getElementById('dailyHigh').textContent = this.statsData.high_24h.toFixed(savedPrecision);
|
||||
document.getElementById('dailyLow').textContent = this.statsData.low_24h.toFixed(savedPrecision);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user