import React, { useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import type { AppToken } from '../../shared/types'; import { useTokenImageUrl } from '../shared/tokens/useTokenImageUrl'; import styles from './SceneEditorApp.module.css'; const TOKEN_DND_MIME = 'application/x-dnd-app-token-id'; type Props = { token: AppToken; onEdit: () => void; onDelete: () => void; }; export function TokenTile({ token, onEdit, onDelete }: Props) { const url = useTokenImageUrl(token.id); const menuBtnRef = useRef(null); const menuRef = useRef(null); const [menu, setMenu] = useState<{ x: number; y: number } | null>(null); useEffect(() => { if (!menu) return; const onPointerDown = (e: PointerEvent) => { const t = e.target; if (!(t instanceof Node)) return; if (menuBtnRef.current?.contains(t)) return; if (menuRef.current?.contains(t)) return; setMenu(null); }; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setMenu(null); }; window.addEventListener('pointerdown', onPointerDown, true); window.addEventListener('keydown', onKey); return () => { window.removeEventListener('pointerdown', onPointerDown, true); window.removeEventListener('keydown', onKey); }; }, [menu]); const openMenu = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); const pad = 8; const menuW = 160; const menuH = 84; const x = Math.max(pad, Math.min(e.clientX, window.innerWidth - menuW - pad)); const y = Math.max(pad, Math.min(e.clientY, window.innerHeight - menuH - pad)); setMenu({ x, y }); }; return (
{ if (menuBtnRef.current?.contains(e.target as Node)) { e.preventDefault(); return; } e.dataTransfer.setData(TOKEN_DND_MIME, token.id); e.dataTransfer.effectAllowed = 'copy'; }} title="Перетащите на карту" >
{url ? : null}
{token.name}
{menu ? createPortal(
e.stopPropagation()} >
, document.body, ) : null}
); } export { TOKEN_DND_MIME };