Урок 21. Примеры: диалоги, панели, меню, вкладки
⚡ Кратко
Пять оверлеев: подтверждение удаления, диалог с формой, боковая панель, меню строки таблицы и вкладки настроек.
1. Подтверждение удаления
// src/components/delete-report-dialog.tsx
import {
Dialog, DialogClose, DialogContent, DialogDescription,
DialogFooter, DialogHeader, DialogTitle, DialogTrigger,
} from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
export function DeleteReportDialog() {
return (
<Dialog>
<DialogTrigger render={<Button variant="destructive">Удалить отчёт</Button>} />
<DialogContent>
<DialogHeader>
<DialogTitle>Удалить отчёт?</DialogTitle>
<DialogDescription>
Действие нельзя отменить. Отчёт и его выгрузки будут удалены.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose render={<Button variant="ghost">Отмена</Button>} />
<Button variant="destructive" onClick={handleDelete}>Удалить</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
Список отчётов…
Удалить отчёт?
Действие нельзя отменить. Отчёт и его выгрузки будут удалены.
<div class="relative h-52 overflow-hidden rounded-xl bg-muted">
<div class="p-4 text-sm text-muted-foreground">Список отчётов…</div>
<div class="absolute inset-0 z-40 bg-black/10 backdrop-blur-xs"></div>
<div class="absolute top-1/2 left-1/2 z-50 grid w-[calc(100%-2rem)] max-w-sm -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10">
<div class="grid gap-1.5">
<h3 class="text-base leading-snug font-medium">Удалить отчёт?</h3>
<p class="text-sm text-muted-foreground">Действие нельзя отменить. Отчёт и его выгрузки будут удалены.</p>
</div>
<div class="flex justify-end gap-2">
<button class="inline-flex h-8 items-center justify-center rounded-lg px-3 text-sm font-medium hover:bg-muted">Отмена</button>
<button class="inline-flex h-8 items-center justify-center rounded-lg bg-destructive/10 px-3 text-sm font-medium text-destructive hover:bg-destructive/20">Удалить</button>
</div>
</div>
</div>
Опасное действие — справа, отмена — слева и менее заметная. Заголовок сформулирован вопросом, описание объясняет последствия.
2. Диалог с формой
<Dialog>
<DialogTrigger render={<Button>Пригласить участника</Button>} />
<DialogContent>
<DialogHeader>
<DialogTitle>Пригласить участника</DialogTitle>
<DialogDescription>Отправим письмо со ссылкой на вступление.</DialogDescription>
</DialogHeader>
<form id="invite" onSubmit={handleSubmit} className="grid gap-3">
<Field>
<FieldLabel htmlFor="invite-email">Почта</FieldLabel>
<Input id="invite-email" type="email" autoComplete="email" required />
</Field>
</form>
<DialogFooter>
<DialogClose render={<Button variant="ghost">Отмена</Button>} />
<Button type="submit" form="invite">Отправить</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Пригласить участника
Отправим письмо со ссылкой на вступление.
<div class="w-full max-w-sm rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10">
<div class="grid gap-1.5">
<h3 class="text-base leading-snug font-medium">Пригласить участника</h3>
<p class="text-sm text-muted-foreground">Отправим письмо со ссылкой на вступление.</p>
</div>
<div class="mt-4 grid gap-1.5">
<label class="text-sm font-medium" for="ov-invite">Почта</label>
<input id="ov-invite" type="email" placeholder="name@example.com"
class="h-8 w-full rounded-lg border border-input bg-transparent px-2.5 text-sm outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50">
</div>
<div class="mt-4 flex justify-end gap-2">
<button class="inline-flex h-8 items-center justify-center rounded-lg px-3 text-sm font-medium hover:bg-muted">Отмена</button>
<button class="inline-flex h-8 items-center justify-center rounded-lg bg-primary px-3 text-sm font-medium text-primary-foreground">Отправить</button>
</div>
</div>
Приём с form="invite": кнопка лежит в футере диалога,
вне тега <form>, но отправляет именно его.
3. Боковая панель фильтров
<Sheet>
<SheetTrigger render={<Button variant="outline">Фильтры</Button>} />
<SheetContent side="right">
<SheetHeader>
<SheetTitle>Фильтры</SheetTitle>
<SheetDescription>Уточните выборку отчётов</SheetDescription>
</SheetHeader>
<div className="grid gap-3 p-4">…</div>
<SheetFooter>
<Button className="w-full">Применить</Button>
</SheetFooter>
</SheetContent>
</Sheet>
Таблица отчётов…
<div class="relative h-56 overflow-hidden rounded-xl bg-muted">
<div class="p-4 text-sm text-muted-foreground">Таблица отчётов…</div>
<div class="absolute inset-0 z-40 bg-black/10"></div>
<aside class="absolute inset-y-0 right-0 z-50 flex w-72 flex-col gap-4 bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10">
<div class="grid gap-1">
<h3 class="text-base font-medium">Фильтры</h3>
<p class="text-sm text-muted-foreground">Уточните выборку отчётов</p>
</div>
<div class="grid gap-2 text-sm">
<label class="flex items-center gap-2"><input type="checkbox" checked class="size-4 accent-primary"> Только мои</label>
<label class="flex items-center gap-2"><input type="checkbox" class="size-4 accent-primary"> С вложениями</label>
<label class="flex items-center gap-2"><input type="checkbox" class="size-4 accent-primary"> Архивные</label>
</div>
<div class="mt-auto flex gap-2">
<button class="inline-flex h-8 flex-1 items-center justify-center rounded-lg px-3 text-sm font-medium hover:bg-muted">Сбросить</button>
<button class="inline-flex h-8 flex-1 items-center justify-center rounded-lg bg-primary px-3 text-sm font-medium text-primary-foreground">Применить</button>
</div>
</aside>
</div>
4. Меню действий в строке таблицы
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="ghost" size="icon-sm" aria-label="Действия" />}>
⋯
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Отчёт № 1042</DropdownMenuLabel>
<DropdownMenuItem>Открыть</DropdownMenuItem>
<DropdownMenuItem>Скачать PDF</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">Удалить</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
Отчёт № 1042
Обновлён вчера
Отчёт № 1042
<div class="flex w-full max-w-lg items-start justify-between gap-4">
<div class="flex-1 rounded-lg bg-card p-3 text-sm text-card-foreground ring-1 ring-border">
<p class="font-medium">Отчёт № 1042</p>
<p class="text-muted-foreground">Обновлён вчера</p>
</div>
<div class="w-52 rounded-xl bg-popover p-1 text-sm text-popover-foreground ring-1 ring-foreground/10">
<div class="px-2 py-1.5 text-xs text-muted-foreground">Отчёт № 1042</div>
<button class="w-full rounded-lg px-2 py-1.5 text-left hover:bg-muted">Открыть</button>
<button class="w-full rounded-lg px-2 py-1.5 text-left hover:bg-muted">Скачать PDF</button>
<div class="my-1 h-px bg-border"></div>
<button class="w-full rounded-lg px-2 py-1.5 text-left text-destructive hover:bg-destructive/10">Удалить</button>
</div>
</div>
5. Вкладки настроек
<Tabs defaultValue="profile">
<TabsList>
<TabsTrigger value="profile">Профиль</TabsTrigger>
<TabsTrigger value="security">Безопасность</TabsTrigger>
<TabsTrigger value="billing">Оплата</TabsTrigger>
</TabsList>
<TabsContent value="profile">…</TabsContent>
<TabsContent value="security">…</TabsContent>
<TabsContent value="billing">…</TabsContent>
</Tabs>
Профиль
Имя, фото и контактные данные.
<div class="w-full max-w-lg">
<div class="inline-flex h-8 w-fit items-center justify-center rounded-lg bg-muted p-[3px] text-muted-foreground">
<button class="inline-flex h-full items-center justify-center rounded-md bg-background px-3 text-sm font-medium text-foreground shadow-sm">Профиль</button>
<button class="inline-flex h-full items-center justify-center rounded-md px-3 text-sm font-medium hover:text-foreground">Безопасность</button>
<button class="inline-flex h-full items-center justify-center rounded-md px-3 text-sm font-medium hover:text-foreground">Оплата</button>
</div>
<div class="mt-3 rounded-xl bg-card p-4 text-sm text-card-foreground ring-1 ring-border">
<p class="font-medium">Профиль</p>
<p class="mt-1 text-muted-foreground">Имя, фото и контактные данные.</p>
</div>
</div>
6. Вкладки-подчёркивания
Вариант оформления TabsList: line.
<div class="w-full max-w-lg">
<div class="flex gap-1 border-b border-border">
<button class="border-b-2 border-primary px-3 pb-2 text-sm font-medium text-foreground">Обзор</button>
<button class="border-b-2 border-transparent px-3 pb-2 text-sm font-medium text-muted-foreground hover:border-border hover:text-foreground">Активность</button>
<button class="border-b-2 border-transparent px-3 pb-2 text-sm font-medium text-muted-foreground hover:border-border hover:text-foreground">Настройки</button>
</div>
<div class="pt-3 text-sm text-muted-foreground">Вариант оформления TabsList: line.</div>
</div>