Files
radixOS/public/javascript/customModal.js
2026-04-30 13:36:45 +02:00

262 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>`,
lock: true,
primary: 'yes',
replace: false,
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