chart

Active segment donut with center label

Active segment donut with center label

Загрузка превью…

src/chart/c-chart-22.vue

<!--
  Замена библиотеки: см. c-chart-19.vue / docs/PORTING.md §19/§27.
  `activeShape` (увеличение сегмента по hover) не переносится — состояние
  не проверяется статичным скриншотом.
-->
<script setup lang="ts">
import { DonutChart } from "vue-chrts"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { ChartContainer, ChartLegendContent, type ChartConfig } from "@/components/ui/chart"

const chartData = [
  { plan: "free", users: 12800 },
  { plan: "starter", users: 5400 },
  { plan: "pro", users: 3600 },
  { plan: "enterprise", users: 1200 },
]

const totalUsers = chartData.reduce((sum, d) => sum + d.users, 0)
const paidUsers = totalUsers - chartData[0]!.users
const conversionRate = ((paidUsers / totalUsers) * 100).toFixed(1)

const chartConfig = {
  users: { label: "Users" },
  free: { label: "Free", color: "var(--chart-5)" },
  starter: { label: "Starter", color: "var(--chart-3)" },
  pro: { label: "Pro", color: "var(--chart-2)" },
  enterprise: { label: "Enterprise", color: "var(--chart-1)" },
} satisfies ChartConfig

const categories = {
  free: { name: "Free", color: "var(--chart-5)" },
  starter: { name: "Starter", color: "var(--chart-3)" },
  pro: { name: "Pro", color: "var(--chart-2)" },
  enterprise: { name: "Enterprise", color: "var(--chart-1)" },
}

const donutData = chartData.map((d) => d.users)

const legendPayload = [
  { dataKey: "free", value: "free", color: "var(--chart-5)", plan: "free" },
  { dataKey: "starter", value: "starter", color: "var(--chart-3)", plan: "starter" },
  { dataKey: "pro", value: "pro", color: "var(--chart-2)", plan: "pro" },
  { dataKey: "enterprise", value: "enterprise", color: "var(--chart-1)", plan: "enterprise" },
]
</script>

<template>
  <Card class="w-full max-w-xs">
    <CardHeader class="items-center pb-0">
      <CardTitle>Conversion Funnel</CardTitle>
      <CardDescription>{{ conversionRate }}% of users are on paid plans</CardDescription>
    </CardHeader>
    <CardContent class="flex-1 pb-0">
      <!--
        `recharts`' `.recharts-legend-wrapper` is `position: absolute` inside
        the SAME fixed-height chart box — see c-chart-20.vue for the full
        explanation. Placed as an absolute sibling in the same
        `ChartContainer` instead of a second, normal-flow one.
      -->
      <ChartContainer :config="chartConfig" class="relative mx-auto aspect-square max-h-[300px]">
        <DonutChart
          :data="donutData"
          :categories="categories"
          :radius="100"
          :arc-width="45"
          :pad-angle="0.02"
          :height="300"
          :duration="0"
          hide-tooltip
          hide-legend
        >
          <div class="flex flex-col items-center">
            <span class="fill-foreground text-2xl font-bold tabular-nums">{{ paidUsers.toLocaleString() }}</span>
            <span class="fill-muted-foreground text-xs">Paid Users</span>
          </div>
        </DonutChart>
        <ChartLegendContent :payload="legendPayload" name-key="plan" class="absolute inset-x-0 bottom-0 -translate-y-2 pt-0" />
      </ChartContainer>
    </CardContent>
  </Card>
</template>

Установка

npx shadcn-vue@latest add https://revueui.rootapi.dev/r/c-chart-22.json

Зависимости реестра

npm-зависимости

  • vue-chrts

Источник: порт из ReUI (Keenthemes, MIT)