fix: context menu via createPortal, renders at viewport coords

This commit is contained in:
Krao Hasanee
2026-06-02 15:15:54 -04:00
parent 36f1179c04
commit aae5214cde
+7 -8
View File
@@ -1,4 +1,5 @@
import { useEffect, useMemo, useRef, useState } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import LoadingButton from './LoadingButton'; import LoadingButton from './LoadingButton';
import SortTh from './SortTh'; import SortTh from './SortTh';
import { supabase } from '../lib/supabase'; import { supabase } from '../lib/supabase';
@@ -310,10 +311,7 @@ export default function FileBrowser({ initialPath = '/', rootPath = '/' }) {
const openCtxMenu = (e, entry, childPath) => { const openCtxMenu = (e, entry, childPath) => {
e.preventDefault(); e.stopPropagation(); e.preventDefault(); e.stopPropagation();
const menuW = 180, menuH = 320; setCtxMenu({ x: e.clientX, y: e.clientY, entry, childPath });
const x = e.clientX + menuW > window.innerWidth ? window.innerWidth - menuW - 8 : e.clientX;
const y = e.clientY + menuH > window.innerHeight ? e.clientY - menuH : e.clientY;
setCtxMenu({ x: Math.max(4, x), y: Math.max(4, y), entry, childPath });
}; };
return ( return (
@@ -445,9 +443,9 @@ export default function FileBrowser({ initialPath = '/', rootPath = '/' }) {
)} )}
</div> </div>
{/* Context menu */} {/* Context menu — portal to escape stacking context */}
{ctxMenu && ( {ctxMenu && createPortal(
<div onClick={e => e.stopPropagation()} style={{ ...popupMenuStyle, position: 'fixed', left: ctxMenu.x, top: ctxMenu.y, zIndex: 1000, padding: '4px 0', minWidth: 160 }}> <div onClick={e => e.stopPropagation()} style={{ ...popupMenuStyle, position: 'fixed', left: ctxMenu.x, top: ctxMenu.y, zIndex: 9999, padding: '4px 0', minWidth: 160 }}>
{ctxMenu.entry?.type === 'dir' && ( {ctxMenu.entry?.type === 'dir' && (
<button style={ITEM_STYLE} onMouseEnter={e => e.currentTarget.style.background = 'var(--interactive-row-hover)'} onMouseLeave={e => e.currentTarget.style.background = 'none'} onClick={() => { loadFiles(ctxMenu.childPath); setCtxMenu(null); }}> <button style={ITEM_STYLE} onMouseEnter={e => e.currentTarget.style.background = 'var(--interactive-row-hover)'} onMouseLeave={e => e.currentTarget.style.background = 'none'} onClick={() => { loadFiles(ctxMenu.childPath); setCtxMenu(null); }}>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z" /></svg> <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z" /></svg>
@@ -514,7 +512,8 @@ export default function FileBrowser({ initialPath = '/', rootPath = '/' }) {
</button> </button>
</> </>
)} )}
</div> </div>,
document.body
)} )}
</div> </div>
); );