143 lines
4.7 KiB
TypeScript
143 lines
4.7 KiB
TypeScript
"use client";
|
|
|
|
import { AnimatePresence, motion } from "framer-motion";
|
|
import { Check, Copy, Link2 } from "lucide-react";
|
|
|
|
import { CurrencyIcon } from "@/components/converter/currency-icon";
|
|
import { Button } from "@/components/ui/button";
|
|
import type { RateAsset } from "@/lib/rates";
|
|
|
|
interface ConverterCardConvertedValueProps {
|
|
shouldReduceMotion: boolean;
|
|
fromAsset?: RateAsset;
|
|
toAsset?: RateAsset;
|
|
pairTransitionKey: string;
|
|
convertedDisplay: string;
|
|
forAmountDisplay: string;
|
|
isShareLinkCopied: boolean;
|
|
isCopied: boolean;
|
|
canCopyConvertedValue: boolean;
|
|
onCopyShareLink: () => void;
|
|
onCopyConvertedValue: () => void;
|
|
}
|
|
|
|
export function ConverterCardConvertedValue({
|
|
shouldReduceMotion,
|
|
fromAsset,
|
|
toAsset,
|
|
pairTransitionKey,
|
|
convertedDisplay,
|
|
forAmountDisplay,
|
|
isShareLinkCopied,
|
|
isCopied,
|
|
canCopyConvertedValue,
|
|
onCopyShareLink,
|
|
onCopyConvertedValue,
|
|
}: ConverterCardConvertedValueProps) {
|
|
return (
|
|
<div className="rounded-xl border border-border/70 bg-background/50 p-4">
|
|
<div className="flex items-center justify-between gap-2">
|
|
<p className="text-xs uppercase tracking-[0.12em] text-muted-foreground">
|
|
Converted value
|
|
</p>
|
|
<div className="flex items-center gap-1">
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={onCopyShareLink}
|
|
className="h-7 w-7 text-muted-foreground hover:text-foreground"
|
|
aria-label={isShareLinkCopied ? "Share link copied" : "Copy share link"}
|
|
>
|
|
{isShareLinkCopied ? (
|
|
<Check className="h-3.5 w-3.5 text-cyan-200" />
|
|
) : (
|
|
<Link2 className="h-3.5 w-3.5" />
|
|
)}
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={onCopyConvertedValue}
|
|
disabled={!canCopyConvertedValue}
|
|
className="h-7 w-7 text-muted-foreground hover:text-foreground"
|
|
aria-label={
|
|
isCopied
|
|
? "Converted value copied"
|
|
: "Copy converted value to clipboard"
|
|
}
|
|
>
|
|
{isCopied ? (
|
|
<Check className="h-3.5 w-3.5 text-cyan-200" />
|
|
) : (
|
|
<Copy className="h-3.5 w-3.5" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
{fromAsset && toAsset ? (
|
|
<AnimatePresence initial={false} mode="wait">
|
|
<motion.div
|
|
key={pairTransitionKey}
|
|
className="mt-2 flex items-center gap-2 text-xs text-muted-foreground"
|
|
initial={
|
|
shouldReduceMotion ? false : { opacity: 0, y: 4, filter: "blur(2px)" }
|
|
}
|
|
animate={
|
|
shouldReduceMotion
|
|
? { opacity: 1 }
|
|
: { opacity: 1, y: 0, filter: "blur(0px)" }
|
|
}
|
|
exit={
|
|
shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: -4, filter: "blur(2px)" }
|
|
}
|
|
transition={{ duration: shouldReduceMotion ? 0.01 : 0.16 }}
|
|
>
|
|
<span className="inline-flex items-center gap-1.5 rounded-full border border-border/70 bg-background/60 px-2 py-1">
|
|
<CurrencyIcon
|
|
code={fromAsset.code}
|
|
type={fromAsset.type}
|
|
size="sm"
|
|
/>
|
|
{fromAsset.code}
|
|
</span>
|
|
<span>to</span>
|
|
<span className="inline-flex items-center gap-1.5 rounded-full border border-border/70 bg-background/60 px-2 py-1">
|
|
<CurrencyIcon code={toAsset.code} type={toAsset.type} size="sm" />
|
|
{toAsset.code}
|
|
</span>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
) : null}
|
|
<p className="mt-2 break-all text-3xl font-semibold tracking-tight text-foreground sm:text-4xl">
|
|
<AnimatePresence initial={false} mode="wait">
|
|
<motion.span
|
|
key={convertedDisplay}
|
|
className="inline-block"
|
|
initial={
|
|
shouldReduceMotion ? false : { opacity: 0, y: 6, filter: "blur(2px)" }
|
|
}
|
|
animate={
|
|
shouldReduceMotion
|
|
? { opacity: 1 }
|
|
: { opacity: 1, y: 0, filter: "blur(0px)" }
|
|
}
|
|
exit={
|
|
shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: -6, filter: "blur(2px)" }
|
|
}
|
|
transition={{ duration: shouldReduceMotion ? 0.01 : 0.18 }}
|
|
>
|
|
{convertedDisplay}
|
|
</motion.span>
|
|
</AnimatePresence>
|
|
</p>
|
|
{fromAsset ? (
|
|
<p className="mt-2 text-sm text-muted-foreground">
|
|
for {forAmountDisplay} {fromAsset.code}
|
|
</p>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|