📜
core.js
Back
📝 Javascript ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
// /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 }; })();