refactor(converter): split converter card into modular section components
This commit is contained in:
142
components/converter/converter-card-converted-value.tsx
Normal file
142
components/converter/converter-card-converted-value.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user