Improve formations drawing UX with step-by-step flow
Changed UX flow from confusing to clear: 1. Click Line/Channel -> Shows instructions + points counter 2. Click points on chart -> Updates counter (Points: 1/2) 3. After enough points -> Shows name input + Save button 4. Enter name + Save -> Formation created formations_hud.html: - Add instructions panel with Cancel button - Add points status display - Separate name input controls (shown after points placed) formations.js: - Add showDrawingInstructions() with type-specific text - Add updatePointsStatus() for live counter - Add showNameInput() after drawing complete - Add hideAllDrawingUI() for cleanup formation_overlay.js: - Add onPointsChangedCallback for UI updates - Store _pointsNeeded per drawing session - Stop accepting clicks after enough points - Change cursor back to default when complete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
fceb040ef0
commit
ac4c085acd
|
|
@ -38,6 +38,12 @@ class FormationOverlay {
|
||||||
// Callback for saving formations
|
// Callback for saving formations
|
||||||
this.onSaveCallback = null;
|
this.onSaveCallback = null;
|
||||||
|
|
||||||
|
// Callback for when points change during drawing
|
||||||
|
this.onPointsChangedCallback = null;
|
||||||
|
|
||||||
|
// Track points needed for current drawing
|
||||||
|
this._pointsNeeded = 0;
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
this.defaultColor = '#667eea';
|
this.defaultColor = '#667eea';
|
||||||
this.selectedColor = '#ff9500';
|
this.selectedColor = '#ff9500';
|
||||||
|
|
@ -57,6 +63,14 @@ class FormationOverlay {
|
||||||
this.onSaveCallback = callback;
|
this.onSaveCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set callback for when points change during drawing.
|
||||||
|
* @param {Function} callback - Called with (currentPoints, pointsNeeded)
|
||||||
|
*/
|
||||||
|
setOnPointsChangedCallback(callback) {
|
||||||
|
this.onPointsChangedCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the SVG overlay layer.
|
* Create the SVG overlay layer.
|
||||||
*/
|
*/
|
||||||
|
|
@ -361,6 +375,7 @@ class FormationOverlay {
|
||||||
startDrawing(type) {
|
startDrawing(type) {
|
||||||
this.drawingMode = type;
|
this.drawingMode = type;
|
||||||
this.currentPoints = [];
|
this.currentPoints = [];
|
||||||
|
this._pointsNeeded = this._getPointsNeeded(type);
|
||||||
this._clearTempElements();
|
this._clearTempElements();
|
||||||
|
|
||||||
// Change cursor
|
// Change cursor
|
||||||
|
|
@ -373,7 +388,12 @@ class FormationOverlay {
|
||||||
this.svg.style.pointerEvents = 'all';
|
this.svg.style.pointerEvents = 'all';
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('FormationOverlay: Started drawing', type);
|
// Notify of initial state
|
||||||
|
if (this.onPointsChangedCallback) {
|
||||||
|
this.onPointsChangedCallback(0, this._pointsNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('FormationOverlay: Started drawing', type, 'needs', this._pointsNeeded, 'points');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -381,16 +401,28 @@ class FormationOverlay {
|
||||||
* @param {Object} coords - {time, price}
|
* @param {Object} coords - {time, price}
|
||||||
*/
|
*/
|
||||||
_handleDrawingClick(coords) {
|
_handleDrawingClick(coords) {
|
||||||
|
// Don't accept more points than needed
|
||||||
|
if (this.currentPoints.length >= this._pointsNeeded) {
|
||||||
|
console.log('FormationOverlay: Already have enough points');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.currentPoints.push(coords);
|
this.currentPoints.push(coords);
|
||||||
|
|
||||||
// Draw anchor at click point
|
// Draw anchor at click point
|
||||||
this._drawTempAnchor(coords);
|
this._drawTempAnchor(coords);
|
||||||
|
|
||||||
// Check if drawing is complete based on formation type
|
// Notify of points change
|
||||||
const pointsNeeded = this._getPointsNeeded(this.drawingMode);
|
if (this.onPointsChangedCallback) {
|
||||||
|
this.onPointsChangedCallback(this.currentPoints.length, this._pointsNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.currentPoints.length >= pointsNeeded) {
|
// Check if drawing is complete
|
||||||
// Drawing complete, wait for name input
|
if (this.currentPoints.length >= this._pointsNeeded) {
|
||||||
|
// Drawing complete, change cursor back
|
||||||
|
if (this.container) {
|
||||||
|
this.container.style.cursor = 'default';
|
||||||
|
}
|
||||||
console.log('FormationOverlay: Points collected', this.currentPoints);
|
console.log('FormationOverlay: Points collected', this.currentPoints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -546,6 +578,8 @@ class FormationOverlay {
|
||||||
_exitDrawingMode() {
|
_exitDrawingMode() {
|
||||||
this.drawingMode = null;
|
this.drawingMode = null;
|
||||||
this.currentPoints = [];
|
this.currentPoints = [];
|
||||||
|
this._pointsNeeded = 0;
|
||||||
|
this.onPointsChangedCallback = null;
|
||||||
this._clearTempElements();
|
this._clearTempElements();
|
||||||
|
|
||||||
// Reset cursor
|
// Reset cursor
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,11 @@
|
||||||
class FormationsUIManager {
|
class FormationsUIManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.targetEl = null;
|
this.targetEl = null;
|
||||||
this.drawingControlsEl = null;
|
this.instructionsEl = null;
|
||||||
|
this.nameControlsEl = null;
|
||||||
this.nameInputEl = null;
|
this.nameInputEl = null;
|
||||||
|
this.instructionTextEl = null;
|
||||||
|
this.pointsStatusEl = null;
|
||||||
this.onDeleteFormation = null;
|
this.onDeleteFormation = null;
|
||||||
this.onEditFormation = null;
|
this.onEditFormation = null;
|
||||||
}
|
}
|
||||||
|
|
@ -20,8 +23,11 @@ class FormationsUIManager {
|
||||||
console.warn(`Formations container "${targetId}" not found.`);
|
console.warn(`Formations container "${targetId}" not found.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.drawingControlsEl = document.getElementById('formation_drawing_controls');
|
this.instructionsEl = document.getElementById('formation_drawing_instructions');
|
||||||
|
this.nameControlsEl = document.getElementById('formation_name_controls');
|
||||||
this.nameInputEl = document.getElementById('formation_name_input');
|
this.nameInputEl = document.getElementById('formation_name_input');
|
||||||
|
this.instructionTextEl = document.getElementById('formation_instruction_text');
|
||||||
|
this.pointsStatusEl = document.getElementById('formation_points_status');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -41,12 +47,62 @@ class FormationsUIManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show drawing controls.
|
* Show drawing instructions for a formation type.
|
||||||
|
* @param {string} type - Formation type
|
||||||
|
* @param {number} pointsNeeded - Number of points needed
|
||||||
*/
|
*/
|
||||||
showDrawingControls() {
|
showDrawingInstructions(type, pointsNeeded) {
|
||||||
if (this.drawingControlsEl) {
|
// Hide name controls if visible
|
||||||
this.drawingControlsEl.style.display = 'block';
|
if (this.nameControlsEl) {
|
||||||
|
this.nameControlsEl.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show instructions
|
||||||
|
if (this.instructionsEl) {
|
||||||
|
this.instructionsEl.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set instruction text based on type
|
||||||
|
const instructions = {
|
||||||
|
'support_resistance': 'Click 2 points on the chart to draw a line',
|
||||||
|
'channel': 'Click 3 points: first line (2 pts) + parallel offset (1 pt)'
|
||||||
|
};
|
||||||
|
if (this.instructionTextEl) {
|
||||||
|
this.instructionTextEl.textContent = instructions[type] || 'Click on chart to place points';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update points status
|
||||||
|
this.updatePointsStatus(0, pointsNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the points status display.
|
||||||
|
* @param {number} current - Current points placed
|
||||||
|
* @param {number} needed - Points needed
|
||||||
|
*/
|
||||||
|
updatePointsStatus(current, needed) {
|
||||||
|
if (this.pointsStatusEl) {
|
||||||
|
this.pointsStatusEl.textContent = `Points: ${current} / ${needed}`;
|
||||||
|
// Change color when complete
|
||||||
|
this.pointsStatusEl.style.color = current >= needed ? '#28a745' : '#667eea';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show name input after points are placed.
|
||||||
|
*/
|
||||||
|
showNameInput() {
|
||||||
|
// Hide instructions
|
||||||
|
if (this.instructionsEl) {
|
||||||
|
this.instructionsEl.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show name controls
|
||||||
|
if (this.nameControlsEl) {
|
||||||
|
this.nameControlsEl.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus and clear input
|
||||||
if (this.nameInputEl) {
|
if (this.nameInputEl) {
|
||||||
this.nameInputEl.value = '';
|
this.nameInputEl.value = '';
|
||||||
this.nameInputEl.focus();
|
this.nameInputEl.focus();
|
||||||
|
|
@ -54,12 +110,29 @@ class FormationsUIManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide drawing controls.
|
* Hide all drawing-related UI.
|
||||||
|
*/
|
||||||
|
hideAllDrawingUI() {
|
||||||
|
if (this.instructionsEl) {
|
||||||
|
this.instructionsEl.style.display = 'none';
|
||||||
|
}
|
||||||
|
if (this.nameControlsEl) {
|
||||||
|
this.nameControlsEl.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy method - kept for compatibility
|
||||||
|
*/
|
||||||
|
showDrawingControls() {
|
||||||
|
// Now handled by showDrawingInstructions and showNameInput
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy method - kept for compatibility
|
||||||
*/
|
*/
|
||||||
hideDrawingControls() {
|
hideDrawingControls() {
|
||||||
if (this.drawingControlsEl) {
|
this.hideAllDrawingUI();
|
||||||
this.drawingControlsEl.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -386,7 +459,7 @@ class Formations {
|
||||||
this.overlay.renderFormation(data.formation);
|
this.overlay.renderFormation(data.formation);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.uiManager.hideDrawingControls();
|
this.uiManager.hideAllDrawingUI();
|
||||||
this.drawingMode = null;
|
this.drawingMode = null;
|
||||||
} else {
|
} else {
|
||||||
alert(`Failed to create formation: ${data.message}`);
|
alert(`Failed to create formation: ${data.message}`);
|
||||||
|
|
@ -438,6 +511,19 @@ class Formations {
|
||||||
|
|
||||||
// ================ Drawing Methods ================
|
// ================ Drawing Methods ================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get points needed for a formation type.
|
||||||
|
* @param {string} type - Formation type
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
_getPointsNeeded(type) {
|
||||||
|
const pointsMap = {
|
||||||
|
'support_resistance': 2,
|
||||||
|
'channel': 3
|
||||||
|
};
|
||||||
|
return pointsMap[type] || 2;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start drawing a new formation.
|
* Start drawing a new formation.
|
||||||
* @param {string} type - Formation type ('support_resistance', 'channel')
|
* @param {string} type - Formation type ('support_resistance', 'channel')
|
||||||
|
|
@ -446,15 +532,33 @@ class Formations {
|
||||||
console.log("Starting drawing mode:", type);
|
console.log("Starting drawing mode:", type);
|
||||||
this.drawingMode = type;
|
this.drawingMode = type;
|
||||||
|
|
||||||
// Show drawing controls
|
const pointsNeeded = this._getPointsNeeded(type);
|
||||||
this.uiManager.showDrawingControls();
|
|
||||||
|
|
||||||
// Tell overlay to start drawing
|
// Show drawing instructions (not name input yet)
|
||||||
|
this.uiManager.showDrawingInstructions(type, pointsNeeded);
|
||||||
|
|
||||||
|
// Tell overlay to start drawing, with callback for point updates
|
||||||
if (this.overlay) {
|
if (this.overlay) {
|
||||||
|
this.overlay.setOnPointsChangedCallback(this._onPointsChanged.bind(this));
|
||||||
this.overlay.startDrawing(type);
|
this.overlay.startDrawing(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when points are added/changed during drawing.
|
||||||
|
* @param {number} currentPoints - Current number of points
|
||||||
|
* @param {number} pointsNeeded - Points needed for completion
|
||||||
|
*/
|
||||||
|
_onPointsChanged(currentPoints, pointsNeeded) {
|
||||||
|
// Update the UI status
|
||||||
|
this.uiManager.updatePointsStatus(currentPoints, pointsNeeded);
|
||||||
|
|
||||||
|
// If we have enough points, show the name input
|
||||||
|
if (currentPoints >= pointsNeeded) {
|
||||||
|
this.uiManager.showNameInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete the current drawing.
|
* Complete the current drawing.
|
||||||
*/
|
*/
|
||||||
|
|
@ -475,7 +579,7 @@ class Formations {
|
||||||
*/
|
*/
|
||||||
cancelDrawing() {
|
cancelDrawing() {
|
||||||
this.drawingMode = null;
|
this.drawingMode = null;
|
||||||
this.uiManager.hideDrawingControls();
|
this.uiManager.hideAllDrawingUI();
|
||||||
|
|
||||||
if (this.overlay) {
|
if (this.overlay) {
|
||||||
this.overlay.cancelDrawing();
|
this.overlay.cancelDrawing();
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,20 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Drawing controls (shown when drawing) -->
|
<!-- Drawing instructions (shown when drawing starts) -->
|
||||||
<div id="formation_drawing_controls" style="display: none; margin-top: 10px; padding: 10px; background: #2a2a2a; border-radius: 5px;">
|
<div id="formation_drawing_instructions" style="display: none; margin-top: 10px; padding: 10px; background: #2a2a2a; border-radius: 5px;">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<span id="formation_instruction_text" style="color: #8899aa; font-size: 12px;">Click on chart to place points...</span>
|
||||||
|
<button class="btn btn-sm" style="background: #dc3545; padding: 4px 8px;" onclick="UI.formations.cancelDrawing()">Cancel</button>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 8px; color: #667eea; font-size: 11px;">
|
||||||
|
<span id="formation_points_status">Points: 0 / 2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Name input (shown after points are placed) -->
|
||||||
|
<div id="formation_name_controls" style="display: none; margin-top: 10px; padding: 10px; background: #2a2a2a; border-radius: 5px;">
|
||||||
|
<div style="margin-bottom: 8px; color: #28a745; font-size: 12px;">Points placed. Enter a name:</div>
|
||||||
<div style="display: flex; gap: 10px; align-items: center;">
|
<div style="display: flex; gap: 10px; align-items: center;">
|
||||||
<input type="text" id="formation_name_input" placeholder="Formation name"
|
<input type="text" id="formation_name_input" placeholder="Formation name"
|
||||||
style="flex: 1; padding: 5px; border-radius: 3px; border: 1px solid #444; background: #1e1e1e; color: #e0e0e0;">
|
style="flex: 1; padding: 5px; border-radius: 3px; border: 1px solid #444; background: #1e1e1e; color: #e0e0e0;">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue