fix: context menu backdrop blocks card onClick on close
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
|
||||||
|
|
||||||
interface MenuItem {
|
interface MenuItem {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -15,40 +14,42 @@ interface ContextMenuProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ContextMenu({ x, y, items, onClose }: ContextMenuProps) {
|
export function ContextMenu({ x, y, items, onClose }: ContextMenuProps) {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const close = (e: MouseEvent) => {
|
|
||||||
if (ref.current && !ref.current.contains(e.target as Node)) onClose();
|
|
||||||
};
|
|
||||||
document.addEventListener('mousedown', close);
|
|
||||||
return () => document.removeEventListener('mousedown', close);
|
|
||||||
}, [onClose]);
|
|
||||||
|
|
||||||
const adjustedY = Math.min(y, window.innerHeight - items.length * 46 - 20);
|
const adjustedY = Math.min(y, window.innerHeight - items.length * 46 - 20);
|
||||||
const adjustedX = Math.min(x, window.innerWidth - 170);
|
const adjustedX = Math.min(x, window.innerWidth - 170);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
ref={ref}
|
{/* 전체 화면 backdrop: mousedown을 잡아 카드 onClick이 발화하지 않도록 막음 */}
|
||||||
style={{ position: 'fixed', top: adjustedY, left: adjustedX, zIndex: 9999 }}
|
<div
|
||||||
className="bg-white rounded-xl shadow-2xl border border-gray-100 py-1.5 min-w-[155px]"
|
className="fixed inset-0"
|
||||||
onContextMenu={(e) => e.preventDefault()}
|
style={{ zIndex: 9998 }}
|
||||||
>
|
onMouseDown={(e) => {
|
||||||
{items.map((item, i) => (
|
e.stopPropagation();
|
||||||
<button
|
onClose();
|
||||||
key={i}
|
}}
|
||||||
onClick={() => { item.onClick(); onClose(); }}
|
onClick={(e) => e.stopPropagation()}
|
||||||
className={`w-full text-left px-4 py-2.5 text-base font-semibold flex items-center gap-2.5 transition-colors ${
|
/>
|
||||||
item.danger
|
<div
|
||||||
? 'text-red-600 hover:bg-red-50'
|
style={{ position: 'fixed', top: adjustedY, left: adjustedX, zIndex: 9999 }}
|
||||||
: 'text-gray-700 hover:bg-gray-50'
|
className="bg-white rounded-xl shadow-2xl border border-gray-100 py-1.5 min-w-[155px]"
|
||||||
}`}
|
onMouseDown={(e) => e.stopPropagation()}
|
||||||
>
|
onContextMenu={(e) => e.preventDefault()}
|
||||||
<span className="text-lg leading-none">{item.icon}</span>
|
>
|
||||||
{item.label}
|
{items.map((item, i) => (
|
||||||
</button>
|
<button
|
||||||
))}
|
key={i}
|
||||||
</div>
|
onClick={() => { item.onClick(); onClose(); }}
|
||||||
|
className={`w-full text-left px-4 py-2.5 text-base font-semibold flex items-center gap-2.5 transition-colors ${
|
||||||
|
item.danger
|
||||||
|
? 'text-red-600 hover:bg-red-50'
|
||||||
|
: 'text-gray-700 hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="text-lg leading-none">{item.icon}</span>
|
||||||
|
{item.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user