Chart
Graficos bonitos. Construidos com Recharts. Copie e cole nos seus apps.
Nota: Estamos trabalhando na atualizacao para o Recharts v3. Enquanto isso, se quiser comecar a testar o v3, veja o codigo no comentario aqui. Em breve teremos um lancamento oficial.
"use client";
import {
type ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@blips/ui/components/chart";
import { Bar, BarChart, XAxis, YAxis } from "recharts";
const chartData = [
{ month: "January", desktop: 186 },
{ month: "February", desktop: 305 },
{ month: "March", desktop: 237 },
{ month: "April", desktop: 73 },
{ month: "May", desktop: 209 },
{ month: "June", desktop: 214 },
];
const chartConfig = {
desktop: { label: "Desktop", color: "hsl(var(--chart-1))" },
} satisfies ChartConfig;
export default function ChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart data={chartData}>
<XAxis
dataKey="month"
tickLine={false}
tickMargin={10}
axisLine={false}
tickFormatter={(value) => value.slice(0, 3)}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
</BarChart>
</ChartContainer>
);
}
Apresentamos os Charts. Uma colecao de componentes de grafico que voce pode copiar e colar nos seus apps.
Os graficos foram projetados para ficar otimos imediatamente. Eles funcionam bem com os outros componentes e sao totalmente personalizaveis para se adequar ao seu projeto.
Navegue pela Biblioteca de Graficos.
Componente
Usamos o Recharts por baixo dos panos.
Projetamos o componente chart pensando em composicao. Voce constroi seus graficos usando componentes do Recharts e so traz componentes customizados, como o ChartTooltip, quando e onde precisar.
import { Bar, BarChart } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@blips/ui/components/chart"
export function MyChart() {
return (
<ChartContainer>
<BarChart data={data}>
<Bar dataKey="value" />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
</ChartContainer>
)
}Nao fazemos wrap do Recharts. Isso significa que voce nao fica preso a uma abstracao. Quando uma nova versao do Recharts for lancada, voce pode seguir o caminho oficial de upgrade para atualizar seus graficos.
Os componentes sao seus.
Instalacao
Seu Primeiro Grafico
Vamos construir seu primeiro grafico. Vamos montar um grafico de barras, adicionar uma grade, eixos, tooltip e legenda.
Comece definindo seus dados
Os dados a seguir representam o numero de usuarios de desktop e mobile para cada mes.
Nota: Seus dados podem ter qualquer formato. Voce nao esta limitado ao formato dos dados abaixo. Use a prop
dataKeypara mapear seus dados ao grafico.
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 },
]Defina a configuracao do grafico
A configuracao do grafico guarda as configuracoes do grafico. E aqui que voce coloca strings legiveis por humanos, como labels, icones e tokens de cor para temas.
import { type ChartConfig } from "@blips/ui/components/chart"
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
},
mobile: {
label: "Mobile",
color: "#60a5fa",
},
} satisfies ChartConfigConstrua seu grafico
Agora voce pode construir seu grafico usando componentes do Recharts.
Importante: Lembre-se de definir um
min-h-[VALUE]no componenteChartContainer. Isso e necessario para que o grafico seja responsivo.
Configuracao do Grafico
A configuracao do grafico e onde voce define as labels, os icones e as cores de um grafico.
Ela e intencionalmente desacoplada dos dados do grafico.
Isso permite que voce compartilhe a configuracao e os tokens de cor entre graficos. Tambem pode funcionar de forma independente para casos em que seus dados ou tokens de cor estao remotos ou em um formato diferente.
import { Monitor } from "@phosphor-icons/react"
import { type ChartConfig } from "@blips/ui/components/chart"
const chartConfig = {
desktop: {
label: "Desktop",
icon: Monitor,
// Uma cor como 'hsl(220, 98%, 61%)' ou 'var(--color-name)'
color: "#2563eb",
// OU um objeto de tema com chaves 'light' e 'dark'
theme: {
light: "#2563eb",
dark: "#dc2626",
},
},
} satisfies ChartConfigTemas
Os graficos tem suporte nativo a temas. Voce pode usar variaveis css (recomendado) ou valores de cor em qualquer formato, como hex, hsl ou oklch.
Tooltip
Um tooltip de grafico contem uma label, um name, um indicator e um value. Voce pode usar uma combinacao deles para personalizar seu tooltip.
Voce pode ativar/desativar qualquer um deles usando as props hideLabel e hideIndicator e personalizar o estilo do indicador usando a prop indicator.
Use labelKey e nameKey para usar uma chave customizada para a label e o name do tooltip.
O Chart vem com os componentes <ChartTooltip> e <ChartTooltipContent>. Voce pode usar esses dois componentes para adicionar tooltips customizados ao seu grafico.
import { ChartTooltip, ChartTooltipContent } from "@blips/ui/components/chart"<ChartTooltip content={<ChartTooltipContent />} />Props
Use as props a seguir para personalizar o tooltip.
Legenda
Voce pode usar os componentes customizados <ChartLegend> e <ChartLegendContent> para adicionar uma legenda ao seu grafico.
import { ChartLegend, ChartLegendContent } from "@blips/ui/components/chart"<ChartLegend content={<ChartLegendContent />} />Acessibilidade
Voce pode ativar a prop accessibilityLayer para adicionar uma camada acessivel ao seu grafico.
Essa prop adiciona acesso por teclado e suporte a leitores de tela aos seus graficos.
<LineChart accessibilityLayer />Exemplos
Grafico Basico
Construa seu primeiro grafico de barras usando componentes do Recharts.
"use client";
import { type ChartConfig, ChartContainer } from "@blips/ui/components/chart";
import { Bar, BarChart } from "recharts";
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 },
];
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
},
mobile: {
label: "Mobile",
color: "#60a5fa",
},
} satisfies ChartConfig;
export function ChartExample() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart accessibilityLayer data={chartData}>
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
</BarChart>
</ChartContainer>
);
}
Adicione uma Grade
Adicione uma grade ao grafico com o componente CartesianGrid.
"use client";
import { type ChartConfig, ChartContainer } from "@blips/ui/components/chart";
import { Bar, BarChart, CartesianGrid } from "recharts";
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 },
];
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
},
mobile: {
label: "Mobile",
color: "#60a5fa",
},
} satisfies ChartConfig;
export function ChartBarDemoGrid() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
</BarChart>
</ChartContainer>
);
}
Adicione um Eixo
Adicione um eixo x ao grafico usando o componente XAxis.
"use client";
import { type ChartConfig, ChartContainer } from "@blips/ui/components/chart";
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 },
];
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
},
mobile: {
label: "Mobile",
color: "#60a5fa",
},
} satisfies ChartConfig;
export function ChartBarDemoAxis() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis
dataKey="month"
tickLine={false}
tickMargin={10}
axisLine={false}
tickFormatter={(value) => value.slice(0, 3)}
/>
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
</BarChart>
</ChartContainer>
);
}
Adicione um Tooltip
Use os componentes ChartTooltip e ChartTooltipContent para adicionar um tooltip customizado.
"use client";
import {
type ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@blips/ui/components/chart";
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 },
];
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
},
mobile: {
label: "Mobile",
color: "#60a5fa",
},
} satisfies ChartConfig;
export function ChartBarDemoTooltip() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis
dataKey="month"
tickLine={false}
tickMargin={10}
axisLine={false}
tickFormatter={(value) => value.slice(0, 3)}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
</BarChart>
</ChartContainer>
);
}
Adicione uma Legenda
Use os componentes ChartLegend e ChartLegendContent para adicionar uma legenda ao grafico.
"use client";
import {
type ChartConfig,
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
} from "@blips/ui/components/chart";
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 },
];
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
},
mobile: {
label: "Mobile",
color: "#60a5fa",
},
} satisfies ChartConfig;
export function ChartBarDemoLegend() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis
dataKey="month"
tickLine={false}
tickMargin={10}
axisLine={false}
tickFormatter={(value) => value.slice(0, 3)}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
</BarChart>
</ChartContainer>
);
}
Tooltip
Um tooltip contem uma label, um name, um indicator e um value que voce pode combinar para personaliza-lo.
"use client";
import { cn } from "@blips/ui/lib/utils";
import type * as React from "react";
export function ChartTooltipDemo() {
return (
<div className="grid aspect-video w-full max-w-md justify-center text-foreground md:grid-cols-2 [&>div]:relative [&>div]:flex [&>div]:h-[137px] [&>div]:w-[224px] [&>div]:items-center [&>div]:justify-center [&>div]:p-4">
<div>
<div className="absolute top-[45px] left-[-35px] z-10 text-sm font-medium">
Label
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 193 40"
width="50"
height="12"
fill="none"
className="absolute top-[50px] left-[5px] z-10"
>
<g clipPath="url(#a)">
<path
fill="currentColor"
d="M173.928 21.13C115.811 44.938 58.751 45.773 0 26.141c4.227-4.386 7.82-2.715 10.567-1.88 21.133 5.64 42.9 6.266 64.457 7.101 31.066 1.253 60.441-5.848 89.183-17.335 1.268-.418 2.325-1.253 4.861-2.924-14.582-2.924-29.165 2.089-41.845-3.76.212-.835.212-1.879.423-2.714 9.51-.627 19.231-1.253 28.742-2.089 9.51-.835 18.808-1.88 28.318-2.506 6.974-.418 9.933 2.924 7.397 9.19-3.17 8.145-7.608 15.664-11.623 23.391-.423.836-1.057 1.88-1.902 2.298-2.325.835-4.65 1.044-7.186 1.67-.422-2.088-1.479-4.386-1.268-6.265.423-2.506 1.902-4.595 3.804-9.19Z"
/>
</g>
<defs>
<clipPath id="a">
<path fill="currentColor" d="M0 0h193v40H0z" />
</clipPath>
</defs>
</svg>
<TooltipDemo
label="Page Views"
payload={[
{ name: "Desktop", value: 186, fill: "var(--chart-1)" },
{ name: "Mobile", value: 80, fill: "var(--chart-2)" },
]}
className="w-[8rem]"
/>
</div>
<div className="items-end">
<div className="absolute top-[0px] left-[122px] z-10 text-sm font-medium">
Name
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
width="35"
height="42"
fill="none"
viewBox="0 0 122 148"
className="absolute top-[10px] left-[85px] z-10 -scale-x-100"
>
<g clipPath="url(#ab)">
<path
fill="currentColor"
d="M0 2.65c6.15-4.024 12.299-2.753 17.812-.847a115.56 115.56 0 0 1 21.84 10.59C70.4 32.727 88.849 61.744 96.483 97.54c1.908 9.108 2.544 18.639 3.817 29.017 8.481-4.871 12.934-14.402 21.416-19.909 1.061 4.236-1.06 6.989-2.756 9.319-6.998 9.531-14.207 19.062-21.63 28.382-3.604 4.448-6.36 4.871-10.177 1.059-8.058-7.837-12.935-17.368-14.42-28.382 0-.424.636-1.059 1.485-2.118 9.118 2.33 6.997 13.979 14.843 18.215 3.393-14.614.848-28.593-2.969-42.149-4.029-14.19-9.33-27.746-17.812-39.82-8.27-11.86-18.66-21.392-30.11-30.287C26.93 11.758 14.207 6.039 0 2.65Z"
/>
</g>
<defs>
<clipPath id="ab">
<path fill="currentColor" d="M0 0h122v148H0z" />
</clipPath>
</defs>
</svg>
<TooltipDemo
label="Browser"
hideLabel
payload={[
{ name: "Chrome", value: 1286, fill: "var(--chart-3)" },
{ name: "Firefox", value: 1000, fill: "var(--chart-4)" },
]}
indicator="dashed"
className="w-[8rem]"
/>
</div>
<div className="hidden! md:flex!">
<TooltipDemo
label="Page Views"
payload={[{ name: "Desktop", value: 12486, fill: "var(--chart-3)" }]}
className="w-[9rem]"
indicator="line"
/>
</div>
<div className="items-start! justify-start!">
<div className="absolute top-[60px] left-[50px] z-10 text-sm font-medium">
Indicator
</div>
<TooltipDemo
label="Browser"
hideLabel
payload={[{ name: "Chrome", value: 1286, fill: "var(--chart-1)" }]}
indicator="dot"
className="w-[8rem]"
/>
<svg
xmlns="http://www.w3.org/2000/svg"
width="15"
height="34"
fill="none"
viewBox="0 0 75 175"
className="absolute top-[38px] left-[30px] z-10 rotate-[-40deg]"
>
<g clipPath="url(#abc)">
<path
fill="currentColor"
d="M20.187 175c-4.439-2.109-7.186-2.531-8.032-4.008-3.17-5.484-6.763-10.968-8.454-17.084-5.073-16.242-4.439-32.694-1.057-49.146 5.707-28.053 18.388-52.942 34.24-76.565 1.692-2.531 3.171-5.063 4.862-7.805 0-.21-.211-.632-.634-1.265-4.65 1.265-9.511 2.53-14.161 3.585-2.537.422-5.496.422-8.032-.421-1.48-.422-3.593-2.742-3.593-4.219 0-1.898 1.48-4.218 2.747-5.906 1.057-1.054 2.96-1.265 4.65-1.687C35.406 7.315 48.088 3.729 60.98.776c10.99-2.53 14.584 1.055 13.95 11.812-.634 11.18-.846 22.358-1.268 33.326-.212 3.375-.846 6.96-1.268 10.757-8.878-4.007-8.878-4.007-12.048-38.177C47.03 33.259 38.153 49.289 29.91 65.741 21.667 82.193 16.17 99.49 13.212 117.84c-2.959 18.984.634 36.912 6.975 57.161Z"
/>
</g>
<defs>
<clipPath id="abc">
<path fill="currentColor" d="M0 0h75v175H0z" />
</clipPath>
</defs>
</svg>
</div>
</div>
);
}
function TooltipDemo({
indicator = "dot",
label,
payload,
hideLabel,
hideIndicator,
className,
}: {
label: string;
hideLabel?: boolean;
hideIndicator?: boolean;
indicator?: "line" | "dot" | "dashed";
payload: {
name: string;
value: number;
fill: string;
}[];
nameKey?: string;
labelKey?: string;
} & React.ComponentProps<"div">) {
const tooltipLabel = hideLabel ? null : (
<div className="font-medium">{label}</div>
);
if (!payload?.length) {
return null;
}
const nestLabel = payload.length === 1 && indicator !== "dot";
return (
<div
className={cn(
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl transition-all ease-in-out hover:-translate-y-0.5",
className
)}
>
{!nestLabel ? tooltipLabel : null}
<div className="grid gap-1.5">
{payload.map((item, index) => {
const indicatorColor = item.fill;
return (
<div
key={index}
className={cn(
"flex w-full items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
indicator === "dot" && "items-center"
)}
>
<>
{!hideIndicator && (
<div
className={cn(
"shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
{
"h-2.5 w-2.5": indicator === "dot",
"w-1": indicator === "line",
"w-0 border-[1.5px] border-dashed bg-transparent":
indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed",
}
)}
style={
{
"--color-bg": indicatorColor,
"--color-border": indicatorColor,
} as React.CSSProperties
}
/>
)}
<div
className={cn(
"flex flex-1 justify-between leading-none",
nestLabel ? "items-end" : "items-center"
)}
>
<div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground">{item.name}</span>
</div>
<span className="font-mono font-medium text-foreground tabular-nums">
{item.value.toLocaleString()}
</span>
</div>
</>
</div>
);
})}
</div>
</div>
);
}