Vector Motion
Finance

Forex Card - Foreign Exchange Trading

Monitor foreign exchange rates and currency trading. Track FX exposure and hedging strategies.

Finance Forex Card

The Finance Forex Card displays real-time foreign exchange rates and currency trends, helping traders and businesses manage currency exposure.

Preview

Installation

npx shadcn@latest add https://vectormotion.vercel.app/registry/finance-forex-card.json
Finance Forex Card
'use client'import React, { useState, useEffect } from 'react';import { RefreshCcw, ArrowRight, TrendingUp } from 'lucide-react';import { motion, AnimatePresence } from 'motion/react';import { clsx, type ClassValue } from "clsx"import { twMerge } from "tailwind-merge"import { ResponsiveContainer, AreaChart, Area, YAxis } from 'recharts';function cn(...inputs: ClassValue[]) {  return twMerge(clsx(inputs))}// Simulated live value componentconst LiveValue = ({ baseValue }: { baseValue: number }) => {  const [value, setValue] = useState(baseValue);  const [prevValue, setPrevValue] = useState(baseValue);  const [data, setData] = useState([0.9180, 0.9190, 0.9185, 0.9195, 0.9192, 0.9200, 0.9205]);  useEffect(() => {    const interval = setInterval(() => {      const change = (Math.random() - 0.5) * 0.002;      setPrevValue(value);      const newValue = Number((value + change).toFixed(4));      setValue(newValue);      setData(prev => [...prev.slice(1), newValue]);    }, 2000);    return () => clearInterval(interval);  }, [value]);  const direction = value > prevValue ? 'up' : 'down';  return (    <div className="flex flex-col items-end">      <div className="h-8 w-24 mb-1">        <ResponsiveContainer width="100%" height="100%">          <AreaChart data={data.map((v) => ({ value: v }))}>            <defs>              <linearGradient id="gradForex" x1="0" y1="0" x2="0" y2="1">                <stop offset="5%" stopColor="#10b981" stopOpacity={0.3} />                <stop offset="95%" stopColor="#10b981" stopOpacity={0} />              </linearGradient>            </defs>            <YAxis domain={['dataMin', 'dataMax']} hide />            <Area type="monotone" dataKey="value" stroke="#10b981" fill="url(#gradForex)" strokeWidth={2} />          </AreaChart>        </ResponsiveContainer>      </div>      <AnimatePresence mode="popLayout">        <motion.p          key={value}          initial={{ opacity: 0 }}          animate={{ opacity: 1 }}          className={`font-mono text-lg font-medium ${direction === 'up'            ? 'text-emerald-500'            : 'text-foreground'            }`}        >          {value.toFixed(4)}        </motion.p>      </AnimatePresence>    </div>  );};interface ForexCardProps {  isInteractive?: boolean;  className?: string;  title?: string;  statusLabel?: string;  usdValue?: string;  eurBaseValue?: number;}const DEFAULT_TITLE = "Exchange Rates";const DEFAULT_STATUS_LABEL = "Live Feed";const DEFAULT_USD_VALUE = "1.0000";const DEFAULT_EUR_BASE_VALUE = 0.9200;export const ForexCard: React.FC<ForexCardProps> = ({  isInteractive = true,  className = "",  title = DEFAULT_TITLE,  statusLabel = DEFAULT_STATUS_LABEL,  usdValue = DEFAULT_USD_VALUE,  eurBaseValue = DEFAULT_EUR_BASE_VALUE,}) => {  const index = 46;  return (    <motion.div      layoutId={isInteractive ? `card-${index}-${title}` : undefined}      transition={{ duration: 0.4, ease: "easeOut" }}      className={cn(        "relative overflow-hidden rounded-xl border border-border bg-card text-card-foreground p-5 shadow-sm transition-all flex flex-col h-full group",        isInteractive ? "cursor-pointer hover:border-primary/50 hover:shadow-md" : "",        className      )}    >      <div className="mb-2 flex items-start justify-between relative z-10">        <div>          <h3 className="font-semibold text-lg text-foreground">            {title}          </h3>          <div className="flex items-center gap-2 mt-1">            <span className="text-xs text-muted-foreground">{statusLabel}</span>          </div>        </div>        <div className="rounded-lg bg-emerald-500/10 p-2 text-emerald-500">          <RefreshCcw className="h-5 w-5" />        </div>      </div>      <div className="relative z-10 flex-1">        <div className="space-y-4 mt-2">          {/* USD */}          <div className="flex items-center justify-between p-3 rounded-lg bg-muted">            <div className="flex items-center gap-3">              <div className="flex h-8 w-8 items-center justify-center rounded-full bg-white dark:bg-zinc-700 shadow-sm border border-zinc-100 dark:border-zinc-600 text-sm">                πŸ‡ΊπŸ‡Έ              </div>              <div>                <p className="text-sm font-bold text-foreground">USD</p>              </div>            </div>            <div className="text-right">              <p className="font-mono text-lg font-medium text-foreground">{usdValue}</p>            </div>          </div>          <div className="relative flex justify-center -my-5 z-20 pointer-events-none">            <div className="bg-card text-card-foreground border border-zinc-200 dark:border-zinc-700 rounded-full p-1 shadow-sm">              <ArrowRight className="h-3 w-3 text-muted-foreground rotate-90" />            </div>          </div>          {/* EUR */}          <div className="flex items-center justify-between p-3 rounded-lg bg-muted">            <div className="flex items-center gap-3">              <div className="flex h-8 w-8 items-center justify-center rounded-full bg-white dark:bg-zinc-700 shadow-sm border border-zinc-100 dark:border-zinc-600 text-sm">                πŸ‡ͺπŸ‡Ί              </div>              <div>                <p className="text-sm font-bold text-foreground">EUR</p>              </div>            </div>            <LiveValue baseValue={eurBaseValue} />          </div>        </div>      </div>    </motion.div>  );};

Props

Prop

Type