IlmHamroh
JavaScript Full-stack/21-qism. React: media, integratsiyalar, PWA4/32-dars24 daqiqa
Mundarija (29)

Recharts bilan React grafiklari: ustun, chiziq, maydon va doira diagrammalari

Qisqacha: Recharts — React komponentlaridan grafik yig'adigan kutubxona: <BarChart data={…}> ichiga <XAxis>, <YAxis>, <Tooltip> va <Bar dataKey="osh"> qo'yasiz — natija SVG. Ma'lumot oddiy obyektlar massivi, dataKey esa qaysi maydon chizilishini aytadi. Tooltip, Legend, o'qlar formatlash va klaviatura bilan yurish tayyor keladi. Narxi bor: vazifalar da bitta grafik uchun +103.4 KB gzip va qattiq tsconfig'da 12 ta tsc xatosi. Shuning uchun kanon oxirida o'z SVG grafigini (+0.3 KB) tanladi.

Bu darsda

  • BarChart, LineChart, AreaChart va PieChart ni ma'lumot massividan chizasiz.
  • Tooltip, Legend, tickFormatter bilan grafikni o'qiladigan qilasiz va o'z tooltip komponentingizni yozasiz.
  • ResponsiveContainer va responsive bilan grafikni konteyner eniga moslaysiz.
  • shadcn Charts nima ekanini va grafikning a11y talablarini bilasiz.
  • Kutubxona narxini o'lchab, qachon o'z SVG grafigi yaxshiroq ekanini asoslaysiz.

Oldin bilishingiz kerak: i18n II: o'zbek lotin/kirill, URL va type-safe kalitlar, Jadvallar I: TanStack Table asoslari, shadcn/ui II: real ilovada asosiy komponentlar, lazy va code splitting, React'da accessibility asoslari.

1. Nega bu kerak?

Jasur aka «Bahor» admin panelini ochdi: «Haftada qaysi kuni ko'p osh sotdik?». Jadvalda raqamlar bor — 32, 41, 28, 45, 60. Lekin ko'z jadvaldan "juma — eng ko'p" degan xulosani darhol chiqara olmaydi. Ustunli grafikda esa eng baland ustun birinchi qarashda ko'rinadi.

Grafikni o'zingiz chizish mumkin: SVG'da har ustun — <rect>, balandligi qiymatga mutanosib. Lekin o'qlar, chiziqlar, sichqoncha ustiga kelganda chiqadigan izoh, ranglar izohi, eni o'zgarganda qayta hisoblash — har biri o'nlab qator kod. Recharts shularni tayyor komponentlar qilib beradi.

Hayotiy o'xshatish: Lego. Har bo'lak alohida: asos (BarChart), o'qlar, izoh, ustunlar. Kerakligini olib, bir-biriga ulaysiz. Keraksizini qo'ymaysiz.

Oldingi darsda statistika sahifasi to'rt tilda gapira boshladi. Bugun unga teglar grafigi qo'shamiz. Avval Recharts'ni o'rganamiz, keyin vazifalar da uni o'lchab ko'ramiz — va kutilmagan qarorga kelamiz.

2. Recharts g'oyasi: grafik — komponentlar

2.1 Ma'lumot va dataKey

Recharts ma'lumotni obyektlar massivi ko'rinishida oladi. Har obyekt — bitta nuqta (ustun, chiziq nuqtasi):

js
const sotuv = [
  { kun: "Du", osh: 32 },
  { kun: "Se", osh: 41 },
];

Qaysi maydon nima bo'lishini dataKey aytadi: <XAxis dataKey="kun" /> — gorizontal o'qda kunlar, <Bar dataKey="osh" /> — ustun balandligi osh soni. Grafik ichidagi qismlar qanday bog'langan:

flowchart TB
  D["data: [{kun, osh}, …]"] --> C["BarChart (o'lcham, ma'lumot)"]
  C --> X["XAxis dataKey=kun"]
  C --> Y["YAxis (son o'qi)"]
  C --> G["CartesianGrid (to'r)"]
  C --> T["Tooltip (izoh)"]
  C --> L["Legend (ranglar izohi)"]
  C --> B["Bar dataKey=osh"]
  B --> S["SVG: rect, text, path"]

Diagrammaga qarang: ma'lumot faqat tepada, BarChart ga beriladi. Ichki komponentlar uni Context orqali oladi — bu compound komponent naqshi. Shuning uchun tartibni o'zgartirish yoki bitta qismni olib tashlash oson: <Tooltip /> ni o'chirsangiz, faqat izoh yo'qoladi.

2.2 Birinchi grafik: BarChart

Ustunli diagramma (bar chart) — har qiymat bitta ustun, balandligi qiymatga mutanosib. Turli toifalarni (kunlar, taomlar) solishtirish uchun eng tushunarli grafik.

jsx
import { BarChart, Bar, XAxis, YAxis, Tooltip } from "recharts";

const sotuv = [
  { kun: "Du", osh: 32 },
  { kun: "Se", osh: 41 },
  { kun: "Ch", osh: 28 },
  { kun: "Pa", osh: 45 },
  { kun: "Ju", osh: 60 },
];

export default function App() {
  return (
    <BarChart width={340} height={220} data={sotuv}>
      <XAxis dataKey="kun" />
      <YAxis />
      <Tooltip />
      <Bar
        dataKey="osh"
        name="Osh (porsiya)"
        fill="#1f6f4a"
        onClick={(ustun) => console.log(ustun.payload)}
      />
    </BarChart>
  );
}

Konsolda (birinchi ustun bosildi):

text
{kun: 'Du', osh: 32}

«Ishga tushir» ni bosing: beshta yashil ustun, pastda kunlar, chapda 0 dan 60 gacha son o'qi. Juma ustuni eng baland. Sichqonchani ustun ustiga olib boring — Tooltip chiqadi: «Ju», «Osh (porsiya) : 60». Ustunni bossangiz, konsolda o'sha kunning asl obyekti chiqadi (payload). Undan foydalanib, masalan, o'sha kunning buyurtmalar sahifasiga o'tish mumkin.

Kodda uchta muhim narsa:

  1. width va height — piksel. Grafik o'z o'lchamini bilishi shart, aks holda o'qlarni hisoblay olmaydi. Konteyner eniga moslash — «O'lcham» bo'limida.
  2. name — Tooltip va Legend'dagi nom. Bo'lmasa dataKey ning o'zi chiqadi (osh).
  3. Animatsiya — ustunlar pastdan o'sib chiqadi. Recharts 3 da isAnimationActive sukut bo'yicha "auto": foydalanuvchi tizimda "harakatni kamaytirish" (prefers-reduced-motion) ni yoqqan bo'lsa, animatsiya o'chadi. Buni Recharts manba kodidan tekshirdik (usePrefersReducedMotion).

Diqqat: Saytdagi natija oynasi Recharts'ni birinchi marta ishga tushirganda ~1 MB lik to'plamni yuklaydi (gzip ~230 KB, development build — o'lchandi). Sekin internetda bir necha soniya kuting. Bu — kutubxona og'irligining birinchi belgisi; quyida haqiqiy ilovada o'lchaymiz.

2.3 Ichida nima bor

Grafik — oddiy SVG va bir nechta div. Natija oynasida DevTools'ni ochsangiz (yoki tekshiruvchimiz jsdom'da ko'rganidek), Recharts 3.10.1 shuni chizadi:

Element Vazifasi
div.recharts-wrapper o'lchamli o'ram, position: relative
svg role="application" tabindex="0" grafikning o'zi, klaviatura bilan fokuslanadi
div.recharts-tooltip-wrapper izoh: role="status", aria-live="assertive"
ul.recharts-default-legend ranglar izohi (Legend bo'lsa)

tabindex="0" va role="application" — Recharts 3 dagi accessibilityLayer, sukut bo'yicha yoqilgan. Bobur (faqat klaviatura) Tab bilan grafikka kiradi va strelkalar bilan ustundan ustunga yuradi — Tooltip shu ustun uchun chiqadi. Buni «Tooltip» bo'limida jonli ko'ramiz.

Tekshirib ko'ring: <Bar dataKey="Osh" /> deb yozildi (katta harf bilan). Nima bo'ladi?

Javob

Xato chiqmaydi, lekin ustunlar ham chiqmaydi — grafik bo'sh. Obyektlarda Osh maydoni yo'q, Recharts har nuqta uchun undefined oladi va hech narsa chizmaydi. JS obyekt kalitlari katta-kichik harfni farqlaydi. Bu jim xato: grafik "ishlayapti", faqat bo'm-bo'sh. Davosi — dataKey ni ma'lumotdagi kalit bilan solishtirish; TypeScript'da ma'lumot turini bersangiz, muharrir yordam beradi.

3. Grafik turlari

3.1 LineChart va AreaChart — vaqt bo'yicha o'zgarish

Chiziqli diagramma (line chart) — nuqtalar chiziq bilan ulanadi. Vaqt bo'yicha o'zgarishni ko'rsatadi: kunlar, oylar. Bir nechta <Line> — bir nechta seriya (osh va manti) bitta grafikda:

jsx
import {
  LineChart,
  Line,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
} from "recharts";

const hafta = [
  { kun: "Du", osh: 32, manti: 20 },
  { kun: "Se", osh: 41, manti: 18 },
  { kun: "Ch", osh: 28, manti: 25 },
  { kun: "Pa", osh: 45, manti: 22 },
];

export default function App() {
  return (
    <LineChart width={340} height={220} data={hafta}>
      <CartesianGrid strokeDasharray="3 3" />
      <XAxis dataKey="kun" />
      <YAxis />
      <Tooltip />
      <Legend />
      <Line dataKey="osh" name="Osh" stroke="#1f6f4a" />
      <Line dataKey="manti" name="Manti" stroke="#b45309" />
    </LineChart>
  );
}

CartesianGrid — fondagi to'r; strokeDasharray="3 3" uni uzuq chiziqqa aylantiradi. Ikki seriya — ikki rang, Legend ularni nomlaydi.

Maydonli diagramma (area chart) — chiziq ostini bo'yaydi. stackId bir xil bo'lsa, maydonlar ustma-ust qo'shiladi: tepasi — jami sotuv. Kod xuddi shunday, faqat Line → Area:

jsx
import { AreaChart, Area, XAxis, YAxis, Tooltip } from "recharts";

const hafta = [
  { kun: "Du", osh: 32, manti: 20 },
  { kun: "Se", osh: 41, manti: 18 },
  { kun: "Ch", osh: 28, manti: 25 },
];

export default function App() {
  return (
    <AreaChart width={340} height={220} data={hafta}>
      <XAxis dataKey="kun" />
      <YAxis />
      <Tooltip />
      <Area dataKey="osh" stackId="1" fill="#1f6f4a" />
      <Area dataKey="manti" stackId="1" fill="#b45309" />
    </AreaChart>
  );
}

Xuddi shu stackId Bar da ham ishlaydi — ustunlar ustma-ust "yig'iladi" (stacked bar). vazifalar dagi teglar grafigi aynan shunday: har teg uchun "bajarildi" va "qolgan".

3.2 PieChart — ulush

Doiraviy diagramma (pie chart) — butunning qismlari. «Bugun sotilgan taomlarning qancha qismi osh?» Har bo'lak rangini Cell beradi:

jsx
import { PieChart, Pie, Cell } from "recharts";

const ulush = [
  { nom: "Osh", soni: 42 },
  { nom: "Manti", soni: 25 },
  { nom: "Lag'mon", soni: 18 },
];
const RANGLAR = ["#1f6f4a", "#b45309", "#1d4ed8"];

export default function App() {
  return (
    <PieChart width={300} height={240}>
      <Pie
        data={ulush}
        dataKey="soni"
        nameKey="nom"
        label
        onClick={(b) => {
          const foiz = Math.round(b.percent * 100);
          console.log(b.nom, b.soni, `${foiz}%`);
        }}
      >
        {ulush.map((b, i) => (
          <Cell key={b.nom} fill={RANGLAR[i]} />
        ))}
      </Pie>
    </PieChart>
  );
}

Konsolda (birinchi bo'lak bosildi):

text
Osh 42 49%

percent ni Recharts o'zi hisoblaydi: 42 / (42 + 25 + 18) ≈ 0.494. label — har bo'lak yonida qiymat.

3.3 Qachon qaysi biri

Savol Grafik
Qaysi toifa ko'p? (kunlar, taomlar) BarChart
Vaqt bo'yicha qanday o'zgardi? LineChart
Jami va uning tarkibi vaqt bo'yicha AreaChart (stackId)
Butunning qismlari (3–5 bo'lak) PieChart

Doiraviy diagramma bilan ehtiyot bo'ling. Ko'z burchaklarni uzunlikdan yomonroq solishtiradi: 25 va 18 lik bo'laklar farqi doirada qiyin ko'rinadi, ustunda — aniq. Bo'lak beshtadan oshsa, ustunli grafik tanlang.

Tekshirib ko'ring: «Bahor» filiallari (Chilonzor, Yunusobod, Sergeli) bo'yicha oylik tushum 12 oy davomida. Qaysi grafik?

Javob

LineChart, uchta <Line> — har filial bitta chiziq. Savol "vaqt bo'yicha qanday o'zgardi" va filiallarni solishtirish. 12 nuqtali uchta chiziq bitta grafikda o'qiladi. BarChart da 36 ustun bo'lardi — tartibsiz. PieChart esa vaqtni umuman ko'rsatmaydi.

4. Tooltip, Legend va formatlash

4.1 Sonni formatlash: Intl'siz

Tooltip'da «60» emas, «2 100 000 so'm» chiqishi kerak. Oldingi darsdagi tuzoqni eslang: Chrome'da Intl.NumberFormat("uz") «35,000» chiqaradi, Node'da «35 000». O'zbekcha uchun o'z funksiyamiz:

js
function som(son) {
  const guruhlar = String(son).replace(/\B(?=(\d{3})+(?!\d))/g, " ");
  return `${guruhlar} so'm`;
}

console.log(som(35000)); // 35 000 so'm
console.log(som(2100000)); // 2 100 000 so'm
console.log(som(500)); // 500 so'm

Muntazam ifoda har uch raqamli guruh oldiga bo'sh joy qo'yadi (Lookahead va lookbehind). Natija har brauzerda bir xil.

Recharts'da formatlash uchta joyda:

Prop Qayerda Misol
tickFormatter XAxis/YAxis — o'q yozuvlari (v) => \${v} ta``
formatter Tooltip, Legend — qiymat (v) => som(v)
labelFormatter Tooltip — sarlavha (kun) => \Kun: ${kun}``

4.2 O'z tooltip'ingiz va klaviatura

Standart Tooltip «Osh : 32» ko'rinishida. O'zimiz chizmoqchi bo'lsak — content ga komponent beramiz. Recharts unga active (ko'rinadimi), payload (shu nuqtadagi seriyalar) va label (o'q qiymati) props'ini uzatadi.

Quyidagi misolni klaviatura bilan yuramiz: Tab — grafikka fokus, keyin → →. Har o'zgarishda tooltip matnini konsolga ham yozamiz — Bobur nimani "ko'rishini" kuzatish uchun:

jsx
import { useEffect } from "react";
import { LineChart, Line, XAxis, YAxis, Tooltip } from "recharts";

const NARX = { osh: 35000, manti: 30000 };
const hafta = [
  { kun: "Du", osh: 32, manti: 20 },
  { kun: "Se", osh: 41, manti: 18 },
  { kun: "Ch", osh: 28, manti: 25 },
];

function som(son) {
  const guruhlar = String(son).replace(/\B(?=(\d{3})+(?!\d))/g, " ");
  return `${guruhlar} so'm`;
}

function SavdoTooltip({ active, payload, label }) {
  const qatorlar = active && payload?.length
    ? payload.map((q) => {
        const tushum = som(q.value * NARX[q.dataKey]);
        return `${q.name} ${tushum}`;
      })
    : [];
  const matn = qatorlar.length
    ? `${label}: ${qatorlar.join(", ")}`
    : "";
  useEffect(() => {
    if (matn) console.log(matn);
  }, [matn]);
  return matn ? <p className="savdo-tooltip">{matn}</p> : null;
}

export default function App() {
  return (
    <LineChart width={340} height={220} data={hafta}>
      <XAxis dataKey="kun" />
      <YAxis tickFormatter={(v) => `${v} ta`} />
      <Tooltip content={<SavdoTooltip />} />
      <Line dataKey="osh" name="Osh" stroke="#1f6f4a" />
      <Line dataKey="manti" name="Manti" stroke="#b45309" />
    </LineChart>
  );
}

Konsolda (Tab, →, →):

text
Du: Osh 1 120 000 so'm, Manti 600 000 so'm
Se: Osh 1 435 000 so'm, Manti 540 000 so'm
Ch: Osh 980 000 so'm, Manti 750 000 so'm

Uch qadam — uch kun. Tab grafikka kirdi va birinchi nuqtani faollashtirdi, har → keyingisiga o'tdi. Bu klaviatura yo'lini biz yozmadik — accessibilityLayer ning ishi. content={<SavdoTooltip />} — element beriladi: Recharts unga props qo'shib, oddiy komponent kabi chizadi, ichida hook (useEffect) ham ishlaydi. Real loyihada konsol qatori kerak emas — u faqat kuzatish uchun.

Chap o'qdagi «20 ta», «40 ta» — tickFormatter. Tooltip'da q.name («Osh») — <Line name> dan, q.dataKey («osh») — narxni topish uchun.

Maslahat: Legend bandlarini Recharts 3 sukut bo'yicha nomi bo'yicha alifbo tartibida chizadi (itemSorter: "value" — manba kodida tekshirdik). «Osh, Manti» emas, «Manti, Osh» chiqsa — sabab shu. O'z tartibingiz kerak bo'lsa: <Legend itemSorter={null} />.

5. O'lcham: ResponsiveContainer va responsive

width={340} telefonda sig'adi, kompyuterda esa mitti. Grafik konteyner eniga moslashishi kerak. Buning ikki yo'li bor:

  • ResponsiveContainer — klassik o'ram: ota-elementning o'lchamini kuzatadi (ResizeObserver) va grafikka beradi.
  • responsive prop (Recharts 3.3 dan) — o'ramsiz: grafikning o'ziga CSS bilan o'lcham berasiz (width: 100%, aspectRatio).

Bu blok faqat saytda ishlaydi. Tekshiruvchimiz jsdom'da ishlaydi, u yerda sahifa joylashuvi (layout) hisoblanmaydi — har element eni 0. «Ishga tushir» ni bosib, brauzer oynasini toraytirib ko'ring:

jsx
import {
  ResponsiveContainer,
  BarChart,
  Bar,
  XAxis,
  YAxis,
} from "recharts";

const sotuv = [
  { kun: "Du", osh: 32 },
  { kun: "Se", osh: 41 },
  { kun: "Ju", osh: 60 },
];

export default function App() {
  return (
    <>
      <div style={{ width: "100%", height: 200 }}>
        <ResponsiveContainer>
          <BarChart data={sotuv}>
            <XAxis dataKey="kun" />
            <YAxis />
            <Bar dataKey="osh" fill="#1f6f4a" />
          </BarChart>
        </ResponsiveContainer>
      </div>
      <BarChart
        responsive
        data={sotuv}
        style={{ width: "100%", maxWidth: 480, aspectRatio: 1.6 }}
      >
        <XAxis dataKey="kun" />
        <Bar dataKey="osh" fill="#b45309" />
      </BarChart>
    </>
  );
}

Ikkala grafik oyna eniga cho'ziladi. Birinchisi — balandligi 200 px, ikkinchisi — eniga nisbatan (aspectRatio: 1.6).

Eng ko'p uchraydigan xato: ResponsiveContainer ning ota-elementida balandlik yo'q. height: 100% "nimaning 100%?" — ota balandligi 0 bo'lsa, grafik ham 0. Recharts konsolga ogohlantirish yozadi. Uning haqiqiy matni (jsdom'da olindi — u yerda hamma o'lcham 0):

text
The width(0) and height(0) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.

Tarjimasi: "Grafik eni va balandligi 0 dan katta bo'lishi kerak. Konteyner stilini yoki width/height props'ini tekshiring, yoki minWidth, minHeight, aspect qo'shing". Davosi — ota-elementga aniq balandlik (height: 200px) yoki aspect.

6. shadcn Charts

shadcn/ui II darsida aytganimizdek, shadcn'ning Chart komponenti — Recharts ustidagi ingichka qatlam. Grafikni Recharts'ning o'zi chizadi, shadcn uchta narsa qo'shadi:

tsx
const chartConfig = {
  osh: { label: "Osh", color: "var(--chart-1)" },
  manti: { label: "Manti", color: "var(--chart-2)" },
} satisfies ChartConfig;

<ChartContainer config={chartConfig} className="min-h-50 w-full">
  <BarChart accessibilityLayer data={hafta}>
    <CartesianGrid vertical={false} />
    <XAxis dataKey="kun" tickLine={false} />
    <ChartTooltip content={<ChartTooltipContent />} />
    <ChartLegend content={<ChartLegendContent />} />
    <Bar dataKey="osh" fill="var(--color-osh)" radius={4} />
    <Bar dataKey="manti" fill="var(--color-manti)" radius={4} />
  </BarChart>
</ChartContainer>
  1. ChartConfig — har seriya nomi va rangi bitta joyda. color CSS o'zgaruvchisiga aylanadi: --color-osh. Tema tokenlari bilan ishlaydi — qorong'i temada rang o'zi almashadi.
  2. ChartContainer — ResponsiveContainer o'rami + o'sha CSS o'zgaruvchilari.
  3. ChartTooltipContent, ChartLegendContent — Tailwind bilan bezalgan tayyor tooltip va legend.

Ikki halol eslatma. Birinchisi — versiya: shadcn reyestri chart uchun recharts@3.8.0 ni yozadi, kanon tajribasida 3.10.1 o'lchangan. CLI ishlatsangiz, qaysi versiya o'rnatilganini tekshiring (shadcn/ui I qoidasi). Ikkinchisi — xavfsizlik siyosati: chart.tsx dagi ChartStyle sahifaga o'z <style> tegini yozadi. vazifalar ning CSP'si bunday tegni to'sadi — Radix chuqur darsidagi saboq. Recharts'ning o'zi esa CSP ostida toza: kanon tajribasida 0 buzilish.

7. Grafik va accessibility

Malika grafikni ko'rmaydi. Unga nima beramiz?

  • Matn muqobili. Eng ishonchli yo'l — o'sha raqamlar jadvalda (Jadvallar I). Kanon shunday qildi: grafik aria-hidden="true", ekran o'quvchi yonidagi TeglarJadvali ni o'qiydi.
  • Faqat rang emas. Ikki seriya faqat rang bilan farqlansa, ranglarni ajrata olmaydigan odam ularni adashtiradi. Legend, yozuv yoki shakl (uzuq chiziq) qo'shing.
  • Kontrast. Grafik elementlari fonga nisbatan kamida 3:1 (WCAG 1.4.11). Kanon "qolgan" qism uchun --color-chegara ni sinab ko'rdi — 1.4:1, o'tmadi; --color-ikkinchi olindi.
  • Harakat. prefers-reduced-motion — Recharts "auto" bilan o'zi hurmat qiladi; o'z animatsiyangizda — o'zingiz (UI animatsiya tamoyillari).

accessibilityLayer yaxshi qadam: klaviatura bilan yurish va aria-live tooltip. Lekin ekran o'quvchi grafik ichidagi o'q yozuvlarini (0, 15, 30…) ham birma-bir o'qishi mumkin. Shuning uchun jadval baribir kerak.

Tekshirib ko'ring: Nega kanon grafikka aria-label="Teglar grafigi" qo'yib qo'ya qolmadi, aria-hidden qildi?

Javob

aria-label faqat nom beradi — "Teglar grafigi". Malika grafik borligini biladi, lekin raqamlarni bilmaydi. Raqamlar esa yonidagi jadvalda allaqachon bor. Grafikni yashirish takrorni oldini oladi: ekran o'quvchi bir xil ma'lumotni ikki marta (grafik va jadval) o'qimaydi. Ko'radigan foydalanuvchi uchun grafik, ko'rmaydigan uchun jadval — har biri o'z vazifasini qiladi.

8. Vazifalar qadami: teglar grafigi (holat 04)

8.1 Avval — Recharts bilan o'lchov

Statistika panelida teglar jadvali bor (Jadvallar I). Endi o'sha raqamlar grafikda: har teg — gorizontal ustun, "bajarildi" va "qolgan" ustma-ust. Kanon avval Recharts 3.10.1 bilan yozdi (tajriba varianti, asosiy qismi):

tsx
export default function TeglarGrafigi({ teglar }: Props) {
  const malumot = teglar.slice(0, ENG_KOPI).map((t) => ({
    teg: t.teg,
    bajarildi: t.bajarildi,
    qolgan: t.jami - t.bajarildi,
  }));
  return (
    <figure id="teglar-grafigi">
      {/* responsive (Recharts 3.3+): kenglik — ota elementdan,
          ResponsiveContainer o'rami kerak emas */}
      <BarChart
        responsive
        data={malumot}
        style={{ width: "100%", maxWidth: "36rem", aspectRatio: 1.6 }}
        margin={{ top: 8, right: 8, bottom: 0, left: 0 }}
      >
        <CartesianGrid vertical={false} />
        <XAxis dataKey="teg" />
        <YAxis allowDecimals={false} width={32} />
        <Tooltip />
        <Legend />
        <Bar
          dataKey="bajarildi"
          name="Bajarildi"
          stackId="hammasi"
          className="grafik-bajarildi"
          isAnimationActive={false}
        />
        <Bar
          dataKey="qolgan"
          name="Qolgan"
          stackId="hammasi"
          className="grafik-qolgan"
          isAnimationActive={false}
        />
      </BarChart>
      <figcaption>Teglar: bajarilgan va qolgan vazifalar</figcaption>
    </figure>
  );
}

Hammasi tanish: stackId, name, responsive, allowDecimals={false} (vazifa soni butun — o'qda «1.5» chiqmasin). Grafik BatafsilStatistika ichida, alohida chunk bo'lib kech yuklanadi. O'rnatish va o'lchov (kanon, 04-recharts/olchov.txt):

bash
npm i -E recharts@3.10.1 react-is@19.3.0

Natija ikki muammo berdi. Birinchisi — hajm: bitta grafik chunk'i 103.4 KB gzip (xom 362 KB). npm 35 ta paket qo'shdi, ular orasida @reduxjs/toolkit, immer, reselect. Recharts 3 ichki holatini Redux bilan boshqaradi — butun Redux ilovaga keladi.

Ikkinchisi — turlar. tsc -b (TypeScript 7.0.2) 12 ta xato berdi (6 xil, har biri ikki marta). Hammasi kutubxonaning tur faylida, birinchi uchtasi:

text
node_modules/@reduxjs/toolkit/dist/index.d.mts(994,146): error TS2344: Type '{ [K in keyof (ThunkApiConfig & Omit<CurriedThunkApiConfig, keyof ThunkApiConfig>)]: (ThunkApiConfig & Omit<...>)[K]; }' do
node_modules/@reduxjs/toolkit/dist/index.d.mts(1690,99): error TS2344: Type '{ [K in keyof (ThunkApiConfig & Omit<CurriedThunkApiConfig, keyof ThunkApiConfig>)]: (ThunkApiConfig & Omit<...>)[K]; }' do
node_modules/@reduxjs/toolkit/dist/index.d.mts(2064,15): error TS2420: Class 'TaskAbortError' incorrectly implements interface 'SerializedError'.

(Qatorlar o'lchov faylida shunday kesilgan.) Oxirgisining tarjimasi: "TaskAbortError klassi SerializedError interfeysini noto'g'ri amalga oshiradi". Sabab — kanonning qattiq tsconfig'i: exactOptionalPropertyTypes: true (Strict bayroqlar oilasi). Bu bayroq x?: string ga undefined ni ochiq yozishni taqiqlaydi. Redux turlari bunga tayyor emas. Kanon tekshirdi: bayroq false bo'lsa — 0 xato.

Ikki yo'l bor edi, ikkalasi ham kanonni susaytiradi:

  • skipLibCheck: true — node_modules dagi hamma tur fayllari tekshirilmaydi. Ko'p loyihalarda shunday (darsdagi tekshiruvchimiz ham shunday ishlaydi, shuning uchun bu xatolarni yuqoridagi bloklarda ko'rmadingiz).
  • Bayroqni o'chirish — o'z kodimizdagi himoyadan voz kechish.

8.2 Qaror: o'z SVG grafigi

Kanon qarori: Recharts kirmaydi. Bitta oddiy grafik — 8 ta gorizontal ustun — uchun 103 KB va tsconfig'ni bo'shatish qimmat. O'rniga TeglarGrafigi.tsx — 77 qatorlik (izohlari bilan) o'z SVG komponenti. Uning g'oyasi:

  • har teg — bitta <g> qator: chapda teg nomi (<text>), keyin ikki <rect> (bajarildi + qolgan), oxirida «2/3»;
  • masshtab = (ENI - YORLIQ - 32) / engKatta — eng uzun ustun mavjud joyni to'ldiradi;
  • viewBox — SVG o'zi eniga moslashadi (inline-size: 100%), ResizeObserver kerak emas;
  • ranglar — CSS tokenlaridan (fill: var(--color-asosiy)), qorong'i temada o'zi almashadi;
  • aria-hidden="true" — matn muqobili jadvalda.

Kanon komponenti — kod aynan, faqat tur va ma'lumot shu blokka ko'chirilgan. Ranglar va CSS natija oynasi uchun soddalashtirilgan (kanonda — tokenlar, asos.css):

tsx
type TegHisobi = {
  readonly teg: string;
  readonly jami: number;
  readonly bajarildi: number;
};

type Props = {
  teglar: readonly TegHisobi[];
};

/** Grafikda eng ko'p 8 ta teg (jadvaldagi tartibda) */
const ENG_KOPI = 8;
const QATOR = 28; // bitta ustun balandligi + oraliq (px, viewBox)
const YORLIQ = 96; // chapdagi teg nomi uchun joy
const ENI = 360;

export function TeglarGrafigi({ teglar }: Props) {
  const qatorlar = teglar.slice(0, ENG_KOPI);
  const engKatta = Math.max(1, ...qatorlar.map((t) => t.jami));
  const masshtab = (ENI - YORLIQ - 32) / engKatta;
  const balandlik = qatorlar.length * QATOR;
  return (
    <figure id="teglar-grafigi">
      <svg
        viewBox={`0 0 ${ENI} ${balandlik}`}
        aria-hidden="true"
        focusable="false"
      >
        {qatorlar.map((t, i) => {
          const y = i * QATOR;
          const bajarildi = t.bajarildi * masshtab;
          const qolgan = (t.jami - t.bajarildi) * masshtab;
          return (
            <g key={t.teg} transform={`translate(0 ${y})`}>
              <text
                x={YORLIQ - 6}
                y={QATOR / 2}
                className="grafik-yorliq"
              >
                {t.teg}
              </text>
              <rect
                x={YORLIQ}
                y={4}
                width={bajarildi}
                height={QATOR - 8}
                className="grafik-bajarildi"
              />
              <rect
                x={YORLIQ + bajarildi}
                y={4}
                width={qolgan}
                height={QATOR - 8}
                className="grafik-qolgan"
              />
              <text
                x={YORLIQ + bajarildi + qolgan + 4}
                y={QATOR / 2}
                className="grafik-son"
              >
                {`${t.bajarildi}/${t.jami}`}
              </text>
            </g>
          );
        })}
      </svg>
      <figcaption>
        <span className="belgi-bajarildi" /> bajarildi,{" "}
        <span className="belgi-qolgan" /> qolgan (son — bajarildi/jami)
      </figcaption>
    </figure>
  );
}

const css = `
  #teglar-grafigi { max-inline-size: 36rem; margin: 0; }
  #teglar-grafigi svg { display: block; inline-size: 100%; }
  #teglar-grafigi text {
    font-size: 12px; dominant-baseline: central;
  }
  .grafik-yorliq { text-anchor: end; }
  .grafik-bajarildi { fill: #1f6f4a; }
  .grafik-qolgan { fill: #6b7280; }
  figcaption span {
    display: inline-block; inline-size: .75em; block-size: .75em;
  }
  .belgi-bajarildi { background: #1f6f4a; }
  .belgi-qolgan { background: #6b7280; }
`;

export default function App() {
  return (
    <>
      <style>{css}</style>
      <TeglarGrafigi
        teglar={[
          { teg: "#bozor", jami: 3, bajarildi: 2 },
          { teg: "#uy", jami: 2, bajarildi: 0 },
          { teg: "#maktab", jami: 1, bajarildi: 1 },
        ]}
      />
    </>
  );
}

«Ishga tushir» — uch qator: «#bozor» uzun ustun (ikki yashil ulush + bir kulrang), «#uy» butunlay kulrang, «#maktab» qisqa yashil. Ustun uzunligi jami ga mutanosib, eng kattasi (3) joyni to'ldiradi. Tooltip, o'qlar va animatsiya yo'q — ular bu yerda kerak emas: son har qator oxirida yozilgan. Bitta mustaqil fayl, bog'liqliksiz.

8.3 Natija va narx

bash
git switch -c feat/teglar-grafigi

BatafsilStatistika.tsx ga ikki qator (kanon):

diff
 import { TeglarJadvali } from "./TeglarJadvali.tsx";
+import { TeglarGrafigi } from "./TeglarGrafigi.tsx";
...
       {s.teglar.length > 0 && <TeglarJadvali teglar={s.teglar} />}
+      {/* 21/#04: o'sha raqamlar grafikda (o'z SVG —
+          TeglarGrafigi.tsx) */}
+      {s.teglar.length > 0 && <TeglarGrafigi teglar={s.teglar} />}

Va asos.css ga 15 qator (ranglar tokenlardan). Tekshiruvlar (haqiqiy, holat 04):

  • npm test — node:test 221, Vitest 65; lint, format:check, tip, knip — exit 0.
  • axe — 0 buzilish. Statistika ko'rinishida 4 ta "qo'lda ko'rish kerak" (color-contrast): axe SVG matni ortidagi fon rangini aniqlay olmadi — kontrast qo'lda tekshirilgan.
  • e2e-21 — 22 ta, grafikka oidlari:
text
✅ grafik: teglar, ustun uzunligi jamiga mutanosib, son: [["#bozor",155,77,"2/3"],["#maktab",0,77,"0/1"]]
✅ grafik ekran o'quvchidan yashirin (jadval — matn muqobili), jadval bor: ["true",true]
✅ ranglar tokenlardan (bajarildi — asosiy, qolgan — ikkinchi): [true,true]
✅ grafik: CSP buzilishi yo'q: []

Birinchi qatorni o'qing: «#bozor» — bajarildi 155 px, qolgan 77 px (2:1), «#maktab» — 0 va 77 (0/1). Mutanosiblik o'lchab tekshirildi.

Endi narx — grafik turgan BatafsilStatistika chunk'i (gzip):

Teglar grafigi narxi (kech chunk, gzip)
  • grafiksiz (holat 03)13,8 KB
  • o'z SVG (holat 04)+0.3 KB14,1 KB
  • Recharts varianti14.0 + 103.4 KB117,4 KB

Manba: O'lchandi: holat 03 va 04, tekshiruv-03.txt, tekshiruv-04.txt, 04-recharts/olchov.txt, gzip -9, 2026-10-17

O'z SVG — +0.3 KB, Recharts — +103.4 KB, ya'ni 300 baravardan ko'p. Birinchi yuklash ikkala variantda ham ~219 KB — grafik kech chunk'da. CSS +0.17 KB.

bash
git add -A
git commit

Commit (kanon): feat: teglar grafigi — o'z SVG (Recharts o'lchandi: +103 KB, tsc xatolari).

Bu "Recharts yomon" degani emas. Dashboard'da o'nta grafik, tooltip, zoom, vaqt o'qi kerak bo'lsa — 103 KB o'n grafikka bo'linadi va o'z kodingizdan arzon tushadi. Bitta oddiy grafik uchun esa — qimmat. Qoida 20-qismdagidek: kutubxona narxini ishlatilishiga bo'lib o'lchang. Boshqa kutubxonalar (Chart.js, ECharts, visx) — keyingi darsda.

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
grafik ko'rinmaydi, konsolda width(0) and height(0) ResponsiveContainer otasida balandlik yo'q otaga height yoki aspect
ustunlar yo'q, xato ham yo'q dataKey ma'lumotdagi kalitga mos emas kalitni tekshiring, ma'lumotga tur bering
Tooltip/Legend'da osh (kichik harf) name berilmagan <Bar name="Osh" />
Legend tartibi o'zgarib ketdi sukut — alifbo (itemSorter: "value") itemSorter={null}
o'qda «1.5 ta vazifa» o'q sonlarni kasr bilan bo'ladi allowDecimals={false}
Chrome'da «35,000» Intl.NumberFormat("uz") o'z som() funksiyasi
tsc -b — @reduxjs/toolkit da TS2344 exactOptionalPropertyTypes skipLibCheck yoki boshqa yechim (yuqorida)

10. Mashqlar

Mashqlar kurs/mashqlar/21/04-recharts/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): qaysi grafik, qaysi prop?

To'ldiring:

  • Haftaning har kuni qancha manti sotilgani —
  • 12 oy davomida tushum o'zgarishi —
  • O'q yozuviga «ta» qo'shuvchi prop —
  • Ikki ustunni ustma-ust qo'yuvchi prop —
  • ResponsiveContainer ishlashi uchun ota-elementga kerak narsa —
Yechim
  • BarChart — toifalarni (kunlarni) solishtirish.
  • LineChart — vaqt bo'yicha o'zgarish.
  • tickFormatter — XAxis/YAxis dagi har yozuvni funksiyadan o'tkazadi.
  • stackId — bir xil qiymatli Bar yoki Area lar ustma-ust yig'iladi.
  • balandlik — eni odatda ota-elementdan keladi, balandlik esa 0 bo'lib qolishi mumkin.

2-mashq (o'rta): osh va manti yonma-yon

Haftaning uch kuni uchun osh va manti sotuvi: Du — 32 va 20, Se — 41 va 18, Ch — 28 va 25. Grafik:

  1. BarChart, ikki Bar yonma-yon (stackId'siz), nomlari «Osh» va «Manti».
  2. YAxis yozuvlari «10 ta» ko'rinishida, Legend bor.
  3. «Osh» ustuni bosilganda konsolga Du: 32 ta osh ko'rinishida yozilsin.

Ishora: «Birinchi grafik» bo'limidagi onClick va «Sonni formatlash» dagi tickFormatter.

Yechim
jsx
import { BarChart, Bar, XAxis, YAxis, Legend } from "recharts";

const sotuv = [
  { kun: "Du", osh: 32, manti: 20 },
  { kun: "Se", osh: 41, manti: 18 },
  { kun: "Ch", osh: 28, manti: 25 },
];

export default function App() {
  return (
    <BarChart width={340} height={240} data={sotuv}>
      <XAxis dataKey="kun" />
      <YAxis tickFormatter={(v) => `${v} ta`} />
      <Legend />
      <Bar
        dataKey="osh"
        name="Osh"
        fill="#1f6f4a"
        onClick={(u) => console.log(`${u.kun}: ${u.osh} ta osh`)}
      />
      <Bar dataKey="manti" name="Manti" fill="#b45309" />
    </BarChart>
  );
}

Konsolda:

text
Du: 32 ta osh

onClick ga kelgan obyektda asl maydonlar ham bor (u.kun, u.osh) — u.payload orqali ham olsa bo'ladi. stackId yo'q — ustunlar yonma-yon turadi. Selektor birinchi uchraganni bosadi — bu «Osh» ning «Du» ustuni.

3-mashq (qiyin): doira va uning matn muqobili

«Bugungi taomlar»: Osh 42, Manti 25, Lag'mon 18. Kanon g'oyasini takrorlang:

  1. PieChart (Cell ranglari bilan) aria-hidden="true" li div ichida.
  2. Ostida <table>: «Taom», «Soni», «Ulush» ustunlari; ulush — butun foiz (Math.round), ulushlar percent dan emas, o'zingiz hisoblang.
  3. Jadvalga <caption> — «Bugungi taomlar».

Ishora: jami — reduce. Jadval — oddiy HTML (Jadvallar I dagi semantika).

Yechim
jsx
import { PieChart, Pie, Cell } from "recharts";

const taomlar = [
  { nom: "Osh", soni: 42 },
  { nom: "Manti", soni: 25 },
  { nom: "Lag'mon", soni: 18 },
];
const RANGLAR = ["#1f6f4a", "#b45309", "#1d4ed8"];

export default function App() {
  const jami = taomlar.reduce((s, t) => s + t.soni, 0);
  return (
    <figure>
      <div aria-hidden="true">
        <PieChart width={240} height={200}>
          <Pie data={taomlar} dataKey="soni" nameKey="nom">
            {taomlar.map((t, i) => (
              <Cell key={t.nom} fill={RANGLAR[i]} />
            ))}
          </Pie>
        </PieChart>
      </div>
      <table>
        <caption>Bugungi taomlar</caption>
        <thead>
          <tr>
            <th>Taom</th>
            <th>Soni</th>
            <th>Ulush</th>
          </tr>
        </thead>
        <tbody>
          {taomlar.map((t) => (
            <tr key={t.nom}>
              <td>{t.nom}</td>
              <td>{t.soni}</td>
              <td>{Math.round((t.soni / jami) * 100)}%</td>
            </tr>
          ))}
        </tbody>
      </table>
    </figure>
  );
}

Ekranda:

text
Bugungi taomlar
Taom Soni Ulush
Osh 42 49%
Manti 25 29%
Lag'mon 18 21%

Ekranda faqat jadval matni — doirada yozuv yo'q (label bermadik). Diqqat qiling: 49 + 29 + 21 = 99%. Yaxlitlashda yo'qolgan foiz — normal holat; aniqlik kerak bo'lsa bitta kasr xona bering. Malika jadvalni o'qiydi, doirani esa eshitmaydi — aria-hidden takrorni yo'qotdi.

4-mashq: Vazifalar qadami

feat/teglar-grafigi branch'ida «Vazifalar qadami» bo'limidagi o'zgarishlarni qiling: TeglarGrafigi.tsx, uni BatafsilStatistika.tsx ga ulash, asos.css dagi ranglar. Ixtiyoriy tajriba: alohida branch'da Recharts variantini yozib, npm run build va npm run tip natijasini o'zingiz ko'ring — keyin o'chiring.

Yechim

Tekshiruv ro'yxati:

  • npm test — node 221, Vitest 65; npm run tip — 0 xato.
  • Statistika sahifasi: jadval ostida grafik, ustunlar jamiga mutanosib; qorong'i temada ranglar almashadi.
  • DevTools: svg da aria-hidden="true"; Lighthouse/axe — buzilish yo'q.
  • npm run build — birinchi yuklash ~219.6 KB, BatafsilStatistika chunk'i ~14.1 KB gzip.
  • Recharts tajribasida: chunk ~103 KB va tsc -b da @reduxjs/toolkit TS2344 xatolari.

11. Real ishda

  • Admin panellar va analitika — sotuv, foydalanuvchilar, buyurtmalar grafigi. React loyihalarida Recharts ko'p uchraydi; shadcn Charts orqali ham keladi.
  • Kod ko'rigida birinchi savol: "bu grafik uchun qancha KB?" Bitta oddiy grafik — o'z SVG; ko'p interaktiv grafik — kutubxona.
  • Intervyu savollari: "Bar va Line qachon?", "ResponsiveContainer nega ishlamayapti?", "grafikni ekran o'quvchiga qanday tushuntirasiz?", "kutubxona qo'shishdan oldin nimani o'lchaysiz?".

Xulosa

  • Recharts — komponentlardan grafik: BarChart + XAxis/YAxis + Tooltip/Legend + Bar dataKey. Ma'lumot — obyektlar massivi.
  • LineChart — vaqt, BarChart — toifa, AreaChart + stackId — tarkib, PieChart — 3–5 bo'lakli ulush.
  • Formatlash — tickFormatter, formatter, o'z tooltip content={<… />}. O'zbekcha son — o'z funksiya, Intl'ga ishonmang.
  • O'lcham: ResponsiveContainer (otada balandlik!) yoki responsive + CSS aspectRatio.
  • A11y: accessibilityLayer klaviatura beradi, lekin matn muqobili — jadval. Rang yolg'iz emas, kontrast ≥ 3:1.
  • Narx: vazifalar da +103.4 KB va 12 tsc xatosi → kanon o'z SVG grafigini oldi (+0.3 KB).

Keyingi dars: Grafiklar II: Chart.js, ECharts, Nivo va moliya grafiklari — canvas va SVG farqi, katta ma'lumot, xavfsizlik siyosati ostida qaysi kutubxona ishlaydi (kanon 6 ta grafik kutubxonasini o'lchadi).

Manbalar

  • Recharts: API va misollar — recharts.github.io (3.10.1); manba kodi: Legend (itemSorter), usePrefersReducedMotion, accessibilityLayer
  • shadcn/ui: Chart — ui.shadcn.com/docs/components/chart (reyestr: recharts@3.8.0)
  • WCAG 2.2: 1.4.11 Non-text Contrast; 1.4.1 Use of Color
  • Kanon: vazifalar holat 04, natijalar/tekshiruv-03.txt, tekshiruv-04.txt, tajriba/04-recharts/olchov.txt, brauzer-tekshiruv/natija-21-04.txt (2026-10-17)
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!