add pull page
This commit is contained in:
207
src/widgets/pools/ui/LpDepositCard.tsx
Normal file
207
src/widgets/pools/ui/LpDepositCard.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
import { PrimaryButton, TokenIcon } from '@shared/ui'
|
||||
import { truncateDecimals } from '@shared/lib/utils/truncateDecimals'
|
||||
import { coinIcon, formatFeeTier, type Pool, type LpQuote } from '../model/meta'
|
||||
import styles from './LpDepositCard.module.css'
|
||||
|
||||
interface Props {
|
||||
pool: Pool | null
|
||||
quote: LpQuote
|
||||
amount0: string
|
||||
amount1: string
|
||||
priceLower: string
|
||||
priceUpper: string
|
||||
slippage: string
|
||||
useNativeEth: boolean
|
||||
onAmount0Change: (v: string) => void
|
||||
onAmount1Change: (v: string) => void
|
||||
onPriceLowerChange: (v: string) => void
|
||||
onPriceUpperChange: (v: string) => void
|
||||
onSlippageChange: (v: string) => void
|
||||
onUseNativeEthChange: (v: boolean) => void
|
||||
onSubmit: () => void
|
||||
}
|
||||
|
||||
const DASH = '—'
|
||||
const decimalRe = /^(\d+\.?\d*|\.?\d*)$/
|
||||
|
||||
function AmountInput({
|
||||
symbol,
|
||||
value,
|
||||
color,
|
||||
onChange,
|
||||
}: {
|
||||
symbol: string
|
||||
value: string
|
||||
color: string
|
||||
onChange: (v: string) => void
|
||||
}) {
|
||||
return (
|
||||
<div className={styles.amountRow}>
|
||||
<input
|
||||
className={styles.amountInput}
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (decimalRe.test(v) || v === '') onChange(v)
|
||||
}}
|
||||
placeholder="0"
|
||||
aria-label={`Сумма ${symbol}`}
|
||||
/>
|
||||
<div className={styles.token}>
|
||||
<TokenIcon letter={symbol[0]} color={color} logo={coinIcon(symbol)} size={28} />
|
||||
<span>{symbol}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function PriceInput({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
label: string
|
||||
value: string
|
||||
onChange: (v: string) => void
|
||||
}) {
|
||||
return (
|
||||
<label className={styles.priceField}>
|
||||
<span className={styles.priceLabel}>{label}</span>
|
||||
<input
|
||||
className={styles.priceInput}
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (decimalRe.test(v) || v === '') onChange(v)
|
||||
}}
|
||||
placeholder="0"
|
||||
/>
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
export function LpDepositCard({
|
||||
pool,
|
||||
quote,
|
||||
amount0,
|
||||
amount1,
|
||||
priceLower,
|
||||
priceUpper,
|
||||
slippage,
|
||||
useNativeEth,
|
||||
onAmount0Change,
|
||||
onAmount1Change,
|
||||
onPriceLowerChange,
|
||||
onPriceUpperChange,
|
||||
onSlippageChange,
|
||||
onUseNativeEthChange,
|
||||
onSubmit,
|
||||
}: Props) {
|
||||
if (!pool) {
|
||||
return (
|
||||
<div className={styles.card}>
|
||||
<div className={styles.placeholder}>
|
||||
<span className={styles.placeholderIcon} aria-hidden>
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
|
||||
<circle cx="9" cy="9" r="5" />
|
||||
<circle cx="15" cy="15" r="5" />
|
||||
</svg>
|
||||
</span>
|
||||
<p className={styles.placeholderTitle}>Выберите пул</p>
|
||||
<p className={styles.placeholderText}>
|
||||
Выберите пул в таблице выше, чтобы внести ликвидность.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const insufficient = quote.balance.sufficient === false
|
||||
const apr = quote.aprPercent != null ? `${quote.aprPercent}%` : DASH
|
||||
const feeUsd = quote.appFeeTotalUsd != null ? `≈ $${quote.appFeeTotalUsd.toFixed(2)}` : DASH
|
||||
|
||||
return (
|
||||
<div className={styles.card}>
|
||||
<div className={styles.top}>
|
||||
<span className={styles.pair}>
|
||||
<span className={styles.icons}>
|
||||
<TokenIcon letter={pool.symbol0[0]} color="var(--grad-center)" logo={coinIcon(pool.symbol0)} size={24} />
|
||||
<TokenIcon letter={pool.symbol1[0]} color="var(--grad-edge)" logo={coinIcon(pool.symbol1)} size={24} />
|
||||
</span>
|
||||
{pool.symbol0}/{pool.symbol1}
|
||||
</span>
|
||||
<span className={styles.fee}>{formatFeeTier(pool.feeTier)}</span>
|
||||
</div>
|
||||
|
||||
<span className={styles.tag}>Суммы взноса</span>
|
||||
<div className={styles.amounts}>
|
||||
<AmountInput symbol={pool.symbol0} value={amount0} color="var(--grad-center)" onChange={onAmount0Change} />
|
||||
<AmountInput symbol={pool.symbol1} value={amount1} color="var(--grad-edge)" onChange={onAmount1Change} />
|
||||
</div>
|
||||
|
||||
<span className={styles.tag}>Диапазон цен ({pool.symbol1} за {pool.symbol0})</span>
|
||||
<div className={styles.range}>
|
||||
<PriceInput label="Мин." value={priceLower} onChange={onPriceLowerChange} />
|
||||
<PriceInput label="Макс." value={priceUpper} onChange={onPriceUpperChange} />
|
||||
</div>
|
||||
|
||||
<div className={styles.options}>
|
||||
<label className={styles.slippage}>
|
||||
<span className={styles.priceLabel}>Проскальзывание, %</span>
|
||||
<input
|
||||
className={styles.priceInput}
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
value={slippage}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (decimalRe.test(v) || v === '') onSlippageChange(v)
|
||||
}}
|
||||
placeholder="1"
|
||||
/>
|
||||
</label>
|
||||
<label className={styles.toggle}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={useNativeEth}
|
||||
onChange={(e) => onUseNativeEthChange(e.target.checked)}
|
||||
/>
|
||||
<span>Использовать нативный ETH</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className={styles.quote}>
|
||||
<div className={styles.quoteRow}>
|
||||
<span className={styles.quoteLabel}>Взнос {pool.symbol0}</span>
|
||||
<span className={styles.quoteValue}>{truncateDecimals(quote.amount0DesiredHuman, 8)}</span>
|
||||
</div>
|
||||
<div className={styles.quoteRow}>
|
||||
<span className={styles.quoteLabel}>Взнос {pool.symbol1}</span>
|
||||
<span className={styles.quoteValue}>{truncateDecimals(quote.amount1DesiredHuman, 8)}</span>
|
||||
</div>
|
||||
<div className={styles.quoteRow}>
|
||||
<span className={styles.quoteLabel}>Комиссия сервиса (0.7%)</span>
|
||||
<span className={styles.quoteValue}>{feeUsd}</span>
|
||||
</div>
|
||||
<div className={styles.quoteRow}>
|
||||
<span className={styles.quoteLabel}>Годовая доходность (APR)</span>
|
||||
<span className={`${styles.quoteValue} ${styles.success}`}>{apr}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{insufficient && (
|
||||
<p className={styles.warning}>
|
||||
Недостаточно средств: не хватает {quote.balance.token1.symbol}. Пополните баланс.
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className={styles.submit}>
|
||||
<PrimaryButton label="Внести в пул" type="button" onClick={onSubmit} disabled={insufficient} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user