import { memo, forwardRef } from 'react'; import { createPortal } from 'react-dom'; import type { Piece, PieceSet, PieceRenderer } from '../types'; import { CachedPieceImg } from '../hooks/usePieceCache'; import { resolvePieceImageSrc } from '../defaultPieces'; interface DragGhostProps { piece: Piece; x: number; y: number; squareSize: number; pieceSet?: PieceSet; customPieces?: PieceRenderer; /** Rotate the dragged piece artwork 180deg. Default: false. */ flipPieces?: boolean; /** Scale factor applied to the dragged piece. Default: 1. */ scale?: number; /** Upward offset (in square-size units) so the piece floats above the finger/cursor. Default: 0. */ liftSquares?: number; } export const DragGhost = memo(forwardRef(function DragGhost({ piece, x, y, squareSize, pieceSet, customPieces, flipPieces = false, scale = 1, liftSquares = 0, }, ref) { const piecePath = pieceSet?.path; const key = `${piece.color}${piece.role.toUpperCase()}`; let content: React.ReactNode; if (customPieces?.[key]) { content = customPieces[key](); } else { const src = resolvePieceImageSrc(key, piecePath); content = ; } // Outer div: positioning only. useInteraction rewrites this `transform` on pointermove, // so keep it a plain translate to avoid interference from scale/lift. const offset = squareSize / 2; // Inner wrapper carries the visual transform (scale + lift). const lift = liftSquares * squareSize; const liftTransform = [ lift !== 0 ? `translate(0, ${-lift}px)` : '', scale !== 1 ? `scale(${scale})` : '', flipPieces ? 'rotate(180deg)' : '', ].filter(Boolean).join(' ') || undefined; const ghost = (
{content}
); if (typeof document === 'undefined') return ghost; return createPortal(ghost, document.body); }));