Compare commits
2 Commits
master
...
709a565397
| Author | SHA1 | Date | |
|---|---|---|---|
| 709a565397 | |||
| 07a27a12a4 |
@ -217,51 +217,3 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Trendline Settings Panel */
|
|
||||||
#trendlineSettingsPanel input[type=range] {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
#trendlineSettingsPanel input[type=range]::-webkit-slider-thumb {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
height: 12px;
|
|
||||||
width: 12px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #ffffff;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 2px solid #1a2333;
|
|
||||||
margin-top: -4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#trendlineSettingsPanel input[type=range]::-webkit-slider-runnable-track {
|
|
||||||
width: 100%;
|
|
||||||
height: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
background: #2d3a4f;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-swatch {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 3px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
transition: transform 0.1s, border-color 0.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-swatch:hover {
|
|
||||||
transform: scale(1.1);
|
|
||||||
border-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-swatch.active {
|
|
||||||
border-color: #ffffff;
|
|
||||||
box-shadow: 0 0 0 1px #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tl-thickness-btn[data-active="true"],
|
|
||||||
.tl-style-btn[data-active="true"] {
|
|
||||||
background-color: #2d3a4f;
|
|
||||||
}
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
BIN
ignore/text.PNG
BIN
ignore/text.PNG
Binary file not shown.
|
Before Width: | Height: | Size: 5.7 KiB |
171
index.html
171
index.html
@ -24,7 +24,7 @@
|
|||||||
#rightSidebar.collapsed {
|
#rightSidebar.collapsed {
|
||||||
transform: translateX(100%);
|
transform: translateX(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tab Styles in Sidebar */
|
/* Tab Styles in Sidebar */
|
||||||
.sidebar-tab {
|
.sidebar-tab {
|
||||||
@apply px-4 py-2 text-sm font-medium text-gray-400 hover:text-white transition-colors border-b-2 border-transparent;
|
@apply px-4 py-2 text-sm font-medium text-gray-400 hover:text-white transition-colors border-b-2 border-transparent;
|
||||||
@ -32,7 +32,7 @@
|
|||||||
.sidebar-tab.active {
|
.sidebar-tab.active {
|
||||||
@apply text-blue-500 border-blue-500;
|
@apply text-blue-500 border-blue-500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide scrollbar for clean UI */
|
/* Hide scrollbar for clean UI */
|
||||||
.no-scrollbar::-webkit-scrollbar {
|
.no-scrollbar::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
@ -52,14 +52,14 @@
|
|||||||
<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">
|
<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>
|
<span class="material-symbols-outlined text-lg">menu</span>
|
||||||
</button>
|
</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="hidden md:flex items-center space-x-3 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 -->
|
<!-- Mobile Search -->
|
||||||
<div class="md:hidden flex items-center bg-[#1a2333] px-3 py-1.5 rounded-md cursor-pointer border border-[#2d3a4f]">
|
<div class="md:hidden flex items-center 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>
|
||||||
@ -67,7 +67,7 @@
|
|||||||
<span class="material-symbols-outlined text-sm text-[#8fa2b3] ml-2">chevron_right</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">
|
<div class="hidden md:flex items-center gap-2 mr-4">
|
||||||
<div class="w-2 h-2 rounded-full bg-green-500" id="statusDot"></div>
|
<div class="w-2 h-2 rounded-full bg-green-500" id="statusDot"></div>
|
||||||
<span class="text-xs text-[#8fa2b3]" id="statusText">Live</span>
|
<span class="text-xs text-[#8fa2b3]" id="statusText">Live</span>
|
||||||
@ -101,152 +101,61 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Chart Container -->
|
<!-- Chart Container -->
|
||||||
<section class="relative w-full bg-[#0d1421] h-[60vh] md:h-[70vh]" data-purpose="chart-container" id="chartWrapper">
|
<section class="relative w-full bg-[#0d1421] h-[60vh] md:h-[70vh]" data-purpose="chart-container" id="chartWrapper">
|
||||||
<div id="chart" class="w-full h-full"></div>
|
<div id="chart" class="w-full h-full"></div>
|
||||||
|
|
||||||
|
<!-- Drawing Layer Overlay -->
|
||||||
|
<canvas id="drawingLayer" class="absolute inset-0 pointer-events-none z-20 w-full h-full"></canvas>
|
||||||
|
|
||||||
<!-- Horizontal Drawing Toolbar (Top) -->
|
<!-- Vertical Drawing Toolbar (Left) -->
|
||||||
<div class="absolute top-2 left-1/2 -translate-x-1/2 flex flex-row gap-1 z-30 bg-[#1a2333]/80 backdrop-blur border border-[#2d3a4f] p-1 rounded-md shadow-xl" id="drawingToolbar">
|
<div class="absolute left-2 top-1/2 -translate-y-1/2 flex flex-col gap-1 z-30 bg-[#1a2333]/80 backdrop-blur border border-[#2d3a4f] p-1 rounded-md shadow-xl" id="drawingToolbar">
|
||||||
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('trend_line', event)" title="Trend Line">
|
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('cursor')" title="Cursor">
|
||||||
|
<span class="material-symbols-outlined text-sm">near_me</span>
|
||||||
|
</button>
|
||||||
|
<div class="w-full h-[1px] bg-[#2d3a4f] my-0.5"></div>
|
||||||
|
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('trend_line')" title="Trend Line">
|
||||||
<span class="material-symbols-outlined text-sm">call_split</span>
|
<span class="material-symbols-outlined text-sm">call_split</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('horizontal_line', event)" title="Horizontal Line">
|
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('ray')" title="Ray">
|
||||||
|
<span class="material-symbols-outlined text-sm">trending_flat</span>
|
||||||
|
</button>
|
||||||
|
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('horizontal_line')" title="Horizontal Line">
|
||||||
<span class="material-symbols-outlined text-sm">swap_horizontal_circle</span>
|
<span class="material-symbols-outlined text-sm">swap_horizontal_circle</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('vertical_line', event)" title="Vertical Line">
|
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('vertical_line')" title="Vertical Line">
|
||||||
<span class="material-symbols-outlined text-sm">swap_vert</span>
|
<span class="material-symbols-outlined text-sm">swap_vert</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('rectangle', event)" title="Rectangle">
|
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('fib_retracement')" title="Fibonacci Retracement">
|
||||||
|
<span class="material-symbols-outlined text-sm">reorder</span>
|
||||||
|
</button>
|
||||||
|
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('rectangle')" title="Rectangle">
|
||||||
<span class="material-symbols-outlined text-sm">crop_square</span>
|
<span class="material-symbols-outlined text-sm">crop_square</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('text', event)" title="Text Label">
|
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('text')" title="Text Label">
|
||||||
<span class="material-symbols-outlined text-sm">text_fields</span>
|
<span class="material-symbols-outlined text-sm">text_fields</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('arrow_up', event)" title="Arrow Up">
|
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('arrow_up')" title="Arrow Up">
|
||||||
<span class="material-symbols-outlined text-sm">arrow_upward</span>
|
<span class="material-symbols-outlined text-sm">arrow_upward</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('arrow_down', event)" title="Arrow Down">
|
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('arrow_down')" title="Arrow Down">
|
||||||
<span class="material-symbols-outlined text-sm">arrow_downward</span>
|
<span class="material-symbols-outlined text-sm">arrow_downward</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('measure', event)" title="Measure">
|
<button class="w-8 h-8 text-gray-400 hover:text-white hover:bg-[#2d3a4f] rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('measure')" title="Measure">
|
||||||
<span class="material-symbols-outlined text-sm">straighten</span>
|
<span class="material-symbols-outlined text-sm">straighten</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="w-[1px] h-full bg-[#2d3a4f] mx-0.5"></div>
|
<div class="w-full h-[1px] bg-[#2d3a4f] my-0.5"></div>
|
||||||
<button class="w-8 h-8 text-red-400 hover:text-red-300 hover:bg-red-900/20 rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('clear', event)" title="Clear All">
|
<button class="w-8 h-8 text-red-400 hover:text-red-300 hover:bg-red-900/20 rounded flex items-center justify-center transition-colors" onclick="window.activateDrawingTool('clear')" title="Clear All">
|
||||||
<span class="material-symbols-outlined text-sm">delete</span>
|
<span class="material-symbols-outlined text-sm">delete</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Trendline Settings Panel -->
|
|
||||||
<div id="trendlineSettingsPanel" class="hidden absolute top-14 left-1/2 -translate-x-1/2 bg-[#1a2333] border border-[#2d3a4f] rounded-lg shadow-2xl z-50 w-[280px] p-0 text-sm font-['Inter']">
|
|
||||||
<!-- Drag Handle -->
|
|
||||||
<div id="tlPanelHeader" class="h-8 flex items-center justify-between px-3 border-b border-[#2d3a4f] cursor-move bg-[#1f2937] rounded-t-lg">
|
|
||||||
<span class="text-[10px] uppercase tracking-wider font-bold text-gray-400">Settings</span>
|
|
||||||
<button class="text-gray-500 hover:text-white" onclick="window.toggleTLSettings(false)">
|
|
||||||
<span class="material-symbols-outlined text-sm">close</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="p-4">
|
|
||||||
<!-- Tabs -->
|
|
||||||
<div class="flex border-b border-[#2d3a4f] mb-4">
|
|
||||||
<button class="flex-1 py-2 text-center text-blue-500 border-b-2 border-blue-500 font-medium" id="tlTabStyle" onclick="window.switchTLTab('style')">Style</button>
|
|
||||||
<button class="flex-1 py-2 text-center text-gray-400 hover:text-white" id="tlTabText" onclick="window.switchTLTab('text')">Text</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Style Tab Content -->
|
|
||||||
<div id="tlContentStyle">
|
|
||||||
<!-- Color Grid (Line) -->
|
|
||||||
<div class="grid grid-cols-8 gap-1 mb-4" id="tlColorGrid">
|
|
||||||
<!-- Colors injected by JS -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Opacity -->
|
|
||||||
<div class="mb-4">
|
|
||||||
<div class="flex justify-between text-xs text-gray-400 mb-1">
|
|
||||||
<span>Opacity</span>
|
|
||||||
<span id="tlOpacityValue">100%</span>
|
|
||||||
</div>
|
|
||||||
<input type="range" min="0" max="100" value="100" class="w-full h-1 bg-[#2d3a4f] rounded-lg appearance-none cursor-pointer" id="tlOpacityInput">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Thickness -->
|
|
||||||
<div class="mb-4">
|
|
||||||
<label class="text-xs text-gray-400 mb-1 block">Thickness</label>
|
|
||||||
<div class="flex bg-[#0d1421] rounded border border-[#2d3a4f] p-1">
|
|
||||||
<button class="flex-1 h-6 flex items-center justify-center hover:bg-[#2d3a4f] rounded data-[active=true]:bg-[#2d3a4f] tl-thickness-btn" onclick="window.setTLThickness(1)" data-thickness="1"><div class="w-4 h-[1px] bg-white"></div></button>
|
|
||||||
<button class="flex-1 h-6 flex items-center justify-center hover:bg-[#2d3a4f] rounded data-[active=true]:bg-[#2d3a4f] tl-thickness-btn" onclick="window.setTLThickness(2)" data-thickness="2"><div class="w-4 h-[2px] bg-white"></div></button>
|
|
||||||
<button class="flex-1 h-6 flex items-center justify-center hover:bg-[#2d3a4f] rounded data-[active=true]:bg-[#2d3a4f] tl-thickness-btn" onclick="window.setTLThickness(3)" data-thickness="3"><div class="w-4 h-[3px] bg-white"></div></button>
|
|
||||||
<button class="flex-1 h-6 flex items-center justify-center hover:bg-[#2d3a4f] rounded data-[active=true]:bg-[#2d3a4f] tl-thickness-btn" onclick="window.setTLThickness(4)" data-thickness="4"><div class="w-4 h-[4px] bg-white"></div></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Line Style -->
|
|
||||||
<div class="mb-4">
|
|
||||||
<label class="text-xs text-gray-400 mb-1 block">Line style</label>
|
|
||||||
<div class="flex bg-[#0d1421] rounded border border-[#2d3a4f] p-1">
|
|
||||||
<button class="flex-1 h-6 flex items-center justify-center hover:bg-[#2d3a4f] rounded data-[active=true]:bg-[#2d3a4f] tl-style-btn" onclick="window.setTLStyle(0)" data-style="0"><div class="w-6 border-b border-white"></div></button>
|
|
||||||
<button class="flex-1 h-6 flex items-center justify-center hover:bg-[#2d3a4f] rounded data-[active=true]:bg-[#2d3a4f] tl-style-btn" onclick="window.setTLStyle(1)" data-style="1"><div class="w-6 border-b border-dashed border-white"></div></button>
|
|
||||||
<button class="flex-1 h-6 flex items-center justify-center hover:bg-[#2d3a4f] rounded data-[active=true]:bg-[#2d3a4f] tl-style-btn" onclick="window.setTLStyle(2)" data-style="2"><div class="w-6 border-b border-dotted border-white"></div></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Text Tab Content -->
|
|
||||||
<div id="tlContentText" class="hidden">
|
|
||||||
<div class="flex gap-2 mb-4 relative">
|
|
||||||
<!-- Text Color Button -->
|
|
||||||
<div class="w-8 h-8 rounded border border-[#2d3a4f] cursor-pointer" id="tlTextColorBtn" style="background-color: #2962ff"></div>
|
|
||||||
|
|
||||||
<!-- Text Color Picker Popup -->
|
|
||||||
<div id="tlTextColorPicker" class="hidden absolute top-10 left-0 bg-[#1a2333] border border-[#2d3a4f] rounded shadow-2xl p-2 z-[60] w-[210px]">
|
|
||||||
<div class="grid grid-cols-8 gap-1" id="tlTextColorGrid">
|
|
||||||
<!-- Colors injected by JS -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Font Size -->
|
|
||||||
<select id="tlFontSize" class="bg-[#0d1421] border border-[#2d3a4f] rounded text-xs px-2 h-8 text-white focus:outline-none">
|
|
||||||
<option value="10">10</option>
|
|
||||||
<option value="12">12</option>
|
|
||||||
<option value="14" selected>14</option>
|
|
||||||
<option value="16">16</option>
|
|
||||||
<option value="20">20</option>
|
|
||||||
<option value="24">24</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- Style Toggles -->
|
|
||||||
<button class="w-8 h-8 border border-[#2d3a4f] rounded flex items-center justify-center hover:bg-[#2d3a4f] data-[active=true]:bg-blue-600" id="tlBoldBtn" onclick="window.toggleTLBold()">B</button>
|
|
||||||
<button class="w-8 h-8 border border-[#2d3a4f] rounded flex items-center justify-center hover:bg-[#2d3a4f] italic data-[active=true]:bg-blue-600" id="tlItalicBtn" onclick="window.toggleTLItalic()">I</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<textarea id="tlTextInput" class="w-full h-24 bg-[#0d1421] border border-[#2d3a4f] rounded p-2 text-xs text-white focus:border-blue-500 focus:outline-none mb-4" placeholder="Add text"></textarea>
|
|
||||||
|
|
||||||
<div class="flex items-center justify-between mb-4">
|
|
||||||
<label class="text-xs text-gray-400">Alignment</label>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<select id="tlAlignVert" class="bg-[#0d1421] border border-[#2d3a4f] rounded text-xs px-2 h-6 text-white focus:outline-none">
|
|
||||||
<option value="top">Top</option>
|
|
||||||
<option value="middle">Middle</option>
|
|
||||||
<option value="bottom">Bottom</option>
|
|
||||||
</select>
|
|
||||||
<select id="tlAlignHorz" class="bg-[#0d1421] border border-[#2d3a4f] rounded text-xs px-2 h-6 text-white focus:outline-none">
|
|
||||||
<option value="left">Left</option>
|
|
||||||
<option value="center">Center</option>
|
|
||||||
<option value="right">Right</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Overlay Controls -->
|
<!-- Overlay Controls -->
|
||||||
<div class="absolute bottom-4 right-4 flex gap-2 z-10 opacity-0 hover:opacity-100 transition-opacity" id="priceScaleControls">
|
<div class="absolute bottom-4 right-4 flex gap-2 z-10 opacity-0 hover:opacity-100 transition-opacity" id="priceScaleControls">
|
||||||
<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="btnSettings" title="Settings">
|
<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="btnSettings" title="Settings">
|
||||||
<span class="material-symbols-outlined text-sm">settings</span>
|
<span class="material-symbols-outlined text-sm">settings</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Settings Popup -->
|
<!-- Settings Popup -->
|
||||||
<div class="hidden absolute bottom-10 right-0 bg-[#1a2333] border border-[#2d3a4f] rounded-lg py-2 z-50 w-64 shadow-xl text-sm" id="settingsPopup">
|
<div class="hidden absolute bottom-10 right-0 bg-[#1a2333] border border-[#2d3a4f] rounded-lg py-2 z-50 w-64 shadow-xl text-sm" id="settingsPopup">
|
||||||
|
|
||||||
@ -291,7 +200,7 @@
|
|||||||
<input type="color" id="candleDownColor" value="#ff9800" class="flex-1 h-6 cursor-pointer">
|
<input type="color" id="candleDownColor" value="#ff9800" class="flex-1 h-6 cursor-pointer">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Decimals -->
|
<!-- Decimals -->
|
||||||
<div class="px-4 py-2 flex items-center justify-between">
|
<div class="px-4 py-2 flex items-center justify-between">
|
||||||
<label class="text-sm text-gray-300">Decimals</label>
|
<label class="text-sm text-gray-300">Decimals</label>
|
||||||
@ -299,7 +208,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Navigation Controls -->
|
<!-- Navigation Controls -->
|
||||||
<div class="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2 z-10 opacity-0 hover:opacity-100 transition-opacity" id="navControls">
|
<div class="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2 z-10 opacity-0 hover:opacity-100 transition-opacity" id="navControls">
|
||||||
<button class="w-8 h-8 bg-[#1e222d]/80 backdrop-blur border border-[#2d3a4f] text-white rounded-full flex items-center justify-center hover:bg-blue-600 transition-colors" id="navLeft">
|
<button class="w-8 h-8 bg-[#1e222d]/80 backdrop-blur border border-[#2d3a4f] text-white rounded-full flex items-center justify-center hover:bg-blue-600 transition-colors" id="navLeft">
|
||||||
@ -319,7 +228,7 @@
|
|||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<h2 class="text-lg font-bold flex items-center gap-2">
|
<h2 class="text-lg font-bold flex items-center gap-2">
|
||||||
<span class="material-symbols-outlined text-[#b6c4ff]">analytics</span>
|
<span class="material-symbols-outlined text-[#b6c4ff]">analytics</span>
|
||||||
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">
|
||||||
@ -332,7 +241,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</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">
|
||||||
<div class="text-gray-500 text-sm p-4 text-center w-full col-span-full">Loading analysis data...</div>
|
<div class="text-gray-500 text-sm p-4 text-center w-full col-span-full">Loading analysis data...</div>
|
||||||
</div>
|
</div>
|
||||||
@ -408,7 +317,7 @@
|
|||||||
<span class="material-symbols-outlined">close</span>
|
<span class="material-symbols-outlined">close</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Desktop Sidebar Toggle -->
|
<!-- Desktop Sidebar Toggle -->
|
||||||
<button id="sidebarToggleBtn" class="md:flex hidden w-8 h-8 bg-[#1a2333] border border-[#2d3a4f] text-gray-300 flex items-center justify-center rounded hover:bg-[#2d3a4f] transition-colors" title="Toggle Sidebar">
|
<button id="sidebarToggleBtn" class="md:flex hidden w-8 h-8 bg-[#1a2333] border border-[#2d3a4f] text-gray-300 flex items-center justify-center rounded hover:bg-[#2d3a4f] transition-colors" title="Toggle Sidebar">
|
||||||
<span class="material-symbols-outlined text-sm">menu</span>
|
<span class="material-symbols-outlined text-sm">menu</span>
|
||||||
@ -447,7 +356,7 @@
|
|||||||
window.toggleSidebarDisplay = function(tabName) {
|
window.toggleSidebarDisplay = function(tabName) {
|
||||||
const sidebar = document.getElementById('rightSidebar');
|
const sidebar = document.getElementById('rightSidebar');
|
||||||
if (!sidebar) return;
|
if (!sidebar) return;
|
||||||
|
|
||||||
const isOpen = !sidebar.classList.contains('collapsed');
|
const isOpen = !sidebar.classList.contains('collapsed');
|
||||||
const activeTab = document.querySelector('.sidebar-tab.active');
|
const activeTab = document.querySelector('.sidebar-tab.active');
|
||||||
const currentTabName = activeTab ? activeTab.dataset.tab : null;
|
const currentTabName = activeTab ? activeTab.dataset.tab : null;
|
||||||
@ -460,7 +369,7 @@
|
|||||||
|
|
||||||
// Otherwise, OPEN it (remove collapsed) and switch tab
|
// Otherwise, OPEN it (remove collapsed) and switch tab
|
||||||
sidebar.classList.remove('collapsed');
|
sidebar.classList.remove('collapsed');
|
||||||
|
|
||||||
if (tabName) {
|
if (tabName) {
|
||||||
// Find and click the hidden tab button to trigger existing logic
|
// Find and click the hidden tab button to trigger existing logic
|
||||||
const tabBtn = document.querySelector(`.sidebar-tab[data-tab="${tabName}"]`);
|
const tabBtn = document.querySelector(`.sidebar-tab[data-tab="${tabName}"]`);
|
||||||
@ -489,7 +398,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="./config.js"></script>
|
<script src="./config.js"></script>
|
||||||
<script type="module" src="./js/app.js"></script>
|
<script type="module" src="./js/app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
373
js/ui/chart.js
373
js/ui/chart.js
@ -149,16 +149,14 @@ function throttle(func, limit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import { DrawingManager } from './drawing-tools.js';
|
|
||||||
|
|
||||||
export class TradingDashboard {
|
export class TradingDashboard {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.chart = null;
|
this.chart = null;
|
||||||
this.candleSeries = null;
|
this.candleSeries = null;
|
||||||
// Load settings from local storage or defaults
|
// Load settings from local storage or defaults
|
||||||
this.symbol = localStorage.getItem('winterfail_symbol') || 'BTC';
|
this.symbol = localStorage.getItem('winterfail_symbol') || 'BTC';
|
||||||
this.currentInterval = localStorage.getItem('winterfail_interval') || '1d';
|
this.currentInterval = localStorage.getItem('winterfail_interval') || '1d';
|
||||||
|
|
||||||
this.intervals = INTERVALS;
|
this.intervals = INTERVALS;
|
||||||
this.allData = new Map();
|
this.allData = new Map();
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
@ -171,13 +169,14 @@ export class TradingDashboard {
|
|||||||
this.avgPriceSeries = null;
|
this.avgPriceSeries = null;
|
||||||
this.dailyMAData = new Map(); // timestamp -> { ma44, ma125, price }
|
this.dailyMAData = new Map(); // timestamp -> { ma44, ma125, price }
|
||||||
this.currentMouseTime = null;
|
this.currentMouseTime = null;
|
||||||
this.drawingManager = null;
|
this.drawingItems = []; // Store drawing items for management
|
||||||
|
|
||||||
// Throttled versions of heavy functions
|
// Throttled versions of heavy functions
|
||||||
this.throttledOnVisibleRangeChange = throttle(this.onVisibleRangeChange.bind(this), 150);
|
this.throttledOnVisibleRangeChange = throttle(this.onVisibleRangeChange.bind(this), 150);
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadDailyMAData() {
|
async loadDailyMAData() {
|
||||||
try {
|
try {
|
||||||
// Use 1d interval for this calculation
|
// Use 1d interval for this calculation
|
||||||
@ -394,7 +393,7 @@ export class TradingDashboard {
|
|||||||
if (candleDownInput) candleDownInput.value = savedDownColor;
|
if (candleDownInput) candleDownInput.value = savedDownColor;
|
||||||
|
|
||||||
// Calculate initial minMove based on saved precision
|
// Calculate initial minMove based on saved precision
|
||||||
const initialMinMove = savedPrecision === 0 ? 1 : Number((1 / Math.pow(10, savedPrecision)).toFixed(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: savedUpColor,
|
upColor: savedUpColor,
|
||||||
@ -455,14 +454,6 @@ export class TradingDashboard {
|
|||||||
|
|
||||||
this.initPriceScaleControls();
|
this.initPriceScaleControls();
|
||||||
this.initNavigationControls();
|
this.initNavigationControls();
|
||||||
|
|
||||||
// Initialize Drawing Manager
|
|
||||||
this.drawingManager = new DrawingManager(this, chartContainer);
|
|
||||||
window.activateDrawingTool = (tool, event) => {
|
|
||||||
const e = event || window.event;
|
|
||||||
this.drawingManager.setTool(tool, e);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Setup price format selector change handler
|
// Setup price format selector change handler
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const priceSelect = document.getElementById("priceFormatSelect");
|
const priceSelect = document.getElementById("priceFormatSelect");
|
||||||
@ -511,6 +502,8 @@ export class TradingDashboard {
|
|||||||
initPriceScaleControls() {
|
initPriceScaleControls() {
|
||||||
const btnSettings = document.getElementById('btnSettings');
|
const btnSettings = document.getElementById('btnSettings');
|
||||||
const settingsPopup = document.getElementById('settingsPopup');
|
const settingsPopup = document.getElementById('settingsPopup');
|
||||||
|
const toggleToolbarBtn = document.getElementById('toggleChartToolbar');
|
||||||
|
const chartToolbar = document.getElementById('chartToolbar');
|
||||||
|
|
||||||
// Settings Popup Toggle and Outside Click
|
// Settings Popup Toggle and Outside Click
|
||||||
if (btnSettings && settingsPopup) {
|
if (btnSettings && settingsPopup) {
|
||||||
@ -532,6 +525,16 @@ export class TradingDashboard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Toggle Chart Toolbar
|
||||||
|
if (toggleToolbarBtn && chartToolbar) {
|
||||||
|
toggleToolbarBtn.addEventListener('click', () => {
|
||||||
|
chartToolbar.style.display = chartToolbar.style.display === 'flex' ? 'none' : 'flex';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drawing Tools
|
||||||
|
this.initDrawingTools();
|
||||||
|
|
||||||
// Initialize state from storage
|
// Initialize state from storage
|
||||||
this.scaleState = {
|
this.scaleState = {
|
||||||
autoScale: localStorage.getItem('winterfail_scale_auto') !== 'false',
|
autoScale: localStorage.getItem('winterfail_scale_auto') !== 'false',
|
||||||
@ -589,6 +592,341 @@ export class TradingDashboard {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initDrawingTools() {
|
||||||
|
const btnDrawingTools = document.getElementById('btnDrawingTools');
|
||||||
|
const drawingToolsPopup = document.getElementById('drawingToolsPopup');
|
||||||
|
|
||||||
|
if (btnDrawingTools && drawingToolsPopup) {
|
||||||
|
btnDrawingTools.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
drawingToolsPopup.classList.toggle('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('click', closeDrawingToolsPopup);
|
||||||
|
document.addEventListener('touchstart', closeDrawingToolsPopup, { passive: true });
|
||||||
|
|
||||||
|
function closeDrawingToolsPopup(e) {
|
||||||
|
const isInside = drawingToolsPopup.contains(e.target) || e.target === btnDrawingTools;
|
||||||
|
const isDrawingButton = e.target.closest('#btnDrawingTools');
|
||||||
|
|
||||||
|
if (!isInside && !isDrawingButton) {
|
||||||
|
drawingToolsPopup.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let isDrawing = false;
|
||||||
|
let drawMode = null;
|
||||||
|
let startPoint = null;
|
||||||
|
let drawLine = null;
|
||||||
|
let drawArrow = null;
|
||||||
|
let drawText = null;
|
||||||
|
|
||||||
|
window.activateDrawingTool = (tool) => {
|
||||||
|
if (tool === 'clear') {
|
||||||
|
this.clearDrawingTools();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawMode = tool;
|
||||||
|
isDrawing = true;
|
||||||
|
|
||||||
|
const chart = this.chart;
|
||||||
|
const container = chart.container();
|
||||||
|
|
||||||
|
const onMouseDown = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const rect = container.getBoundingClientRect();
|
||||||
|
const x = e.clientX - rect.left;
|
||||||
|
const y = e.clientY - rect.top;
|
||||||
|
|
||||||
|
if (drawMode === 'trend_line' || drawMode === 'horizontal_line' || drawMode === 'vertical_line') {
|
||||||
|
this.createLine(x, y);
|
||||||
|
} else if (drawMode === 'arrow_up' || drawMode === 'arrow_down') {
|
||||||
|
this.createArrow(drawMode === 'arrow_up' ? 'arrowUp' : 'arrowDown', x, y);
|
||||||
|
} else if (drawMode === 'text') {
|
||||||
|
this.createText(x, y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTouchStart = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const touch = e.touches[0];
|
||||||
|
const rect = container.getBoundingClientRect();
|
||||||
|
const x = touch.clientX - rect.left;
|
||||||
|
const y = touch.clientY - rect.top;
|
||||||
|
|
||||||
|
if (drawMode === 'trend_line' || drawMode === 'horizontal_line' || drawMode === 'vertical_line') {
|
||||||
|
this.createLine(x, y);
|
||||||
|
} else if (drawMode === 'arrow_up' || drawMode === 'arrow_down') {
|
||||||
|
this.createArrow(drawMode === 'arrow_up' ? 'arrowUp' : 'arrowDown', x, y);
|
||||||
|
} else if (drawMode === 'text') {
|
||||||
|
this.createText(x, y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
container.addEventListener('mousedown', onMouseDown);
|
||||||
|
container.addEventListener('touchstart', onTouchStart);
|
||||||
|
|
||||||
|
const closeDrawing = () => {
|
||||||
|
container.removeEventListener('mousedown', onMouseDown);
|
||||||
|
container.removeEventListener('touchstart', onTouchStart);
|
||||||
|
document.removeEventListener('click', closeDrawing);
|
||||||
|
document.removeEventListener('touchstart', closeDrawing);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('click', closeDrawing);
|
||||||
|
document.addEventListener('touchstart', closeDrawing);
|
||||||
|
|
||||||
|
drawingToolsPopup.classList.add('hidden');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createLine(x, y) {
|
||||||
|
const chart = this.chart;
|
||||||
|
const candleSeries = this.candleSeries;
|
||||||
|
|
||||||
|
let lineData = {
|
||||||
|
time: [],
|
||||||
|
value: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const line = chart.addLineSeries({
|
||||||
|
color: '#2962ff',
|
||||||
|
lineWidth: 2,
|
||||||
|
lineStyle: LightweightCharts.LineStyle.Solid,
|
||||||
|
crosshairMarkerVisible: true,
|
||||||
|
crosshairMarkerRadius: 4,
|
||||||
|
baseIndex: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
let points = [];
|
||||||
|
|
||||||
|
const container = chart.container();
|
||||||
|
let startTime = null;
|
||||||
|
let startPrice = null;
|
||||||
|
|
||||||
|
const onMouseDown = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const rect = container.getBoundingClientRect();
|
||||||
|
const clientX = e.clientX;
|
||||||
|
const clientY = e.clientY;
|
||||||
|
|
||||||
|
const timeCoord = chart.timeScale().coordinateToTime(x);
|
||||||
|
const price = chart.priceScale().coordinateToPrice(y);
|
||||||
|
|
||||||
|
if (startTime === null) {
|
||||||
|
startTime = timeCoord;
|
||||||
|
startPrice = price;
|
||||||
|
|
||||||
|
line.setData([{
|
||||||
|
time: startTime,
|
||||||
|
value: startPrice
|
||||||
|
}]);
|
||||||
|
} else {
|
||||||
|
line.setData([
|
||||||
|
{ time: startTime, value: startPrice },
|
||||||
|
{ time: timeCoord, value: price }
|
||||||
|
]);
|
||||||
|
startTime = null;
|
||||||
|
startPrice = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTouchStart = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const touch = e.touches[0];
|
||||||
|
const rect = container.getBoundingClientRect();
|
||||||
|
const clientX = touch.clientX;
|
||||||
|
const clientY = touch.clientY;
|
||||||
|
|
||||||
|
const timeCoord = chart.timeScale().coordinateToTime(x);
|
||||||
|
const price = chart.priceScale().coordinateToPrice(y);
|
||||||
|
|
||||||
|
if (startTime === null) {
|
||||||
|
startTime = timeCoord;
|
||||||
|
startPrice = price;
|
||||||
|
|
||||||
|
line.setData([{
|
||||||
|
time: startTime,
|
||||||
|
value: startPrice
|
||||||
|
}]);
|
||||||
|
} else {
|
||||||
|
line.setData([
|
||||||
|
{ time: startTime, value: startPrice },
|
||||||
|
{ time: timeCoord, value: price }
|
||||||
|
]);
|
||||||
|
startTime = null;
|
||||||
|
startPrice = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
container.addEventListener('mousedown', onMouseDown);
|
||||||
|
container.addEventListener('touchstart', onTouchStart);
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
container.removeEventListener('mousedown', onMouseDown);
|
||||||
|
container.removeEventListener('touchstart', onTouchStart);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('click', cleanup);
|
||||||
|
document.addEventListener('touchstart', cleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
createArrow(arrowType, x, y) {
|
||||||
|
const chart = this.chart;
|
||||||
|
const container = chart.container();
|
||||||
|
|
||||||
|
const markerPrimitive = new SeriesMarkersPrimitive();
|
||||||
|
this.candleSeries.attachPrimitive(markerPrimitive);
|
||||||
|
|
||||||
|
const rect = container.getBoundingClientRect();
|
||||||
|
const time = chart.timeScale().coordinateToTime(x);
|
||||||
|
const price = chart.priceScale().coordinateToPrice(y);
|
||||||
|
|
||||||
|
const marker = {
|
||||||
|
time: time,
|
||||||
|
position: arrowType === 'arrowUp' ? 'belowBar' : 'aboveBar',
|
||||||
|
color: arrowType === 'arrowUp' ? '#26a69a' : '#ef5350',
|
||||||
|
shape: arrowType === 'arrowUp' ? 'arrowUp' : 'arrowDown',
|
||||||
|
text: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
markerPrimitive.setMarkers([marker]);
|
||||||
|
|
||||||
|
let isDragging = false;
|
||||||
|
let startX = 0;
|
||||||
|
let startY = 0;
|
||||||
|
let startTime = time;
|
||||||
|
let startPrice = price;
|
||||||
|
|
||||||
|
const onMouseDown = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
isDragging = true;
|
||||||
|
startX = e.clientX;
|
||||||
|
startY = e.clientY;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseMove = (e) => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
|
||||||
|
const dx = e.clientX - startX;
|
||||||
|
const dy = e.clientY - startY;
|
||||||
|
const rect = container.getBoundingClientRect();
|
||||||
|
|
||||||
|
const timeCoord = chart.timeScale().coordinateToTime(x + dx);
|
||||||
|
const newPrice = chart.priceScale().coordinateToPrice(y + dy);
|
||||||
|
|
||||||
|
const newMarker = {
|
||||||
|
time: timeCoord,
|
||||||
|
position: arrowType === 'arrowUp' ? 'belowBar' : 'aboveBar',
|
||||||
|
color: arrowType === 'arrowUp' ? '#26a69a' : '#ef5350',
|
||||||
|
shape: arrowType === 'arrowUp' ? 'arrowUp' : 'arrowDown',
|
||||||
|
text: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
markerPrimitive.setMarkers([newMarker]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
isDragging = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
container.addEventListener('mousedown', onMouseDown);
|
||||||
|
document.addEventListener('mousemove', onMouseMove);
|
||||||
|
document.addEventListener('mouseup', onMouseUp);
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
container.removeEventListener('mousedown', onMouseDown);
|
||||||
|
document.removeEventListener('mousemove', onMouseMove);
|
||||||
|
document.removeEventListener('mouseup', onMouseUp);
|
||||||
|
document.removeEventListener('click', cleanup);
|
||||||
|
document.removeEventListener('touchstart', cleanup);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('click', cleanup);
|
||||||
|
document.addEventListener('touchstart', cleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
createText(x, y) {
|
||||||
|
const chart = this.chart;
|
||||||
|
const container = chart.container();
|
||||||
|
|
||||||
|
const text = prompt('Enter text label:', '');
|
||||||
|
if (!text) return;
|
||||||
|
|
||||||
|
const rect = container.getBoundingClientRect();
|
||||||
|
const time = chart.timeScale().coordinateToTime(x);
|
||||||
|
const price = chart.priceScale().coordinateToPrice(y);
|
||||||
|
|
||||||
|
const label = chart.addLabel({
|
||||||
|
text: text,
|
||||||
|
color: '#ffffff',
|
||||||
|
fontSize: 11,
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
crosshairMarkerVisible: false,
|
||||||
|
time: time,
|
||||||
|
price: price
|
||||||
|
});
|
||||||
|
|
||||||
|
let isDragging = false;
|
||||||
|
let startX = 0;
|
||||||
|
let startY = 0;
|
||||||
|
let startTime = time;
|
||||||
|
let startPrice = price;
|
||||||
|
|
||||||
|
const onMouseDown = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
isDragging = true;
|
||||||
|
startX = e.clientX;
|
||||||
|
startY = e.clientY;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseMove = (e) => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
|
||||||
|
const dx = e.clientX - startX;
|
||||||
|
const dy = e.clientY - startY;
|
||||||
|
|
||||||
|
const newTime = chart.timeScale().coordinateToTime(x + dx);
|
||||||
|
const newPrice = chart.priceScale().coordinateToPrice(y + dy);
|
||||||
|
|
||||||
|
label.applyOptions({
|
||||||
|
time: newTime,
|
||||||
|
price: newPrice
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
isDragging = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
container.addEventListener('mousedown', onMouseDown);
|
||||||
|
document.addEventListener('mousemove', onMouseMove);
|
||||||
|
document.addEventListener('mouseup', onMouseUp);
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
container.removeEventListener('mousedown', onMouseDown);
|
||||||
|
document.removeEventListener('mousemove', onMouseMove);
|
||||||
|
document.removeEventListener('mouseup', onMouseUp);
|
||||||
|
document.removeEventListener('click', cleanup);
|
||||||
|
document.removeEventListener('touchstart', cleanup);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('click', cleanup);
|
||||||
|
document.addEventListener('touchstart', cleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearDrawingTools() {
|
||||||
|
const allSeries = this.chart.series();
|
||||||
|
allSeries.forEach(series => {
|
||||||
|
if (series.type !== 'Candlestick' && series.type !== 'Line' && series.type !== 'Area') {
|
||||||
|
this.chart.removeSeries(series);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
initNavigationControls() {
|
initNavigationControls() {
|
||||||
const chartWrapper = document.getElementById('chartWrapper');
|
const chartWrapper = document.getElementById('chartWrapper');
|
||||||
const navLeft = document.getElementById('navLeft');
|
const navLeft = document.getElementById('navLeft');
|
||||||
@ -788,12 +1126,9 @@ async loadNewData() {
|
|||||||
|
|
||||||
//console.log(`[NewData Load] Added ${chartData.length} new candles, total in dataset: ${this.allData.get(this.currentInterval).length}`);
|
//console.log(`[NewData Load] Added ${chartData.length} new candles, total in dataset: ${this.allData.get(this.currentInterval).length}`);
|
||||||
|
|
||||||
// Auto-scrolling disabled per user request
|
|
||||||
/*
|
|
||||||
if (atEdge) {
|
if (atEdge) {
|
||||||
this.chart.timeScale().scrollToRealTime();
|
this.chart.timeScale().scrollToRealTime();
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
this.updateStats(latest);
|
this.updateStats(latest);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user