(x, y, menuItems)
| 2827 | } |
| 2828 | |
| 2829 | export function showFolderManagerContextMenu(x, y, menuItems) { |
| 2830 | const menu = getFolderMenu(); |
| 2831 | menu.innerHTML = ''; |
| 2832 | |
| 2833 | // Build items (same DOM as file menu: <button.mi><i.material-icons/><span/>) |
| 2834 | menuItems.forEach((item, idx) => { |
| 2835 | // optional separator after first item (like file menu top block) |
| 2836 | if (idx === 1) { |
| 2837 | const sep = document.createElement('div'); |
| 2838 | sep.className = 'sep'; |
| 2839 | menu.appendChild(sep); |
| 2840 | } |
| 2841 | |
| 2842 | const btn = document.createElement('button'); |
| 2843 | btn.type = 'button'; |
| 2844 | btn.className = 'mi'; |
| 2845 | btn.setAttribute('role', 'menuitem'); |
| 2846 | |
| 2847 | const ic = document.createElement('i'); |
| 2848 | ic.className = 'material-icons'; |
| 2849 | ic.textContent = item.icon || iconForFolderLabel(item.label); |
| 2850 | |
| 2851 | const tx = document.createElement('span'); |
| 2852 | tx.textContent = item.label; |
| 2853 | |
| 2854 | btn.append(ic, tx); |
| 2855 | btn.addEventListener('click', (e) => { |
| 2856 | e.stopPropagation(); |
| 2857 | hideFolderManagerContextMenu(); // close first so it never overlays your modal |
| 2858 | try { item.action && item.action(); } catch (err) { console.error(err); } |
| 2859 | }); |
| 2860 | |
| 2861 | menu.appendChild(btn); |
| 2862 | }); |
| 2863 | |
| 2864 | // Show + clamp to viewport |
| 2865 | menu.hidden = false; |
| 2866 | menu.style.left = `${x}px`; |
| 2867 | menu.style.top = `${y}px`; |
| 2868 | |
| 2869 | const r = menu.getBoundingClientRect(); |
| 2870 | let nx = r.left, ny = r.top; |
| 2871 | |
| 2872 | if (r.right > window.innerWidth) nx -= (r.right - window.innerWidth + 6); |
| 2873 | if (r.bottom > window.innerHeight) ny -= (r.bottom - window.innerHeight + 6); |
| 2874 | |
| 2875 | menu.style.left = `${Math.max(6, nx)}px`; |
| 2876 | menu.style.top = `${Math.max(6, ny)}px`; |
| 2877 | |
| 2878 | // Defensive: opening a context menu can (rarely) blank out inline SVGs in other parts of the UI. |
| 2879 | // Repair after the menu is painted (and again shortly after) without forcing a full re-render. |
| 2880 | try { |
| 2881 | const kick = () => { try { repairBlankFolderIcons({ force: true }); } catch (e) {} }; |
| 2882 | queueMicrotask(kick); |
| 2883 | setTimeout(kick, 80); |
| 2884 | setTimeout(kick, 250); |
| 2885 | } catch (e) { /* ignore */ } |
| 2886 | } |
no test coverage detected