UNPKG

@blocklet/payment-react

Version:

Reusable react components for payment kit v2

316 lines (315 loc) 10.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); module.exports = SlippageConfig; var _jsxRuntime = require("react/jsx-runtime"); var _react = require("react"); var _material = require("@mui/material"); var _context = require("@arcblock/ux/lib/Locale/context"); const PRESET_SLIPPAGE = [0.5, 1, 3, 5, 10]; function SlippageConfig({ value, onChange, config = void 0, onConfigChange = void 0, exchangeRate = null, baseCurrency = "USD", disabled = false, sx = {}, onCancel = void 0, onSave = void 0 }) { const { t } = (0, _context.useLocaleContext)(); const [inputValue, setInputValue] = (0, _react.useState)(""); const [inputMode, setInputMode] = (0, _react.useState)(config?.mode || "percent"); const [error, setError] = (0, _react.useState)(null); const [isEditing, setIsEditing] = (0, _react.useState)(false); const percentValue = config?.percent ?? value; const roundedRate = (0, _react.useMemo)(() => { if (!exchangeRate) return null; const rateNum = Number(exchangeRate); if (Number.isNaN(rateNum) || rateNum <= 0) return null; return Math.round(rateNum * 100) / 100; }, [exchangeRate]); const computeMinRateFromPercent = percent => { if (!roundedRate) return ""; const slippageMultiplier = 1 + percent / 100; return (roundedRate / slippageMultiplier).toFixed(2); }; (0, _react.useEffect)(() => { if (config?.mode && config.mode !== inputMode) { setInputMode(config.mode); } }, [config?.mode, inputMode]); (0, _react.useEffect)(() => { if (isEditing) { return; } if (inputMode === "percent") { setInputValue(percentValue.toFixed(2)); return; } if (config?.min_acceptable_rate) { setInputValue(String(config.min_acceptable_rate)); return; } const minRate = computeMinRateFromPercent(percentValue); setInputValue(minRate); }, [percentValue, inputMode, exchangeRate, config?.min_acceptable_rate, isEditing]); const handlePresetClick = preset => { if (disabled) return; setInputValue(preset.toFixed(2)); setInputMode("percent"); setError(null); onChange(preset); const minRate = computeMinRateFromPercent(preset); onConfigChange?.({ mode: "percent", percent: preset, ...(minRate ? { min_acceptable_rate: minRate } : {}) }); }; const handleInputChange = newValue => { setInputValue(newValue); setError(null); const numValue = Number(newValue); if (!newValue || Number.isNaN(numValue)) { setError(t("payment.checkout.quote.slippage.invalid")); return; } if (numValue <= 0) { setError(t("payment.checkout.quote.slippage.invalidPositive")); return; } if (inputMode === "percent") { onChange(numValue); const minRate = computeMinRateFromPercent(numValue); onConfigChange?.({ mode: "percent", percent: numValue, ...(minRate ? { min_acceptable_rate: minRate } : {}) }); } else { if (!roundedRate) { setError(t("payment.checkout.quote.slippage.rateRequired")); return; } const percent = (roundedRate - numValue) / numValue * 100; onChange(Math.max(0, percent)); onConfigChange?.({ mode: "rate", percent: Math.max(0, percent), min_acceptable_rate: newValue }); } }; const handleModeChange = (_, newMode) => { if (disabled || !newMode) return; setInputMode(newMode); setError(null); if (newMode === "rate") { if (!roundedRate) { setError(t("payment.checkout.quote.slippage.rateRequired")); return; } const minRate = config?.min_acceptable_rate || computeMinRateFromPercent(percentValue); setInputValue(minRate); onConfigChange?.({ mode: "rate", percent: percentValue, min_acceptable_rate: minRate }); } else { setInputValue(percentValue.toFixed(2)); const minRate = computeMinRateFromPercent(percentValue); onConfigChange?.({ mode: "percent", percent: percentValue, ...(minRate ? { min_acceptable_rate: minRate } : {}) }); } }; const minAcceptableRate = (0, _react.useMemo)(() => { if (!roundedRate) return null; const slippageMultiplier = 1 + percentValue / 100; return (roundedRate / slippageMultiplier).toFixed(2); }, [roundedRate, percentValue]); const currentRateLabel = (0, _react.useMemo)(() => { if (!roundedRate) { return "\u2014"; } return roundedRate.toFixed(2); }, [roundedRate]); const handleCancel = () => { onCancel?.(); }; const handleSave = () => { onSave?.(); }; return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, { spacing: 2.5, sx, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { variant: "body2", color: "text.secondary", children: t("payment.checkout.quote.slippageLimit.description") }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.ToggleButtonGroup, { value: inputMode, exclusive: true, onChange: handleModeChange, size: "small", fullWidth: true, disabled, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.ToggleButton, { value: "percent", children: t("payment.checkout.quote.slippageLimit.configTogglePercent") }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.ToggleButton, { value: "rate", disabled: !roundedRate, children: t("payment.checkout.quote.slippageLimit.configToggleRate") })] }), inputMode === "percent" && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, { spacing: 1.5, children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Stack, { direction: "row", spacing: 1, children: PRESET_SLIPPAGE.map(preset => /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.ToggleButton, { value: preset, selected: Math.abs(percentValue - preset) < 0.01, onClick: () => handlePresetClick(preset), size: "small", disabled, sx: { flex: 1, py: 1, borderRadius: 1, border: "1px solid", borderColor: "divider", "&.Mui-selected": { bgcolor: "primary.main", color: "primary.contrastText", borderColor: "primary.main", "&:hover": { bgcolor: "primary.dark" } } }, children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, { variant: "body2", children: [preset, "%"] }) }, preset)) }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.TextField, { size: "small", fullWidth: true, value: inputValue, onChange: e => handleInputChange(e.target.value), onFocus: () => setIsEditing(true), onBlur: () => setIsEditing(false), error: !!error, helperText: error, disabled, label: t("common.custom"), InputProps: { endAdornment: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { variant: "body2", sx: { color: "text.secondary", mr: 1 }, children: "%" }) }, placeholder: "0.50" })] }), inputMode === "rate" && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Stack, { spacing: 1.5, children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.TextField, { size: "small", fullWidth: true, value: inputValue, onChange: e => handleInputChange(e.target.value), onFocus: () => setIsEditing(true), onBlur: () => setIsEditing(false), error: !!error, helperText: error, disabled: disabled || !roundedRate, label: t("payment.checkout.quote.slippage.rateInputLabel", { currency: baseCurrency }), InputProps: { endAdornment: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { variant: "body2", sx: { color: "text.secondary", mr: 1 }, children: baseCurrency }) }, placeholder: roundedRate?.toFixed(2) || "0.00" }) }), roundedRate && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, { sx: { borderRadius: 1, p: 1.5, bgcolor: "action.hover" }, children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, { spacing: 0.5, children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { variant: "body2", color: "text.secondary", children: t("payment.checkout.quote.slippageLimit.derivedCurrentRate") }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, { variant: "body2", fontWeight: 500, children: [currentRateLabel, " ", baseCurrency] })] }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, { variant: "body2", color: "text.secondary", children: t("payment.checkout.quote.slippageLimit.derivedMinRate") }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, { variant: "body2", fontWeight: 500, color: "primary.main", children: [inputMode === "rate" ? inputValue : minAcceptableRate || "\u2014", " ", baseCurrency] })] })] }) }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, { direction: "row", spacing: 1, justifyContent: "flex-end", children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Button, { onClick: handleCancel, disabled, color: "inherit", children: t("common.cancel") }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Button, { variant: "contained", onClick: handleSave, disabled: disabled || !!error, children: t("common.save") })] })] }); }