resizable drawing list
This commit is contained in:
parent
5ee32b327c
commit
3adb98c1fe
3 changed files with 52 additions and 3 deletions
|
|
@ -1,4 +1,4 @@
|
|||
const { createApp, ref, computed, watch, onMounted, onUnmounted } = Vue;
|
||||
const { createApp, ref, computed, watch, onMounted, onUnmounted, nextTick } = Vue;
|
||||
|
||||
// Keep an existing token long enough to migrate an already-open browser to the
|
||||
// password session. Successful login removes it; new browsers never store the
|
||||
|
|
@ -136,6 +136,7 @@ createApp({
|
|||
const snap = ref(true);
|
||||
const selectedDrawing = ref(null);
|
||||
const selectedDrawings = ref([]);
|
||||
const drawingList = ref(null);
|
||||
const alertPrice = ref(null);
|
||||
const alertNote = ref('');
|
||||
const levelColor = ref('#e0a34a');
|
||||
|
|
@ -1125,11 +1126,42 @@ createApp({
|
|||
sendPrefs();
|
||||
}, { deep: true });
|
||||
|
||||
function startDrawingListResize(event) {
|
||||
const el = drawingList.value;
|
||||
if (!el) return;
|
||||
event.preventDefault();
|
||||
const origin = event.clientY;
|
||||
const start = el.getBoundingClientRect().height;
|
||||
const move = ev => {
|
||||
el.style.height = `${Math.max(88, Math.round(start + ev.clientY - origin))}px`;
|
||||
};
|
||||
const stop = () => {
|
||||
window.removeEventListener('pointermove', move);
|
||||
window.removeEventListener('pointerup', stop);
|
||||
const height = Math.round(el.getBoundingClientRect().height);
|
||||
if (height > 80) localStorage.setItem('chart-drawing-list-height', String(height));
|
||||
};
|
||||
window.addEventListener('pointermove', move);
|
||||
window.addEventListener('pointerup', stop);
|
||||
}
|
||||
|
||||
function revealSelectedDrawing(id) {
|
||||
if (!id) return;
|
||||
const list = drawingList.value;
|
||||
const section = list?.closest('details');
|
||||
if (section) section.open = true;
|
||||
nextTick(() => {
|
||||
const row = list?.querySelector(`[data-drawing-id="${CSS.escape(id)}"]`);
|
||||
row?.scrollIntoView({ block: 'nearest' });
|
||||
});
|
||||
}
|
||||
|
||||
watch(selectedDrawing, id => {
|
||||
if (chartApi) {
|
||||
chartApi.setSelectedDrawing(id);
|
||||
chartApi.setSelectedLine(levels.value.some(level => level.id === id) ? id : null);
|
||||
}
|
||||
revealSelectedDrawing(id);
|
||||
});
|
||||
|
||||
watch(snap, value => {
|
||||
|
|
@ -1171,6 +1203,11 @@ createApp({
|
|||
chartApi.setAutoScrollLivePrice(autoScrollLivePrice.value);
|
||||
chartApi.setCommentHandlers({ onToggle: toggleComment, onMove: moveComment, onSelect: selectComment });
|
||||
window.addEventListener('keydown', handleKeydown);
|
||||
nextTick(() => {
|
||||
const el = drawingList.value;
|
||||
const saved = Number(localStorage.getItem('chart-drawing-list-height'));
|
||||
if (el && saved > 80) el.style.height = `${saved}px`;
|
||||
});
|
||||
await migrateLegacyToken();
|
||||
await refreshStatus();
|
||||
await refreshComments();
|
||||
|
|
@ -1186,6 +1223,6 @@ createApp({
|
|||
window.removeEventListener('keydown', handleKeydown);
|
||||
});
|
||||
|
||||
return { status, price, sessionOpen, quoteChange, animateCurrentPrice, autoScrollLivePrice, barAge, dataUpdatedAt, buildStamp, timeframe, timeframes, drawingColors, drawingColorRows, drawingColorName, colorRowLabels, symbolChoices, selectedSymbol, symbolColor, symbolScale, symbolScales, symbolPanelOpen, prefs, clusters, clustersByPrice, events, diagnosticMode, captureBusy, captureDiagnostic, armedTool, drawName, drawColor, drawWidth, drawSide, snap, selectedDrawing, selectedDrawings, manualLines, hasDrawingSelection, allShownSelected, selectedAreHidden, alertPrice, alertNote, alertEarlyPoints, levelColor, levelWidth, addPriceAlert, armTool, selectTimeframe, allEnabled, toggleGroup, deleteSelected, deleteLine, toggleDrawingSelection, toggleSelectAll, toggleSelectedVisibility, renameLine, updateLineStyle, updateLevelNumber, setArmed, commentText, commentFloat, comments, drawings, filteredDrawings, drawingFilter, drawingKind, deleteDrawing, toggleComment, togglePinned, chooseSymbol, toggleSymbolPanel, startSymbolDrag, dropSymbol, optionExpirations, optionExpiryId, optionSide, optionMode, optionMin, optionMax, optionContracts, optionUnderlying, optionBusy, optionError, optionSearched, optionCopied, onOptionsToggle, searchOptions, copyOption };
|
||||
return { status, price, sessionOpen, quoteChange, animateCurrentPrice, autoScrollLivePrice, barAge, dataUpdatedAt, buildStamp, timeframe, timeframes, drawingColors, drawingColorRows, drawingColorName, colorRowLabels, symbolChoices, selectedSymbol, symbolColor, symbolScale, symbolScales, symbolPanelOpen, prefs, clusters, clustersByPrice, events, diagnosticMode, captureBusy, captureDiagnostic, armedTool, drawName, drawColor, drawWidth, drawSide, snap, selectedDrawing, selectedDrawings, drawingList, startDrawingListResize, manualLines, hasDrawingSelection, allShownSelected, selectedAreHidden, alertPrice, alertNote, alertEarlyPoints, levelColor, levelWidth, addPriceAlert, armTool, selectTimeframe, allEnabled, toggleGroup, deleteSelected, deleteLine, toggleDrawingSelection, toggleSelectAll, toggleSelectedVisibility, renameLine, updateLineStyle, updateLevelNumber, setArmed, commentText, commentFloat, comments, drawings, filteredDrawings, drawingFilter, drawingKind, deleteDrawing, toggleComment, togglePinned, chooseSymbol, toggleSymbolPanel, startSymbolDrag, dropSymbol, optionExpirations, optionExpiryId, optionSide, optionMode, optionMin, optionMax, optionContracts, optionUnderlying, optionBusy, optionError, optionSearched, optionCopied, onOptionsToggle, searchOptions, copyOption };
|
||||
},
|
||||
}).mount('#app');
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<details class="sidebar-section" open>
|
||||
<details class="sidebar-section drawings-section" open>
|
||||
<summary>Drawings ({{ filteredDrawings.length }}<span v-if="filteredDrawings.length !== drawings.length"> of {{ drawings.length }}</span>)</summary>
|
||||
<div class="drawing-filters">
|
||||
<select v-model="drawingKind" aria-label="Filter drawings by type">
|
||||
|
|
@ -243,10 +243,13 @@
|
|||
</button>
|
||||
<button @click="deleteSelected" :disabled="!selectedDrawings.length" aria-label="Delete selected drawings">Delete</button>
|
||||
</div>
|
||||
<div class="drawing-list-shell">
|
||||
<div class="drawing-list" ref="drawingList">
|
||||
<div v-if="!drawings.length" class="empty">Nothing drawn yet.</div>
|
||||
<div v-else-if="!filteredDrawings.length" class="empty">No drawings match this filter.</div>
|
||||
|
||||
<div v-for="item in filteredDrawings" :key="item.id" class="trendline-row"
|
||||
:data-drawing-id="item.id"
|
||||
:class="{selected: selectedDrawings.includes(item.id), 'hidden-drawing': item.hidden}">
|
||||
<input class="line-select" type="checkbox"
|
||||
:checked="selectedDrawings.includes(item.id)" :aria-label="`Select drawing ${item.number}`"
|
||||
|
|
@ -344,6 +347,10 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drawing-list-resize" role="separator" aria-orientation="horizontal"
|
||||
aria-label="Resize drawings list" @pointerdown="startDrawingListResize"></div>
|
||||
</div>
|
||||
</details>
|
||||
<details class="sidebar-section">
|
||||
<summary>Confluence zones</summary>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ button { border:1px solid var(--line); background:transparent; color:var(--muted
|
|||
.statusbar { min-height:34px; display:flex; align-items:center; gap:24px; padding:6px 13px; border-top:1px solid var(--line); color:var(--muted); font-size:10px; }.statusbar b { color:var(--fg); text-transform:uppercase; }.data-freshness { display:flex; gap:24px; margin-left:auto; white-space:nowrap; }.diag-capture { padding:3px 7px; border-color:var(--accent); color:var(--accent); font-size:9px; white-space:nowrap; }.diag-capture:disabled { opacity:.55; cursor:wait; }
|
||||
aside { padding:16px; }h2 { margin:0 0 12px; color:var(--muted); font-size:11px; text-transform:uppercase; letter-spacing:1.3px; }h2:not(:first-child) { margin-top:30px; }.empty { border-left:2px solid var(--line); padding:10px 12px; color:var(--muted); font-size:11px; }
|
||||
.sidebar-section { margin-top:30px; }.sidebar-section:first-of-type { margin-top:0; }.sidebar-section summary { margin-bottom:12px; color:var(--muted); font-size:11px; text-transform:uppercase; letter-spacing:1.3px; cursor:pointer; user-select:none; }.sidebar-section:not([open]) summary { margin-bottom:0; }
|
||||
.drawing-list-shell { display:flex; flex-direction:column; min-width:0; }
|
||||
.drawing-list { overflow:auto; min-height:88px; height:180px; border:1px solid var(--line); border-bottom:0; }
|
||||
.drawing-list-resize { flex:none; height:11px; border:1px solid var(--line); background:var(--chart-bg); cursor:ns-resize; touch-action:none; }
|
||||
.drawing-list-resize::after { content:''; display:block; width:36px; height:3px; margin:3px auto 0; border-radius:1px; background:var(--muted); }
|
||||
.drawing-list-resize:hover::after { background:var(--accent); }
|
||||
.trendline-actions { display:grid; grid-template-columns:auto 1fr auto auto; align-items:center; gap:4px; margin-bottom:7px; }.trendline-actions button { padding:3px 5px; font-size:8px; white-space:nowrap; }.selection-count { color:var(--muted); font-size:8px; text-align:center; white-space:nowrap; }
|
||||
.trendline-row { display:grid; grid-template-columns:14px minmax(0,1fr); gap:4px; padding:2px 4px; border:1px solid transparent; border-bottom-color:var(--line); }.trendline-row.selected { border-color:var(--accent); }.trendline-row.hidden-drawing .drawing-content { opacity:.48; }.trendline-row.hidden-drawing .drawing-secondary>span::before { content:'HIDDEN · '; }.trendline-row>.line-select { align-self:center; width:12px; height:12px; margin:0; accent-color:var(--accent); }.trendline-row>.drawing-icon { align-self:center; }
|
||||
.drawing-content { min-width:0; display:grid; gap:1px; }.drawing-primary,.drawing-secondary { display:flex; align-items:center; min-width:0; }.drawing-primary { gap:3px; }.drawing-primary>input { flex:1; min-width:0; height:20px; padding:1px 3px; border:0; border-bottom:1px solid var(--line); background:transparent; color:var(--fg); font:inherit; font-size:10px; }.drawing-secondary { justify-content:space-between; gap:5px; min-height:19px; }.drawing-secondary>span { overflow:hidden; color:var(--muted); font-size:8px; letter-spacing:.25px; text-transform:uppercase; white-space:nowrap; text-overflow:ellipsis; }
|
||||
|
|
|
|||
Loading…
Reference in a new issue