Files
frontend/src/widgets/swap-form/ui/SwapCard.tsx
2026-05-17 14:45:28 +03:00

119 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Token } from '../model/useSwapForm'
import { truncateDecimals } from '@shared/lib/utils/truncateDecimals'
import { TokenSelect } from './TokenSelect'
import styles from './SwapCard.module.css'
interface Props {
mode: 'from' | 'to'
token: Token
tokenOptions: Token[]
amount: string
usd?: string
onTokenChange: (token: Token) => void
onAmountChange?: (v: string) => void
onSetPercent?: (p: number) => void
selectedNetwork?: string
onNetworkChange?: (network: string) => void
hideNetworkSelect?: boolean
}
const NETWORKS = ['ETH', 'BSC', 'TRX', 'SOL']
const PERCENTS = [25, 50, 100]
export function SwapCard({
mode, token, tokenOptions, amount, usd,
onTokenChange, onAmountChange, onSetPercent,
selectedNetwork, onNetworkChange, hideNetworkSelect,
}: Props) {
const [intPart, decPart] = amount.split('.')
const pills = onSetPercent && (
<>
{PERCENTS.map(p => (
<button key={p} className={styles.pill} onClick={() => onSetPercent(p)}>
{p}%
</button>
))}
</>
)
return (
<div className={styles.wrapper}>
{/* Пиллы над карточкой — только мобайл */}
{mode === 'from' && pills && (
<div className={styles.pillsOuter}>{pills}</div>
)}
<div className={styles.card}>
<div className={styles.top}>
<div className={styles.label}>
{mode === 'from' && !hideNetworkSelect && (
<div className={styles.networkPills}>
{NETWORKS.map(n => (
<button
key={n}
className={`${styles.networkBtn} ${n === selectedNetwork ? styles.networkBtnActive : ''}`}
onClick={() => onNetworkChange?.(n)}
>
{n}
</button>
))}
</div>
)}
</div>
{/* Пиллы внутри карточки — только десктоп */}
{mode === 'from' && pills && (
<div className={styles.pillsInner}>{pills}</div>
)}
{/* Селект в топ-строке — только мобайл */}
<div className={styles.selectTop}>
<TokenSelect value={token} options={tokenOptions} onChange={onTokenChange} compact />
</div>
</div>
<div className={styles.mid}>
{mode === 'from' ? (
<input
className={styles.input}
type="text"
value={amount}
onChange={e => onAmountChange?.(e.target.value)}
placeholder="0"
/>
) : (
<div className={styles.display}>
<span className={styles.int}>{intPart}</span>
{decPart && <span className={styles.dec}>.{decPart}</span>}
</div>
)}
{/* Селект справа от инпута — только десктоп */}
<div className={styles.selectMid}>
<TokenSelect value={token} options={tokenOptions} onChange={onTokenChange} />
</div>
</div>
<div className={styles.bottom}>
{usd && (
<span className={styles.usd}>
${usd}
</span>
)}
<span className={styles.balance}>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--text-secondary)" strokeWidth="2">
<rect x="2" y="6" width="20" height="14" rx="3" />
<path d="M6 6V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v2" />
</svg>
{truncateDecimals(token.balance, 8)}
{mode === 'from' && onSetPercent && (
<button className={styles.max} onClick={() => onSetPercent(100)}>МАКС</button>
)}
</span>
</div>
</div>
</div>
)
}