FileBrowser LineDelete
This commit is contained in:
@@ -68,13 +68,20 @@
|
||||
<th class="idx">#</th>
|
||||
<th class="line">G-Code (gespeichert in Grad)</th>
|
||||
<th class="ts">Zeitstempel</th>
|
||||
<th class="del-col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="lines-body">
|
||||
<tr><td colspan="3" class="empty-hint" style="padding:10px">Kein aktives Programm</td></tr>
|
||||
<tr><td colspan="4" class="empty-hint" style="padding:10px">Kein aktives Programm</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="edit-bar" class="edit-bar" style="display:none">
|
||||
<button id="btn-cancel-delete">✗ Abbrechen</button>
|
||||
<button id="btn-save-delete" class="danger-confirm">✓ Speichern</button>
|
||||
<span id="edit-bar-info" class="edit-bar-info"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- .sections -->
|
||||
@@ -85,9 +92,11 @@
|
||||
const REFRESH_MS = 5000; // Auto-Refresh-Intervall
|
||||
|
||||
// ─── Zustand ─────────────────────────────────────────────────────────────────
|
||||
let currentActiveId = null;
|
||||
let selectedId = null; // in der Liste ausgewähltes Item (unabhängig vom aktiven Programm)
|
||||
let selectedType = null; // 'file' | 'folder'
|
||||
let currentActiveId = null;
|
||||
let selectedId = null; // in der Liste ausgewähltes Item (unabhängig vom aktiven Programm)
|
||||
let selectedType = null; // 'file' | 'folder'
|
||||
let pendingDeletes = new Set(); // Indices markierter Zeilen (noch nicht gespeichert)
|
||||
let cachedState = null; // letzter bekannter Serverzustand (für Abbrechen)
|
||||
|
||||
// ─── DOM-Referenzen ───────────────────────────────────────────────────────────
|
||||
const elProgCount = document.getElementById('prog-count');
|
||||
@@ -190,8 +199,35 @@
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Edit-Bar (Abbrechen / Speichern) ───────────────────────────────────────
|
||||
const elEditBar = document.getElementById('edit-bar');
|
||||
const elEditBarInfo = document.getElementById('edit-bar-info');
|
||||
|
||||
function updateEditBar() {
|
||||
const n = pendingDeletes.size;
|
||||
if (n === 0) {
|
||||
elEditBar.style.display = 'none';
|
||||
} else {
|
||||
elEditBar.style.display = 'flex';
|
||||
elEditBarInfo.textContent = `${n} Zeile${n === 1 ? '' : 'n'} zum Löschen markiert`;
|
||||
}
|
||||
}
|
||||
|
||||
function markDelete(index) {
|
||||
if (pendingDeletes.has(index)) {
|
||||
pendingDeletes.delete(index);
|
||||
} else {
|
||||
pendingDeletes.add(index);
|
||||
}
|
||||
// Nur die betroffene Zeile neu stylen statt ganzer Tabelle
|
||||
const row = elLinesBody.querySelector(`tr[data-index="${index}"]`);
|
||||
if (row) row.classList.toggle('pending-delete', pendingDeletes.has(index));
|
||||
updateEditBar();
|
||||
}
|
||||
|
||||
// ─── Zeilen-Tabelle rendern ──────────────────────────────────────────────────
|
||||
function renderLines(state) {
|
||||
cachedState = state;
|
||||
const { programId, cursor, lineCount, lines = [], version } = state;
|
||||
currentActiveId = programId;
|
||||
|
||||
@@ -228,29 +264,45 @@
|
||||
|
||||
// Tabelle
|
||||
if (!lines.length) {
|
||||
elLinesBody.innerHTML = `<tr><td colspan="3" class="empty-hint" style="padding:10px">${
|
||||
elLinesBody.innerHTML = `<tr><td colspan="4" class="empty-hint" style="padding:10px">${
|
||||
hasProgram ? 'Programm ist leer' : 'Kein aktives Programm — links ein Programm anklicken'
|
||||
}</td></tr>`;
|
||||
updateEditBar();
|
||||
return;
|
||||
}
|
||||
|
||||
elLinesBody.innerHTML = lines.map((line, i) => {
|
||||
const { code, ts } = parseLine(line);
|
||||
const isCursor = i === cursor;
|
||||
return `<tr class="${isCursor ? 'cursor-row' : ''}">
|
||||
const isCursor = i === cursor;
|
||||
const isPending = pendingDeletes.has(i);
|
||||
return `<tr class="${isCursor ? 'cursor-row' : ''}${isPending ? ' pending-delete' : ''}" data-index="${i}">
|
||||
<td class="idx">${i}</td>
|
||||
<td class="line">${isCursor ? '▶ ' : ''}${esc(code)}</td>
|
||||
<td class="ts">${esc(ts)}</td>
|
||||
<td class="del-col"><button class="btn-row-del" data-index="${i}" title="Zeile löschen">🗑</button></td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
|
||||
// Zur Cursor-Zeile scrollen
|
||||
const cursorRow = elLinesBody.querySelector('.cursor-row');
|
||||
if (cursorRow) cursorRow.scrollIntoView({ block: 'nearest' });
|
||||
// Delete-Buttons verdrahten
|
||||
elLinesBody.querySelectorAll('.btn-row-del').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
markDelete(Number(btn.dataset.index));
|
||||
});
|
||||
});
|
||||
|
||||
// Zur Cursor-Zeile scrollen (nur wenn kein edit-Modus aktiv)
|
||||
if (pendingDeletes.size === 0) {
|
||||
const cursorRow = elLinesBody.querySelector('.cursor-row');
|
||||
if (cursorRow) cursorRow.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
|
||||
updateEditBar();
|
||||
}
|
||||
|
||||
// ─── Daten laden ─────────────────────────────────────────────────────────────
|
||||
async function refresh() {
|
||||
if (pendingDeletes.size > 0) return; // edit-Modus schützen
|
||||
try {
|
||||
const [programs, active] = await Promise.all([
|
||||
apiFetch('GET', '/api/programs'),
|
||||
@@ -303,6 +355,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Zeilen-Löschung: Abbrechen / Speichern ─────────────────────────────────
|
||||
function cancelDeletes() {
|
||||
pendingDeletes.clear();
|
||||
if (cachedState) renderLines(cachedState);
|
||||
else updateEditBar();
|
||||
}
|
||||
|
||||
async function saveDeletes() {
|
||||
const indices = [...pendingDeletes].sort((a, b) => b - a); // höchste zuerst → kein Index-Shift
|
||||
setStatus(elActStatus, `Lösche ${indices.length} Zeile(n)…`);
|
||||
document.getElementById('btn-save-delete').disabled = true;
|
||||
try {
|
||||
for (const i of indices) {
|
||||
await apiFetch('DELETE', `/api/active/lines/${i}`);
|
||||
}
|
||||
pendingDeletes.clear();
|
||||
setStatus(elActStatus, `${indices.length} Zeile(n) gelöscht`);
|
||||
await refresh();
|
||||
} catch (err) {
|
||||
setStatus(elActStatus, `Fehler: ${err.message}`, true);
|
||||
document.getElementById('btn-save-delete').disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Programm löschen (aus Toolbar) ─────────────────────────────────────────
|
||||
async function deleteSelected() {
|
||||
if (!selectedId) return;
|
||||
@@ -332,6 +408,8 @@
|
||||
// ─── Event-Listener ──────────────────────────────────────────────────────────
|
||||
document.getElementById('btn-refresh').addEventListener('click', refresh);
|
||||
document.getElementById('btn-new-folder').addEventListener('click', createFolder);
|
||||
document.getElementById('btn-cancel-delete').addEventListener('click', cancelDeletes);
|
||||
document.getElementById('btn-save-delete').addEventListener('click', saveDeletes);
|
||||
elBtnDeleteSelected.addEventListener('click', deleteSelected);
|
||||
elBtnFirst.addEventListener('click', () => step('first'));
|
||||
elBtnPrev .addEventListener('click', () => step('prev'));
|
||||
|
||||
Reference in New Issue
Block a user