Charts
Recharts asosidagi grafiklar: tayyor AreaChart, BarChart, LineChart, DonutChart, Sparkline va o'z grafigingiz uchun primitivlar.
- Tushum
- Xarajat
'use client';
import { AreaChart } from '@akhatmirzo/ui/charts';
const sales = [
{ month: 'Yan', tushum: 42_500_000, xarajat: 28_300_000 },
{ month: 'Fev', tushum: 38_900_000, xarajat: 26_100_000 },
{ month: 'Mar', tushum: 51_200_000, xarajat: 30_400_000 },
{ month: 'Apr', tushum: 47_800_000, xarajat: 31_900_000 },
{ month: 'May', tushum: 58_300_000, xarajat: 33_200_000 },
{ month: 'Iyn', tushum: 63_100_000, xarajat: 35_700_000 },
];
/** Qisqa ko'rinish: 42,5 mln so'm */
const short = (v: number) => `${(v / 1_000_000).toFixed(1).replace('.', ',')} mln so'm`;
export default function ChartsArea() {
return (
<AreaChart
aria-label="Oylik tushum va xarajat, yanvar–iyun"
className="w-full"
data={sales}
index="month"
series={[
{ key: 'tushum', label: 'Tushum' },
{ key: 'xarajat', label: 'Xarajat', color: 'amber' },
]}
valueFormat={short}
height={280}
/>
);
}
O'rnatish
Grafiklar alohida kirish nuqtasida — @akhatmirzo/ui/charts. Ular Recharts
ustiga qurilgan, u esa ixtiyoriy peer bog'liqlik: grafik ishlatmasangiz, o'rnatish shart emas va
asosiy @akhatmirzo/ui importi uni tortmaydi.
pnpm add rechartsImport
import { AreaChart, BarChart, LineChart, DonutChart, Sparkline } from '@akhatmirzo/ui/charts';Qachon ishlatish
- Vaqt bo'yicha o'zgarish —
AreaChartyokiLineChart(oylik tushum, kunlik tashriflar). - Kategoriyalarni solishtirish —
BarChart(viloyatlar, mahsulotlar). Nomlar uzun bo'lsa —orientation="horizontal". - Butunning qismlari —
DonutChart, 5–6 bo'lakdan oshmasin; qolganini "Boshqalar"ga yig'ing. - Kichik trend —
SparklineKPI kartalar va jadval kataklarida. - Aniq qiymat muhim bo'lsa — grafik o'rniga yoki yonida jadval bering (pastda Accessibility).
Tayyor grafiklar
Hammasi bir xil props bilan ishlaydi: data — obyektlar ro'yxati, index — kategoriya maydoni
(x o'qi), series — ko'rsatiladigan raqamli maydonlar. Ranglar faqat tokenlardan olinadi, shuning
uchun dark rejimda o'zi moslashadi.
AreaChart
Yuqoridagi namuna. stacked — seriyalarni ustma-ust yig'adi, curve — monotone (standart),
linear yoki step.
BarChart
stacked — bitta ustunga yig'ish, umumiy hajmni va tarkibni birga ko'rsatadi.
- Onlayn
- Do'kondan
'use client';
import { formatNumber } from '@akhatmirzo/core';
import { BarChart } from '@akhatmirzo/ui/charts';
const orders = [
{ region: 'Toshkent', onlayn: 1240, dokon: 860 },
{ region: 'Samarqand', onlayn: 610, dokon: 540 },
{ region: "Farg'ona", onlayn: 480, dokon: 520 },
{ region: 'Buxoro', onlayn: 350, dokon: 290 },
{ region: 'Namangan', onlayn: 410, dokon: 380 },
];
export default function ChartsBar() {
return (
<BarChart
aria-label="Viloyatlar bo'yicha buyurtmalar: onlayn va do'kondan"
className="w-full"
data={orders}
index="region"
series={[
{ key: 'onlayn', label: 'Onlayn' },
{ key: 'dokon', label: "Do'kondan", color: 'sky' },
]}
valueFormat={(v) => `${formatNumber(v)} ta`}
stacked
/>
);
}
orientation="horizontal" — yotiq chiziqlar: uzun nomlar va reytinglar uchun qulay. Prop nomi
ko'rinishga qarab tanlangan (vertical — tik ustunlar) va ataylab Recharts'ning layout idan
farqli: Recharts'da layout="vertical" aksincha yotiq chiziqlar chizadi.
'use client';
import { BarChart } from '@akhatmirzo/ui/charts';
const products = [
{ name: 'Smartfonlar', tushum: 184_000_000 },
{ name: 'Noutbuklar', tushum: 142_500_000 },
{ name: 'Quloqchinlar', tushum: 61_200_000 },
{ name: 'Aqlli soatlar', tushum: 48_900_000 },
{ name: 'Aksessuarlar', tushum: 23_400_000 },
];
const short = (v: number) => `${Math.round(v / 1_000_000)} mln so'm`;
export default function ChartsBarHorizontal() {
return (
<BarChart
aria-label="Eng ko'p sotilgan toifalar, tushum bo'yicha"
className="w-full"
data={products}
index="name"
series={[{ key: 'tushum', label: 'Tushum', color: 'emerald' }]}
valueFormat={short}
orientation="horizontal"
legend={false}
height={240}
/>
);
}
LineChart
Bir nechta ko'rsatkichni solishtirish uchun. Bu yerda curve="linear".
- Yangi
- Qaytgan
'use client';
import { LineChart } from '@akhatmirzo/ui/charts';
const visits = [
{ day: 'Du', yangi: 320, qaytgan: 540 },
{ day: 'Se', yangi: 410, qaytgan: 580 },
{ day: 'Ch', yangi: 380, qaytgan: 610 },
{ day: 'Pa', yangi: 520, qaytgan: 640 },
{ day: 'Ju', yangi: 610, qaytgan: 700 },
{ day: 'Sh', yangi: 450, qaytgan: 520 },
{ day: 'Ya', yangi: 300, qaytgan: 410 },
];
export default function ChartsLine() {
return (
<LineChart
aria-label="Haftalik tashriflar: yangi va qaytgan foydalanuvchilar"
className="w-full"
data={visits}
index="day"
series={[
{ key: 'yangi', label: 'Yangi' },
{ key: 'qaytgan', label: 'Qaytgan', color: 'success' },
]}
curve="linear"
height={240}
/>
);
}
DonutChart
index — bo'lak nomi, value — qiymat maydoni. Markazda centerLabel va jami (valueFormat
bilan) ko'rinadi; centerValue bilan o'zgartiring yoki null bilan yashiring.
- Oziq-ovqat
- Transport
- Kommunal
- Ko'ngilochar
- Boshqalar
'use client';
import { formatMoney } from '@akhatmirzo/core';
import { DonutChart } from '@akhatmirzo/ui/charts';
const expenses = [
{ name: 'Oziq-ovqat', total: 3_850_000 },
{ name: 'Transport', total: 1_240_000 },
{ name: 'Kommunal', total: 960_000 },
{ name: "Ko'ngilochar", total: 720_000 },
{ name: 'Boshqalar', total: 430_000 },
];
const total = expenses.reduce((sum, e) => sum + e.total, 0);
export default function ChartsDonut() {
return (
<DonutChart
aria-label="Oktabr xarajatlari toifalar bo'yicha"
className="w-full max-w-sm"
data={expenses}
index="name"
value="total"
valueFormat={(v) => formatMoney(v)}
centerLabel="Jami, so'm"
centerValue={`${(total / 1_000_000).toFixed(1).replace('.', ',')} mln`}
height={220}
/>
);
}
Sparkline
O'qlar, to'r va tooltip'siz kichik chiziq. Qiymat yonida matn bilan berilgan bo'lsa — aria-hidden
bilan bezak sifatida belgilang, aks holda aria-label bering.
Tushum
63 100 000 so'm
Qaytarishlar
38 ta
'use client';
import { formatMoney } from '@akhatmirzo/core';
import { Card } from '@akhatmirzo/ui';
import { Sparkline } from '@akhatmirzo/ui/charts';
const kpis = [
{ title: 'Tushum', value: formatMoney(63_100_000), trend: [42, 39, 51, 48, 58, 63], up: true },
{ title: 'Qaytarishlar', value: '38 ta', trend: [52, 47, 49, 44, 41, 38], up: false },
];
export default function ChartsSparkline() {
return (
<div className="grid w-full max-w-xl gap-3 sm:grid-cols-2">
{kpis.map((kpi) => (
<Card key={kpi.title} padding="sm" className="gap-2">
<p className="text-sm text-text-muted">{kpi.title}</p>
<div className="flex items-end justify-between gap-3">
<p className="text-xl font-bold whitespace-nowrap tabular-nums">{kpi.value}</p>
{/* Qiymat matn bilan berilgan — grafik faqat bezak */}
<Sparkline
aria-hidden
data={kpi.trend}
color={kpi.up ? 'success' : 'danger'}
className="w-24"
/>
</div>
</Card>
))}
</div>
);
}
Formatlash
valueFormat — tooltip va qiymat o'qidagi ko'rinish (standart — formatNumber: 1 250 000),
indexFormat — kategoriya o'qi va tooltip sarlavhasi. Pul uchun formatMoney ni ishlating; o'qda
uzun qiymatlar joy egallasa, qisqa ko'rinish bering (42,5 mln so'm).
import { formatMoney } from '@akhatmirzo/core';
<BarChart valueFormat={(v) => formatMoney(v)} indexFormat={(d) => d.slice(0, 3)} … />Holatlar
loading — grafik o'rnida Skeleton (aria-busy), ma'lumot bo'sh bo'lsa — EmptyState
("Ma'lumot yo'q"). O'z bo'sh holatingizni emptyState bilan bering, matnlarni — labels bilan.
'use client';
import { SegmentedControl } from '@akhatmirzo/ui';
import { AreaChart } from '@akhatmirzo/ui/charts';
import { useState } from 'react';
const data = [
{ week: '1-hafta', buyurtma: 120 },
{ week: '2-hafta', buyurtma: 164 },
{ week: '3-hafta', buyurtma: 142 },
{ week: '4-hafta', buyurtma: 190 },
];
export default function ChartsStates() {
const [state, setState] = useState('ready');
return (
<div className="flex w-full flex-col gap-4">
<SegmentedControl value={state} onValueChange={setState} aria-label="Holat" size="sm">
<SegmentedControl.Item value="ready">Ma'lumot</SegmentedControl.Item>
<SegmentedControl.Item value="loading">Yuklanmoqda</SegmentedControl.Item>
<SegmentedControl.Item value="empty">Bo'sh</SegmentedControl.Item>
</SegmentedControl>
<AreaChart
aria-label="Haftalik buyurtmalar"
data={state === 'empty' ? [] : data}
index="week"
series={[{ key: 'buyurtma', label: 'Buyurtmalar' }]}
loading={state === 'loading'}
legend={false}
height={220}
/>
</div>
);
}
Primitivlar: o'z grafigingiz
Tayyor grafik yetmasa (ikki o'qli, aralash, maxsus shakllar), Recharts'ning o'zi bilan yig'ing — kutubxona uslubini primitivlar beradi:
ChartContainer— responsiv o'rovchi:role="figure"+aria-label(majburiy),description,config→ CSS o'zgaruvchilar (tushum→var(--chart-tushum)). Balandlik —heightyokiclassName="h-72"(standart 280px).ChartTooltip—<Tooltip content={<ChartTooltip format={formatMoney} />} />; nomlarniconfigdagilabeldan oladi.ChartLegend—<Legend content={<ChartLegend />} />yoki grafikdan tashqaridapayloadbilan.chartColors(5 ta seriya rangi),axisProps,gridProps,cursorProps,resolveChartColor.
'use client';
import {
ChartContainer,
ChartLegend,
ChartTooltip,
axisProps,
cursorProps,
gridProps,
type ChartConfig,
} from '@akhatmirzo/ui/charts';
import { Bar, CartesianGrid, ComposedChart, Legend, Line, Tooltip, XAxis, YAxis } from 'recharts';
const data = [
{ month: 'Yan', buyurtma: 820, konversiya: 2.4 },
{ month: 'Fev', buyurtma: 760, konversiya: 2.1 },
{ month: 'Mar', buyurtma: 940, konversiya: 2.8 },
{ month: 'Apr', buyurtma: 1010, konversiya: 3.1 },
{ month: 'May', buyurtma: 1180, konversiya: 3.4 },
];
// Har kalit → `var(--chart-<kalit>)`; label — tooltip va legend'da
const config: ChartConfig = {
buyurtma: { label: 'Buyurtmalar', color: 'primary' },
konversiya: { label: 'Konversiya, %', color: 'amber' },
};
export default function ChartsCustom() {
return (
<ChartContainer aria-label="Buyurtmalar va konversiya" config={config} className="h-72">
<ComposedChart data={data} margin={{ top: 8, right: 4, left: 0, bottom: 0 }}>
<CartesianGrid {...gridProps} />
<XAxis dataKey="month" {...axisProps} tickMargin={8} />
<YAxis yAxisId="left" {...axisProps} width="auto" />
<YAxis yAxisId="right" orientation="right" {...axisProps} width="auto" />
<Tooltip cursor={cursorProps} content={<ChartTooltip />} />
<Legend content={<ChartLegend className="pt-3" />} />
<Bar yAxisId="left" dataKey="buyurtma" fill="var(--chart-buyurtma)" radius={[6, 6, 0, 0]} />
<Line
yAxisId="right"
dataKey="konversiya"
stroke="var(--chart-konversiya)"
strokeWidth={2}
dot={false}
/>
</ComposedChart>
</ChartContainer>
);
}
Props
Tayyor grafiklar qolgan <div> atributlarini ildizga o'tkazadi (className, ref, id ...).
aria-label yoki title dan biri majburiy — TypeScript talab qiladi.
Umumiy (AreaChart, BarChart, LineChart)
| Prop | Tip | Standart | Tavsif |
|---|---|---|---|
datamajburiy | readonly T[] | — | Ma'lumotlar |
indexmajburiy | keyof T | — | Kategoriya (x o'qi) maydoni |
seriesmajburiy | { key: keyof T; label?: string; color?: ChartColor }[] | — | Seriyalar. Rang berilmasa — chartColors palitrasidan ketma-ket |
aria-label / titlemajburiy | string | — | Ekran o'quvchi uchun nom (biri yetarli) |
description | ReactNode | — | Qisqa xulosa (aria-describedby) |
valueFormat | (value: number) => string | formatNumber | Tooltip va qiymat o'qi |
indexFormat | (value: T[K]) => string | — | Kategoriya o'qi va tooltip sarlavhasi |
height | number | 280 | Balandlik, px |
legend | boolean | true | |
grid | boolean | true | Punktir to'r chiziqlari |
tooltip | boolean | true | |
loading | boolean | false | Skeleton ko'rsatadi |
emptyState | ReactNode | — | Bo'sh holat. Standart — EmptyState ("Ma'lumot yo'q") |
labels | { empty?: string; loading?: string } | { empty: "Ma'lumot yo'q", loading: 'Yuklanmoqda' } |
Qo'shimcha
| Prop | Tip | Standart | Tavsif |
|---|---|---|---|
stacked | boolean | false | AreaChart, BarChart — seriyalarni yig'ish |
curve | 'monotone' | 'linear' | 'step' | 'monotone' | AreaChart, LineChart |
orientation | 'vertical' | 'horizontal' | 'vertical' | BarChart: vertical — tik ustunlar, horizontal — yotiq chiziqlar |
DonutChart
data, aria-label/title, description, valueFormat, indexFormat, height, legend,
tooltip, loading, emptyState, labels — yuqoridagidek (series va grid yo'q).
| Prop | Tip | Standart | Tavsif |
|---|---|---|---|
indexmajburiy | keyof T | — | Bo'lak nomi maydoni |
valuemajburiy | keyof T | — | Bo'lak qiymati maydoni |
colors | ChartColor[] | — | Ranglar ketma-ketligi. Standart — chartColors |
centerLabel | ReactNode | — | Markazdagi izoh ("Jami") |
centerValue | ReactNode | valueFormat(jami) | Markazdagi qiymat; null — ko'rsatilmaydi |
Sparkline
| Prop | Tip | Standart | Tavsif |
|---|---|---|---|
datamajburiy | readonly number[] | — | |
aria-label / title / aria-hiddenmajburiy | string | true | — | Nom yoki bezak belgisi |
color | ChartColor | 'primary' | |
variant | 'area' | 'line' | 'area' | |
curve | 'monotone' | 'linear' | 'step' | 'monotone' | |
height | number | 36 | Balandlik, px |
ChartContainer
| Prop | Tip | Standart | Tavsif |
|---|---|---|---|
childrenmajburiy | ReactNode | — | Recharts grafigi |
aria-label / titlemajburiy | string | — | Ekran o'quvchi uchun nom |
config | Record<string, { label?: ReactNode; color?: ChartColor }> | — | Kalit → var(--chart-<kalit>), label — tooltip va legend uchun |
height | number | string | — | Standart — 280px (h-70) |
description | ReactNode | — | Qisqa xulosa (aria-describedby) |
ChartColor — token nomi (primary, success, warning, danger, info, neutral, sky,
emerald, amber, rose) yoki istalgan CSS qiymat (var(--ak-indigo-400)). Hex dark rejimga
moslashmaydi — tokenlarni afzal ko'ring.
Data atributlari
Tayyor grafik ildizida: data-state (ready · loading · empty), data-loading.
Accessibility
- Nom majburiy — har grafik
role="figure"vaaria-label(yokititle) oladi (imgemas: ichidagi klaviatura qatlami interaktiv,imgesa bolalarini yashiradi;Sparkline—img). Nomda nima ko'rsatilganini va davrni yozing: "Oylik tushum, yanvar–iyun". - Xulosa — asosiy xulosani
descriptionga yozing ("Iyunda tushum 63 mln so'mga yetdi"), uaria-describedbyorqali o'qiladi. - Klaviatura — Recharts
accessibilityLayeryoqilgan: grafikka Tab bilan o'tib,←→strelkalari bilan nuqtalar orasida yurasiz, tooltip ochiladi.Sparklineda tooltip yo'q, shuning uchun u fokus olmaydi. - Jadval ko'rinishini ham bering — grafik aniq qiymatni o'qishga mos emas. Muhim hisobotlarda "Grafik / Jadval" almashtirgichi yoki grafik ostida jadval qo'ying:
'use client';
import { formatMoney } from '@akhatmirzo/core';
import { SegmentedControl } from '@akhatmirzo/ui';
import { BarChart } from '@akhatmirzo/ui/charts';
import { useState } from 'react';
const data = [
{ quarter: 'I chorak', tushum: 132_600_000 },
{ quarter: 'II chorak', tushum: 169_200_000 },
{ quarter: 'III chorak', tushum: 151_800_000 },
];
export default function ChartsTableView() {
const [view, setView] = useState('chart');
return (
<div className="flex w-full flex-col gap-4">
<SegmentedControl value={view} onValueChange={setView} aria-label="Ko'rinish" size="sm">
<SegmentedControl.Item value="chart">Grafik</SegmentedControl.Item>
<SegmentedControl.Item value="table">Jadval</SegmentedControl.Item>
</SegmentedControl>
{view === 'chart' ? (
<BarChart
aria-label="Choraklar bo'yicha tushum"
data={data}
index="quarter"
series={[{ key: 'tushum', label: 'Tushum' }]}
valueFormat={(v) => formatMoney(v)}
legend={false}
height={220}
/>
) : (
<table className="w-full text-sm">
<caption className="sr-only">Choraklar bo'yicha tushum</caption>
<thead className="text-left text-text-muted">
<tr>
<th className="py-2 font-medium">Chorak</th>
<th className="py-2 text-right font-medium">Tushum</th>
</tr>
</thead>
<tbody>
{data.map((row) => (
<tr key={row.quarter} className="border-t border-border">
<td className="py-2">{row.quarter}</td>
<td className="py-2 text-right tabular-nums">{formatMoney(row.tushum)}</td>
</tr>
))}
</tbody>
</table>
)}
</div>
);
}
- Rang yagona signal emas — legend va tooltip seriya nomini matn bilan ko'rsatadi.
- Harakat —
prefers-reduced-motionyoqilgan bo'lsa, Recharts kirish animatsiyasini o'chiradi.