feat(ui): Custom Dialog-System, Onboarding und Backup-Reminder

- HellionDialog.alert/confirm ersetzt alle nativen confirm() und alert() Aufrufe
- 6-stufiger Onboarding-Flow beim ersten Start (Boards, Themes, Features, Backup)
- Backup-Reminder erinnert alle 7 Tage an JSON-Export
- innerHTML komplett durch createElement/createElementNS ersetzt (XSS-Schutz)
- Drag & Drop Inline-Styles durch CSS-Klassen ersetzt
This commit is contained in:
2026-03-21 19:08:17 +01:00
parent 36bf38a92c
commit 00baa0231b
7 changed files with 577 additions and 72 deletions
+42 -31
View File
@@ -37,13 +37,11 @@ function initBoardDragDrop() {
// Ghost
const ghost = boardEl.cloneNode(true);
ghost.style.cssText = `
position:fixed; left:${rect.left}px; top:${rect.top}px;
width:${rect.width}px; height:${rect.height}px;
opacity:0.75; pointer-events:none; z-index:9999;
transform:rotate(1.5deg) scale(1.02);
box-shadow:0 12px 40px rgba(0,0,0,0.6);
`;
ghost.className += ' drag-ghost';
ghost.style.left = rect.left + 'px';
ghost.style.top = rect.top + 'px';
ghost.style.width = rect.width + 'px';
ghost.style.height = rect.height + 'px';
document.body.appendChild(ghost);
// Placeholder
@@ -104,29 +102,42 @@ function initBoardDragDrop() {
function initBookmarkDragDrop(listEl, board) {
let dragSrcBmId = null;
listEl.querySelectorAll('.bm-item').forEach(item => {
item.addEventListener('dragstart', e => {
dragSrcBmId = item.dataset.bmId;
e.dataTransfer.effectAllowed = 'move';
setTimeout(() => item.style.opacity = '0.4', 0);
});
item.addEventListener('dragend', () => { item.style.opacity = ''; });
item.addEventListener('dragover', e => {
e.preventDefault();
item.style.background = 'rgba(255,160,50,0.07)';
});
item.addEventListener('dragleave', () => { item.style.background = ''; });
item.addEventListener('drop', async e => {
e.preventDefault(); e.stopPropagation();
item.style.background = '';
const targetBmId = item.dataset.bmId;
if (!dragSrcBmId || dragSrcBmId === targetBmId) return;
const srcIdx = board.bookmarks.findIndex(b => b.id === dragSrcBmId);
const tgtIdx = board.bookmarks.findIndex(b => b.id === targetBmId);
const [moved] = board.bookmarks.splice(srcIdx, 1);
board.bookmarks.splice(tgtIdx, 0, moved);
await saveBoards();
renderBoards();
});
listEl.addEventListener('dragstart', e => {
const item = e.target.closest('.bm-item');
if (!item) return;
dragSrcBmId = item.dataset.bmId;
e.dataTransfer.effectAllowed = 'move';
setTimeout(() => item.classList.add('dragging-source'), 0);
});
listEl.addEventListener('dragend', e => {
const item = e.target.closest('.bm-item');
if (item) item.classList.remove('dragging-source');
});
listEl.addEventListener('dragover', e => {
e.preventDefault();
const item = e.target.closest('.bm-item');
if (item) item.classList.add('drag-over');
});
listEl.addEventListener('dragleave', e => {
const item = e.target.closest('.bm-item');
if (item) item.classList.remove('drag-over');
});
listEl.addEventListener('drop', async e => {
e.preventDefault(); e.stopPropagation();
const item = e.target.closest('.bm-item');
if (!item) return;
item.classList.remove('drag-over');
const targetBmId = item.dataset.bmId;
if (!dragSrcBmId || dragSrcBmId === targetBmId) return;
const srcIdx = board.bookmarks.findIndex(b => b.id === dragSrcBmId);
const tgtIdx = board.bookmarks.findIndex(b => b.id === targetBmId);
const [moved] = board.bookmarks.splice(srcIdx, 1);
board.bookmarks.splice(tgtIdx, 0, moved);
await saveBoards();
renderBoards();
});
}