Combobox
Qidiruvli tanlash: o'zbekcha harflarni tushunadigan qidiruv, bir nechta tanlov, yangi variant yaratish va server qidiruvi.
Hali tanlanmagan
'use client';
import { Combobox, Field } from '@akhatmirzo/ui';
import { useState } from 'react';
const cities = [
'Toshkent',
'Samarqand',
'Buxoro',
'Xiva',
'Namangan',
'Andijon',
"Farg'ona",
"Qo'qon",
"Marg'ilon",
'Nukus',
'Qarshi',
'Termiz',
].map((label) => ({ value: label.toLowerCase().replace(/'/g, ''), label }));
export default function ComboboxBasic() {
const [city, setCity] = useState<string | null>(null);
return (
<Field
label="Shahar"
description={city ? `Qiymat: ${city}` : 'Hali tanlanmagan'}
className="w-full max-w-xs"
>
<Combobox
options={cities}
value={city}
onValueChange={setCity}
placeholder="Shahar tanlang"
clearable
/>
</Field>
);
}
Import
import { Combobox, type ComboboxOption } from '@akhatmirzo/ui';Qachon ishlatish
- Ro'yxat uzun bo'lsa — shaharlar, tumanlar, mijozlar, mahsulotlar. 10–15 tadan ko'p variantni ko'z bilan qidirish qiyin, yozib topish tezroq.
- Bir nechta qiymat kerak bo'lsa —
multiple: teglar, filiallar, mas'ul xodimlar. - Ro'yxatda yo'q qiymat ham bo'lishi mumkin bo'lsa —
creatable(yangi teg, yangi toifa). - Ma'lumot serverda bo'lsa —
onSearchvaloadingbilan har bir so'rovni serverga yuborasiz. - Variantlar kam va qidiruv kerak bo'lmasa — oddiy
Selectyetarli.
O'zbekcha qidiruv
Qidiruv @akhatmirzo/core dagi searchMatch bilan ishlaydi va o'zbek yozuvining odatiy
"chalkashliklarini" kechiradi:
- Apostrof turlari bir xil:
gʻ,g',g‘,g’— barchasi bitta harf. Foydalanuvchi apostrofni umuman yozmasa ham topiladi:ulugbek→ "Mirzo Ulug'bek". - Kirill ↔ lotin:
Чилонзорyozilsa "Chilonzor" topiladi,Тошкент— "Toshkent". - Registr farqi yo'q:
TOSHKENT,toshkent— bir xil. - So'z o'rtasidan ham:
chirchiq→ "Oʻrta Chirchiq". - Mos kelgan qism qalin bilan ajratiladi —
descriptionmatnida ham.
Yozib ko'ring: guzor, g‘allaorol, Чилонзор, ulugbek
'use client';
import { Combobox, Field } from '@akhatmirzo/ui';
const districts = [
'Gʻuzor',
"G'allaorol",
'Oʻrta Chirchiq',
"Qo'shrabot",
'Shahrisabz',
'Chilonzor',
'Yunusobod',
"Mirzo Ulug'bek",
'Toshkent tumani',
].map((label, i) => ({ value: String(i), label }));
export default function ComboboxUzbekSearch() {
return (
<div className="flex w-full max-w-xs flex-col gap-3">
<Field label="Tuman">
<Combobox options={districts} placeholder="Tumanni tanlang" />
</Field>
<p className="text-sm text-text-muted">
Yozib ko'ring: <span className="font-medium text-text">guzor</span>,{' '}
<span className="font-medium text-text">g‘allaorol</span>,{' '}
<span className="font-medium text-text">Чилонзор</span>,{' '}
<span className="font-medium text-text">ulugbek</span>
</p>
</div>
);
}
O'z qidiruvingizda ham ishlating
Xuddi shu funksiyalar alohida eksport qilinadi: searchMatch, normalizeUz, splitByRanges —
core/text sahifasiga qarang. Server qidiruvida ham so'rovni normalizeUz
bilan normallashtirish foydali.
Bir nechta tanlov
multiple bilan value massiv bo'ladi (string[]). Tanlanganlar trigger ichida Badge
ko'rinishida turadi va ✕ bilan olib tashlanadi. Panel har tanlovdan keyin ochiq qoladi; qidiruv
bo'sh bo'lganda Backspace oxirgi tanlovni o'chiradi.
2 ta tanlangan
'use client';
import { Combobox, Field } from '@akhatmirzo/ui';
import { useState } from 'react';
const branches = [
'Toshkent',
'Samarqand',
'Buxoro',
'Namangan',
'Andijon',
"Farg'ona",
'Nukus',
].map((label) => ({ value: label.toLowerCase().replace(/'/g, ''), label }));
export default function ComboboxMultiple() {
const [selected, setSelected] = useState<string[]>(['toshkent', 'samarqand']);
return (
<Field
label="Filiallar"
description={`${selected.length} ta tanlangan`}
className="w-full max-w-sm"
>
<Combobox
multiple
options={branches}
value={selected}
onValueChange={setSelected}
placeholder="Filiallarni tanlang"
/>
</Field>
);
}
Yangi variant yaratish
creatable yoqilsa va qidiruv matni mavjud variantlarning birortasiga to'liq mos kelmasa,
ro'yxat oxirida "«matn» yaratish" elementi chiqadi. onCreate yangi variantni qaytarsa, aynan u
tanlanadi; hech narsa qaytarmasa — qiymat va nom sifatida matnning o'zi olinadi. Yangi variantni
options ga o'zingiz qo'shasiz.
Ro'yxatda yo'q tegni yozing va Enter bosing
'use client';
import { Combobox, Field, type ComboboxOption } from '@akhatmirzo/ui';
import { useState } from 'react';
const initialTags: ComboboxOption[] = ['dizayn', 'frontend', 'backend', 'mobil'].map((t) => ({
value: t,
label: t,
}));
export default function ComboboxCreatable() {
const [tags, setTags] = useState(initialTags);
const [chosen, setChosen] = useState<string[]>(['frontend']);
return (
<Field
label="Teglar"
description="Ro'yxatda yo'q tegni yozing va Enter bosing"
className="w-full max-w-sm"
>
<Combobox
multiple
creatable
options={tags}
value={chosen}
onValueChange={setChosen}
onCreate={(text) => {
const option = { value: text.toLowerCase(), label: text };
setTags((prev) => [...prev, option]);
return option;
}}
placeholder="Teg qo'shing"
leftIcon={null}
/>
</Field>
);
}
Server qidiruvi
onSearch berilsa, mahalliy filtr o'chadi: so'rov 300ms debounce bilan sizga keladi, siz esa
yangi options ni berasiz. Javob kutilayotganda loading ro'yxat o'rnida spinner ko'rsatadi.
Panel yopilganda onSearch('') chaqiriladi — boshlang'ich ro'yxatni qaytarish uchun qulay.
'use client';
import { searchMatch } from '@akhatmirzo/core';
import { Combobox, Field, type ComboboxOption } from '@akhatmirzo/ui';
import { useRef, useState } from 'react';
const all: ComboboxOption[] = ['Toshkent', 'Samarqand', 'Buxoro', 'Xiva', 'Nukus', 'Termiz'].map(
(label) => ({ value: label.toLowerCase(), label }),
);
export default function ComboboxServer() {
const [options, setOptions] = useState(all.slice(0, 3));
const [loading, setLoading] = useState(false);
const timer = useRef<ReturnType<typeof setTimeout>>(undefined);
// Server so'rovini taqlid qiladi: javob 700ms dan keyin keladi
const search = (query: string) => {
clearTimeout(timer.current);
setLoading(true);
timer.current = setTimeout(() => {
setOptions(query ? all.filter((o) => searchMatch(o.label, query).match) : all.slice(0, 3));
setLoading(false);
}, 700);
};
return (
<Field label="Shahar (serverdan)" className="w-full max-w-xs">
<Combobox
options={options}
loading={loading}
onSearch={search}
placeholder="Qidirib tanlang"
searchPlaceholder="Shahar nomi..."
emptyText="Server hech narsa qaytarmadi"
/>
</Field>
);
}
Tanlangan qiymat yo'qolmaydi
Serverdan kelgan yangi ro'yxatda tanlangan variant bo'lmasa ham, Combobox uning nomini eslab qoladi va triggerda to'g'ri ko'rsatadi.
Izoh va ikonkalar
ComboboxOption da description (ikkinchi qator), icon va disabled bor. Qidiruv izoh
matnida ham ishlaydi. Triggerdagi lupa ikonkasini leftIcon={null} bilan olib tashlaysiz yoki
o'zingiznikiga almashtirasiz.
'use client';
import { Combobox, Field, type ComboboxOption } from '@akhatmirzo/ui';
function Dot({ className }: { className: string }) {
return <span className={`size-2.5 rounded-full ${className}`} />;
}
const statuses: ComboboxOption[] = [
{
value: 'new',
label: 'Yangi',
description: 'Buyurtma qabul qilindi',
icon: <Dot className="bg-info" />,
},
{
value: 'packing',
label: "Yig'ilmoqda",
description: 'Omborda tayyorlanmoqda',
icon: <Dot className="bg-warning" />,
},
{
value: 'shipping',
label: "Yo'lda",
description: 'Kuryerga topshirildi',
icon: <Dot className="bg-primary" />,
},
{
value: 'cancelled',
label: 'Bekor qilingan',
description: 'Arxivlangan holat',
icon: <Dot className="bg-danger" />,
disabled: true,
},
];
export default function ComboboxDetails() {
return (
<Field label="Buyurtma holati" className="w-full max-w-xs">
<Combobox options={statuses} defaultValue="packing" leftIcon={null} />
</Field>
);
}
interface ComboboxOption {
value: string;
label: string;
description?: string;
icon?: ReactNode;
disabled?: boolean;
}O'lchamlar
Input va Select bilan bir xil: 32, 40 va 48px. multiple da balandlik tanlovlar soniga
qarab o'sadi.
'use client';
import { Combobox } from '@akhatmirzo/ui';
const cities = ['Toshkent', 'Samarqand', 'Buxoro'].map((label) => ({
value: label.toLowerCase(),
label,
}));
export default function ComboboxSizes() {
return (
<div className="flex w-full max-w-xs flex-col gap-4">
<Combobox size="sm" options={cities} aria-label="Shahar" placeholder="sm — 32px" />
<Combobox size="md" options={cities} aria-label="Shahar" placeholder="md — 40px" />
<Combobox size="lg" options={cities} aria-label="Shahar" placeholder="lg — 48px" />
</div>
);
}
Holatlar
Field ichida disabled, error va required o'zi ulanadi. loading panel ochilganda
ro'yxat o'rnida spinner ko'rsatadi.
Shaharni tanlang
'use client';
import { Combobox, Field } from '@akhatmirzo/ui';
const cities = ['Toshkent', 'Samarqand', 'Buxoro'].map((label) => ({
value: label.toLowerCase(),
label,
}));
export default function ComboboxStates() {
return (
<div className="grid w-full gap-4 sm:grid-cols-3">
<Field label="O'chirilgan" disabled>
<Combobox options={cities} defaultValue="toshkent" />
</Field>
<Field label="Shahar" error="Shaharni tanlang" required>
<Combobox options={cities} placeholder="Shahar tanlang" />
</Field>
<Field label="Yuklanmoqda">
<Combobox options={[]} loading placeholder="Ochib ko'ring" />
</Field>
</div>
);
}
Dialog ichida
Dialog yoki Drawer ichida Combobox paneli modal qatlamiga
o'tadi va oyna ustida ochiladi — hech narsa sozlash shart emas.
Props
Combobox
Qolgan <button> atributlari (id, aria-label, aria-*, data-*, onBlur ...) va ref
triggerga uzatiladi.
| Prop | Tip | Standart | Tavsif |
|---|---|---|---|
optionsmajburiy | ComboboxOption[] | — | Variantlar ro'yxati |
multiple | boolean | false | Bir nechta tanlov: value — string[] |
value | string | null · string[] | — | Tanlangan qiymat (multiple da massiv) |
defaultValue | string | null · string[] | — | Boshlang'ich qiymat |
onValueChange | (value: string | null) => void · (value: string[]) => void | — | Qiymat o'zgarganda. Tozalanganda — null (yoki []) |
placeholder | string | 'Tanlang' | Qiymat yo'q paytdagi matn |
searchPlaceholder | string | 'Qidirish...' | Qidiruv maydonidagi matn |
emptyText | ReactNode | 'Hech narsa topilmadi' | Natija bo'lmaganda |
onSearch | (query: string) => void | — | Server qidiruvi: 300ms debounce, mahalliy filtr o'chadi |
loading | boolean | false | Ro'yxat o'rnida spinner |
creatable | boolean | false | "«matn» yaratish" elementi |
onCreate | (text: string) => ComboboxOption | void | — | Yaratilganda. Qaytarilgan variant tanlanadi |
clearable | boolean | false | Qiymat bo'lsa ✕ (tozalash) tugmasi |
leftIcon | ReactNode | lupa ikonkasi | Trigger chapidagi ikonka; null — ikonkasiz |
size | 'sm' | 'md' | 'lg' | 'md' | Balandlik: 32 / 40 / 48px |
invalid | boolean | — | Xato holati (Field ichida — avtomatik) |
disabled | boolean | — | Combobox'ni o'chiradi |
required | boolean | — | Majburiy maydon (aria-required) |
name | string | — | Forma uchun: har bir tanlov yashirin <input> sifatida yuboriladi |
open | boolean | — | Panel ochiqmi (boshqariladigan rejim) |
defaultOpen | boolean | false | Boshida ochiq |
onOpenChange | (open: boolean) => void | — | Ochilib-yopilganda |
labels | ComboboxLabels | — | Ekran o'quvchi matnlari (pastda) |
contentClassName | string | — | Panel klassi |
ComboboxLabels
Inglizcha yoki boshqa tildagi interfeys uchun matnlarni almashtiring.
| Prop | Tip | Standart | Tavsif |
|---|---|---|---|
search | string | 'Qidirish' | Qidiruv maydoni nomi |
list | string | 'Variantlar' | Ro'yxat nomi |
loading | string | 'Yuklanmoqda' | Spinner matni |
clear | string | 'Tozalash' | ✕ tugmasi nomi |
remove | (label: string) => string | `Olib tashlash: ${label}` | Badge'dagi ✕ nomi |
create | (text: string) => ReactNode | `«${text}» yaratish` | Yaratish elementi matni |
Data atributlari
| Atribut | Qayerda | Qiymat |
|---|---|---|
data-size | konteyner | sm · md · lg |
data-state | konteyner | open · closed |
data-invalid | konteyner | xato holatida mavjud |
data-disabled | konteyner | o'chirilganda mavjud |
data-placeholder | trigger | qiymat tanlanmaganda mavjud |
data-checked | variant | tanlangan variantda mavjud |
Klaviatura
| Tugma | Harakat |
|---|---|
Enter / Space / ↓ / ↑ | Trigger fokusda — panelni ochadi |
| Harf yoki raqam | Yopiq triggerda — panelni ochadi va qidiruvni shu belgidan boshlaydi |
| Yozish | Ochiq panelda — qidiruv |
↓ / ↑ | Variantlar bo'ylab yurish (aylanma) |
Home / End | Birinchi / oxirgi variant |
Enter | Tanlash. Bitta tanlovda panel yopiladi, multiple da ochiq qoladi |
Esc | Avval qidiruvni tozalaydi, ikkinchi marta — panelni yopadi |
Backspace | multiple: qidiruv bo'sh bo'lsa — oxirgi tanlovni o'chiradi |
Tab | Panelni yopadi va fokusni keyingi elementga o'tkazadi |
Accessibility
- Rollar. Trigger —
role="combobox",aria-haspopup="listbox", ochiq bo'lgandaaria-controlsro'yxatga ishora qiladi. Ro'yxatlistbox,multipledaaria-multiselectable="true"; har bir variantdaaria-checked. - Nom.
Fieldichida label va tanlangan qiymat birga o'qiladi ("Shahar, Toshkent").multipleda tanlanganlar ro'yxati ekran o'quvchi uchun yashirin matn sifatida qo'shiladi. - Field bilan bog'lanish.
descriptionvaerror—aria-describedby, xatodaaria-invalid="true",requireddaaria-required="true". - Badge'dagi ✕ har bir tanlov uchun alohida nom oladi ("Olib tashlash: Toshkent") —
labels.removebilan o'zgartiriladi. - Fokus. Panel ochilganda fokus qidiruv maydoniga o'tadi, yopilganda triggerga qaytadi
(
Tabbilan yopilsa — keyingi elementga). - Ikonkalar (
leftIcon, varianticon)aria-hidden.