mtg-deck-completion / web /src /components /CardPreview.tsx
nsaroiu's picture
Deploy 005b9d6
15c3f7a verified
Raw History Blame Contribute Delete
1.79 kB
import { createPortal } from 'react-dom'
import { PREVIEW_WIDTH, type PreviewState } from '../hooks/usePreview'
import { formatUsd, printingLabel } from '../lib/price'
import { ManaCost, ManaText } from './ManaCost'
// Rules-text panel shown on hover, built from the server's own card data
// (no external image requests). Rendered into <body>, not where it's used:
// a panel inside the sidebar (the ban list) would otherwise sit in the
// sidebar's stacking context, and the deck column would paint over it.
export function CardPreview({ state }: { state: PreviewState | null }) {
if (!state) return null
const { card } = state
// Multi-face cards (split, adventure, transform) join each field with " // ".
const names = card.name.split(' // ')
const types = card.type_line.split(' // ')
const costs = card.mana_cost.split(' // ')
const texts = card.oracle_text.split(' // ')
return createPortal(
<div className="card-preview" style={{ left: state.x, top: state.y, width: PREVIEW_WIDTH }} aria-hidden="true">
{names.map((name, i) => (
<div key={i} className="preview-face">
<div className="preview-title">
<span>{name}</span>
<ManaCost cost={costs[i] ?? ''} />
</div>
<div className="preview-type">{types[i] ?? types[0]}</div>
{(texts[i] ?? '').split('\n').filter(Boolean).map((para, j) => (
<p key={j}><ManaText text={para} /></p>
))}
{card.stats?.[i] && <div className="preview-stats">{card.stats[i]}</div>}
</div>
))}
{card.price && (
<div className="preview-price">
Cheapest: <strong>{formatUsd(card.price.usd)}</strong> · {printingLabel(card.price)}
</div>
)}
</div>,
document.body,
)
}