- Accordion
- Alert
- Alert Dialog
- Autocomplete
- Auth Surface
- Avatar
- Badge
- Browse Catalog Dialog
- Button
- Card
- Checkbox
- Checkbox Group
- Collapsible
- Combobox
- Command
- Connector Setup Dialog
- Cookie Banner
- Dialog
- Directory Card
- Directory Detail
- Directory Skeleton
- DrawerНовое
- Token Parts Input
- Empty
- Field
- Fieldset
- File Preview Modal
- File Preview Skeleton
- Form
- Frame
- Group
- Icon
- Input
- Input Group
- Kbd
- Label
- Legal Shell
- Menu
- Mermaid Diagram
- Mind Map Diagram
- Not Found Screen
- Onboarding Frame
- Popover
- PDF Thumbnail
- Personalization Landing
- Preview Card
- Pricing Page
- Progress
- Radio Group
- Ring Spinner
- Scroll Area
- Select
- Separator
- Settings Page
- Settings Skills
- Settings Connectors
- Settings Capabilities
- Settings Usage
- Settings Account
- Settings Billing
- Sheet
- Sidebar
- Skeleton
- Skill Create Dialog
- Slider
- Spinner
- Stat
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Tooltip
- Компоненты AI
- Chat Conversation
- Chat Message
- Chat Response
- Chat Suggestion
- Chat Prompt Input
- Slash Highlighted Textarea
- Chat Search Dialog
- Chat Skill Doc
- Chat Connector Detail
- Chat Attachments
- Chat File Card
- Chat Token Chip
- Chat Code Block
- Chat Image
- Chat Inline Citation
- Chat Sources
- Chat Web Search
- Chat Research
- Chat Source
- Chat Actions
- Chat Context
- Chat Loader
- Chat Compaction
- Chat Timeline
- Chat Snippet
- Chat Terminal
- Chat Stack Trace
- Chat Test Results
- Chat File Tree
- Chat Environment Variables
- Chat Audio Player
- Chat Transcription
- Chat Speech Input
- Chat Mic Selector
- Chat Voice Selector
- Chat Agent
- Chat Persona
- Chat Connection
- Chat Connector Suggestion
- Chat Queue
- Chat Checkpoint
- Chat Confirmation
- Chat Artifact
- Chat JSX Preview
- Chat Schema Display
- Chat Package Info
- Chat Commit
- Chat Plan
- Chat Open In Chat
- Chat Sandbox
- Chat Model Selector
- Chat Canvas
- Chat Node
- Chat Edge
Drawer
Панель, выезжающая с края экрана, с поддержкой свайп-жестов, точек привязки и вложенных панелей.
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerClose,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
export default function Particle() {
return (
<Drawer>
<DrawerTrigger render={<Button variant="outline" />}>
Открыть панель
</DrawerTrigger>
<DrawerPopup showBar>
<DrawerHeader className="text-center">
<DrawerTitle>Уведомления</DrawerTitle>
<DrawerDescription>Это описание выдвижной панели.</DrawerDescription>
</DrawerHeader>
<DrawerFooter
className="justify-center sm:justify-center"
variant="bare"
>
<DrawerClose render={<Button variant="outline" />}>
Закрыть
</DrawerClose>
</DrawerFooter>
</DrawerPopup>
</Drawer>
);
}
Установка
pnpm dlx shadcn@latest add @oracul/drawer
Использование
import {
Drawer,
DrawerCreateHandle,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerMenu,
DrawerMenuCheckboxItem,
DrawerMenuGroup,
DrawerMenuGroupLabel,
DrawerMenuItem,
DrawerMenuRadioGroup,
DrawerMenuRadioItem,
DrawerMenuSeparator,
DrawerPanel,
DrawerPopup,
DrawerMenuTrigger,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"<Drawer>
<DrawerTrigger>Open</DrawerTrigger>
<DrawerPopup>
<DrawerHeader>
<DrawerTitle>Drawer Title</DrawerTitle>
<DrawerDescription>Drawer Description</DrawerDescription>
</DrawerHeader>
<DrawerPanel>Content</DrawerPanel>
<DrawerFooter>
<DrawerClose>Close</DrawerClose>
</DrawerFooter>
</DrawerPopup>
</Drawer>API
Drawer
Корневой компонент. Оборачивает Drawer.Root из Base UI с автоматическим сопоставлением swipeDirection на основе position.
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
position | "right" | "left" | "top" | "bottom" | "bottom" | Управляет тем, с какого края открывается панель. Автоматически задаёт направление свайпа и передаёт его дочерним компонентам через контекст |
swipeDirection | "up" | "down" | "left" | "right" | вычисляется | Переопределяет направление свайпа, выведенное из position. Используйте, когда нужно другое поведение свайпа |
Поддерживаются все остальные свойства Drawer.Root, включая open, onOpenChange, modal, snapPoints, snapPoint, onSnapPointChange и snapToSequentialPoints. Примечание: точки привязки работают только для нижних панелей.
DrawerCreateHandle
Создаёт хэндл для отсоединённых триггеров панели. Используйте его, когда триггеры и содержимое панели нужно согласовать между разными частями дерева.
DrawerTrigger
Кнопка-триггер, открывающая панель. Псевдоним для Drawer.Trigger из Base UI.
DrawerPopup
Контейнер всплывающего окна, отображающий содержимое панели.
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
variant | "default" | "straight" | "inset" | "default" | Управляет стилем панели. straight убирает скруглённые углы, inset добавляет отступы вокруг панели на десктопных экранах |
showCloseButton | boolean | false | Если true, отображает кнопку закрытия в правом верхнем углу |
showBar | boolean | false | Если true, отображает индикатор-полоску для перетаскивания |
portalProps | Drawer.Portal.Props | - | Свойства, передаваемые внутреннему порталу (keepMounted, container и т. д.); см. API портала Drawer в Base UI |
Пример:
// Bottom drawer (default)
<Drawer>
<DrawerPopup>...</DrawerPopup>
</Drawer>
// Right side drawer
<Drawer position="right">
<DrawerPopup>...</DrawerPopup>
</Drawer>
// Left side drawer
<Drawer position="left">
<DrawerPopup>...</DrawerPopup>
</Drawer>
// Drawer with inset variant
<Drawer position="right">
<DrawerPopup variant="inset">...</DrawerPopup>
</Drawer>DrawerHeader
Контейнер для заголовка и описания панели. Поддерживает свойство render для полиморфной композиции (например, render={<header />}).
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
allowSelection | boolean | false | Если true, оборачивает заголовок в DrawerContent, чтобы уменьшить помехи свайпа при выделении текста мышью |
DrawerFooter
Секция футера для кнопок действий. Поддерживает свойство render для полиморфной композиции.
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
variant | "default" | "bare" | "default" | Управляет оформлением футера. default включает рамку и фон, bare убирает их |
allowSelection | boolean | true | Если true, оборачивает футер в DrawerContent, чтобы уменьшить помехи свайпа при выделении текста мышью |
DrawerTitle
Компонент заголовка. Псевдоним для Drawer.Title из Base UI.
DrawerDescription
Компонент описания. Псевдоним для Drawer.Description из Base UI.
DrawerPanel
Контейнер содержимого. Когда scrollable равно true (по умолчанию), оборачивает содержимое в компонент ScrollArea. Поддерживает свойство render для полиморфной композиции.
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
scrollable | boolean | true | Если true, оборачивает содержимое в ScrollArea. Установите false для непрокручиваемого содержимого |
scrollFade | boolean | true | Если true, показывает эффект затухания у краёв прокрутки (применяется, только когда scrollable равно true) |
allowSelection | boolean | true | Если true, оборачивает панель в DrawerContent, чтобы уменьшить помехи свайпа при выделении текста мышью |
DrawerMenu
Контейнер для пунктов меню панели. Используйте вместе с DrawerMenuItem, DrawerMenuSeparator, DrawerMenuGroup, DrawerMenuGroupLabel, DrawerMenuCheckboxItem, DrawerMenuRadioGroup, DrawerMenuRadioItem и DrawerMenuTrigger, чтобы создавать меню, повторяющие API компонента Menu. Поддерживает свойство render для полиморфной композиции.
DrawerMenuItem
Стилизованный пункт меню, соответствующий внешнему виду MenuItem. Не закрывает панель — оберните в DrawerClose, когда нужно закрытие по клику. Поддерживает свойство render для полиморфной композиции.
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
variant | "default" | "destructive" | "default" | При destructive использует цвет деструктивного текста |
disabled | boolean | — | Если true, отключает пункт |
Пример: используйте с DrawerClose для закрытия по клику: <DrawerClose render={<DrawerMenuItem />}>Edit</DrawerClose>
DrawerMenuSeparator
Горизонтальный разделитель между пунктами меню или группами. Поддерживает свойство render для полиморфной композиции.
DrawerMenuGroup
Контейнер для группировки связанных пунктов меню. Используйте с DrawerMenuGroupLabel для секций с подписями. Поддерживает свойство render для полиморфной композиции.
DrawerMenuGroupLabel
Подпись для секции DrawerMenuGroup. Отображает приглушённый текст меньшего размера над группой пунктов. Во вложенных панелях используйте как заголовок секции в верхней части меню (кнопка «назад» не нужна — свайпните, чтобы вернуться). Поддерживает свойство render для полиморфной композиции.
DrawerMenuTrigger
Триггер, открывающий вложенную панель. Стилизован как пункт меню с шевроном в конце. Используйте для пунктов меню, открывающих вложенные панели (например, «Add to Playlist» → вложенная панель с вариантами плейлистов). Внутри оборачивает DrawerTrigger.
Пример: <DrawerMenuTrigger>Add to Playlist</DrawerMenuTrigger> открывает вложенную панель при касании.
DrawerMenuCheckboxItem
Пункт меню с чекбоксом для независимых переключений. Поддерживает variant="switch" для переключателей в виде свитчей. Для групп выбора используйте DrawerMenuRadioGroup вместе с DrawerMenuRadioItem.
DrawerMenuRadioGroup
Контейнер для радио-пунктов меню. Используйте с DrawerMenuRadioItem для взаимоисключающих вариантов (например, «Sort by» с вариантами Artist, Album, Title).
DrawerMenuRadioItem
Радио-пункт меню. Должен использоваться внутри DrawerMenuRadioGroup со свойством value.
DrawerClose
Компонент кнопки закрытия. Псевдоним для Drawer.Close из Base UI.
DrawerPortal
Компонент портала для рендеринга вне иерархии DOM. Псевдоним для Drawer.Portal из Base UI.
DrawerBackdrop
Компонент подложки/оверлея. Псевдоним для Drawer.Backdrop из Base UI.
DrawerViewport
Компонент области просмотра для позиционирования. Псевдоним для Drawer.Viewport из Base UI. Обычно не используется напрямую — DrawerPopup рендерит его внутри.
DrawerBar
Индикатор-ручка для перетаскивания, отображаемый, когда showBar равно true у DrawerPopup. Обычно не используется напрямую.
DrawerContent
Примитивная обёртка содержимого, используемая для полиморфной композиции со свойством render. Псевдоним для Drawer.Content из Base UI.
Примеры
Вариант inset
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerHeader,
DrawerPanel,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
export default function Particle() {
return (
<div className="flex flex-wrap gap-2">
<Drawer position="right">
<DrawerTrigger render={<Button variant="outline" />}>
Справа
</DrawerTrigger>
<DrawerPopup variant="inset">
<DrawerHeader>
<DrawerTitle>Справа</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<p className="text-muted-foreground text-sm">Содержимое справа.</p>
</DrawerPanel>
</DrawerPopup>
</Drawer>
<Drawer position="left">
<DrawerTrigger render={<Button variant="outline" />}>
Слева
</DrawerTrigger>
<DrawerPopup variant="inset">
<DrawerHeader>
<DrawerTitle>Слева</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<p className="text-muted-foreground text-sm">Содержимое слева.</p>
</DrawerPanel>
</DrawerPopup>
</Drawer>
<Drawer position="top">
<DrawerTrigger render={<Button variant="outline" />}>
Сверху
</DrawerTrigger>
<DrawerPopup variant="inset">
<DrawerHeader>
<DrawerTitle>Сверху</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<p className="text-muted-foreground text-sm">Содержимое сверху.</p>
</DrawerPanel>
</DrawerPopup>
</Drawer>
<Drawer>
<DrawerTrigger render={<Button variant="outline" />}>
Снизу
</DrawerTrigger>
<DrawerPopup variant="inset">
<DrawerHeader>
<DrawerTitle>Снизу</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<p className="text-muted-foreground text-sm">Содержимое снизу.</p>
</DrawerPanel>
</DrawerPopup>
</Drawer>
</div>
);
}
Вариант straight
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerHeader,
DrawerPanel,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
export default function Particle() {
return (
<div className="flex flex-wrap gap-2">
<Drawer position="right">
<DrawerTrigger render={<Button variant="outline" />}>
Справа
</DrawerTrigger>
<DrawerPopup variant="straight">
<DrawerHeader>
<DrawerTitle>Справа</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<p className="text-muted-foreground text-sm">Содержимое справа.</p>
</DrawerPanel>
</DrawerPopup>
</Drawer>
<Drawer position="left">
<DrawerTrigger render={<Button variant="outline" />}>
Слева
</DrawerTrigger>
<DrawerPopup variant="straight">
<DrawerHeader>
<DrawerTitle>Слева</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<p className="text-muted-foreground text-sm">Содержимое слева.</p>
</DrawerPanel>
</DrawerPopup>
</Drawer>
<Drawer position="top">
<DrawerTrigger render={<Button variant="outline" />}>
Сверху
</DrawerTrigger>
<DrawerPopup variant="straight">
<DrawerHeader>
<DrawerTitle>Сверху</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<p className="text-muted-foreground text-sm">Содержимое сверху.</p>
</DrawerPanel>
</DrawerPopup>
</Drawer>
<Drawer>
<DrawerTrigger render={<Button variant="outline" />}>
Снизу
</DrawerTrigger>
<DrawerPopup variant="straight">
<DrawerHeader>
<DrawerTitle>Снизу</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<p className="text-muted-foreground text-sm">Содержимое снизу.</p>
</DrawerPanel>
</DrawerPopup>
</Drawer>
</div>
);
}
Прокручиваемое содержимое
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerClose,
DrawerFooter,
DrawerHeader,
DrawerPanel,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
export default function Particle() {
return (
<Drawer>
<DrawerTrigger render={<Button variant="outline" />}>
Прокручиваемое содержимое
</DrawerTrigger>
<DrawerPopup showBar>
<DrawerHeader>
<DrawerTitle>Прокручиваемое содержимое</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<div className="flex flex-col gap-2">
{Array.from({ length: 48 }, (_, i) => `box-${i}`).map((key) => (
<div
className="h-12 shrink-0 rounded-xl border bg-muted"
key={key}
/>
))}
</div>
</DrawerPanel>
<DrawerFooter>
<DrawerClose render={<Button variant="outline" />}>
Закрыть
</DrawerClose>
</DrawerFooter>
</DrawerPopup>
</Drawer>
);
}
Вложенные панели
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerClose,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerPanel,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
export default function Particle() {
return (
<Drawer>
<DrawerTrigger render={<Button variant="outline" />}>
Вложенные шторки
</DrawerTrigger>
<DrawerPopup showBar>
<DrawerHeader className="text-center">
<DrawerTitle>Первый шаг</DrawerTitle>
<DrawerDescription>
Это первый шаг. Нажмите кнопку ниже, чтобы перейти к следующему
экрану.
</DrawerDescription>
</DrawerHeader>
<DrawerFooter
className="justify-center sm:justify-center"
variant="bare"
>
<DrawerClose render={<Button variant="ghost" />}>Отмена</DrawerClose>
<Drawer>
<DrawerTrigger render={<Button variant="outline" />}>
Продолжить
</DrawerTrigger>
<DrawerPopup showBar>
<DrawerHeader className="text-center">
<DrawerTitle>Второй шаг</DrawerTitle>
<DrawerDescription>
Вы перешли ко второму шагу. Нажмите кнопку ниже, чтобы перейти
к следующему экрану.
</DrawerDescription>
</DrawerHeader>
<DrawerPanel>
<div className="flex justify-center">
<div className="size-48 shrink-0 rounded-xl border bg-muted" />
</div>
</DrawerPanel>
<DrawerFooter
className="justify-center sm:justify-center"
variant="bare"
>
<DrawerClose render={<Button variant="ghost" />}>
Назад
</DrawerClose>
<Drawer>
<DrawerTrigger render={<Button variant="outline" />}>
Продолжить
</DrawerTrigger>
<DrawerPopup showBar>
<DrawerHeader className="text-center">
<DrawerTitle>Третий шаг</DrawerTitle>
<DrawerDescription>
Вы дошли до последнего шага. Можно закрыть шторку или
вернуться назад.
</DrawerDescription>
</DrawerHeader>
<DrawerPanel>
<div className="flex justify-center">
<div className="size-32 shrink-0 rounded-full border bg-muted" />
</div>
</DrawerPanel>
</DrawerPopup>
</Drawer>
</DrawerFooter>
</DrawerPopup>
</Drawer>
</DrawerFooter>
</DrawerPopup>
</Drawer>
);
}
Точки привязки
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerDescription,
DrawerHeader,
DrawerPanel,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
export default function Particle() {
const snapPoints = ["300px", 1] as const;
const [snapPoint, setSnapPoint] = useState<
(typeof snapPoints)[number] | null
>(snapPoints[0]);
return (
<Drawer
onSnapPointChange={(point) =>
setSnapPoint(point as (typeof snapPoints)[number] | null)
}
position="bottom"
snapPoint={snapPoint}
snapPoints={[...snapPoints]}
snapToSequentialPoints
>
<DrawerTrigger render={<Button variant="outline" />}>
С точками привязки
</DrawerTrigger>
<DrawerPopup showBar>
<DrawerHeader>
<DrawerTitle>Точки привязки</DrawerTitle>
<DrawerDescription>
Перетащите шторку, чтобы переключиться между компактным режимом и
полноэкранным видом.
</DrawerDescription>
</DrawerHeader>
<DrawerPanel>
<div className="flex flex-col gap-2">
{Array.from({ length: 48 }, (_, i) => `box-${i}`).map((key) => (
<div
className="h-12 shrink-0 rounded-xl border bg-muted"
key={key}
/>
))}
</div>
</DrawerPanel>
</DrawerPopup>
</Drawer>
);
}
Мобильное меню
import Link from "next/link";
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerClose,
DrawerHeader,
DrawerPanel,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
export default function Particle() {
return (
<Drawer position="left">
<DrawerTrigger render={<Button variant="outline" />}>
Открыть меню
</DrawerTrigger>
<DrawerPopup showCloseButton variant="straight">
<DrawerHeader>
<DrawerTitle>Меню</DrawerTitle>
</DrawerHeader>
<DrawerPanel>
<nav className="-mx-[calc(--spacing(3)-1px)] flex flex-col gap-0.5">
<DrawerClose
nativeButton={false}
render={
<Button
className="justify-start"
render={<Link href="#" />}
variant="ghost"
/>
}
>
Главная
</DrawerClose>
<DrawerClose
nativeButton={false}
render={
<Button
className="justify-start"
render={<Link href="#" />}
variant="ghost"
/>
}
>
Профиль
</DrawerClose>
<DrawerClose
nativeButton={false}
render={
<Button
className="justify-start"
render={<Link href="#" />}
variant="ghost"
/>
}
>
Настройки
</DrawerClose>
<DrawerClose
nativeButton={false}
render={
<Button
className="justify-start"
render={<Link href="#" />}
variant="ghost"
/>
}
>
Выйти
</DrawerClose>
</nav>
</DrawerPanel>
</DrawerPopup>
</Drawer>
);
}
Адаптивный диалог
Используйте Drawer на маленьких экранах и Dialog на больших, чтобы показывать одно и то же содержимое в модальном окне на десктопе и в нижней панели на мобильных.
"use client";
import { useMediaQuery } from "@/hooks/use-media-query";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogClose,
DialogDescription,
DialogFooter,
DialogHeader,
DialogPanel,
DialogPopup,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Drawer,
DrawerClose,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerPanel,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
import { Field, FieldLabel } from "@/components/ui/field";
import { Form } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
const FORM_TITLE = "Редактирование профиля";
const FORM_DESCRIPTION =
"Измените данные профиля. По завершении нажмите «Сохранить».";
const TRIGGER_LABEL = "Открыть";
const CANCEL_LABEL = "Отмена";
const SAVE_LABEL = "Сохранить";
const formFields = (
<>
<Field>
<FieldLabel>Имя</FieldLabel>
<Input defaultValue="Маргарита Уэлш" type="text" />
</Field>
<Field>
<FieldLabel>Имя пользователя</FieldLabel>
<Input defaultValue="@maggie.welsh" type="text" />
</Field>
</>
);
export default function Particle() {
const isMobile = useMediaQuery("max-md");
if (isMobile) {
return (
<Drawer>
<DrawerTrigger render={<Button variant="outline" />}>
{TRIGGER_LABEL}
</DrawerTrigger>
<DrawerPopup showBar>
<DrawerHeader>
<DrawerTitle>{FORM_TITLE}</DrawerTitle>
<DrawerDescription>{FORM_DESCRIPTION}</DrawerDescription>
</DrawerHeader>
<Form className="contents">
<DrawerPanel className="grid gap-4" scrollable={false}>
{formFields}
</DrawerPanel>
<DrawerFooter>
<DrawerClose render={<Button variant="ghost" />}>
{CANCEL_LABEL}
</DrawerClose>
<Button type="submit">{SAVE_LABEL}</Button>
</DrawerFooter>
</Form>
</DrawerPopup>
</Drawer>
);
}
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
{TRIGGER_LABEL}
</DialogTrigger>
<DialogPopup className="sm:max-w-sm">
<DialogHeader>
<DialogTitle>{FORM_TITLE}</DialogTitle>
<DialogDescription>{FORM_DESCRIPTION}</DialogDescription>
</DialogHeader>
<Form className="contents">
<DialogPanel className="grid gap-4">{formFields}</DialogPanel>
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>
{CANCEL_LABEL}
</DialogClose>
<Button type="submit">{SAVE_LABEL}</Button>
</DialogFooter>
</Form>
</DialogPopup>
</Dialog>
);
}
Адаптивное меню
Сочетайте Drawer с Menu, чтобы действия отображались в выпадающем меню на десктопе и в выезжающей снизу панели на мобильных. Используйте DrawerMenuTrigger для пунктов, открывающих вложенные панели на мобильных. Во вложенных панелях DrawerMenuGroupLabel выступает заголовком секции; свайпните, чтобы вернуться.
"use client";
import {
IconCopy,
IconDots,
IconPencil,
IconShare,
IconTrash,
} from "@tabler/icons-react";
import { useMediaQuery } from "@/hooks/use-media-query";
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerClose,
DrawerMenu,
DrawerMenuCheckboxItem,
DrawerMenuGroup,
DrawerMenuGroupLabel,
DrawerMenuItem,
DrawerMenuRadioGroup,
DrawerMenuRadioItem,
DrawerMenuSeparator,
DrawerMenuTrigger,
DrawerPanel,
DrawerPopup,
DrawerTrigger,
} from "@/components/ui/drawer";
import {
Menu,
MenuCheckboxItem,
MenuGroup,
MenuGroupLabel,
MenuItem,
MenuPopup,
MenuRadioGroup,
MenuRadioItem,
MenuSeparator,
MenuSub,
MenuSubPopup,
MenuSubTrigger,
MenuTrigger,
} from "@/components/ui/menu";
const TRIGGER_ARIA_LABEL = "Открыть меню";
export default function Particle() {
const isMobile = useMediaQuery("max-md");
if (isMobile) {
return (
<Drawer>
<DrawerTrigger
render={
<Button
aria-label={TRIGGER_ARIA_LABEL}
size="icon"
variant="outline"
/>
}
>
<IconDots aria-hidden stroke={1.5} />
</DrawerTrigger>
<DrawerPopup showBar>
<DrawerPanel>
<DrawerMenu>
<DrawerMenuGroup>
<DrawerMenuGroupLabel>Действия</DrawerMenuGroupLabel>
<DrawerClose render={<DrawerMenuItem />}>
<IconPencil aria-hidden stroke={1.5} />
Редактировать
</DrawerClose>
<DrawerClose render={<DrawerMenuItem />}>
<IconCopy aria-hidden stroke={1.5} />
Дублировать
</DrawerClose>
<DrawerClose render={<DrawerMenuItem />}>
<IconShare aria-hidden stroke={1.5} />
Поделиться
</DrawerClose>
</DrawerMenuGroup>
<DrawerMenuSeparator />
<DrawerMenuCheckboxItem>Перемешать</DrawerMenuCheckboxItem>
<DrawerMenuCheckboxItem>Повтор</DrawerMenuCheckboxItem>
<DrawerMenuCheckboxItem disabled>
Улучшенный звук
</DrawerMenuCheckboxItem>
<DrawerMenuSeparator />
<DrawerMenuGroup>
<DrawerMenuGroupLabel>Сортировать по</DrawerMenuGroupLabel>
<DrawerMenuRadioGroup defaultValue="artist">
<DrawerMenuRadioItem value="artist">
Исполнителю
</DrawerMenuRadioItem>
<DrawerMenuRadioItem value="album">
Альбому
</DrawerMenuRadioItem>
<DrawerMenuRadioItem value="title">
Названию
</DrawerMenuRadioItem>
</DrawerMenuRadioGroup>
</DrawerMenuGroup>
<DrawerMenuSeparator />
<DrawerMenuCheckboxItem variant="switch">
Автосохранение
</DrawerMenuCheckboxItem>
<DrawerMenuSeparator />
<Drawer>
<DrawerMenuTrigger>Добавить в плейлист</DrawerMenuTrigger>
<DrawerPopup showBar>
<DrawerPanel>
<DrawerMenu>
<DrawerMenuGroup>
<DrawerMenuGroupLabel>
Добавить в плейлист
</DrawerMenuGroupLabel>
</DrawerMenuGroup>
<DrawerClose render={<DrawerMenuItem />}>
Джаз
</DrawerClose>
<Drawer>
<DrawerMenuTrigger>Рок</DrawerMenuTrigger>
<DrawerPopup showBar>
<DrawerPanel>
<DrawerMenu>
<DrawerMenuGroup>
<DrawerMenuGroupLabel>Рок</DrawerMenuGroupLabel>
</DrawerMenuGroup>
<DrawerClose render={<DrawerMenuItem />}>
Хард-рок
</DrawerClose>
<DrawerClose render={<DrawerMenuItem />}>
Софт-рок
</DrawerClose>
<DrawerClose render={<DrawerMenuItem />}>
Классический рок
</DrawerClose>
<DrawerMenuSeparator />
<DrawerClose render={<DrawerMenuItem />}>
Метал
</DrawerClose>
<DrawerClose render={<DrawerMenuItem />}>
Панк
</DrawerClose>
<DrawerClose render={<DrawerMenuItem />}>
Гранж
</DrawerClose>
<DrawerClose render={<DrawerMenuItem />}>
Альтернатива
</DrawerClose>
<DrawerClose render={<DrawerMenuItem />}>
Инди
</DrawerClose>
<DrawerClose render={<DrawerMenuItem />}>
Электроника
</DrawerClose>
</DrawerMenu>
</DrawerPanel>
</DrawerPopup>
</Drawer>
<DrawerClose render={<DrawerMenuItem />}>Поп</DrawerClose>
</DrawerMenu>
</DrawerPanel>
</DrawerPopup>
</Drawer>
<DrawerMenuSeparator />
<DrawerMenuGroup>
<DrawerMenuGroupLabel>Опасная зона</DrawerMenuGroupLabel>
<DrawerClose render={<DrawerMenuItem variant="destructive" />}>
<IconTrash aria-hidden stroke={1.5} />
Удалить
</DrawerClose>
</DrawerMenuGroup>
</DrawerMenu>
</DrawerPanel>
</DrawerPopup>
</Drawer>
);
}
return (
<Menu>
<MenuTrigger
render={
<Button
aria-label={TRIGGER_ARIA_LABEL}
size="icon"
variant="outline"
/>
}
>
<IconDots aria-hidden stroke={1.5} />
</MenuTrigger>
<MenuPopup>
<MenuGroup>
<MenuGroupLabel>Действия</MenuGroupLabel>
<MenuItem>
<IconPencil aria-hidden stroke={1.5} />
Редактировать
</MenuItem>
<MenuItem>
<IconCopy aria-hidden stroke={1.5} />
Дублировать
</MenuItem>
<MenuItem>
<IconShare aria-hidden stroke={1.5} />
Поделиться
</MenuItem>
</MenuGroup>
<MenuSeparator />
<MenuCheckboxItem>Перемешать</MenuCheckboxItem>
<MenuCheckboxItem>Повтор</MenuCheckboxItem>
<MenuCheckboxItem disabled>Улучшенный звук</MenuCheckboxItem>
<MenuSeparator />
<MenuGroup>
<MenuGroupLabel>Сортировать по</MenuGroupLabel>
<MenuRadioGroup defaultValue="artist">
<MenuRadioItem value="artist">Исполнителю</MenuRadioItem>
<MenuRadioItem value="album">Альбому</MenuRadioItem>
<MenuRadioItem value="title">Названию</MenuRadioItem>
</MenuRadioGroup>
</MenuGroup>
<MenuSeparator />
<MenuCheckboxItem variant="switch">Автосохранение</MenuCheckboxItem>
<MenuSeparator />
<MenuSub>
<MenuSubTrigger>Добавить в плейлист</MenuSubTrigger>
<MenuSubPopup>
<MenuItem>Джаз</MenuItem>
<MenuSub>
<MenuSubTrigger>Рок</MenuSubTrigger>
<MenuSubPopup>
<MenuItem>Хард-рок</MenuItem>
<MenuItem>Софт-рок</MenuItem>
<MenuItem>Классический рок</MenuItem>
<MenuSeparator />
<MenuItem>Метал</MenuItem>
<MenuItem>Панк</MenuItem>
<MenuItem>Гранж</MenuItem>
<MenuItem>Альтернатива</MenuItem>
<MenuItem>Инди</MenuItem>
<MenuItem>Электроника</MenuItem>
</MenuSubPopup>
</MenuSub>
<MenuItem>Поп</MenuItem>
</MenuSubPopup>
</MenuSub>
<MenuSeparator />
<MenuGroup>
<MenuGroupLabel>Опасная зона</MenuGroupLabel>
<MenuItem variant="destructive">
<IconTrash aria-hidden stroke={1.5} />
Удалить
</MenuItem>
</MenuGroup>
</MenuPopup>
</Menu>
);
}
На этой странице