✨ feat: resizable queue table columns (JDownloader-style)
Drag the right edge of any queue column header to resize it. Cursor changes to col-resize on hover. Widths are saved to localStorage and restored on next launch. - Resizer handles in all 8 queue table columns - Resize state visible via dragging class + body cursor override - Min width 40px, no max (table can scroll horizontally) - Click on resizer doesn't trigger column sort - Persisted across sessions via localStorage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e02926e849
commit
e7dd91ae59
@ -24,6 +24,7 @@ let queuePersistTimer = null;
|
||||
let settingsSaveTimer = null;
|
||||
let lastUploadStats = { state: 'idle', globalSpeedKbs: 0, totalBytes: 0, elapsed: 0, activeJobs: 0 };
|
||||
const AUTO_CHECK_PREF_KEY = 'autoHealthCheckBeforeUpload';
|
||||
const QUEUE_COL_WIDTHS_KEY = 'queueColumnWidthsPx';
|
||||
const STARTABLE_QUEUE_STATUSES = new Set(['preview', 'queued', 'error', 'aborted', 'skipped']);
|
||||
|
||||
function isStartableQueueStatus(status) {
|
||||
@ -71,6 +72,7 @@ async function init() {
|
||||
renderAccounts();
|
||||
setupListeners();
|
||||
setupDragDrop();
|
||||
restoreQueueColumnWidths();
|
||||
loadHistory();
|
||||
renderRecentUploadsPanel();
|
||||
updateUploadView();
|
||||
@ -3263,7 +3265,9 @@ function setupListeners() {
|
||||
|
||||
// Queue table sorting
|
||||
document.querySelectorAll('#queueTable th.sortable').forEach(th => {
|
||||
th.addEventListener('click', () => {
|
||||
th.addEventListener('click', (e) => {
|
||||
// Don't sort if click was on the resizer handle
|
||||
if (e.target.classList.contains('col-resizer')) return;
|
||||
const key = th.dataset.sort;
|
||||
if (queueSortState.key === key) queueSortState.direction = queueSortState.direction === 'asc' ? 'desc' : 'asc';
|
||||
else { queueSortState.key = key; queueSortState.direction = 'asc'; }
|
||||
@ -3272,6 +3276,9 @@ function setupListeners() {
|
||||
});
|
||||
});
|
||||
|
||||
// Queue table column resizing (JDownloader-style)
|
||||
setupColumnResizing();
|
||||
|
||||
// Shutdown cancel
|
||||
document.getElementById('cancelShutdownBtn').addEventListener('click', async () => {
|
||||
await window.api.cancelShutdown();
|
||||
@ -3516,6 +3523,67 @@ function loadAutoCheckPreference() {
|
||||
catch { return true; }
|
||||
}
|
||||
|
||||
// --- Queue table column resizing (JDownloader-style) ---
|
||||
function restoreQueueColumnWidths() {
|
||||
try {
|
||||
const raw = localStorage.getItem(QUEUE_COL_WIDTHS_KEY);
|
||||
if (!raw) return;
|
||||
const widths = JSON.parse(raw);
|
||||
if (!widths || typeof widths !== 'object') return;
|
||||
for (const [col, px] of Object.entries(widths)) {
|
||||
const th = document.querySelector(`#queueTable th[data-col="${col}"]`);
|
||||
if (th && typeof px === 'number' && px > 20) {
|
||||
th.style.width = px + 'px';
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function saveQueueColumnWidths() {
|
||||
try {
|
||||
const widths = {};
|
||||
document.querySelectorAll('#queueTable th[data-col]').forEach(th => {
|
||||
widths[th.dataset.col] = th.getBoundingClientRect().width;
|
||||
});
|
||||
localStorage.setItem(QUEUE_COL_WIDTHS_KEY, JSON.stringify(widths));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function setupColumnResizing() {
|
||||
const headers = document.querySelectorAll('#queueTable th[data-col]');
|
||||
headers.forEach(th => {
|
||||
const resizer = th.querySelector('.col-resizer');
|
||||
if (!resizer) return;
|
||||
|
||||
resizer.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const startX = e.clientX;
|
||||
const startWidth = th.getBoundingClientRect().width;
|
||||
resizer.classList.add('dragging');
|
||||
document.body.classList.add('col-resizing');
|
||||
|
||||
const onMove = (ev) => {
|
||||
const delta = ev.clientX - startX;
|
||||
const newWidth = Math.max(40, startWidth + delta);
|
||||
th.style.width = newWidth + 'px';
|
||||
};
|
||||
|
||||
const onUp = () => {
|
||||
document.removeEventListener('mousemove', onMove);
|
||||
document.removeEventListener('mouseup', onUp);
|
||||
resizer.classList.remove('dragging');
|
||||
document.body.classList.remove('col-resizing');
|
||||
saveQueueColumnWidths();
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', onMove);
|
||||
document.addEventListener('mouseup', onUp);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
|
||||
@ -77,14 +77,14 @@
|
||||
<table class="queue-table" id="queueTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-filename sortable" data-sort="filename">Filename</th>
|
||||
<th class="col-size sortable" data-sort="size">Uploaded / Size</th>
|
||||
<th class="col-host sortable" data-sort="host">Host</th>
|
||||
<th class="col-status sortable" data-sort="status">Status</th>
|
||||
<th class="col-elapsed">Zeit</th>
|
||||
<th class="col-remaining">Rest</th>
|
||||
<th class="col-speed sortable" data-sort="speed">Speed</th>
|
||||
<th class="col-progress sortable" data-sort="progress">Progress</th>
|
||||
<th class="col-filename sortable" data-col="filename" data-sort="filename">Filename<span class="col-resizer"></span></th>
|
||||
<th class="col-size sortable" data-col="size" data-sort="size">Uploaded / Size<span class="col-resizer"></span></th>
|
||||
<th class="col-host sortable" data-col="host" data-sort="host">Host<span class="col-resizer"></span></th>
|
||||
<th class="col-status sortable" data-col="status" data-sort="status">Status<span class="col-resizer"></span></th>
|
||||
<th class="col-elapsed" data-col="elapsed">Zeit<span class="col-resizer"></span></th>
|
||||
<th class="col-remaining" data-col="remaining">Rest<span class="col-resizer"></span></th>
|
||||
<th class="col-speed sortable" data-col="speed" data-sort="speed">Speed<span class="col-resizer"></span></th>
|
||||
<th class="col-progress sortable" data-col="progress" data-sort="progress">Progress</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="queueBody"></tbody>
|
||||
|
||||
@ -267,6 +267,23 @@ body {
|
||||
}
|
||||
.queue-table th.sortable { cursor: pointer; }
|
||||
.queue-table th.sortable:hover { color: var(--text); }
|
||||
.queue-table th { position: relative; }
|
||||
|
||||
.col-resizer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 6px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
user-select: none;
|
||||
z-index: 6;
|
||||
background: transparent;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.col-resizer:hover { background: rgba(102, 126, 234, 0.4); }
|
||||
.col-resizer.dragging { background: rgba(102, 126, 234, 0.6); }
|
||||
body.col-resizing, body.col-resizing * { cursor: col-resize !important; user-select: none !important; }
|
||||
|
||||
.col-filename { width: 30%; }
|
||||
.col-size { width: 12%; }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user