chart

Donut chart with center stats

Donut chart with center stats

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

src/chart/c-chart-20.vue

<!-- Замена библиотеки: см. c-chart-19.vue / docs/PORTING.md §19/§27. -->
<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 = [
  { status: "completed", count: 186 },
  { status: "inProgress", count: 94 },
  { status: "pending", count: 62 },
  { status: "cancelled", count: 28 },
]

const totalTasks = chartData.reduce((sum, d) => sum + d.count, 0)
const completionRate = Math.round((chartData[0]!.count / totalTasks) * 100)

const chartConfig = {
  count: { label: "Tasks" },
  completed: { label: "Completed", color: "var(--chart-1)" },
  inProgress: { label: "In Progress", color: "var(--chart-2)" },
  pending: { label: "Pending", color: "var(--chart-3)" },
  cancelled: { label: "Cancelled", color: "var(--chart-5)" },
} satisfies ChartConfig

const categories = {
  completed: { name: "Completed", color: "var(--chart-1)" },
  inProgress: { name: "In Progress", color: "var(--chart-2)" },
  pending: { name: "Pending", color: "var(--chart-3)" },
  cancelled: { name: "Cancelled", color: "var(--chart-5)" },
}

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

const legendPayload = [
  { dataKey: "completed", value: "completed", color: "var(--chart-1)", status: "completed" },
  { dataKey: "inProgress", value: "inProgress", color: "var(--chart-2)", status: "inProgress" },
  { dataKey: "pending", value: "pending", color: "var(--chart-3)", status: "pending" },
  { dataKey: "cancelled", value: "cancelled", color: "var(--chart-5)", status: "cancelled" },
]
</script>

<template>
  <Card class="w-full max-w-xs">
    <CardHeader class="items-center pb-0">
      <CardTitle>Task Status</CardTitle>
      <CardDescription>Current sprint task breakdown</CardDescription>
    </CardHeader>
    <CardContent class="flex-1 pb-0">
      <!--
        `recharts`' `.recharts-legend-wrapper` is `position: absolute` inside
        the SAME fixed-height chart box (verified via getComputedStyle on the
        upstream render) — it overlays the plot area rather than adding to
        layout height. `vue-chrts` has no equivalent composable legend slot,
        so `ChartLegendContent` is placed here as an absolutely-positioned
        sibling inside the same `ChartContainer` (already providing the
        chart context it needs via `useChart`) instead of a second,
        normal-flow `ChartContainer`, matching that overlay behavior instead
        of adding extra page height.
      -->
      <ChartContainer :config="chartConfig" class="relative mx-auto aspect-square max-h-[280px]">
        <DonutChart
          :data="donutData"
          :categories="categories"
          :radius="95"
          :arc-width="35"
          :pad-angle="0.02"
          :height="280"
          :duration="0"
          hide-tooltip
          hide-legend
        >
          <div class="flex flex-col items-center">
            <span class="fill-foreground text-3xl font-bold tabular-nums">{{ completionRate }}%</span>
            <span class="fill-muted-foreground text-xs">Completed</span>
          </div>
        </DonutChart>
        <ChartLegendContent :payload="legendPayload" name-key="status" 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-20.json

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

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

  • vue-chrts

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