// /core/js/overlay.js
(function () {
const LS_KEY = 'AppOverlayConfig';
const defaultConfig = { showArrows: false, enableSwipe: false }; // 👈 hidden + swipe off
function loadConfig() {
try { return Object.assign({}, defaultConfig, JSON.parse(localStorage.getItem(LS_KEY) || '{}')); }
catch { return { ...defaultConfig }; }
}
function saveConfig(cfg) {
try { localStorage.setItem(LS_KEY, JSON.stringify(cfg)); } catch {}
}
let config = loadConfig();
// Menu items array
window.AppOverlayMenuItems = window.AppOverlayMenuItems || [];
// ------------------------------------------------------------
// DOM CREATION
// ------------------------------------------------------------
function createOverlayDOM() {
const overlay = document.createElement('div');
overlay.className = 'app-overlay';
overlay.style.cssText = `
position: fixed;
inset: 0;
display: none;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.8);
z-index: 2147483647;
`;
const dialog = document.createElement('section');
dialog.className = 'app-dialog';
dialog.style.cssText = `
background: #1e1e1e;
border: 1px solid #3a3a3a;
border-radius: 8px;
width: 90%;
max-width: 1200px;
max-height: 85vh;
display: flex;
flex-direction: column;
box-shadow: 0 10px 40px rgba(0,0,0,0.5);
overflow: hidden;
position: relative;
`;
const header = document.createElement('header');
header.className = 'app-dialog__header';
header.style.cssText = `
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 12px 16px;
background: #2d2d2d;
border-bottom: 1px solid #3a3a3a;
`;
// --- MENU BUTTON (restored)
const menuEl = document.createElement('div');
menuEl.className = 'app-dialog__menu';
menuEl.innerHTML = '☰';
menuEl.style.cssText = `
font-size: 20px;
cursor: pointer;
user-select: none;
color: #e0e0e0;
padding: 4px 8px;
`;
menuEl.title = 'Menu';
const titleEl = document.createElement('div');
titleEl.className = 'app-dialog__title';
titleEl.id = 'appDialogTitle';
titleEl.textContent = 'Title';
titleEl.style.cssText = 'color:#e0e0e0;font-size:16px;font-weight:500;';
const navEl = document.createElement('div');
navEl.className = 'app-dialog__nav';
navEl.style.cssText = 'display:none;gap:8px;margin-left:auto;';
const prevBtn = document.createElement('button');
prevBtn.textContent = '←';
prevBtn.className = 'app-navbtn';
Object.assign(prevBtn.style, {
background: '#3a3a3a',
color: '#e0e0e0',
border: 'none',
padding: '6px 12px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px'
});
const nextBtn = document.createElement('button');
nextBtn.textContent = '→';
nextBtn.className = 'app-navbtn';
Object.assign(nextBtn.style, {
background: '#3a3a3a',
color: '#e0e0e0',
border: 'none',
padding: '6px 12px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px'
});
navEl.appendChild(prevBtn);
navEl.appendChild(nextBtn);
const closeBtn = document.createElement('button');
closeBtn.textContent = '✕';
Object.assign(closeBtn.style, {
background: '#3a3a3a',
color: '#e0e0e0',
border: 'none',
padding: '6px 12px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px'
});
header.append(menuEl, titleEl, navEl, closeBtn);
const bodyEl = document.createElement('div');
bodyEl.className = 'app-dialog__body';
bodyEl.style.cssText = `
background:#1e1e1e;
padding:20px;
min-height:60vh;
overflow:auto;
color:#e0e0e0;
font-family:system-ui, sans-serif;
`;
dialog.append(header, bodyEl);
overlay.appendChild(dialog);
document.body.appendChild(overlay);
return { overlay, bodyEl, titleEl, menuEl, navEl, prevBtn, nextBtn, closeBtn };
}
const { overlay, bodyEl, titleEl, menuEl, navEl, prevBtn, nextBtn, closeBtn } = createOverlayDOM();
let slides = [], slideEls = [], current = 0, opener = null;
let menuOpen = false;
let currentMenuStack = [];
// ------------------------------------------------------------
// MENU SYSTEM (restored)
// ------------------------------------------------------------
function hideMenu() {
document.querySelectorAll('.app-menu-dropdown').forEach(m => m.remove());
currentMenuStack = [];
menuOpen = false;
}
function showMenu(items = window.AppOverlayMenuItems, parentDropdown = null) {
const rect = menuEl.getBoundingClientRect();
const dropdown = document.createElement('div');
dropdown.className = 'app-menu-dropdown';
dropdown.style.cssText = `
position: fixed;
background: #fff;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
min-width: 180px;
z-index: 2147483647;
overflow: hidden;
`;
if (!parentDropdown) {
dropdown.style.top = rect.bottom + 4 + 'px';
dropdown.style.left = rect.left + 'px';
currentMenuStack = [];
} else {
const r = parentDropdown.getBoundingClientRect();
dropdown.style.top = r.top + 'px';
dropdown.style.left = r.right - 2 + 'px';
items = [{ label: '◀ Back', type: 'back' }, ...items];
}
items.forEach(item => {
const btn = document.createElement('button');
btn.textContent = item.label;
btn.style.cssText = `
display:flex;
align-items:center;
justify-content:space-between;
width:100%;
padding:8px 12px;
border:none;
background:transparent;
cursor:pointer;
font-size:14px;
color:#333;
text-align:left;
`;
btn.onmouseenter = () => btn.style.background = '#f0f0f0';
btn.onmouseleave = () => btn.style.background = 'transparent';
if (item.type === 'toggle') {
const toggle = document.createElement('span');
toggle.textContent = item.state ? '✅' : '⬜';
btn.appendChild(toggle);
btn.onclick = e => {
e.stopPropagation();
item.state = !item.state;
toggle.textContent = item.state ? '✅' : '⬜';
if (item.onToggle) item.onToggle(item.state);
hideMenu();
};
} else if (item.submenu) {
const arrow = document.createElement('span');
arrow.textContent = '▶';
btn.appendChild(arrow);
btn.onclick = e => {
e.stopPropagation();
currentMenuStack.push(dropdown);
dropdown.style.display = 'none';
const sub = showMenu(item.submenu, dropdown);
document.body.appendChild(sub);
};
} else if (item.type === 'back') {
btn.onclick = e => {
e.stopPropagation();
dropdown.remove();
if (currentMenuStack.length > 0) {
const prevMenu = currentMenuStack.pop();
prevMenu.style.display = 'block';
}
};
} else {
btn.onclick = e => {
e.stopPropagation();
if (item.action) item.action();
hideMenu();
};
}
dropdown.appendChild(btn);
});
if (!parentDropdown) {
hideMenu();
document.body.appendChild(dropdown);
setTimeout(() => {
const closeOnOutsideClick = e => {
const inside = e.target.closest('.app-menu-dropdown') || e.target === menuEl;
if (!inside) {
hideMenu();
document.removeEventListener('click', closeOnOutsideClick);
}
};
document.addEventListener('click', closeOnOutsideClick);
}, 10);
}
dropdown.style.display = 'block';
menuOpen = true;
return dropdown;
}
menuEl.addEventListener('click', e => {
e.stopPropagation();
if (menuOpen) hideMenu();
else showMenu();
});
// ------------------------------------------------------------
// SLIDES + NAV
// ------------------------------------------------------------
function renderSlides() {
bodyEl.innerHTML = '';
slideEls = slides.map(s => {
const el = document.createElement('article');
el.className = 'app-slide';
el.innerHTML = s.html || '';
bodyEl.appendChild(el);
if (typeof s.onRender === 'function') requestAnimationFrame(() => s.onRender(el));
return el;
});
}
function update() {
titleEl.textContent = slides[current]?.title || '';
slideEls.forEach((el, i) => el.classList.toggle('is-active', i === current));
navEl.style.display = (config.showArrows && slides.length > 1) ? 'flex' : 'none';
}
function mountOnTop() { document.body.appendChild(overlay); }
// ------------------------------------------------------------
// CONTROLS
// ------------------------------------------------------------
function open(items, startIndex = 0, openerEl = null) {
if (!Array.isArray(items) || items.length === 0) return;
slides = items.slice();
opener = openerEl || document.activeElement;
current = Math.max(0, Math.min(startIndex, slides.length - 1));
renderSlides();
mountOnTop();
overlay.style.display = 'flex';
update();
closeBtn.focus();
}
function close() {
overlay.style.display = 'none';
hideMenu();
if (opener && typeof opener.focus === 'function') opener.focus();
}
function next() { if (slides.length > 1) { current = (current + 1) % slides.length; update(); } }
function prev() { if (slides.length > 1) { current = (current - 1 + slides.length) % slides.length; update(); } }
// ------------------------------------------------------------
// EVENTS
// ------------------------------------------------------------
closeBtn.addEventListener('click', close);
nextBtn.addEventListener('click', next);
prevBtn.addEventListener('click', prev);
overlay.addEventListener('click', e => {
if (e.target === overlay) close();
if (menuOpen && !menuEl.contains(e.target)) hideMenu();
});
window.addEventListener('keydown', e => {
if (overlay.style.display !== 'flex') return;
if (e.key === 'Escape') { e.preventDefault(); hideMenu(); close(); }
if (e.key === 'ArrowRight') { e.preventDefault(); next(); }
if (e.key === 'ArrowLeft') { e.preventDefault(); prev(); }
});
// ------------------------------------------------------------
// API
// ------------------------------------------------------------
function configure(partial) {
if (!partial || typeof partial !== 'object') return;
config = Object.assign({}, config, partial);
saveConfig(config);
update();
}
function getConfig() { return Object.assign({}, config); }
window.AppOverlay = { open, close, next, prev, configure, getConfig };
})();