@akhatmirzo/ui

DataTable

Ma'lumotlar jadvali: saralash, o'zbekcha qidiruv, filtrlar, tanlash, sahifalash, server rejimi va virtual scroll.

Holat
#1042Jasur KarimovToshkent21.10.2026To'langan85 000 so'm
#1043Nodira SaidovaBuxoro20.10.2026Kutilmoqda462 311 so'm
#1044Malika RahimovaXorazm19.10.2026Yo'lda839 622 so'm
#1045Aziz NormatovSamarqand18.10.2026Bekor qilingan1 216 933 so'm
#1046Bekzod G'aniyevFarg'ona17.10.2026Kutilmoqda1 594 244 so'm
1–5 / 48

Import

import { DataTable, type DataTableColumn } from '@akhatmirzo/ui';

DataTable TanStack Table ustida qurilgan, lekin sizdan uning API'sini bilish talab qilinmaydi: ma'lumot (data) va ustunlar ro'yxati (columns) yetarli — saralash, qidiruv, filtr, sahifalash va holatlar (yuklanish, bo'sh, xato) tayyor.

Qachon ishlatish

  • Bir xil tuzilishdagi ko'p yozuv uchun — buyurtmalar, mijozlar, to'lovlar, xodimlar. Foydalanuvchi solishtiradi, saralaydi, qidiradi.
  • Kichik ro'yxat uchun (5–10 qator, saralash kerak emas) oddiy <table> yoki Cardlar yetarli.
  • Ma'lumot ko'p bo'lsa (minglab qator) — ikki yo'l: serverda sahifalash (manual) yoki hammasi brauzerda bo'lsa — virtual.
  • Qatordan boshqa sahifaga o'tish kerak bo'lsa — onRowClick; bir nechta amal bo'lsa — rowActions (odatda DropdownMenu).
  • Sonli ustunlarga numeric bering — o'ngga tekislanadi va raqamlar bir xil kenglikda (tabular-nums) chiziladi, summalarni ko'z bilan solishtirish oson.

Ustunlar

Har bir ustun — DataTableColumn<T> obyekti. Eng kami — key va header; qiymat row[key] dan olinadi va matn sifatida chiziladi. Qolgan hamma narsa ixtiyoriy:

columns.tsx
import { formatDate, formatMoney } from '@akhatmirzo/core';
import { Badge, type DataTableColumn } from '@akhatmirzo/ui';

export interface Order {
  id: number;
  customer: { name: string; phone: string };
  date: Date;
  status: 'paid' | 'pending';
  total: number;
}

export const columns: DataTableColumn<Order>[] = [
  { key: 'id', header: '№', width: 88, sortable: true, hideable: false },
  {
    key: 'customer',
    header: 'Mijoz',
    accessor: (o) => o.customer.name, // saralash, filtr va qidiruv shu qiymat bo'yicha
    sortable: true,
  },
  {
    key: 'phone',
    header: 'Telefon',
    accessor: (o) => o.customer.phone,
    hidden: true, // "Ustunlar" menyusidan yoqiladi
  },
  {
    key: 'date',
    header: 'Sana',
    sortable: true,
    filter: 'date',
    searchable: false,
    cell: (o) => formatDate(o.date, 'dd.MM.yyyy, HH:mm'),
  },
  {
    key: 'status',
    header: 'Holat',
    filter: 'multiselect',
    filterOptions: [
      { value: 'paid', label: "To'langan" },
      { value: 'pending', label: 'Kutilmoqda' },
    ],
    searchValue: (o) => (o.status === 'paid' ? "To'langan" : 'Kutilmoqda'),
    cell: (o) =>
      o.status === 'paid' ? <Badge color="success">To'langan</Badge> : <Badge>Kutilmoqda</Badge>,
  },
  {
    key: 'total',
    header: 'Summa',
    numeric: true,
    sortable: true,
    filter: 'range',
    cell: (o) => formatMoney(o.total),
  },
];
PropTipStandartTavsif
keymajburiystring—Ustun id'si; accessor bo'lmasa — row[key] qiymati
headermajburiyReactNode—Sarlavha
accessor(row: T) => unknown—Saralash, filtr va qidiruv uchun qiymat (standart — row[key])
cell(row: T) => ReactNode—Katak ko'rinishi (standart — qiymat matni; sana — dd.MM.yyyy)
widthnumber | string—Kenglik: px yoki CSS qiymat
align'left' | 'center' | 'right'—Tekislash (standart — left, numeric bo'lsa right)
numericboolean—O'ngga tekislanadi, tabular-nums
sortableboolean—Sarlavha bosilsa saralanadi
filter'select' | 'multiselect' | 'range' | 'date'—Filtr turi ("Filtrlar" oynasida)
filterOptionsDataTableFilterOption[]—select / multiselect variantlari { value, label } (standart — ma'lumotdagi noyob qiymatlar)
filterLabelstring—Filtr va badge'dagi nom (standart — header matni yoki key)
searchablebooleantrueUmumiy qidiruvda qatnashadimi
searchValue(row: T) => string—Qidiruv uchun matn (masalan, holat kodi o'rniga uning nomi)
hideablebooleantrue"Ustunlar" menyusida yashirish mumkinmi
hiddenboolean—Boshlang'ich holatda yashirin
classNamestring—Katak (td) klassi

Qiymat va ko'rinishni ajrating

cell faqat ko'rinish uchun. Saralash, filtr va qidiruv qiymat bilan ishlaydi (accessor yoki row[key]). Shuning uchun summani number, sanani Date holida saqlang va cell da formatlang — saralash to'g'ri bo'ladi, qidiruvda ham "1042" va "1 042" ikkalasi topiladi.

Saralash

sortable ustun sarlavhasini bosing: o'sish → kamayish → o'chiq. Shift bilan bosilsa, ustun avvalgisiga qo'shiladi (bir nechta ustun bo'yicha saralash), sarlavhada tartib raqami ko'rinadi. Saralash qiymat turini taniydi: son, sana, mantiqiy qiymat va matn (o'zbek alifbosi bo'yicha, "10" > "9"); bo'sh qiymatlar oxirida turadi.

Boshlang'ich saralash — initialState:

<DataTable
  data={orders}
  columns={columns}
  initialState={{ sorting: [{ id: 'date', desc: true }] }}
/>

Qidiruv

searchable jadval ustiga qidiruv maydonini qo'shadi. Qidiruv 300ms kechikish (debounce) bilan ishlaydi va o'zbek yozuviga moslashgan: kirill va lotin farqsiz (жасур → "Jasur"), oʻ, o' va o‘ bir xil. Qidiruv barcha ustunlarda (searchable: false dan tashqari) ishlaydi; ko'rinishi boshqa bo'lgan ustunlar uchun searchValue bering.

Filtrlar

Ustunga filter bersangiz, asboblar panelida "Filtrlar" tugmasi paydo bo'ladi. Faol filtrlar jadval ustida badge sifatida ko'rinadi va ✕ bilan o'chiriladi. Hech narsa topilmasa — "Filtrlarni tozalash" tugmasi.

TurBoshqaruvQiymat (DataTableFilterValue)
selectRadio: bitta qiymatstring
multiselectCheckbox'lar: bir nechta qiymatstring[]
rangeIkki son: "dan" — "gacha"{ min?: number; max?: number }
dateIkki sana: boshlanish — tugash{ from?: Date | null; to?: Date | null }

select va multiselect variantlari filterOptions dan, bo'lmasa ma'lumotdagi noyob qiymatlardan olinadi — har birining yonida nechta qator borligi ko'rsatiladi. date oralig'ida chegaralar kiradi (tugash kuni ham). Quyidagi demoda boshlang'ich filtr initialState orqali berilgan.

To'lov turi: Click
№MijozTo'lov turiViloyat
#501Dilnoza YusupovaClickToshkent25.10.202650 000 so'm
#505Dilnoza YusupovaClickSamarqand21.10.2026903 828 so'm
#509Dilnoza YusupovaClickAndijon17.10.20261 757 656 so'm
#513Dilnoza YusupovaClickNamangan13.10.20262 611 484 so'm
#517Dilnoza YusupovaClickToshkent09.10.2026465 312 so'm
1–5 / 10

Tanlash va ommaviy amallar

selectable — chapda Checkbox. Sarlavhadagi Checkbox joriy sahifani tanlaydi (qisman tanlanganda — indeterminate). Qator tanlansa, asboblar paneli ommaviy amallar paneliga aylanadi: "2 ta tanlandi", bulkActions dagi tugmalar va "Bekor qilish". Shift bilan Checkbox bosilsa, oraliq tanlanadi.

ArtikulAmallar
Paxta ko'ylakKY-00142
Atlas ro'molRM-0148
Choynak (1.5 l)CH-2030
Do'ppiDP-077120
Sopol laganLG-31015
<DataTable
  data={products}
  columns={columns}
  selectable
  getRowId={(p) => p.sku} // standart — row.id, bo'lmasa tartib raqami
  onSelectionChange={(ids) => setSelectedIds(ids)}
  bulkActions={(rows, clearSelection) => (
    <Button
      size="sm"
      onClick={() => {
        toast(`${rows.length} ta mahsulot arxivlandi`);
        clearSelection();
      }}
    >
      Arxivlash
    </Button>
  )}
/>

Tanlash boshqariladigan bo'lishi ham mumkin: selectedIds + onSelectionChange. rowActions — qator oxiridagi amallar (bosilganda onRowClick ishlamaydi).

Sahifalash

pagination={{ pageSize: 10 }} — pastda "1–10 / 248", sahifa raqamlari va sahifa hajmi tanlovi (standart variantlar: 10, 25, 50, 100; o'zgartirish — pageSizeOptions). Saralash, qidiruv yoki filtr o'zgarganda birinchi sahifaga qaytiladi; o'chirishdan keyin sahifa bo'shab qolsa — oxirgi mavjud sahifaga. pagination bo'lmasa, barcha qatorlar birdaniga chiziladi — ko'p ma'lumot uchun virtual ni ishlating (u pastda jami qatorlar sonini ko'rsatadi).

Holatlar

HolatKo'rinish
Birinchi yuklanishloading + bo'sh data — Skeleton qatorlar
Qayta yuklanishloading + eski data — 60% shaffof va yuqorida ingichka progress
Ma'lumot yo'qempty — ikonka, sarlavha, izoh, amal tugmasi
Hech narsa topilmadiQidiruv/filtr natijasi bo'sh — "Filtrlarni tozalash"
Xatoerror (matn yoki true) + onRetry — "Qayta urinish" tugmasi

Qayta yuklanishda jadval "sakramaydi" — eski ma'lumot joyida qoladi. Tugmalarni bosib ko'ring:

№MijozSumma

Server rejimi

Ma'lumot serverda saralansa, filtrlansa va sahifalansa — manual bering. Jadval ma'lumotga tegmaydi: saralash, qidiruv, filtr va sahifa holatini onStateChange orqali beradi, siz shu holat bilan serverga so'rov yuborasiz. Jami yozuvlar soni — rowCount (sahifalash uchun).

onStateChange ga keladigan holat (DataTableState):

interface DataTableState {
  sorting: { id: string; desc: boolean }[]; // id — ustun key'i
  search: string; // 300ms debounce'dan keyingi qiymat
  filters: Record<string, DataTableFilterValue>; // ustun key'i → filtr qiymati
  pagination: { pageIndex: number; pageSize: number }; // pageIndex 0 dan
}

Haqiqiy loyihada holatni URL parametrlariga aylantirib, fetch qilasiz:

OrdersTable.tsx
import { DataTable, type DataTableState } from '@akhatmirzo/ui';
import { useEffect, useState } from 'react';
import { columns, type Order } from './columns';

export function OrdersTable() {
  const [state, setState] = useState<DataTableState>({
    sorting: [],
    search: '',
    filters: {},
    pagination: { pageIndex: 0, pageSize: 25 },
  });
  const [page, setPage] = useState<{ rows: Order[]; total: number }>({ rows: [], total: 0 });
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string>();

  useEffect(() => {
    const controller = new AbortController();
    const params = new URLSearchParams({
      page: String(state.pagination.pageIndex + 1),
      size: String(state.pagination.pageSize),
      q: state.search,
      sort: state.sorting.map((s) => (s.desc ? '-' : '') + s.id).join(','),
      filters: JSON.stringify(state.filters),
    });
    setLoading(true);
    fetch(`/api/orders?${params}`, { signal: controller.signal })
      .then((res) => res.json())
      .then((data: { rows: Order[]; total: number }) => {
        setPage(data);
        setError(undefined);
        setLoading(false);
      })
      .catch((err: Error) => {
        if (err.name === 'AbortError') return;
        setError('Server javob bermadi');
        setLoading(false);
      });
    return () => controller.abort();
  }, [state]);

  return (
    <DataTable
      label="Buyurtmalar"
      data={page.rows}
      rowCount={page.total}
      columns={columns}
      manual
      searchable
      pagination={{ pageSize: 25 }}
      loading={loading}
      error={error}
      onRetry={() => setState({ ...state })}
      onStateChange={setState}
    />
  );
}

Server rejimida filtr variantlari

manual da jadval faqat joriy sahifani ko'radi, shuning uchun select / multiselect filtrlari uchun filterOptions ni albatta bering — aks holda variantlar faqat ko'rinib turgan qatorlardan yig'iladi. Variantlar yonidagi qatorlar soni bu rejimda ko'rsatilmaydi.

Virtual scroll — 10 000 qator

Hamma ma'lumot brauzerda bo'lsa-yu, sahifalash kerak bo'lmasa — virtual. DOM'da faqat ko'rinadigan qatorlar (va bir oz zaxira, virtual={{ overscan: 10 }}) chiziladi, saralash, qidiruv va filtr esa butun ma'lumot bo'yicha ishlaydi. Balandlik — maxHeight (standart 480px), sarlavha yopishqoq. virtual bilan pagination e'tiborsiz qoldiriladi.

Viloyat
1Jasur KarimovToshkent50 000 so'm
2Malika RahimovaNavoiy427 311 so'm
3Farhod TursunovSamarqand804 622 so'm
4Mohira NazarovaXorazm1 181 933 so'm
5Jasur KarimovBuxoro1 559 244 so'm
6Malika RahimovaToshkent1 936 555 so'm
7Farhod TursunovNavoiy2 313 866 so'm
8Mohira NazarovaSamarqand2 691 177 so'm
9Jasur KarimovXorazm3 068 488 so'm
10Malika RahimovaBuxoro3 445 799 so'm
11Farhod TursunovToshkent3 823 110 so'm
12Mohira NazarovaNavoiy4 200 421 so'm
13Jasur KarimovSamarqand4 577 732 so'm
14Malika RahimovaXorazm155 043 so'm
15Farhod TursunovBuxoro532 354 so'm
16Mohira NazarovaToshkent909 665 so'm
17Jasur KarimovNavoiy1 286 976 so'm
10 000 ta qator

Ro'yxat jadval bo'lmasa, xuddi shu texnika — VirtualList.

Zichlik, yopishqoq ustun va ustunlarni yashirish

  • density="compact" — 40px qatorlar (standart comfortable — 52px).
  • stickyFirstColumn — gorizontal scroll'da birinchi ustun (va tanlash Checkbox'i) joyida qoladi. Tor ekranda demoni yon tomonga suring.
  • "Ustunlar" menyusi (columnToggle, standart yoqiq) ustunlarni ko'rsatadi/yashiradi; hideable: false ustun ro'yxatda chiqmaydi, hidden: true — boshida yashirin.
  • maxHeight berilsa, sarlavha jadval ichida yopishqoq bo'ladi.
XodimLavozimBo'limTelefonMaosh
Sarvar AbduxoliqovFrontend dasturchiIT+998 90 123 45 6714 500 000 so'm
Gulnora O'rinovaBuxgalterMoliya+998 93 555 12 349 800 000 so'm
Javohir IsmoilovDizaynerMarketing+998 97 700 00 1111 200 000 so'm

Telefonda kartalar

Standart holatda tor ekranda jadval gorizontal scroll bo'ladi. mobileView="cards" — 640px dan tor ekranda har bir qator karta ko'rinishida (sarlavha: qiymat). Brauzer oynasini toraytirib ko'ring.

№MijozViloyatSumma
1042Jasur KarimovToshkent1 250 000 so'm
1043Malika RahimovaSamarqand480 000 so'm
1044Aziz NormatovFarg'ona2 730 000 so'm

Props

Qolgan <div> atributlarini qabul qiladi (ildizga). className — ildizga, tableClassName — <table> ga.

Ma'lumot

PropTipStandartTavsif
datamajburiyreadonly T[]—Qatorlar
columnsmajburiyreadonly DataTableColumn<T>[]—Ustunlar (yuqoridagi jadval)
getRowId(row: T, index: number) => string—Qator id'si (tanlash uchun). Standart — row.id, bo'lmasa tartib raqami
labelstring—Jadval nomi (ekran o'quvchi uchun)

Imkoniyatlar

PropTipStandartTavsif
searchablebooleanfalseUmumiy qidiruv (300ms, o'zbekcha normallashtirish)
selectablebooleanfalseCheckbox ustuni
selectedIdsstring[]—Tanlangan qatorlar (boshqariladigan)
defaultSelectedIdsstring[]—
onSelectionChange(ids: string[], rows: T[]) => void—
bulkActions(rows: T[], clearSelection: () => void) => ReactNode—Ommaviy amallar paneli
paginationfalse | { pageSize?: number; pageSizeOptions?: number[] }—Sahifalash; berilmasa — o'chiq
virtualboolean | { overscan?: number }falseVirtual scroll (sahifalash o'rniga)
maxHeightnumber | string—Jadval qismi balandligi; virtual da standart 480. Sarlavha yopishqoq bo'ladi
columnTogglebooleantrue"Ustunlar" menyusi
initialStatePartial<DataTableState>—Boshlang'ich saralash, qidiruv, filtr, sahifa
onRowClick(row: T) => void—Qator bosilganda (yoki Enter)
rowActions(row: T) => ReactNode—Qator oxiridagi amallar
toolbarReactNode—Asboblar panelidagi qo'shimcha elementlar (masalan "Qo'shish")

Server rejimi

PropTipStandartTavsif
manualbooleanfalseSaralash/filtr/sahifa ma'lumotga qo'llanmaydi
rowCountnumber—manual da jami yozuvlar soni
onStateChange(state: DataTableState) => void—Holat o'zgarganda (har qanday rejimda)

Holatlar va ko'rinish

PropTipStandartTavsif
loadingbooleanfalseMa'lumot yo'q — Skeleton; bor — shaffof + progress
errorReactNode—Xato matni yoki true
onRetry() => void—"Qayta urinish" tugmasi
emptyDataTableEmptyState—Bo'sh holat: { icon, title, description, action } (hammasi ReactNode)
density'comfortable' | 'compact''comfortable'Qator balandligi: 52 / 40px
stickyFirstColumnbooleanfalse
mobileView'table' | 'cards''table'Telefonda (< 640px) ko'rinish
labelsDataTableLabels—Barcha matnlar
tableClassNamestring—<table> klassi

Matnlar (labels)

Barcha matnlar o'zbekcha standartga ega; faqat kerakli kalitlarni almashtirasiz. Son bilan bog'liqlari — funksiya:

<DataTable
  labels={{
    search: "Mijoz, № yoki viloyat bo'yicha",
    selected: (n) => `${n} ta buyurtma tanlandi`,
    range: (from, to, total) => `${from}–${to}, jami ${total}`,
  }}
/>

Kalitlar: search, searchAria, filters, columns, selectAll, selectRow, selected, clearSelection, emptyTitle, emptyDescription, noResultsTitle, noResultsDescription, clearFilters, errorTitle, retry, range, total, noRows, pageSize, prevPage, nextPage, page, pagination, rowActions, loading, all, min, max, from, to, removeFilter.

Data atributlari

AtributElementQiymat
data-row-idtrqator id'si
data-selectedtrtanlangan qatorda mavjud
data-sortedsaralash tugmasiasc · desc

data-slot: data-table, data-table-toolbar, data-table-bulk, data-table-scroll, data-table-footer, data-table-state.

Klaviatura

TugmaHarakat
TabQidiruv, filtrlar, sarlavha tugmalari, qatorlar, amallar
Enter (sarlavhada)Saralash: o'sish → kamayish → o'chiq
Enter (qatorda)onRowClick
Space (qatorda)Qatorni tanlash (selectable)

Qatorlar faqat onRowClick yoki selectable bo'lganda fokuslanadi.

Accessibility

  • Haqiqiy <table> — <th scope="col">, jadval nomi label orqali (aria-label).
  • Saralash holati sarlavhada aria-sort (ascending / descending / none) bilan; saralanadigan sarlavha — <button>.
  • Yuklanish — jadvalda aria-busy, qayta yuklanishdagi chiziq — role="progressbar".
  • Holat xabarlari (bo'sh, topilmadi) — role="status", xato — role="alert".
  • Tanlash: har bir Checkbox'da "Qatorni tanlash: <id>", sarlavhada "Hammasini tanlash"; "N ta tanlandi" aria-live bilan e'lon qilinadi.
  • Sahifalash — <nav aria-label="Sahifalash">, joriy sahifa aria-current="page", "1–10 / 248" aria-live hududida.
  • Filtrlar: har bir guruh nomlangan, faol filtr badge'ining ✕ tugmasi "Holat filtrini olib tashlash" kabi nomga ega.
  • Virtual rejim — aria-rowcount va har qatorda aria-rowindex: ekran o'quvchi umumiy qatorlar sonini biladi.

Sahifada