56 lines
2.0 KiB
React
56 lines
2.0 KiB
React
|
|
import { useEffect, useRef } from 'react'
|
||
|
|
|
||
|
|
export default function ContextMenu({ x, y, items, onClose }) {
|
||
|
|
const ref = useRef(null)
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const onMouseDown = (e) => { if (ref.current && !ref.current.contains(e.target)) onClose() }
|
||
|
|
const onKey = (e) => { if (e.key === 'Escape') onClose() }
|
||
|
|
document.addEventListener('mousedown', onMouseDown)
|
||
|
|
document.addEventListener('keydown', onKey)
|
||
|
|
return () => {
|
||
|
|
document.removeEventListener('mousedown', onMouseDown)
|
||
|
|
document.removeEventListener('keydown', onKey)
|
||
|
|
}
|
||
|
|
}, [onClose])
|
||
|
|
|
||
|
|
// Keep menu inside viewport
|
||
|
|
const W = 192
|
||
|
|
const H = items.length * 34
|
||
|
|
const adjX = Math.min(x, window.innerWidth - W - 8)
|
||
|
|
const adjY = Math.min(y, window.innerHeight - H - 8)
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
ref={ref}
|
||
|
|
style={{ left: adjX, top: adjY }}
|
||
|
|
className="fixed z-[200] min-w-[192px] bg-surface-elevated border border-surface-border rounded-xl shadow-2xl py-1.5 overflow-hidden"
|
||
|
|
>
|
||
|
|
{items.map((item, i) =>
|
||
|
|
item.separator ? (
|
||
|
|
<div key={i} className="h-px bg-surface-border my-1 mx-2" />
|
||
|
|
) : (
|
||
|
|
<button
|
||
|
|
key={i}
|
||
|
|
disabled={item.disabled}
|
||
|
|
onClick={() => { item.action(); onClose() }}
|
||
|
|
className={`w-full flex items-center gap-2.5 px-3 py-2 text-xs text-left transition-colors disabled:opacity-40 disabled:cursor-not-allowed
|
||
|
|
${item.danger
|
||
|
|
? 'text-red-400 hover:bg-red-500/10'
|
||
|
|
: item.highlight
|
||
|
|
? 'text-gold hover:bg-gold/10'
|
||
|
|
: 'text-text-primary hover:bg-surface-border/40'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
<span className="text-sm w-4 text-center leading-none flex-shrink-0">{item.icon}</span>
|
||
|
|
<span>{item.label}</span>
|
||
|
|
{item.shortcut && (
|
||
|
|
<span className="ml-auto text-text-muted/50 text-[10px]">{item.shortcut}</span>
|
||
|
|
)}
|
||
|
|
</button>
|
||
|
|
)
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|