Add comparison between plots
This commit is contained in:
+466
@@ -562,6 +562,232 @@
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
PLOT COMPARISON OVERLAY
|
||||
======================================== */
|
||||
.comparison-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
z-index: 2000;
|
||||
display: none;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.comparison-overlay.open {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.comparison-container {
|
||||
width: 95%;
|
||||
height: 90%;
|
||||
background: var(--bg-color);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.comparison-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.comparison-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.comparison-close {
|
||||
background: var(--button-bg);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.comparison-close:hover {
|
||||
background: var(--button-hover);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.comparison-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.comparison-panel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.comparison-panel-header {
|
||||
background: var(--tree-bg);
|
||||
padding: 0.8rem 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.comparison-panel-title {
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.comparison-replace-btn {
|
||||
background: var(--success-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.4rem 0.8rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.comparison-replace-btn:hover {
|
||||
background: var(--success-hover);
|
||||
}
|
||||
|
||||
.comparison-panel-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.comparison-plot-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.comparison-plot {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.comparison-plot:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.comparison-placeholder {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
border: 2px dashed var(--border-color);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--breadcrumb-color);
|
||||
font-size: 1.1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.comparison-placeholder:hover {
|
||||
border-color: var(--button-bg);
|
||||
color: var(--button-bg);
|
||||
}
|
||||
|
||||
.comparison-plot-info {
|
||||
position: absolute;
|
||||
bottom: 0.5rem;
|
||||
left: 0.5rem;
|
||||
right: 0.5rem;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.comparison-plot-container:hover .comparison-plot-info {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Plot selection mode */
|
||||
.selecting-plots .grid-item {
|
||||
cursor: pointer !important;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.selecting-plots .grid-item:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 6px 20px rgba(0, 120, 212, 0.3);
|
||||
border-color: var(--button-bg);
|
||||
}
|
||||
|
||||
.selecting-plots .grid-item::before {
|
||||
content: '📊 Click to compare';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(0, 120, 212, 0.95);
|
||||
color: white;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.selecting-plots .grid-item:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Ensure grid items are clickable in selection mode */
|
||||
.selecting-plots .grid-item * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.selecting-plots .grid-item {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
SUBDIRECTORIES LIST
|
||||
======================================== */
|
||||
@@ -690,6 +916,9 @@
|
||||
<button class="floating-btn sidebar-toggle" onclick="toggleSidebar()" id="sidebarToggle" title="Recent Plots (Ctrl+R)">
|
||||
📋
|
||||
</button>
|
||||
<button class="floating-btn compare-toggle" onclick="app.toggleCompareMode()" id="compareToggle" title="Compare Plots (Ctrl+C)">
|
||||
⚖️
|
||||
</button>
|
||||
<button class="floating-btn theme-toggle" onclick="toggleTheme()" id="themeToggle" title="Toggle Theme (Ctrl+T)">
|
||||
☀️
|
||||
</button>
|
||||
@@ -709,6 +938,10 @@
|
||||
<span>Recent plots</span>
|
||||
<span class="shortcut-key">Ctrl+R</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<span>Compare plots</span>
|
||||
<span class="shortcut-key">Ctrl+C</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<span>Refresh</span>
|
||||
<span class="shortcut-key">F5</span>
|
||||
@@ -743,6 +976,44 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Plot Comparison Overlay -->
|
||||
<div class="comparison-overlay" id="comparisonOverlay">
|
||||
<div class="comparison-container">
|
||||
<div class="comparison-header">
|
||||
<h2 class="comparison-title">Plot Comparison</h2>
|
||||
<button class="comparison-close" onclick="app.closeComparison()" title="Close Comparison (Esc)">×</button>
|
||||
</div>
|
||||
<div class="comparison-content">
|
||||
<div class="comparison-panel">
|
||||
<div class="comparison-panel-header">
|
||||
<span class="comparison-panel-title" id="leftPlotTitle">Plot A</span>
|
||||
<button class="comparison-replace-btn" onclick="app.replacePlot('left')" id="leftReplaceBtn">Replace</button>
|
||||
</div>
|
||||
<div class="comparison-panel-content">
|
||||
<div class="comparison-plot-container" id="leftPlotContainer">
|
||||
<div class="comparison-placeholder" onclick="app.selectPlotForComparison('left')">
|
||||
📊 Click to select first plot
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-panel">
|
||||
<div class="comparison-panel-header">
|
||||
<span class="comparison-panel-title" id="rightPlotTitle">Plot B</span>
|
||||
<button class="comparison-replace-btn" onclick="app.replacePlot('right')" id="rightReplaceBtn">Replace</button>
|
||||
</div>
|
||||
<div class="comparison-panel-content">
|
||||
<div class="comparison-plot-container" id="rightPlotContainer">
|
||||
<div class="comparison-placeholder" onclick="app.selectPlotForComparison('right')">
|
||||
📊 Click to select second plot
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ========================================
|
||||
// GALLERY APPLICATION CLASS
|
||||
@@ -752,6 +1023,12 @@
|
||||
this.searchTimeout = null;
|
||||
this.SEARCH_DEBOUNCE_MS = {{ ui.search_debounce_ms|default(300) }};
|
||||
this.MAX_RECENT_PLOTS = {{ ui.max_recent_plots|default(20) }};
|
||||
|
||||
// Comparison state
|
||||
this.comparisonMode = false;
|
||||
this.comparisonSlot = null; // 'left' or 'right'
|
||||
this.plots = { left: null, right: null };
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
@@ -1444,6 +1721,11 @@
|
||||
this.toggleSidebar();
|
||||
}
|
||||
|
||||
if (e.ctrlKey && e.key === 'c') {
|
||||
e.preventDefault();
|
||||
this.toggleCompareMode();
|
||||
}
|
||||
|
||||
if (e.ctrlKey && e.key === 't') {
|
||||
e.preventDefault();
|
||||
this.toggleTheme();
|
||||
@@ -1460,10 +1742,32 @@
|
||||
}
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
// If in plot selection mode, cancel it
|
||||
if (this.comparisonSlot) {
|
||||
// Find and remove instruction element
|
||||
const instruction = document.getElementById('comparisonInstruction');
|
||||
if (instruction) instruction.remove();
|
||||
|
||||
// Reset grid item styles
|
||||
const gridItems = document.querySelectorAll('.grid-item');
|
||||
gridItems.forEach(item => {
|
||||
item.style.cursor = '';
|
||||
item.style.border = '';
|
||||
});
|
||||
|
||||
// Show overlay again
|
||||
document.getElementById('comparisonOverlay').style.display = 'flex';
|
||||
this.comparisonSlot = null;
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('searchResults').style.display = 'none';
|
||||
if (document.getElementById('sidebar').classList.contains('open')) {
|
||||
this.toggleSidebar();
|
||||
}
|
||||
if (document.getElementById('comparisonOverlay').classList.contains('open')) {
|
||||
this.hideComparisonOverlay();
|
||||
}
|
||||
document.getElementById('shortcutsHelp').style.display = 'none';
|
||||
}
|
||||
});
|
||||
@@ -1510,6 +1814,168 @@
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle comparison mode
|
||||
*/
|
||||
toggleCompareMode() {
|
||||
this.comparisonMode = !this.comparisonMode;
|
||||
const compareBtn = document.getElementById('compareToggle');
|
||||
|
||||
if (this.comparisonMode) {
|
||||
compareBtn.style.background = 'var(--success-color)';
|
||||
compareBtn.title = 'Exit Compare Mode (Ctrl+C)';
|
||||
this.showComparisonOverlay();
|
||||
} else {
|
||||
compareBtn.style.background = 'var(--button-bg)';
|
||||
compareBtn.title = 'Compare Plots (Ctrl+C)';
|
||||
this.hideComparisonOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show comparison overlay
|
||||
*/
|
||||
showComparisonOverlay() {
|
||||
const overlay = document.getElementById('comparisonOverlay');
|
||||
overlay.classList.add('open');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide comparison overlay
|
||||
*/
|
||||
hideComparisonOverlay() {
|
||||
const overlay = document.getElementById('comparisonOverlay');
|
||||
overlay.classList.remove('open');
|
||||
this.comparisonMode = false;
|
||||
|
||||
const compareBtn = document.getElementById('compareToggle');
|
||||
compareBtn.style.background = 'var(--button-bg)';
|
||||
compareBtn.title = 'Compare Plots (Ctrl+C)';
|
||||
}
|
||||
|
||||
/**
|
||||
* Close comparison overlay (alias for hideComparisonOverlay)
|
||||
*/
|
||||
closeComparison() {
|
||||
this.hideComparisonOverlay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Select plot for comparison - simple approach
|
||||
*/
|
||||
selectPlotForComparison(slot) {
|
||||
this.comparisonSlot = slot;
|
||||
|
||||
// Hide overlay temporarily
|
||||
const overlay = document.getElementById('comparisonOverlay');
|
||||
overlay.style.display = 'none';
|
||||
|
||||
// Show simple alert with instructions
|
||||
const instruction = document.createElement('div');
|
||||
instruction.id = 'comparisonInstruction';
|
||||
instruction.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--button-bg);
|
||||
color: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 8px;
|
||||
z-index: 3000;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
|
||||
`;
|
||||
instruction.innerHTML = `📊 Click any plot to select for ${slot === 'left' ? 'Plot A' : 'Plot B'} (ESC to cancel)`;
|
||||
document.body.appendChild(instruction);
|
||||
|
||||
// Add one-time click listener to all grid items
|
||||
const gridItems = document.querySelectorAll('.grid-item');
|
||||
const handleClick = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const gridItem = event.currentTarget;
|
||||
const img = gridItem.querySelector('img');
|
||||
const nameEl = gridItem.querySelector('.plot-name');
|
||||
|
||||
if (img && nameEl) {
|
||||
const plotInfo = {
|
||||
name: nameEl.textContent.trim(),
|
||||
imgSrc: img.src,
|
||||
pdfSrc: img.src.replace('.png', '.pdf'),
|
||||
path: window.location.pathname
|
||||
};
|
||||
|
||||
this.addPlotToComparison(plotInfo, slot);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
instruction.remove();
|
||||
gridItems.forEach(item => item.removeEventListener('click', handleClick));
|
||||
overlay.style.display = 'flex';
|
||||
};
|
||||
|
||||
gridItems.forEach(item => {
|
||||
item.style.cursor = 'pointer';
|
||||
item.style.border = '2px dashed var(--button-bg)';
|
||||
item.addEventListener('click', handleClick);
|
||||
});
|
||||
|
||||
// ESC to cancel
|
||||
const handleEscape = (event) => {
|
||||
if (event.key === 'Escape') {
|
||||
instruction.remove();
|
||||
gridItems.forEach(item => {
|
||||
item.removeEventListener('click', handleClick);
|
||||
item.style.cursor = '';
|
||||
item.style.border = '';
|
||||
});
|
||||
overlay.style.display = 'flex';
|
||||
document.removeEventListener('keydown', handleEscape);
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', handleEscape);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add plot to comparison panel
|
||||
*/
|
||||
addPlotToComparison(plotInfo, slot) {
|
||||
this.plots[slot] = plotInfo;
|
||||
|
||||
const container = document.getElementById(`${slot}PlotContainer`);
|
||||
const title = document.getElementById(`${slot}PlotTitle`);
|
||||
const replaceBtn = document.getElementById(`${slot}ReplaceBtn`);
|
||||
|
||||
container.innerHTML = `
|
||||
<img src="${plotInfo.imgSrc}" class="comparison-plot" alt="${plotInfo.name}"
|
||||
onclick="window.open('${plotInfo.pdfSrc}', '_blank')" />
|
||||
<div class="comparison-plot-info">
|
||||
<strong>${plotInfo.name}</strong><br>
|
||||
<small>${plotInfo.path}</small>
|
||||
</div>
|
||||
`;
|
||||
|
||||
title.textContent = plotInfo.name;
|
||||
replaceBtn.style.display = 'block';
|
||||
|
||||
// Reset grid item styles
|
||||
const gridItems = document.querySelectorAll('.grid-item');
|
||||
gridItems.forEach(item => {
|
||||
item.style.cursor = '';
|
||||
item.style.border = '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace plot in comparison
|
||||
*/
|
||||
replacePlot(slot) {
|
||||
this.selectPlotForComparison(slot);
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
|
||||
Reference in New Issue
Block a user