chart
Forecast line chart with stripe pattern zone
Forecast line chart with stripe pattern zone
Загрузка превью…
src/chart/c-chart-17.vue
<!--
Замена библиотеки: см. c-chart-13.vue / docs/PORTING.md §19/§27. Оригинал
использует `ComposedChart` (`Area` без обводки + `Line` поверх — два
разных recharts-примитива в одном холсте) — прямого аналога `ComposedChart`
у vue-chrts нет. `AreaChart` неявно рисует и область, и линию для каждой
категории одновременно (см. исходник `AreaChart.js`), что структурно
близко: одна категория "forecast" даёт и заливку, и линию за раз.
Диагональный SVG-паттерн заливки не переносится (та же причина, что у
c-chart-13.vue). `ChartLegend` в оригинале — всегда видимая (не по hover)
часть кадра, поэтому легенда воспроизведена через перенесённый
`ChartLegendContent` с вручную собранным `payload` (тот же приём, что в
кейсе `ui-chart`), а встроенная легенда `vue-chrts` скрыта.
-->
<script setup lang="ts">
import { AreaChart, CurveType } from "vue-chrts"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { ChartContainer, ChartLegendContent, type ChartConfig } from "@/components/ui/chart"
const chartData = [
{ month: "January", forecast: 2600 },
{ month: "February", forecast: 4200 },
{ month: "March", forecast: 2400 },
{ month: "April", forecast: 5000 },
{ month: "May", forecast: 2800 },
{ month: "June", forecast: 5800 },
{ month: "July", forecast: 3200 },
{ month: "August", forecast: 6200 },
{ month: "September", forecast: 3800 },
]
const chartConfig = {
forecast: { label: "Forecast", color: "var(--chart-4)" },
} satisfies ChartConfig
const categories = { forecast: { name: "Forecast", color: "var(--chart-4)" } }
const legendPayload = [{ dataKey: "forecast", value: "forecast", color: "var(--chart-4)" }]
function xFormatter(tick: number) {
return chartData[tick]?.month.slice(0, 3) ?? ""
}
</script>
<template>
<Card class="w-full max-w-md">
<CardHeader>
<CardTitle>Sales Forecast</CardTitle>
<CardDescription>Projected sales performance with trends</CardDescription>
</CardHeader>
<CardContent>
<!--
`recharts`' `ChartLegend`/`.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`
(which already provides the chart context it needs via `useChart`)
instead of a second, normal-flow `ChartContainer`, to match that
same "overlay, don't grow the box" behavior instead of adding extra
page height.
-->
<ChartContainer :config="chartConfig" class="relative">
<AreaChart
:data="chartData"
:categories="categories"
:height="200"
:x-formatter="xFormatter"
:curve-type="CurveType.Natural"
:line-width="2.5"
:duration="0"
hide-tooltip
hide-legend
/>
<ChartLegendContent :payload="legendPayload" class="absolute inset-x-0 bottom-0 pt-0" />
</ChartContainer>
</CardContent>
</Card>
</template>