chart

Filled radar with glowing stroke

Filled radar with glowing stroke

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

src/chart/c-chart-24.vue

<!--
  Пробел в API: см. c-chart-23.vue / docs/PORTING.md §19/§27 — у vue-chrts
  нет ни одного полярного графика. Минимальная самостоятельная SVG-паутина.
-->
<script setup lang="ts">
import { computed } from "vue"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { ChartContainer, type ChartConfig } from "@/components/ui/chart"

const chartData = [
  { metric: "Speed", value: 88 },
  { metric: "Reliability", value: 94 },
  { metric: "Scalability", value: 72 },
  { metric: "Cost", value: 68 },
  { metric: "Security", value: 82 },
  { metric: "DX", value: 78 },
]

const chartConfig = {
  value: { label: "Score", color: "var(--chart-2)" },
} satisfies ChartConfig

const SIZE = 280
const CENTER = SIZE / 2
const RADIUS = 100
const MAX = 100
const LEVELS = 4

function pointAt(index: number, total: number, fraction: number) {
  const angle = (Math.PI * 2 * index) / total - Math.PI / 2
  return {
    x: CENTER + Math.cos(angle) * RADIUS * fraction,
    y: CENTER + Math.sin(angle) * RADIUS * fraction,
  }
}

const gridPolygons = computed(() =>
  Array.from({ length: LEVELS }, (_, level) => {
    const fraction = (level + 1) / LEVELS
    return chartData
      .map((_, i) => {
        const p = pointAt(i, chartData.length, fraction)
        return `${p.x},${p.y}`
      })
      .join(" ")
  })
)

const spokes = computed(() => chartData.map((_, i) => pointAt(i, chartData.length, 1)))

const dataPolygon = computed(() =>
  chartData
    .map((d, i) => {
      const p = pointAt(i, chartData.length, d.value / MAX)
      return `${p.x},${p.y}`
    })
    .join(" ")
)

const dataPoints = computed(() => chartData.map((d, i) => pointAt(i, chartData.length, d.value / MAX)))

const labels = computed(() =>
  chartData.map((d, i) => ({ ...pointAt(i, chartData.length, 1.18), label: d.metric }))
)
</script>

<template>
  <Card class="w-full max-w-xs">
    <CardHeader class="items-center pb-0">
      <CardTitle>Infrastructure Score</CardTitle>
      <CardDescription>Platform performance metrics</CardDescription>
    </CardHeader>
    <CardContent class="flex-1 pb-0">
      <ChartContainer :config="chartConfig" class="mx-auto aspect-square max-h-[280px]">
        <svg :viewBox="`0 0 ${SIZE} ${SIZE}`" class="h-full w-full">
          <defs>
            <linearGradient id="chart24-fill" x1="0" y1="0" x2="1" y2="1">
              <stop offset="0%" stop-color="var(--color-value)" stop-opacity="0.45" />
              <stop offset="100%" stop-color="var(--color-value)" stop-opacity="0.1" />
            </linearGradient>
          </defs>
          <polygon
            v-for="(poly, i) in gridPolygons"
            :key="i"
            :points="poly"
            fill="none"
            stroke="var(--border)"
            stroke-dasharray="3 3"
          />
          <line
            v-for="(p, i) in spokes"
            :key="i"
            :x1="CENTER"
            :y1="CENTER"
            :x2="p.x"
            :y2="p.y"
            stroke="var(--border)"
            stroke-dasharray="3 3"
          />
          <polygon :points="dataPolygon" fill="url(#chart24-fill)" stroke="var(--color-value)" stroke-width="2.5" />
          <circle
            v-for="(p, i) in dataPoints"
            :key="i"
            :cx="p.x"
            :cy="p.y"
            r="4"
            fill="var(--background)"
            stroke="var(--color-value)"
            stroke-width="2.5"
          />
          <text
            v-for="(l, i) in labels"
            :key="i"
            :x="l.x"
            :y="l.y"
            text-anchor="middle"
            dominant-baseline="middle"
            font-size="11"
            fill="var(--muted-foreground)"
          >{{ l.label }}</text>
        </svg>
      </ChartContainer>
    </CardContent>
  </Card>
</template>

Установка

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

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

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