area-chart-5

PreviousNext
Docs
reuiblock

Preview

Loading preview…
registry/default/blocks/charts/area-charts/area-chart-5.tsx
'use client';

import React, { useState } from 'react';
import { Card, CardContent, CardHeader, CardHeading, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { ToggleGroup, ToggleGroupItem } from '@/registry/default/ui/toggle-group';
import { Area, ComposedChart, Line, XAxis, YAxis } from 'recharts';

// DeFi protocol financial data
const financeData = {
  day: [
    { month: '00:00', totalDeposits: 4.2, totalBorrowed: 3.1 },
    { month: '04:00', totalDeposits: 4.3, totalBorrowed: 3.2 },
    { month: '08:00', totalDeposits: 4.5, totalBorrowed: 3.3 },
    { month: '12:00', totalDeposits: 4.7, totalBorrowed: 3.3 },
    { month: '16:00', totalDeposits: 4.6, totalBorrowed: 3.2 },
    { month: '20:00', totalDeposits: 4.4, totalBorrowed: 3.1 },
  ],
  week: [
    { month: 'Mon', totalDeposits: 4.2, totalBorrowed: 3.1 },
    { month: 'Tue', totalDeposits: 4.3, totalBorrowed: 3.2 },
    { month: 'Wed', totalDeposits: 4.5, totalBorrowed: 3.3 },
    { month: 'Thu', totalDeposits: 4.7, totalBorrowed: 3.3 },
    { month: 'Fri', totalDeposits: 4.6, totalBorrowed: 3.2 },
    { month: 'Sat', totalDeposits: 4.4, totalBorrowed: 3.1 },
    { month: 'Sun', totalDeposits: 4.3, totalBorrowed: 3.0 },
  ],
  month: [
    { month: 'Jun', totalDeposits: 4.2, totalBorrowed: 3.1 },
    { month: 'Jul', totalDeposits: 4.0, totalBorrowed: 2.9 },
    { month: 'Aug', totalDeposits: 4.1, totalBorrowed: 3.0 },
    { month: 'Sep', totalDeposits: 4.3, totalBorrowed: 3.1 },
    { month: 'Oct', totalDeposits: 4.5, totalBorrowed: 3.2 },
    { month: 'Nov', totalDeposits: 4.7, totalBorrowed: 3.3 },
    { month: 'Dec', totalDeposits: 4.6, totalBorrowed: 3.2 },
    { month: 'Jan', totalDeposits: 4.4, totalBorrowed: 3.1 },
    { month: 'Feb', totalDeposits: 4.3, totalBorrowed: 3.0 },
    { month: 'Mar', totalDeposits: 4.5, totalBorrowed: 3.2 },
    { month: 'Apr', totalDeposits: 4.8, totalBorrowed: 3.4 },
    { month: 'May', totalDeposits: 4.7, totalBorrowed: 3.3 },
  ],
};

const chartConfig = {
  totalDeposits: {
    label: 'Total Deposits',
    color: 'hsl(264, 82%, 70%)',
  },
  totalBorrowed: {
    label: 'Total Borrowed',
    color: 'hsl(172, 82%, 60%)',
  },
} satisfies ChartConfig;

// Custom Tooltip
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    dataKey: string;
    value: number;
    color: string;
  }>;
  label?: string;
}

const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
  if (active && payload && payload.length) {
    // Filter to unique dataKeys to avoid duplicates from Area + Line components
    const uniquePayload = payload.filter(
      (entry, index, self) => index === self.findIndex((item) => item.dataKey === entry.dataKey),
    );

    return (
      <div className="rounded-lg bg-zinc-800 border border-zinc-700 text-white p-3 shadow-lg">
        <div className="text-xs text-zinc-400 mb-2">{label}</div>
        {uniquePayload.map((entry, index) => (
          <div key={index} className="flex items-center gap-2 mb-1">
            <div className="w-2 h-2 rounded-full" style={{ backgroundColor: entry.color }} />
            <span className="text-sm text-zinc-300">
              {entry.dataKey === 'totalDeposits' ? 'Total Deposits' : 'Total Borrowed'}:
            </span>
            <span className="font-semibold">${entry.value.toFixed(2)}M</span>
          </div>
        ))}
      </div>
    );
  }
  return null;
};

// Period configuration
const PERIODS = {
  day: { key: 'day', label: 'Day' },
  week: { key: 'week', label: 'Week' },
  month: { key: 'month', label: 'Month' },
} as const;

type PeriodKey = keyof typeof PERIODS;

export default function AreaChart5() {
  const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('month');

  // Get data for selected period
  const currentData = financeData[selectedPeriod];

  // Calculate total values
  const latestData = currentData[currentData.length - 1];
  const totalValueLocked = latestData.totalDeposits + latestData.totalBorrowed;

  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="w-full rounded-3xl lg:max-w-4xl bg-zinc-950 border-zinc-800 text-white">
        <CardHeader className="min-h-auto gap-5 p-8 border-0">
          <CardHeading className="flex flex-wrap items-end gap-5">
            <div className="min-w-40 space-y-0.5 me-2.5">
              <div className="text-sm text-zinc-400 mb-1">Total Value Locked</div>
              <div className="text-3xl leading-none font-bold">${(totalValueLocked * 1000).toLocaleString()}.15</div>
            </div>
            <div className="flex items-center flex-wrap gap-2.5 mb-1.5">
              <div className="space-y-0.5 pe-10">
                <div
                  className="text-[11px] font-normal flex items-center gap-1.5"
                  style={{ color: chartConfig.totalDeposits.color }}
                >
                  <div
                    className="size-1.5 rounded-full "
                    style={{ backgroundColor: chartConfig.totalDeposits.color }}
                  />
                  Total Deposits
                </div>
                <div className="text-xl font-bold leading-none">
                  ${(latestData.totalDeposits * 1000).toLocaleString()}.43
                </div>
              </div>

              <div className="space-y-0.5">
                <div
                  className="text-[11px] font-normal flex items-center gap-1.5"
                  style={{ color: chartConfig.totalBorrowed.color }}
                >
                  <div
                    className="size-1.5 rounded-full "
                    style={{ backgroundColor: chartConfig.totalBorrowed.color }}
                  />
                  Total Borrowed
                </div>
                <div className="text-xl font-bold leading-none">
                  ${(latestData.totalBorrowed * 1000).toLocaleString()}.15
                </div>
              </div>
            </div>
          </CardHeading>
          <CardToolbar>
            <ToggleGroup
              type="single"
              value={selectedPeriod}
              onValueChange={(value) => value && setSelectedPeriod(value as PeriodKey)}
              className="bg-zinc-800 p-1 rounded-full"
            >
              {Object.values(PERIODS).map((period) => (
                <ToggleGroupItem
                  key={period.key}
                  value={period.key}
                  className="px-4 py-2 text-sm data-[state=on]:bg-zinc-700 data-[state=on]:text-white text-zinc-400 hover:bg-zinc-700 hover:text-white rounded-full"
                >
                  {period.label}
                </ToggleGroupItem>
              ))}
            </ToggleGroup>
          </CardToolbar>
        </CardHeader>

        <CardContent className="ps-2.5 pe-4.5">
          <div className="h-[400px] w-full">
            <ChartContainer
              config={chartConfig}
              className="h-full w-full overflow-visible [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
            >
              <ComposedChart
                data={currentData}
                margin={{
                  top: 25,
                  right: 25,
                  left: 15,
                  bottom: 25,
                }}
                style={{ overflow: 'visible' }}
              >
                <defs>
                  {/* Grid pattern */}
                  <pattern id="gridPattern" x="0" y="0" width="30" height="30" patternUnits="userSpaceOnUse">
                    <path
                      d="M 30 0 L 0 0 0 30"
                      fill="none"
                      stroke="rgb(51 65 85)"
                      strokeWidth="0.5"
                      strokeOpacity="0.3"
                    />
                  </pattern>

                  {/* Linear gradients for areas */}
                  <linearGradient id="depositsAreaGradient" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stopColor={chartConfig.totalDeposits.color} stopOpacity="0.3" />
                    <stop offset="100%" stopColor={chartConfig.totalDeposits.color} stopOpacity="0.02" />
                  </linearGradient>

                  <linearGradient id="borrowedAreaGradient" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stopColor={chartConfig.totalBorrowed.color} stopOpacity="0.3" />
                    <stop offset="100%" stopColor={chartConfig.totalBorrowed.color} stopOpacity="0.02" />
                  </linearGradient>

                  {/* Shadow filters for dots */}
                  <filter id="dotShadow" x="-100%" y="-100%" width="300%" height="300%">
                    <feDropShadow dx="2" dy="2" stdDeviation="3" floodColor="rgba(0,0,0,0.4)" />
                  </filter>
                  <filter id="activeDotShadow" x="-100%" y="-100%" width="300%" height="300%">
                    <feDropShadow dx="3" dy="4" stdDeviation="6" floodColor="rgba(0,0,0,0.6)" />
                  </filter>
                </defs>

                {/* Background grid */}
                <rect
                  x="0"
                  y="0"
                  width="100%"
                  height="100%"
                  fill="url(#gridPattern)"
                  style={{ pointerEvents: 'none' }}
                />

                <XAxis
                  dataKey="month"
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 12, fill: 'rgb(148 163 184)' }}
                  tickMargin={15}
                />

                <YAxis
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 12, fill: 'rgb(148 163 184)' }}
                  tickFormatter={(value) => `$${value.toFixed(1)}M`}
                  domain={['dataMin - 0.2', 'dataMax + 0.2']}
                  tickMargin={15}
                />

                <ChartTooltip content={<CustomTooltip />} />

                {/* Area fills with gradients */}
                <Area
                  type="monotone"
                  dataKey="totalDeposits"
                  stroke="transparent"
                  fill="url(#depositsAreaGradient)"
                  strokeWidth={0}
                  dot={false}
                />

                <Area
                  type="monotone"
                  dataKey="totalBorrowed"
                  stroke="transparent"
                  fill="url(#borrowedAreaGradient)"
                  strokeWidth={0}
                  dot={false}
                />

                {/* Line strokes on top */}
                <Line
                  type="monotone"
                  dataKey="totalDeposits"
                  stroke={chartConfig.totalDeposits.color}
                  strokeWidth={2}
                  dot={{
                    r: 4,
                    fill: chartConfig.totalDeposits.color,
                    stroke: 'white',
                    strokeWidth: 2,
                    filter: 'url(#dotShadow)',
                  }}
                  activeDot={{
                    r: 6,
                    fill: chartConfig.totalDeposits.color,
                    strokeWidth: 2,
                    stroke: 'white',
                    filter: 'url(#activeDotShadow)',
                  }}
                />

                <Line
                  type="monotone"
                  dataKey="totalBorrowed"
                  stroke={chartConfig.totalBorrowed.color}
                  strokeWidth={2}
                  dot={{
                    r: 4,
                    fill: chartConfig.totalBorrowed.color,
                    stroke: 'white',
                    strokeWidth: 2,
                    filter: 'url(#dotShadow)',
                  }}
                  activeDot={{
                    r: 6,
                    fill: chartConfig.totalBorrowed.color,
                    strokeWidth: 2,
                    stroke: 'white',
                    filter: 'url(#activeDotShadow)',
                  }}
                />
              </ComposedChart>
            </ChartContainer>
          </div>
        </CardContent>
      </Card>
    </div>
  );
}

Installation

npx shadcn@latest add @reui/area-chart-5

Usage

import { AreaChart5 } from "@/components/area-chart-5"
<AreaChart5 />