259 lines
7.4 KiB
JavaScript
259 lines
7.4 KiB
JavaScript
let activeFeedbox = null;
|
|
|
|
|
|
//#region Messagebox
|
|
function getMessageColorByLevelId(levelId) {
|
|
return levelId == -1 ? 'test' :
|
|
levelId == 0 ? 'success' :
|
|
levelId == 1 ? 'info' :
|
|
levelId == 2 ? 'warn' :
|
|
levelId == 4 ? 'error' : 'throw_exception';
|
|
}
|
|
|
|
/**
|
|
* Shows messages in a containerbox on the right
|
|
* @param {string} title - Title
|
|
* @param {string} text - Message content
|
|
* @param {string} levelId - 1=info | 2=warn | 4=error | 8=throw_exception | 16=success
|
|
* @param {string} [targetId] - Which element is getting focused
|
|
* @param {number} duration - Time in ms until auto-close
|
|
*/
|
|
// function showMessage(title, text, levelId = 1, targetId = null, duration = 4000) {
|
|
function showMessage(title, text, levelId = 1, onclick = null, duration = 4000) {
|
|
// Falls kein Container existiert → automatisch anlegen
|
|
let container = document.getElementById('message-container');
|
|
if (!container) {
|
|
container = document.createElement('div');
|
|
container.id = 'message-container';
|
|
container.style.position = 'fixed';
|
|
container.style.top = '20px';
|
|
container.style.right = '20px';
|
|
container.style.display = 'flex';
|
|
container.style.flexDirection = 'column';
|
|
container.style.alignItems = 'flex-end';
|
|
container.style.zIndex = '9999';
|
|
document.body.appendChild(container);
|
|
}
|
|
const message = document.createElement('div');
|
|
message.classList.add('message', getMessageColorByLevelId(levelId));
|
|
|
|
// --- HEADER ---
|
|
const header = document.createElement('div');
|
|
header.className = 'message-header';
|
|
|
|
const titleContainer = document.createElement('div');
|
|
titleContainer.className = 'message-title';
|
|
titleContainer.innerHTML = title;
|
|
|
|
const pinDiv = document.createElement('div');
|
|
pinDiv.className = 'pin-div';
|
|
pinDiv.innerHTML = '📌';
|
|
pinDiv.title = 'Anpinnen';
|
|
|
|
const countdown = document.createElement('span');
|
|
countdown.className = 'countdown';
|
|
countdown.textContent = `${(duration / 1000).toFixed(1)}s`;
|
|
|
|
// Titel + Countdown + Pin in eine Zeile
|
|
header.appendChild(titleContainer);
|
|
header.appendChild(countdown);
|
|
header.appendChild(pinDiv);
|
|
|
|
// --- BODY ---
|
|
const body = document.createElement('div');
|
|
body.className = 'message-text';
|
|
body.innerHTML = text;
|
|
|
|
message.appendChild(header);
|
|
message.appendChild(body);
|
|
container.appendChild(message);
|
|
|
|
// --- LOGIK ---
|
|
let pinned = false;
|
|
let remainingTime = duration;
|
|
let interval = null;
|
|
let lastTick = Date.now();
|
|
|
|
function startCountdown() {
|
|
lastTick = Date.now();
|
|
clearInterval(interval);
|
|
interval = setInterval(() => {
|
|
const now = Date.now();
|
|
const delta = now - lastTick;
|
|
lastTick = now;
|
|
remainingTime -= delta;
|
|
|
|
if (remainingTime <= 0 && !pinned) {
|
|
clearInterval(interval);
|
|
slideOutMessage(message);
|
|
} else if (!pinned) {
|
|
countdown.textContent = `${(remainingTime / 1000).toFixed(1)}s`;
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
function stopCountdown() {
|
|
clearInterval(interval);
|
|
}
|
|
|
|
// Start Countdown
|
|
startCountdown();
|
|
|
|
// Hover pausiert Countdown
|
|
message.addEventListener('mouseenter', stopCountdown);
|
|
message.addEventListener('mouseleave', () => {
|
|
if (!pinned) startCountdown();
|
|
});
|
|
|
|
// Klick auf Nachricht → schließen (nur wenn nicht gepinnt)
|
|
message.addEventListener('click', (e) => {
|
|
if(onclick) {
|
|
onclick();
|
|
}
|
|
// if (e.target === pinDiv) return;
|
|
// if (targetId) {
|
|
// const target = document.getElementById(targetId);
|
|
// if (target) target.scrollIntoView({ behavior: 'smooth' });
|
|
// }
|
|
if (!pinned) slideOutMessage(message);
|
|
});
|
|
|
|
// Klick auf Pin → anheften / lösen
|
|
pinDiv.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
pinned = !pinned;
|
|
if (pinned) {
|
|
pinDiv.classList.add('pinned');
|
|
pinDiv.title = 'Loslösen';
|
|
stopCountdown();
|
|
countdown.textContent = '📍 Angeheftet';
|
|
} else {
|
|
pinDiv.classList.remove('pinned');
|
|
pinDiv.title = 'Anpinnen';
|
|
startCountdown();
|
|
}
|
|
});
|
|
}
|
|
|
|
function slideOutMessage(message) {
|
|
if (!message) return;
|
|
message.style.animation = 'slideOut 0.5s forwards';
|
|
setTimeout(() => {
|
|
if (message.parentNode) message.parentNode.removeChild(message);
|
|
}, 300);
|
|
}
|
|
|
|
|
|
//#region Feedbox
|
|
|
|
/**
|
|
feedbox({
|
|
title: `<span style="color:#f44336">⚠ Upload abbrechen?</span>`,
|
|
message: `
|
|
<p>Es laufen noch <b>aktive Uploads</b>.</p>
|
|
<p>Möchtest du wirklich <u>alle abbrechen</u>?</p>
|
|
`,
|
|
buttons: {
|
|
yes: {
|
|
text: '<b>Ja</b>, abbrechen',
|
|
onClick: () => stopUploadQueue()
|
|
},
|
|
no: {
|
|
text: 'Weiter hochladen'
|
|
},
|
|
cancel: {
|
|
text: 'Zurück'
|
|
}
|
|
}
|
|
});
|
|
*/
|
|
function feedbox({
|
|
title = '',
|
|
message = '',
|
|
buttons = {}, // buttons: { yes: { text: 'Yes', onClick: () => { } } }
|
|
primary = null, // name of the primary button, to accept with enter-key
|
|
lock = false, // locks desktop
|
|
replace = false // replaces an actually shown feedbox
|
|
}) {
|
|
// 🚫 Nested verhindern
|
|
if (activeFeedbox) {
|
|
if (!replace) {
|
|
return Promise.resolve('blocked');
|
|
}
|
|
activeFeedbox.close('replaced');
|
|
}
|
|
|
|
return new Promise(resolve => {
|
|
|
|
const overlay = document.createElement('div');
|
|
overlay.className = 'feedbox-overlay';
|
|
|
|
const box = document.createElement('div');
|
|
box.className = 'feedbox';
|
|
|
|
const h = document.createElement('h3');
|
|
h.innerHTML = title;
|
|
|
|
const msg = document.createElement('div');
|
|
msg.className = 'feedbox-message';
|
|
msg.innerHTML = message;
|
|
|
|
const actions = document.createElement('div');
|
|
actions.className = 'feedbox-actions';
|
|
|
|
const btnMap = {};
|
|
|
|
Object.entries(buttons).forEach(([key, cfg]) => {
|
|
if (!cfg) return;
|
|
|
|
const btn = document.createElement('button');
|
|
btn.className = `feedbox-btn ${key}`;
|
|
btn.innerHTML = cfg.text ?? key;
|
|
|
|
btn.onclick = () => {
|
|
cfg.onClick?.();
|
|
close(key);
|
|
};
|
|
|
|
btnMap[key] = btn;
|
|
actions.appendChild(btn);
|
|
});
|
|
|
|
function close(result) {
|
|
document.removeEventListener('keydown', keyHandler);
|
|
overlay.remove();
|
|
activeFeedbox = null;
|
|
resolve(result);
|
|
}
|
|
|
|
function keyHandler(e) {
|
|
if (e.key === 'Escape') close('cancel');
|
|
if (e.key === 'Enter' && primary && btnMap[primary]) {
|
|
btnMap[primary].click();
|
|
}
|
|
}
|
|
|
|
if (!lock) {
|
|
overlay.onclick = e => {
|
|
if (e.target === overlay) close('cancel');
|
|
};
|
|
}
|
|
|
|
document.addEventListener('keydown', keyHandler);
|
|
|
|
box.append(h, msg, actions);
|
|
overlay.appendChild(box);
|
|
document.body.appendChild(overlay);
|
|
|
|
// Fokus
|
|
if (primary && btnMap[primary]) {
|
|
setTimeout(() => btnMap[primary].focus(), 0);
|
|
}
|
|
|
|
// 🔐 Singleton setzen
|
|
activeFeedbox = { close };
|
|
});
|
|
}
|
|
|
|
//#endregion
|
|
|