Popover
Ichida interaktiv kontent bo'ladigan suzuvchi panel: filtr, kichik forma, sozlamalar.
'use client';
import { Button, Input, Popover } from '@akhatmirzo/ui';
import { Funnel } from 'lucide-react';
import { useState } from 'react';
export default function PopoverFilter() {
const [open, setOpen] = useState(false);
const [range, setRange] = useState({ from: '', to: '' });
const [applied, setApplied] = useState<string | null>(null);
return (
<div className="flex flex-wrap items-center gap-3">
<Popover open={open} onOpenChange={setOpen}>
<Popover.Trigger asChild>
<Button leftIcon={<Funnel />}>Narx</Button>
</Popover.Trigger>
<Popover.Content align="start" aria-label="Narx filtri">
<form
className="flex flex-col gap-3"
onSubmit={(e) => {
e.preventDefault();
setApplied(`${range.from || '0'} – ${range.to || '∞'} so'm`);
setOpen(false);
}}
>
<p className="m-0 font-semibold">Narx oralig'i</p>
<Input
size="sm"
inputMode="numeric"
placeholder="dan"
suffix="so'm"
aria-label="Narx: dan"
value={range.from}
onChange={(e) => setRange((r) => ({ ...r, from: e.target.value }))}
/>
<Input
size="sm"
inputMode="numeric"
placeholder="gacha"
suffix="so'm"
aria-label="Narx: gacha"
value={range.to}
onChange={(e) => setRange((r) => ({ ...r, to: e.target.value }))}
/>
<div className="mt-1 flex justify-end gap-2">
<Popover.Close asChild>
<Button size="sm">Bekor</Button>
</Popover.Close>
<Button size="sm" variant="primary" type="submit">
Qo'llash
</Button>
</div>
</form>
</Popover.Content>
</Popover>
<span className="text-sm text-text-muted">{applied ?? "Filtr qo'llanmagan"}</span>
</div>
);
}
Import
import { Popover } from '@akhatmirzo/ui';Qachon ishlatish
- Kichik, o'zaro ta'sirli kontent — narx filtri, rang tanlash, bir nechta sozlama. Sahifa bloklanmaydi, panel trigger yonida ochiladi.
- Faqat matnli izoh uchun
Tooltipyetarli. Popover'dan farqi: bosish bilan ochiladi va ichida tugma, maydon bo'lishi mumkin. - Buyruqlar ro'yxati ("Tahrirlash", "O'chirish") —
DropdownMenu: unda menyu rollari va klaviatura navigatsiyasi tayyor. - Katta forma yoki diqqat talab qiladigan vazifa —
Dialog.
Tuzilishi
| Qism | Vazifasi |
|---|---|
Popover | Holat: open, defaultOpen, onOpenChange |
Popover.Trigger | Ochuvchi element (asChild bilan) |
Popover.Content | Panel: portal, joylashuv, animatsiya, ixtiyoriy strelka |
Popover.Close | Bosilganda panelni yopadi (asChild bilan) |
Popover.Anchor | Panelni triggerga emas, boshqa elementga nisbatan joylashtirish |
<Popover>
<Popover.Trigger asChild>
<Button>Narx</Button>
</Popover.Trigger>
<Popover.Content align="start" aria-label="Narx filtri">
{/* forma */}
</Popover.Content>
</Popover>Panelning standart kengligi — 288px (w-72). O'zgartirish uchun className bering: w-56,
w-auto va hokazo.
Joylashuv va strelka
side — top, right, bottom (standart) yoki left; align — start, center (standart)
yoki end. Panel triggerdan 6px uzoqlikda turadi va ekran chetiga yetsa, o'zi teskari tomonga
o'tadi. arrow triggerga qaragan strelka chizadi.
'use client';
import { Button, Popover } from '@akhatmirzo/ui';
export default function PopoverPlacement() {
return (
<div className="flex flex-wrap items-center gap-3">
{(['top', 'right', 'bottom', 'left'] as const).map((side) => (
<Popover key={side}>
<Popover.Trigger asChild>
<Button variant="outline">{side}</Button>
</Popover.Trigger>
<Popover.Content side={side} arrow className="w-56" aria-label={`Tomon: ${side}`}>
<p className="m-0 text-text-muted">
Panel <b className="text-text">{side}</b> tomonda, triggerdan 6px uzoqlikda.
</p>
</Popover.Content>
</Popover>
))}
<Popover>
<Popover.Trigger asChild>
<Button variant="outline">align=end</Button>
</Popover.Trigger>
<Popover.Content align="end" className="w-56" aria-label="O'ngga tekislangan">
<p className="m-0 text-text-muted">Panel triggerning o'ng chetiga tekislangan.</p>
</Popover.Content>
</Popover>
</div>
);
}
Sozlamalar paneli
Ikonka tugma va bir nechta Switch — kichik sozlamalar uchun tipik misol. Panel ochilganda fokus
ichidagi birinchi elementga o'tadi.
'use client';
import { Field, IconButton, Popover, Switch } from '@akhatmirzo/ui';
import { Bell } from 'lucide-react';
export default function PopoverSettings() {
return (
<Popover>
<Popover.Trigger asChild>
<IconButton variant="outline" aria-label="Bildirishnoma sozlamalari">
<Bell />
</IconButton>
</Popover.Trigger>
<Popover.Content side="right" align="start" arrow aria-label="Bildirishnomalar">
<div className="flex flex-col gap-3">
<p className="m-0 font-semibold">Bildirishnomalar</p>
<Field label="Email orqali" orientation="horizontal">
<Switch defaultChecked />
</Field>
<Field label="SMS orqali" orientation="horizontal">
<Switch />
</Field>
<Field label="Telegram bot" orientation="horizontal">
<Switch defaultChecked />
</Field>
</div>
</Popover.Content>
</Popover>
);
}
Tanlab yopish
Popover.Close asChild ga o'ralgan element bosilganda panel o'zi yopiladi — rang yoki variant
tanlash uchun qulay. Boshqariladigan rejimda (open + onOpenChange) panelni kod orqali ham
yopasiz — birinchi misoldagi forma shunday ishlaydi.
'use client';
import { Button, Popover } from '@akhatmirzo/ui';
import { useState } from 'react';
const colors = [
{ name: 'Asosiy', className: 'bg-primary' },
{ name: 'Yashil', className: 'bg-success' },
{ name: 'Sariq', className: 'bg-warning' },
{ name: 'Qizil', className: 'bg-danger' },
{ name: "Ko'k", className: 'bg-info' },
];
export default function PopoverColorPicker() {
const [color, setColor] = useState(colors[0]!);
return (
<Popover>
<Popover.Trigger asChild>
<Button
variant="outline"
leftIcon={<span className={`size-3 rounded-full ${color.className}`} />}
>
Teg rangi
</Button>
</Popover.Trigger>
<Popover.Content className="w-auto" aria-label="Rang tanlash">
<div className="flex gap-2">
{colors.map((c) => (
<Popover.Close key={c.name} asChild>
<button
type="button"
aria-label={c.name}
aria-pressed={c.name === color.name}
onClick={() => setColor(c)}
className={`focus-ring size-7 cursor-pointer rounded-full border-2 border-surface-raised ${c.className} aria-pressed:ring-2 aria-pressed:ring-primary`}
/>
</Popover.Close>
))}
</div>
</Popover.Content>
</Popover>
);
}
Dialog ichida
Dialog yoki Drawer ichidagi Popover paneli modal qatlamiga o'tadi va oyna ustida ochiladi —
qo'shimcha sozlash kerak emas.
Props
Popover
Radix Popover Root prop'larini qabul qiladi.
| Prop | Tip | Standart | Tavsif |
|---|---|---|---|
open | boolean | — | Ochiqmi (boshqariladigan rejim) |
defaultOpen | boolean | — | Boshlang'ich holatda ochiq |
onOpenChange | (open: boolean) => void | — | Ochilib-yopilganda |
modal | boolean | false | true — tashqi kontent bilan ishlash bloklanadi |
Popover.Content
Radix Popover.Content ning qolgan prop'lari (onOpenAutoFocus, onEscapeKeyDown,
alignOffset, avoidCollisions ...), <div> atributlari va ref ham qabul qilinadi.
| Prop | Tip | Standart | Tavsif |
|---|---|---|---|
side | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | Qaysi tomonda ochiladi |
align | 'start' | 'center' | 'end' | 'center' | Trigger bo'ylab tekislash |
sideOffset | number | 6 | Triggerdan masofa, px |
collisionPadding | number | 8 | Ekran chetidan minimal masofa, px |
arrow | boolean | false | Triggerga qaragan strelka |
container | HTMLElement | null | — | Portal konteyneri (standart — document.body) |
Popover.Trigger, Popover.Close, Popover.Anchor
Uchalasi ham asChild qabul qiladi. Popover.Anchor bo'lsa, panel trigger o'rniga shu elementga
nisbatan joylashadi (masalan, butun qatorga).
Data atributlari
| Atribut | Qayerda | Qiymat |
|---|---|---|
data-state | trigger, panel | open · closed |
data-side | panel | top · right · bottom · left |
data-align | panel | start · center · end |
Klaviatura
| Tugma | Harakat |
|---|---|
Enter / Space | Trigger fokusda — panelni ochadi yoki yopadi |
Tab | Panel ichidagi elementlar bo'ylab yurish |
Esc | Panelni yopadi, fokus triggerga qaytadi |
Fokus paneldan tashqariga chiqsa yoki tashqariga bosilsa, panel yopiladi.
Accessibility
- Bog'lanish. Trigger
aria-haspopup="dialog",aria-expandedvaaria-controlsoladi; panel —role="dialog". Panelgaaria-labelbering (masalan, "Narx filtri") — ekran o'quvchi uni shu nom bilan e'lon qiladi. - Fokus. Ochilganda fokus panel ichidagi birinchi elementga o'tadi, yopilgach triggerga qaytadi.
- Ikonka tugma trigger bo'lsa,
IconButtongaaria-labelyozishni unutmang. - Strelka faqat bezak.