fix: rewrite backup viewer close button — match editor-btn pattern
- Close button: <button class="backup-close-btn"><i data-lucide="x"></i></button> (same HTML pattern as AdminPanel editor-btn) - Handler: addEventListener via DOM query in setBody() after innerHTML (same JS pattern as AdminPanel) - Removed all inline onclick, global capture handler, window._backupViewerClose - CSS: .backup-close-btn now matches .editor-btn style (bordered icon button) - Escape key + overlay click still work for closing
This commit is contained in:
+29
-65
@@ -23,8 +23,8 @@ const state = {
|
|||||||
path: null,
|
path: null,
|
||||||
backups: [],
|
backups: [],
|
||||||
overlay: null,
|
overlay: null,
|
||||||
leftVersion: null, // timestamp of backup shown on left
|
leftVersion: null,
|
||||||
rightVersion: null, // timestamp of backup shown on right, or null for current
|
rightVersion: null,
|
||||||
diffText: '',
|
diffText: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,11 +32,6 @@ const state = {
|
|||||||
// Public API
|
// Public API
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* Open the backup/diff viewer for a file.
|
|
||||||
* @param {string} vault - Vault name
|
|
||||||
* @param {string} path - Relative file path
|
|
||||||
*/
|
|
||||||
export async function openBackupViewer(vault, path) {
|
export async function openBackupViewer(vault, path) {
|
||||||
state.vault = vault;
|
state.vault = vault;
|
||||||
state.path = path;
|
state.path = path;
|
||||||
@@ -45,16 +40,13 @@ export async function openBackupViewer(vault, path) {
|
|||||||
state.rightVersion = null;
|
state.rightVersion = null;
|
||||||
state.diffText = '';
|
state.diffText = '';
|
||||||
|
|
||||||
// Create and show overlay
|
|
||||||
const overlay = createOverlay();
|
const overlay = createOverlay();
|
||||||
document.body.appendChild(overlay);
|
document.body.appendChild(overlay);
|
||||||
state.overlay = overlay;
|
state.overlay = overlay;
|
||||||
requestAnimationFrame(() => overlay.classList.add('active'));
|
requestAnimationFrame(() => overlay.classList.add('active'));
|
||||||
|
|
||||||
// Show loading state
|
|
||||||
setBody(renderLoading());
|
setBody(renderLoading());
|
||||||
|
|
||||||
// Fetch backups
|
|
||||||
try {
|
try {
|
||||||
const data = await api(`/api/file/${encodeURIComponent(vault)}/backups?path=${encodeURIComponent(path)}`);
|
const data = await api(`/api/file/${encodeURIComponent(vault)}/backups?path=${encodeURIComponent(path)}`);
|
||||||
state.backups = data.backups || [];
|
state.backups = data.backups || [];
|
||||||
@@ -69,14 +61,9 @@ export async function openBackupViewer(vault, path) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-select: left = most recent, right = current
|
|
||||||
state.leftVersion = state.backups[0].timestamp;
|
state.leftVersion = state.backups[0].timestamp;
|
||||||
state.rightVersion = null;
|
state.rightVersion = null;
|
||||||
|
|
||||||
// Render version selectors
|
|
||||||
renderMainView();
|
renderMainView();
|
||||||
|
|
||||||
// Load initial diff
|
|
||||||
await loadDiff();
|
await loadDiff();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,16 +71,6 @@ export async function openBackupViewer(vault, path) {
|
|||||||
// DOM helpers
|
// DOM helpers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Global click handler: catches close button clicks anywhere in the document
|
|
||||||
document.addEventListener('click', function(e) {
|
|
||||||
const btn = e.target.closest ? e.target.closest('.backup-close-btn') : null;
|
|
||||||
if (btn && state.overlay) {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}, true); // capture phase — fires before other handlers
|
|
||||||
|
|
||||||
function createOverlay() {
|
function createOverlay() {
|
||||||
const overlay = document.createElement('div');
|
const overlay = document.createElement('div');
|
||||||
overlay.className = 'obsigate-modal-overlay backup-viewer-overlay';
|
overlay.className = 'obsigate-modal-overlay backup-viewer-overlay';
|
||||||
@@ -108,25 +85,33 @@ function createOverlay() {
|
|||||||
|
|
||||||
// Keyboard: Escape closes the viewer
|
// Keyboard: Escape closes the viewer
|
||||||
_escHandler = (e) => {
|
_escHandler = (e) => {
|
||||||
if (e.key === 'Escape' && state.overlay) {
|
if (e.key === 'Escape' && state.overlay) close();
|
||||||
close();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
document.addEventListener('keydown', _escHandler);
|
document.addEventListener('keydown', _escHandler);
|
||||||
|
|
||||||
return overlay;
|
return overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Replace panel content and re-bind the close button. */
|
||||||
function setBody(html) {
|
function setBody(html) {
|
||||||
const panel = document.getElementById('backup-viewer-panel');
|
const panel = document.getElementById('backup-viewer-panel');
|
||||||
if (panel) panel.innerHTML = html;
|
if (!panel) return;
|
||||||
|
panel.innerHTML = html;
|
||||||
|
safeCreateIcons();
|
||||||
|
// Bind close button — same pattern as AdminPanel
|
||||||
|
const closeBtn = panel.querySelector('.backup-close-btn');
|
||||||
|
if (closeBtn) {
|
||||||
|
closeBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _escHandler = null;
|
let _escHandler = null;
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
if (!state.overlay) return;
|
if (!state.overlay) return;
|
||||||
// Remove keyboard listener
|
|
||||||
if (_escHandler) {
|
if (_escHandler) {
|
||||||
document.removeEventListener('keydown', _escHandler);
|
document.removeEventListener('keydown', _escHandler);
|
||||||
_escHandler = null;
|
_escHandler = null;
|
||||||
@@ -138,18 +123,18 @@ function close() {
|
|||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose globally for inline onclick handlers
|
|
||||||
window._backupViewerClose = close;
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Rendering
|
// Rendering
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Close button HTML — same pattern as AdminPanel editor-btn */
|
||||||
|
const CLOSE_BTN = '<button class="backup-close-btn" title="Fermer"><i data-lucide="x" style="width:16px;height:16px"></i></button>';
|
||||||
|
|
||||||
function renderLoading() {
|
function renderLoading() {
|
||||||
return `
|
return `
|
||||||
<div class="backup-header">
|
<div class="backup-header">
|
||||||
<h3>Backups</h3>
|
<h3>Backups</h3>
|
||||||
<button class="backup-close-btn" title="Fermer" onclick="window._backupViewerClose()">×</button>
|
${CLOSE_BTN}
|
||||||
</div>
|
</div>
|
||||||
<div class="backup-body">
|
<div class="backup-body">
|
||||||
<div class="tree-loading" style="padding:2rem;text-align:center">
|
<div class="tree-loading" style="padding:2rem;text-align:center">
|
||||||
@@ -160,10 +145,10 @@ function renderLoading() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderError(msg) {
|
function renderError(msg) {
|
||||||
return \`
|
return `
|
||||||
<div class="backup-header">
|
<div class="backup-header">
|
||||||
<h3>Backups</h3>
|
<h3>Backups</h3>
|
||||||
<button class="backup-close-btn" title="Fermer" onclick="window._backupViewerClose()">×</button>
|
${CLOSE_BTN}
|
||||||
</div>
|
</div>
|
||||||
<div class="backup-body" style="text-align:center;padding:2rem;color:var(--text-muted)">
|
<div class="backup-body" style="text-align:center;padding:2rem;color:var(--text-muted)">
|
||||||
<p>${escapeHtml(msg)}</p>
|
<p>${escapeHtml(msg)}</p>
|
||||||
@@ -171,10 +156,10 @@ function renderError(msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderEmpty() {
|
function renderEmpty() {
|
||||||
return \`
|
return `
|
||||||
<div class="backup-header">
|
<div class="backup-header">
|
||||||
<h3>Backups — \${escapeHtml(state.path)}</h3>
|
<h3>Backups — ${escapeHtml(state.path)}</h3>
|
||||||
<button class="backup-close-btn" title="Fermer" onclick="window._backupViewerClose()">×</button>
|
${CLOSE_BTN}
|
||||||
</div>
|
</div>
|
||||||
<div class="backup-body" style="text-align:center;padding:2rem;color:var(--text-muted)">
|
<div class="backup-body" style="text-align:center;padding:2rem;color:var(--text-muted)">
|
||||||
<p>Aucun backup disponible pour ce fichier.</p>
|
<p>Aucun backup disponible pour ce fichier.</p>
|
||||||
@@ -205,7 +190,7 @@ function renderMainView() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="backup-header-right">
|
<div class="backup-header-right">
|
||||||
<span class="backup-count">${b.length} backup${b.length > 1 ? 's' : ''}</span>
|
<span class="backup-count">${b.length} backup${b.length > 1 ? 's' : ''}</span>
|
||||||
<button class="backup-close-btn" title="Fermer" onclick="window._backupViewerClose()">×</button>
|
${CLOSE_BTN}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -234,10 +219,7 @@ function renderMainView() {
|
|||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
setBody(html);
|
setBody(html);
|
||||||
|
|
||||||
// Bind events
|
|
||||||
bindEvents();
|
bindEvents();
|
||||||
safeCreateIcons();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -245,17 +227,12 @@ function renderMainView() {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function bindEvents() {
|
function bindEvents() {
|
||||||
const panel = document.getElementById('backup-viewer-panel');
|
|
||||||
if (!panel) return;
|
|
||||||
|
|
||||||
// Left select
|
|
||||||
const leftSelect = document.getElementById('backup-left-select');
|
const leftSelect = document.getElementById('backup-left-select');
|
||||||
leftSelect?.addEventListener('change', async () => {
|
leftSelect?.addEventListener('change', async () => {
|
||||||
state.leftVersion = parseInt(leftSelect.value);
|
state.leftVersion = parseInt(leftSelect.value);
|
||||||
await loadDiff();
|
await loadDiff();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Right select
|
|
||||||
const rightSelect = document.getElementById('backup-right-select');
|
const rightSelect = document.getElementById('backup-right-select');
|
||||||
rightSelect?.addEventListener('change', async () => {
|
rightSelect?.addEventListener('change', async () => {
|
||||||
const val = rightSelect.value;
|
const val = rightSelect.value;
|
||||||
@@ -263,7 +240,6 @@ function bindEvents() {
|
|||||||
await loadDiff();
|
await loadDiff();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Restore button
|
|
||||||
const restoreBtn = document.getElementById('backup-restore-btn');
|
const restoreBtn = document.getElementById('backup-restore-btn');
|
||||||
restoreBtn?.addEventListener('click', async () => {
|
restoreBtn?.addEventListener('click', async () => {
|
||||||
await restoreBackup();
|
await restoreBackup();
|
||||||
@@ -324,10 +300,8 @@ async function restoreBackup() {
|
|||||||
|
|
||||||
if (resp && resp.success) {
|
if (resp && resp.success) {
|
||||||
showToast('Fichier restauré avec succès', 'success');
|
showToast('Fichier restauré avec succès', 'success');
|
||||||
// Refresh sidebar and close
|
|
||||||
await refreshSidebarTreePreservingState();
|
await refreshSidebarTreePreservingState();
|
||||||
close();
|
close();
|
||||||
// Reload current file if open
|
|
||||||
if (typeof TabManager !== 'undefined' && TabManager.reloadCurrent) {
|
if (typeof TabManager !== 'undefined' && TabManager.reloadCurrent) {
|
||||||
TabManager.reloadCurrent();
|
TabManager.reloadCurrent();
|
||||||
}
|
}
|
||||||
@@ -360,20 +334,10 @@ function renderDiff(diffText) {
|
|||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
let cssClass = 'diff-context';
|
let cssClass = 'diff-context';
|
||||||
let prefix = ' ';
|
let prefix = ' ';
|
||||||
|
if (line.startsWith('@@')) { cssClass = 'diff-hunk'; prefix = ''; }
|
||||||
if (line.startsWith('@@')) {
|
else if (line.startsWith('+')) { cssClass = 'diff-add'; prefix = '+'; }
|
||||||
cssClass = 'diff-hunk';
|
else if (line.startsWith('-')) { cssClass = 'diff-del'; prefix = '-'; }
|
||||||
prefix = '';
|
else if (line.startsWith('---') || line.startsWith('+++')) { cssClass = 'diff-header-line'; prefix = ''; }
|
||||||
} else if (line.startsWith('+')) {
|
|
||||||
cssClass = 'diff-add';
|
|
||||||
prefix = '+';
|
|
||||||
} else if (line.startsWith('-')) {
|
|
||||||
cssClass = 'diff-del';
|
|
||||||
prefix = '-';
|
|
||||||
} else if (line.startsWith('---') || line.startsWith('+++')) {
|
|
||||||
cssClass = 'diff-header-line';
|
|
||||||
prefix = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
html += `<tr class="${cssClass}"><td class="diff-prefix">${prefix}</td><td class="diff-content"><pre>${escapeHtml(line)}</pre></td></tr>`;
|
html += `<tr class="${cssClass}"><td class="diff-prefix">${prefix}</td><td class="diff-content"><pre>${escapeHtml(line)}</pre></td></tr>`;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -7250,24 +7250,24 @@ body.popup-mode .content-area {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.backup-close-btn {
|
.backup-close-btn {
|
||||||
width: 32px;
|
width: 36px;
|
||||||
height: 32px;
|
height: 36px;
|
||||||
border: none;
|
padding: 0;
|
||||||
|
border: 1px solid var(--border);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--text-muted);
|
color: var(--text-secondary);
|
||||||
font-size: 1.4rem;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
transition: all 150ms ease;
|
||||||
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
line-height: 1;
|
flex: 0 0 auto;
|
||||||
transition: color 0.15s, background 0.15s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.backup-close-btn:hover {
|
.backup-close-btn:hover {
|
||||||
color: var(--text-primary);
|
color: var(--accent);
|
||||||
background: var(--bg-tertiary);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.backup-toolbar {
|
.backup-toolbar {
|
||||||
|
|||||||
Reference in New Issue
Block a user