Compare commits

...

6 Commits

Author SHA1 Message Date
02c54cb354 feat: set default zoom to 300 candles desktop, 125 candles mobile 2026-03-24 08:24:14 +01:00
c3ca5670e3 style: update chart type icons and header layout for better mobile/desktop experience 2026-03-23 10:49:08 +01:00
bde7945a1b feat: add chart type selector with candlestick, line, and bar charts
- Add chart type selector with 3 chart types: candlestick (default), line, and bar

- Fix data format conversion when switching between OHLC and simple {time,value} formats

- Fix line chart refresh to use update() instead of setData() to preserve chart data

- Remove area chart type and AI Insight/refresh buttons

- Improve data handling in loadData, loadNewData, loadHistoricalData, and switchChartType methods
2026-03-23 09:47:07 +01:00
eccfcc4b79 hide indicators panel when clicking on chart 2026-03-22 21:46:31 +01:00
31ac1ead5b Disable crosshair magnet mode 2026-03-22 21:22:15 +01:00
a3bf8624fb fix: improve chart resizer for mobile with touch support, save chart height, disable sidebar scrollbar 2026-03-22 20:31:59 +01:00
2 changed files with 593 additions and 275 deletions

View File

@ -34,47 +34,67 @@
} }
/* Hide scrollbar for clean UI */ /* Hide scrollbar for clean UI */
.no-scrollbar::-webkit-scrollbar { .no-scrollbar::-webkit-scrollbar {
display: none; display: none;
} }
.no-scrollbar { .no-scrollbar {
-ms-overflow-style: none; -ms-overflow-style: none;
scrollbar-width: none; scrollbar-width: none;
} }
/* Chart type button active state */
.chart-type-btn.active {
background-color: #2d3a4f;
color: #3b82f6;
}
/* Chart type button hover effect */
.chart-type-btn:hover {
background-color: #2d3a4f;
}
</style> </style>
</head> </head>
<body class="flex flex-col h-screen overflow-hidden bg-[#0d1421] text-white font-['Inter']"> <body class="flex flex-col h-screen overflow-hidden bg-[#0d1421] text-white font-['Inter']">
<!-- Top Navigation Bar --> <!-- Top Navigation Bar -->
<header class="bg-[#0f131e] fixed top-0 w-full z-[60] h-16 px-6 flex items-center justify-between border-b border-[#1b1f2b]"> <header class="bg-[#0f131e] fixed top-0 w-full z-[60] h-16 px-4 flex items-center justify-between border-b border-[#1b1f2b]">
<div class="flex items-center gap-3"> <div class="flex items-center shrink-0">
<!-- Mobile Menu Button -->
<button id="mobileMenuBtn" class="md:hidden w-10 h-10 flex items-center justify-center text-[#8fa2b3] hover:text-white hover:bg-[#2d3a4f] rounded-md transition-colors z-50">
<span class="material-symbols-outlined text-lg">menu</span>
</button>
<!-- Search/Symbol Button --> <!-- Search/Symbol Button -->
<div class="hidden md:flex items-center space-x-3 bg-[#1a2333] px-3 py-1.5 rounded-md cursor-pointer border border-[#2d3a4f]"> <div class="flex items-center space-x-2 bg-[#1a2333] px-3 py-1.5 rounded-md cursor-pointer border border-[#2d3a4f]">
<span class="material-symbols-outlined text-sm text-[#8fa2b3]">search</span> <span class="material-symbols-outlined text-sm text-[#8fa2b3]">search</span>
<span class="font-bold text-sm text-[#dfe2f2]">BTC/USD</span> <span class="font-bold text-sm text-[#dfe2f2]">BTC/USD</span>
<span class="material-symbols-outlined text-sm text-[#8fa2b3]">chevron_right</span> <span class="material-symbols-outlined text-sm text-[#8fa2b3]">chevron_right</span>
</div> </div>
<!-- Mobile Search --> <!-- Desktop Status (Moved inside the left-aligned group) -->
<div class="md:hidden flex items-center bg-[#1a2333] px-3 py-1.5 rounded-md cursor-pointer border border-[#2d3a4f]"> <div class="hidden lg:flex items-center gap-2 ml-6 shrink-0 border-l border-[#2d3a4f] pl-6">
<span class="material-symbols-outlined text-sm text-[#8fa2b3]">search</span> <div class="w-2 h-2 rounded-full bg-green-500" id="statusDot"></div>
<span class="font-bold text-sm text-[#dfe2f2] ml-2">BTC/USD</span> <span class="text-xs text-[#8fa2b3]" id="statusText">Live</span>
<span class="material-symbols-outlined text-sm text-[#8fa2b3] ml-2">chevron_right</span>
</div> </div>
</div> </div>
<div class="hidden md:flex items-center gap-2 mr-4"> <!-- Scrollable Controls Container (Right-aligned on desktop via parent justify-between) -->
<div class="w-2 h-2 rounded-full bg-green-500" id="statusDot"></div> <div class="flex-1 md:flex-none flex items-center gap-2 ml-4 overflow-x-auto no-scrollbar touch-pan-x justify-end" style="-webkit-overflow-scrolling: touch;">
<span class="text-xs text-[#8fa2b3]" id="statusText">Live</span> <!-- Chart Type Buttons -->
</div> <div class="flex space-x-1 shrink-0">
<button class="chart-type-btn w-10 h-10 flex items-center justify-center text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded transition-colors" data-chart-type="candlestick" title="Candlestick">
<div class="flex space-x-1 items-center overflow-x-auto no-scrollbar" id="timeframeContainer"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M7 5v3M7 16v3M17 3v2M17 11v4"/><rect x="5" y="8" width="4" height="8" rx="0.5"/><rect x="15" y="5" width="4" height="6" rx="0.5"/></svg>
<!-- Timeframes injected by JS --> </button>
<button class="chart-type-btn w-10 h-10 flex items-center justify-center text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded transition-colors" data-chart-type="line" title="Line">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 17l6-6 4 4 8-8"/></svg>
</button>
<button class="chart-type-btn w-10 h-10 flex items-center justify-center text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded transition-colors" data-chart-type="bar" title="Bar">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M7 5v14M7 10H5M7 15h2M17 5v14M17 7h-2M17 12h2"/></svg>
</button>
</div>
<!-- Separator -->
<div class="w-px h-8 bg-[#2d3a4f] mx-1 shrink-0"></div>
<!-- Timeframes Container -->
<div class="flex space-x-1 items-center shrink-0" id="timeframeContainer">
<!-- Timeframes injected by JS -->
</div>
</div> </div>
</header> </header>
@ -324,8 +344,8 @@
</section> </section>
<!-- Draggable Divider --> <!-- Draggable Divider -->
<div id="mainChartResizer" class="h-1.5 bg-[#1e293b] hover:bg-blue-500 cursor-row-resize shrink-0 transition-colors z-20 flex items-center justify-center group relative"> <div id="mainChartResizer" class="h-4 bg-[#1e293b] hover:bg-blue-500 cursor-row-resize shrink-0 transition-colors z-20 flex items-center justify-center group relative">
<div class="w-10 h-0.5 bg-gray-500 rounded opacity-0 group-hover:opacity-100 transition-opacity"></div> <div class="w-10 h-0.5 bg-gray-500 rounded opacity-50 transition-all"></div>
</div> </div>
<!-- Technical Analysis Section --> <!-- Technical Analysis Section -->
@ -336,15 +356,9 @@
Technical Analysis Technical Analysis
<span id="taInterval" class="text-[10px] bg-blue-600/20 text-blue-400 px-2 py-0.5 rounded ml-2 border border-blue-600/30">1D</span> <span id="taInterval" class="text-[10px] bg-blue-600/20 text-blue-400 px-2 py-0.5 rounded ml-2 border border-blue-600/30">1D</span>
</h2> </h2>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span id="taLastUpdate" class="text-xs text-gray-600 mr-2 hidden sm:inline-block">--</span> <span id="taLastUpdate" class="text-xs text-gray-600 mr-2 hidden sm:inline-block">--</span>
<button class="bg-gradient-to-r from-blue-600 to-indigo-600 text-white px-3 py-1.5 rounded text-xs font-bold hover:shadow-lg transition-all flex items-center gap-1" id="aiBtn" onclick="window.openAIAnalysis()"> </div>
<span class="material-symbols-outlined text-sm">smart_toy</span> AI Insight
</button>
<button class="bg-[#1e222d] border border-[#2d3a4f] text-gray-400 px-3 py-1.5 rounded text-xs hover:text-white hover:border-gray-500 transition-colors" onclick="window.refreshTA()">
<span class="material-symbols-outlined text-sm align-bottom">refresh</span>
</button>
</div>
</div> </div>
<div id="taContent" class="grid grid-cols-2 md:grid-cols-4 gap-4"> <div id="taContent" class="grid grid-cols-2 md:grid-cols-4 gap-4">
@ -428,7 +442,7 @@
<span class="material-symbols-outlined text-sm">menu</span> <span class="material-symbols-outlined text-sm">menu</span>
</button> </button>
<div class="flex-1 overflow-y-auto p-0 sidebar-content bg-[#0d1421]"> <div class="flex-1 overflow-y-auto p-0 sidebar-content bg-[#0d1421] no-scrollbar">
<div class="sidebar-tab-panel active h-full" id="tab-indicators"> <div class="sidebar-tab-panel active h-full" id="tab-indicators">
<div id="indicatorPanel" class="p-2"> <div id="indicatorPanel" class="p-2">
<!-- Indicators content injected here --> <!-- Indicators content injected here -->
@ -503,6 +517,20 @@
} }
}; };
window.hideSidebar = function() {
const sidebar = document.getElementById('rightSidebar');
if (sidebar && !sidebar.classList.contains('collapsed')) {
sidebar.classList.add('collapsed');
}
};
window.hideSidebarAndClearDrawings = function() {
window.hideSidebar();
if (window.dashboard?.drawingManager) {
window.dashboard.drawingManager.clearAll();
}
};
// Chart Resizer Logic // Chart Resizer Logic
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const resizer = document.getElementById('mainChartResizer'); const resizer = document.getElementById('mainChartResizer');
@ -510,29 +538,33 @@
let isResizing = false; let isResizing = false;
let lastDownY = 0; let lastDownY = 0;
let savedHeight = localStorage.getItem('chart_height');
if (resizer && chartWrapper) { if (resizer && chartWrapper) {
resizer.addEventListener('mousedown', (e) => { if (savedHeight) {
isResizing = true; chartWrapper.style.flex = 'none';
lastDownY = e.clientY; chartWrapper.style.height = savedHeight + 'px';
document.body.style.cursor = 'row-resize'; }
chartWrapper.style.pointerEvents = 'none'; // Prevent iframe/canvas capturing mouse
});
document.addEventListener('mousemove', (e) => { const startResize = (y) => {
isResizing = true;
lastDownY = y;
document.body.style.cursor = 'row-resize';
chartWrapper.style.pointerEvents = 'none';
};
const doResize = (y) => {
if (!isResizing) return; if (!isResizing) return;
const deltaY = e.clientY - lastDownY; const deltaY = y - lastDownY;
lastDownY = e.clientY; lastDownY = y;
const newHeight = chartWrapper.offsetHeight + deltaY; const newHeight = chartWrapper.offsetHeight + deltaY;
// Min 200px, Max windowHeight - 150px
if (newHeight > 200 && newHeight < window.innerHeight - 150) { if (newHeight > 200 && newHeight < window.innerHeight - 150) {
chartWrapper.style.flex = 'none'; chartWrapper.style.flex = 'none';
chartWrapper.style.height = newHeight + 'px'; chartWrapper.style.height = newHeight + 'px';
// Force chart layout recalculation if applicable
if (window.dashboard && window.dashboard.chart) { if (window.dashboard && window.dashboard.chart) {
const container = document.getElementById('chart'); const container = document.getElementById('chart');
window.dashboard.chart.applyOptions({ window.dashboard.chart.applyOptions({
@ -541,17 +573,47 @@
}); });
} }
} }
}); };
document.addEventListener('mouseup', () => { const endResize = () => {
if (isResizing) { if (isResizing) {
isResizing = false; isResizing = false;
document.body.style.cursor = ''; document.body.style.cursor = '';
chartWrapper.style.pointerEvents = 'auto'; chartWrapper.style.pointerEvents = 'auto';
const currentHeight = chartWrapper.style.height;
localStorage.setItem('chart_height', currentHeight ? parseFloat(currentHeight) : 75);
} }
}); };
resizer.addEventListener('mousedown', (e) => startResize(e.clientY));
resizer.addEventListener('touchstart', (e) => startResize(e.touches[0].clientY), {passive: false});
document.addEventListener('mousemove', (e) => doResize(e.clientY));
document.addEventListener('touchmove', (e) => doResize(e.touches[0].clientY), {passive: false});
document.addEventListener('mouseup', endResize);
document.addEventListener('touchend', endResize);
document.addEventListener('touchcancel', endResize);
} }
}); });
// Hide sidebar when clicking on chart container (but not on drawing toolbar or sidebar itself)
const chartWrapper = document.getElementById('chartWrapper');
if (chartWrapper) {
chartWrapper.addEventListener('click', function(e) {
const sidebar = document.getElementById('rightSidebar');
const drawingToolbar = document.getElementById('drawingToolbar');
if (!sidebar || sidebar.classList.contains('collapsed')) return;
const isDrawingToolbar = e.target.closest('#drawingToolbar');
const isSidebar = e.target.closest('#rightSidebar');
if (!isDrawingToolbar && !isSidebar) {
hideSidebar();
}
});
}
</script> </script>
<script src="./config.js"></script> <script src="./config.js"></script>

File diff suppressed because it is too large Load Diff