@blocklet/payment-react
Version:
Reusable react components for payment kit v2
171 lines (170 loc) • 6.38 kB
JavaScript
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import SettingsIcon from "@mui/icons-material/Settings";
import {
Box,
Collapse,
Dialog,
DialogTitle,
DialogContent,
Fade,
IconButton,
Stack,
Tooltip,
Typography
} from "@mui/material";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
import SlippageConfig from "./slippage-config.js";
export default function QuoteDetailsPanel({
rateLine,
rows,
isSubscription = false,
slippageValue = 0.5,
onSlippageChange = void 0,
slippageConfig = void 0,
exchangeRate = null,
baseCurrency = "USD",
disabled = false
}) {
const { t } = useLocaleContext();
const [open, setOpen] = useState(false);
const [showContent, setShowContent] = useState(true);
const [dialogOpen, setDialogOpen] = useState(false);
const [pendingConfig, setPendingConfig] = useState(null);
const [submitting, setSubmitting] = useState(false);
const hasRows = rows.length > 0;
const handleOpenDialog = useCallback(() => {
setPendingConfig(slippageConfig || { mode: "percent", percent: slippageValue });
setDialogOpen(true);
}, [slippageValue, slippageConfig]);
const handleCloseDialog = () => {
setDialogOpen(false);
setPendingConfig(null);
};
const handleSlippageChange = (value) => {
setPendingConfig((prev) => prev ? { ...prev, percent: value } : { mode: "percent", percent: value });
};
const handleConfigChange = (config) => {
setPendingConfig(config);
};
const handleSubmit = async () => {
if (!pendingConfig || !onSlippageChange) return;
setSubmitting(true);
try {
const configToSave = {
...pendingConfig,
...baseCurrency ? { base_currency: baseCurrency } : {}
};
await onSlippageChange(configToSave);
setDialogOpen(false);
setPendingConfig(null);
} catch (err) {
console.error("Failed to update slippage", err);
} finally {
setSubmitting(false);
}
};
useEffect(() => {
if (!rateLine) return void 0;
setShowContent(false);
const timer = setTimeout(() => {
setShowContent(true);
}, 150);
return () => clearTimeout(timer);
}, [rateLine]);
const renderedRows = useMemo(
() => rows.map((row) => {
const isSlippageRow = row.isSlippage && isSubscription && onSlippageChange;
return /* @__PURE__ */ jsxs(
Stack,
{
direction: "row",
sx: {
alignItems: "center",
justifyContent: "space-between",
gap: 2
},
children: [
/* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", children: [
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: "0.75rem", color: "text.secondary" }, children: row.label }),
row.tooltip && /* @__PURE__ */ jsx(Tooltip, { title: row.tooltip, placement: "top", children: /* @__PURE__ */ jsx(InfoOutlinedIcon, { sx: { fontSize: "0.75rem", color: "text.lighter" } }) })
] }),
/* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: "0.75rem", color: "text.primary" }, children: row.value }),
isSlippageRow && !disabled && /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: handleOpenDialog, sx: { p: 0.25 }, children: /* @__PURE__ */ jsx(SettingsIcon, { sx: { fontSize: "0.875rem", color: "text.secondary" } }) })
] })
]
},
row.label
);
}),
[rows, isSubscription, onSlippageChange, disabled, handleOpenDialog]
);
const showSlippageOnly = !rateLine && isSubscription && onSlippageChange && rows.some((r) => r.isSlippage);
if (!rateLine && !showSlippageOnly) {
return null;
}
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsxs(Box, { sx: { width: "100%", mt: 0.5 }, children: [
/* @__PURE__ */ jsxs(
Stack,
{
direction: "row",
sx: {
alignItems: "center",
justifyContent: "space-between",
gap: 1
},
children: [
rateLine ? /* @__PURE__ */ jsx(Fade, { in: showContent, timeout: 300, children: /* @__PURE__ */ jsx(Typography, { sx: { fontSize: "0.7875rem", color: "text.lighter" }, children: rateLine }) }) : /* @__PURE__ */ jsx(Box, {}),
hasRows && /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => setOpen((prev) => !prev), "aria-label": open ? "collapse" : "expand", children: /* @__PURE__ */ jsx(
ExpandMoreIcon,
{
sx: {
fontSize: "1rem",
transition: "transform 0.2s ease",
transform: open ? "rotate(180deg)" : "rotate(0deg)"
}
}
) })
]
}
),
hasRows && /* @__PURE__ */ jsx(Collapse, { in: open, timeout: "auto", unmountOnExit: true, children: /* @__PURE__ */ jsx(Fade, { in: showContent, timeout: 300, children: /* @__PURE__ */ jsx(
Stack,
{
sx: {
mt: 1,
p: 1.25,
borderRadius: 1,
border: "1px solid",
borderColor: "divider",
bgcolor: "action.hover"
},
spacing: 1,
children: renderedRows
}
) }) })
] }),
/* @__PURE__ */ jsxs(Dialog, { open: dialogOpen, onClose: handleCloseDialog, maxWidth: "sm", fullWidth: true, children: [
/* @__PURE__ */ jsx(DialogTitle, { children: t("payment.checkout.quote.slippage.title") }),
/* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsx(
SlippageConfig,
{
value: pendingConfig?.percent ?? slippageValue,
onChange: handleSlippageChange,
config: pendingConfig || slippageConfig,
onConfigChange: handleConfigChange,
exchangeRate,
baseCurrency,
disabled: disabled || submitting,
sx: { mt: 1 },
onCancel: handleCloseDialog,
onSave: handleSubmit
}
) })
] })
] });
}