Area Chart

PreviousNext

Beautiful area chart for data visualization with retro styling

Docs
retrouicomponent

Preview

Loading preview…
components/retroui/charts/AreaChart.tsx
"use client"

import { cn } from "@/lib/utils"
import React from "react"
import {
  Area,
  AreaChart as RechartsAreaChart,
  CartesianGrid,
  ResponsiveContainer,
  Tooltip,
  XAxis,
  YAxis,
} from "recharts"

interface AreaChartProps extends React.HTMLAttributes<HTMLDivElement> {
  data: Record<string, any>[]
  index: string
  categories: string[]
  strokeColors?: string[]
  fillColors?: string[]
  tooltipBgColor?: string
  tooltipBorderColor?: string
  gridColor?: string
  valueFormatter?: (value: number) => string
  showGrid?: boolean
  showTooltip?: boolean
  fill?: "gradient" | "solid"
  className?: string
}

const AreaChart = React.forwardRef<HTMLDivElement, AreaChartProps>(
  (
    {
      data = [],
      index,
      categories = [],
      strokeColors = ["var(--foreground)"],
      fillColors = ["var(--primary)"],
      tooltipBgColor = "var(--background)",
      tooltipBorderColor = "var(--border)",
      gridColor = "var(--muted)",
      valueFormatter = (value: number) => value.toString(),
      showGrid = true,
      showTooltip = true,
      fill = "gradient",
      className,
      ...props
    },
    ref
  ) => {
    const chartId = React.useId()

    return (
      <div ref={ref} className={cn("h-80 w-full", className)} {...props}>
        <ResponsiveContainer width="100%" height="100%">
          <RechartsAreaChart data={data} margin={{ top: 10, right: 30, left: 0, bottom: 0 }}>
            <defs>
              {categories.map((category, index) => {
                const fillColor = fillColors[index] || fillColors[0]
                const gradientId = `gradient-${chartId}-${category}`
                return (
                  <linearGradient key={category} id={gradientId} x1="0" y1="0" x2="0" y2="1">
                    {fill === "gradient" ? (
                      <>
                        <stop offset="5%" stopColor={fillColor} stopOpacity={0.8} />
                        <stop offset="95%" stopColor={fillColor} stopOpacity={0} />
                      </>
                    ) : (
                      <stop stopColor={fillColor} stopOpacity={0.6} />
                    )}
                  </linearGradient>
                )
              })}
            </defs>
            
            {showGrid && (
              <CartesianGrid strokeDasharray="3 3" stroke={gridColor} />
            )}
            
            <XAxis 
              dataKey={index}
              axisLine={false}
              tickLine={false}
              className="text-xs fill-muted-foreground"
            />
            
            <YAxis
              axisLine={false}
              tickLine={false}
              className="text-xs fill-muted-foreground"
              tickFormatter={valueFormatter}
            />
            
            {showTooltip && (
              <Tooltip
                content={({ active, payload, label }) => {
                  if (!active || !payload?.length) return null
                  
                  return (
                    <div 
                      className="border p-2 shadow"
                      style={{ 
                        backgroundColor: tooltipBgColor,
                        borderColor: tooltipBorderColor 
                      }}
                    >
                      <div className="grid grid-cols-2 gap-2">
                        <div className="flex flex-col">
                          <span className="text-[0.70rem] uppercase text-muted-foreground">
                            {index}
                          </span>
                          <span className="font-bold text-muted-foreground">
                            {label}
                          </span>
                        </div>
                        {payload.map((entry, index) => (
                          <div key={index} className="flex flex-col">
                            <span className="text-[0.70rem] uppercase text-muted-foreground">
                              {entry.dataKey}
                            </span>
                            <span className="font-bold" style={{ color: strokeColors[0] }}>
                              {valueFormatter(entry.value as number)}
                            </span>
                          </div>
                        ))}
                      </div>
                    </div>
                  )
                }}
              />
            )}
            
            {categories.map((category, index) => {
              const strokeColor = strokeColors[index] || strokeColors[0]
              const gradientId = `gradient-${chartId}-${category}`
              
              return (
                <Area
                  key={category}
                  dataKey={category}
                  stroke={strokeColor}
                  fill={`url(#${gradientId})`}
                  strokeWidth={2}
                />
              )
            })}
          </RechartsAreaChart>
        </ResponsiveContainer>
      </div>
    )
  }
)

AreaChart.displayName = "AreaChart"

export { AreaChart, type AreaChartProps }

Installation

npx shadcn@latest add @retroui/area-chart

Usage

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