HextaUI

Dropdown menu

Um menu de ações e opções atrás de um botão, com grupos, submenus, itens de checkbox e de rádio e atalhos.

import {
  IconBrandGithub,
  IconCreditCard,
  IconExternalLink,
  IconKeyboard,
  IconLogout,
  IconMail,
  IconMessage,
  IconPlus,
  IconSettings,
  IconUser,
  IconUserPlus,
} from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export function DropdownMenuDemo() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        Open
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56">
        <DropdownMenuGroup>
          <DropdownMenuLabel>My account</DropdownMenuLabel>
          <DropdownMenuItem>
            <IconUser />
            Profile
            <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            <IconCreditCard />
            Billing
            <DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            <IconSettings />
            Settings
            <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuItem>
            <IconKeyboard />
            Keyboard shortcuts
            <DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
          </DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuGroup>
          <DropdownMenuSub>
            <DropdownMenuSubTrigger>
              <IconUserPlus />
              Invite users
            </DropdownMenuSubTrigger>
            <DropdownMenuSubContent>
              <DropdownMenuItem>
                <IconMail />
                Email
              </DropdownMenuItem>
              <DropdownMenuItem>
                <IconMessage />
                Message
              </DropdownMenuItem>
              <DropdownMenuSeparator />
              <DropdownMenuItem>
                <IconPlus />
                More…
              </DropdownMenuItem>
            </DropdownMenuSubContent>
          </DropdownMenuSub>
          <DropdownMenuItem
            render={<a href="https://github.com" target="_blank" />}
          >
            <IconBrandGithub />
            GitHub
            <IconExternalLink className="ms-auto" />
          </DropdownMenuItem>
          <DropdownMenuItem disabled>API</DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuItem variant="destructive">
          <IconLogout />
          Log out
          <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/dropdown-menu.json

Adiciona o componente, os tokens de tema do HextaUI e quaisquer componentes do HextaUI dos quais ele depende.

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
<DropdownMenu>
  <DropdownMenuTrigger render={<Button variant="outline" />}>
    Open
  </DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuItem>Profile</DropdownMenuItem>
    <DropdownMenuItem>Billing</DropdownMenuItem>
    <DropdownMenuSeparator />
    <DropdownMenuItem variant="destructive">Log out</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>
DropdownMenu
├── DropdownMenuTrigger
└── DropdownMenuContent
    ├── DropdownMenuGroup
    │   ├── DropdownMenuLabel
    │   └── DropdownMenuItem
    │       └── DropdownMenuShortcut
    ├── DropdownMenuCheckboxItem
    ├── DropdownMenuRadioGroup
    │   └── DropdownMenuRadioItem
    ├── DropdownMenuSeparator
    └── DropdownMenuSub
        ├── DropdownMenuSubTrigger
        └── DropdownMenuSubContent

Área segura

Mover-se na diagonal de um gatilho de submenu em direção ao seu submenu cruza gatilhos vizinhos. A área segura mantém o submenu aberto enquanto o ponteiro se dirige a ele. Ative showSafeArea para vê-la ao vivo: o ponto fica verde dentro da área e vermelho onde o submenu trocaria.

"use client"

import * as React from "react"
import { IconDots } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

const folders = [
  "Inbox",
  "Design",
  "Engineering",
  "Marketing",
  "Finance",
  "Legal",
  "Archive",
  "Shared with me",
]

const submenus = [
  { label: "Move to", items: folders },
  { label: "Copy to", items: folders },
  { label: "Share", items: ["Copy link", "Email", "Slack"] },
  { label: "Export as", items: ["PDF", "PNG", "SVG"] },
]

export function DropdownMenuSafeArea() {
  const [showSafeArea, setShowSafeArea] = React.useState(true)

  return (
    <div className="flex flex-col items-center gap-6">
      <label className="flex items-center gap-3 text-sm">
        <Checkbox
          checked={showSafeArea}
          onCheckedChange={(checked) => setShowSafeArea(checked === true)}
        />
        Show safe area
      </label>
      <DropdownMenu showSafeArea={showSafeArea}>
        <DropdownMenuTrigger render={<Button variant="outline" />}>
          <IconDots data-icon="inline-start" />
          Q3 report.pdf
        </DropdownMenuTrigger>
        <DropdownMenuContent className="w-44">
          {submenus.map((submenu) => (
            <DropdownMenuSub key={submenu.label}>
              <DropdownMenuSubTrigger>{submenu.label}</DropdownMenuSubTrigger>
              <DropdownMenuSubContent className="w-44">
                {submenu.items.map((item) => (
                  <DropdownMenuItem key={item}>{item}</DropdownMenuItem>
                ))}
              </DropdownMenuSubContent>
            </DropdownMenuSub>
          ))}
          <DropdownMenuSeparator />
          <DropdownMenuItem>Rename</DropdownMenuItem>
          <DropdownMenuItem variant="destructive">Delete</DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </div>
  )
}

Checkboxes

<DropdownMenuCheckboxItem /> alterna uma configuração e mantém o menu aberto, para que várias possam ser alteradas em sequência. A marca é desenhada e apagada em vez de surgir de repente.

"use client"

import * as React from "react"
import { IconChevronDown } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuLabel,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export function DropdownMenuCheckboxes() {
  const [statusBar, setStatusBar] = React.useState(true)
  const [activityBar, setActivityBar] = React.useState(false)

  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        View
        <IconChevronDown data-icon="inline-end" />
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuGroup>
          <DropdownMenuLabel>Appearance</DropdownMenuLabel>
          <DropdownMenuCheckboxItem
            checked={statusBar}
            onCheckedChange={setStatusBar}
          >
            Status bar
          </DropdownMenuCheckboxItem>
          <DropdownMenuCheckboxItem
            checked={activityBar}
            onCheckedChange={setActivityBar}
          >
            Activity bar
          </DropdownMenuCheckboxItem>
          <DropdownMenuCheckboxItem disabled>Panel</DropdownMenuCheckboxItem>
        </DropdownMenuGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

Radio group

Envolva <DropdownMenuRadioItem /> em um <DropdownMenuRadioGroup /> para escolher uma opção. Um rótulo dentro do grupo dá nome a ele para os leitores de tela.

"use client"

import * as React from "react"
import { IconChevronDown } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuLabel,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export function DropdownMenuRadioGroupDemo() {
  const [position, setPosition] = React.useState("bottom")

  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        Panel position
        <IconChevronDown data-icon="inline-end" />
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
          <DropdownMenuLabel>Panel position</DropdownMenuLabel>
          <DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
          <DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
          <DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
        </DropdownMenuRadioGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

Marca à direita

Defina indicator="end" em itens de checkbox e radio para mostrar a marca depois do rótulo em vez de recuá-la, o que combina com seletores que têm uma descrição sob cada opção.

"use client"

import * as React from "react"
import { IconChevronDown } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuLabel,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

const plans = [
  { value: "starter", label: "Starter", description: "For side projects" },
  { value: "team", label: "Team", description: "Shared workspace and roles" },
  { value: "scale", label: "Scale", description: "SSO, audit log and SLA" },
]

export function DropdownMenuIndicatorEnd() {
  const [plan, setPlan] = React.useState("team")
  const current = plans.find((item) => item.value === plan)

  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        {current?.label}
        <IconChevronDown data-icon="inline-end" />
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuRadioGroup value={plan} onValueChange={setPlan}>
          <DropdownMenuLabel>Plan</DropdownMenuLabel>
          {plans.map((item) => (
            <DropdownMenuRadioItem
              key={item.value}
              value={item.value}
              indicator="end"
              closeOnClick
            >
              <span className="flex min-w-0 flex-col">
                <span>{item.label}</span>
                <span className="text-xs text-muted-foreground">
                  {item.description}
                </span>
              </span>
            </DropdownMenuRadioItem>
          ))}
        </DropdownMenuRadioGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

Estilo select

O menu nunca fica mais estreito que seu gatilho. Defina closeOnClick em itens radio quando escolher um também deve fechar o menu.

"use client"

import * as React from "react"
import { IconChevronDown, IconPlus } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

const workspaces = [
  "Acme Inc.",
  "Monsters Inc.",
  "A workspace with a very long name that wraps",
]

export function DropdownMenuSelect() {
  const [workspace, setWorkspace] = React.useState(workspaces[0])

  return (
    <div className="w-64 max-w-full">
      <DropdownMenu>
        <DropdownMenuTrigger
          render={
            <Button variant="outline" className="w-full justify-between" />
          }
        >
          <span className="truncate">{workspace}</span>
          <IconChevronDown data-icon="inline-end" />
        </DropdownMenuTrigger>
        <DropdownMenuContent>
          <DropdownMenuRadioGroup
            value={workspace}
            onValueChange={setWorkspace}
          >
            {workspaces.map((name) => (
              <DropdownMenuRadioItem key={name} value={name} closeOnClick>
                {name}
              </DropdownMenuRadioItem>
            ))}
          </DropdownMenuRadioGroup>
          <DropdownMenuSeparator />
          <DropdownMenuItem>
            <IconPlus />
            New workspace
          </DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </div>
  )
}

Ações de linha

Um menu atende todas as linhas. Crie um handle com createDropdownMenuHandle, passe-o a cada gatilho com um payload e leia o payload no menu. Delete passa a um alert dialog.

"use client"

import * as React from "react"
import { IconDots, IconPencil, IconTrash } from "@tabler/icons-react"

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import {
  createDropdownMenuHandle,
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

const files = ["Quarterly report.pdf", "Brand assets", "Roadmap 2027.md"]

const rowMenu = createDropdownMenuHandle<{ name: string }>()

export function DropdownMenuRowActions() {
  const [pending, setPending] = React.useState<string | null>(null)
  const [removed, setRemoved] = React.useState<string[]>([])

  return (
    <div className="flex w-full max-w-sm flex-col gap-3">
      <ul className="flex flex-col gap-0.5 rounded-xl border p-1">
        {files
          .filter((name) => !removed.includes(name))
          .map((name) => (
            <li
              key={name}
              className="flex items-center gap-3 rounded-lg py-1 ps-3 pe-1 text-sm"
            >
              <span className="min-w-0 flex-1 truncate">{name}</span>
              <DropdownMenuTrigger
                handle={rowMenu}
                payload={{ name }}
                aria-label={`Actions for ${name}`}
                render={<Button variant="ghost" size="icon-sm" />}
              >
                <IconDots />
              </DropdownMenuTrigger>
            </li>
          ))}
      </ul>
      <div>
        <Button variant="outline" size="sm" onClick={() => setRemoved([])}>
          Restore files
        </Button>
      </div>
      <DropdownMenu handle={rowMenu}>
        {({ payload }) => (
          <DropdownMenuContent align="end">
            <DropdownMenuItem>
              <IconPencil />
              Rename
            </DropdownMenuItem>
            <DropdownMenuSeparator />
            <DropdownMenuItem
              variant="destructive"
              onClick={() => setPending(payload?.name ?? null)}
            >
              <IconTrash />
              Delete…
            </DropdownMenuItem>
          </DropdownMenuContent>
        )}
      </DropdownMenu>
      <AlertDialog
        open={pending !== null}
        onOpenChange={(open) => {
          if (!open) {
            setPending(null)
          }
        }}
      >
        <AlertDialogContent size="sm">
          <AlertDialogHeader>
            <AlertDialogTitle>Delete “{pending}”?</AlertDialogTitle>
            <AlertDialogDescription>
              It moves to the trash for 30 days.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>Cancel</AlertDialogCancel>
            <AlertDialogAction
              variant="destructive"
              onClick={() => {
                if (pending) {
                  setRemoved((value) => [...value, pending])
                }
                setPending(null)
              }}
            >
              Delete
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </div>
  )
}

Controlado

Passe open e onOpenChange. O segundo argumento diz por que o menu mudou, como um toque no gatilho, em um item ou Escape. Itens com closeOnClick={false} o mantêm aberto.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export function DropdownMenuControlled() {
  const [open, setOpen] = React.useState(false)
  const [reason, setReason] = React.useState("none")

  return (
    <div className="flex flex-col items-center gap-3">
      <DropdownMenu
        open={open}
        onOpenChange={(next, details) => {
          setOpen(next)
          setReason(details.reason)
        }}
      >
        <DropdownMenuTrigger render={<Button variant="outline" />}>
          Controlled
        </DropdownMenuTrigger>
        <DropdownMenuContent>
          <DropdownMenuItem>Closes the menu</DropdownMenuItem>
          <DropdownMenuItem closeOnClick={false}>Stays open</DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
      <p className="text-sm text-muted-foreground">
        Open: {String(open)} · last reason: {reason}
      </p>
    </div>
  )
}

Abrir ao passar o mouse

openOnHover no gatilho abre o menu após delay e o fecha após closeDelay. O clique e o teclado continuam funcionando, então usuários de toque e de teclado não ficam de fora.

import { IconChevronDown } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export function DropdownMenuHover() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger
        openOnHover
        delay={120}
        closeDelay={120}
        render={<Button variant="ghost" />}
      >
        Products
        <IconChevronDown data-icon="inline-end" />
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem>Analytics</DropdownMenuItem>
        <DropdownMenuItem>Engagement</DropdownMenuItem>
        <DropdownMenuItem>Security</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

Desabilitado

Um gatilho desativado nunca abre. Itens desativados continuam visíveis e são ignorados pelas teclas de seta.

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export function DropdownMenuDisabled() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-2">
      <DropdownMenu>
        <DropdownMenuTrigger disabled render={<Button variant="outline" />}>
          Disabled trigger
        </DropdownMenuTrigger>
        <DropdownMenuContent>
          <DropdownMenuItem>Never shown</DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
      <DropdownMenu>
        <DropdownMenuTrigger render={<Button variant="outline" />}>
          Disabled items
        </DropdownMenuTrigger>
        <DropdownMenuContent>
          <DropdownMenuItem>Rename</DropdownMenuItem>
          <DropdownMenuItem disabled>Move (no permission)</DropdownMenuItem>
          <DropdownMenuItem disabled>Share (no permission)</DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </div>
  )
}

Conteúdo longo

Os rótulos quebram de linha em vez de esticar o menu além de 20rem, strings sem quebra se partem em qualquer ponto e os atalhos permanecem na primeira linha.

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuShortcut,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export function DropdownMenuLongContent() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        Long labels
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuLabel>
          A label that goes on for much longer than any menu label should
        </DropdownMenuLabel>
        <DropdownMenuItem>
          Move to “Supercalifragilisticexpialidocious archive 2026”
          <DropdownMenuShortcut>⌥⌘M</DropdownMenuShortcut>
        </DropdownMenuItem>
        <DropdownMenuItem>
          https://example.com/a/really/long/url/without/any/spaces/at/all
        </DropdownMenuItem>
        <DropdownMenuItem>👩‍👩‍👧‍👦 日本語のテキスト</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

Rolagem

Quando não há espaço para todos os itens, o menu rola dentro do viewport e o item destacado rola para a vista.

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export function DropdownMenuScrolling() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        40 documents
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        {Array.from({ length: 40 }, (_, index) => (
          <DropdownMenuItem key={index}>Document {index + 1}</DropdownMenuItem>
        ))}
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

Dentro de uma sheet

O menu fica acima do sheet. Escape fecha primeiro o menu e depois o sheet.

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
  Sheet,
  SheetBody,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"

export function DropdownMenuInSheet() {
  return (
    <Sheet>
      <SheetTrigger render={<Button variant="outline" />}>
        Open sheet
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Sheet</SheetTitle>
          <SheetDescription>
            The menu layers above the sheet, and Escape closes only the menu.
          </SheetDescription>
        </SheetHeader>
        <SheetBody>
          <DropdownMenu>
            <DropdownMenuTrigger render={<Button variant="outline" />}>
              Open menu
            </DropdownMenuTrigger>
            <DropdownMenuContent>
              <DropdownMenuItem>Copy</DropdownMenuItem>
              <DropdownMenuSub>
                <DropdownMenuSubTrigger>Share</DropdownMenuSubTrigger>
                <DropdownMenuSubContent>
                  <DropdownMenuItem>Email</DropdownMenuItem>
                  <DropdownMenuItem>Link</DropdownMenuItem>
                </DropdownMenuSubContent>
              </DropdownMenuSub>
            </DropdownMenuContent>
          </DropdownMenu>
        </SheetBody>
      </SheetContent>
    </Sheet>
  )
}

Da direita para a esquerda

O menu, o chevron do submenu e o lado do submenu seguem a direção do gatilho. Os atalhos mantêm a ordem da esquerda para a direita.

import {
  IconChevronDown,
  IconLogout,
  IconUser,
  IconUserPlus,
} from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export function DropdownMenuRtl() {
  return (
    <div dir="rtl">
      <DropdownMenu>
        <DropdownMenuTrigger render={<Button variant="outline" />}>
          الحساب
          <IconChevronDown data-icon="inline-end" />
        </DropdownMenuTrigger>
        <DropdownMenuContent>
          <DropdownMenuItem>
            <IconUser />
            الملف الشخصي
            <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
          </DropdownMenuItem>
          <DropdownMenuCheckboxItem defaultChecked>
            إظهار الشريط
          </DropdownMenuCheckboxItem>
          <DropdownMenuSub>
            <DropdownMenuSubTrigger>
              <IconUserPlus />
              دعوة
            </DropdownMenuSubTrigger>
            <DropdownMenuSubContent>
              <DropdownMenuItem>بريد</DropdownMenuItem>
              <DropdownMenuItem>رسالة</DropdownMenuItem>
            </DropdownMenuSubContent>
          </DropdownMenuSub>
          <DropdownMenuSeparator />
          <DropdownMenuItem variant="destructive">
            <IconLogout />
            تسجيل الخروج
          </DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </div>
  )
}
TeclaAção
EnterSpace↓No gatilho, abre o menu e destaca o primeiro item.
↓Destaca o próximo item, voltando ao início no fim.
↑Destaca o item anterior, voltando ao fim no início.
HomeDestaca o primeiro item.
EndDestaca o último item.
EnterSpaceExecuta o item destacado. Itens de checkbox e radio alternam e mantêm o menu aberto.
→Abre o submenu destacado e entra nele. ← em layouts da direita para a esquerda.
←Fecha o submenu atual e volta ao seu gatilho. → em layouts da direita para a esquerda.
EscFecha o menu atual e devolve o foco ao seu gatilho. Em um submenu, apenas esse submenu fecha.
A–ZDestaca o próximo item que começa com aquela letra.
  • O gatilho é anunciado como um botão de menu, e o foco volta a ele quando o menu fecha.
  • Dê aos gatilhos só com ícone um aria-label, como faz o exemplo de ações de linha.
  • <DropdownMenuShortcut /> é apenas um rótulo visual. Associe as teclas você mesmo.
  • Com movimento reduzido ativado, o menu aparece com fade, sem escala, e o piscar do item é ignorado.

Construído sobre o menu do Base UI. Cada parte aceita as props da primitiva que envolve.

PropTipoPadrão
open
boolean–
defaultOpen
booleanfalse
showSafeAreaDesenha a área segura do submenu enquanto o menu está aberto. Para depuração e demonstrações.
booleanfalse
onOpenChangedetails.reason informa o que causou a mudança.
(open: boolean, details) => void–
modalBloqueia a rolagem da página e os cliques externos enquanto aberto.
booleantrue
loopFocusVolta ao início nas duas pontas das teclas de seta.
booleantrue
handleConecta gatilhos desanexados a este menu.
DropdownMenuHandle<Payload>–
childrenUse a forma de função para ler o payload do gatilho que abriu o menu.
ReactNode | ({ payload }) => ReactNode–
disabled
booleanfalse
PropTipoPadrão
openOnHover
booleanfalse
delayMilissegundos antes de o hover abrir o menu.
number100
closeDelayMilissegundos antes de um menu aberto por hover fechar.
number0
handle
DropdownMenuHandle<Payload>–
payloadRepassado ao menu quando este gatilho o abre.
Payload–
disabled
booleanfalse
render
ReactElement | (props, state) => ReactElement<button>
AtributoDescrição
data-slot="dropdown-menu-trigger"Selecione o gatilho no CSS.
data-popup-openPresente enquanto o menu está aberto.
data-pressedPresente enquanto o gatilho está pressionado.
data-disabledPresente quando o gatilho está desativado.
PropTipoPadrão
side
"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end""bottom"
align
"start" | "center" | "end""start"
sideOffset
number4
alignOffset
number0
collisionPaddingEspaço mantido entre o menu e a borda do viewport.
number | Rect8
anchorPosiciona em relação a outro elemento em vez do gatilho.
Element | RefObject | VirtualElement–
sticky
booleanfalse
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescrição
data-slot="dropdown-menu-content"Seleciona o menu no CSS.
data-openPresente enquanto o menu está aberto.
data-starting-stylePresente enquanto o menu anima a entrada.
data-ending-stylePresente enquanto o menu anima a saída.
data-sideO lado em que o menu se fixou após o tratamento de colisões.
data-alignO alinhamento em que ele se fixou.
data-chosenPresente depois que um item é clicado, então o menu espera o piscar de confirmação do item antes de fechar.
--anchor-widthA largura do gatilho. O menu tem pelo menos essa largura.
--available-heightEspaço restante no viewport. Menus longos rolam dentro dele.
--transform-originDe onde a animação de escala cresce, ao lado do gatilho.
PropTipoPadrão
variant
"default" | "destructive""default"
insetRecua o item para alinhá-lo aos itens com checkbox.
booleanfalse
closeOnClick
booleantrue
onClick
(event) => void–
labelTexto usado na busca por digitação quando os children não são texto simples.
string–
disabled
booleanfalse
renderRenderize um link com render={<a href="…" />}.
ReactElement | (props, state) => ReactElement<div>
AtributoDescrição
data-slot="dropdown-menu-item"Selecione itens no CSS.
data-variantA variante atual.
data-highlightedPresente enquanto o item está destacado pelo ponteiro ou pelo teclado.
data-disabledPresente quando o item está desabilitado.
data-chosenPresente no item que acabou de ser clicado, enquanto ele pisca.
data-insetPresente quando inset está definido.
PropTipoPadrão
checked
boolean–
defaultChecked
booleanfalse
onCheckedChange
(checked: boolean, details) => void–
closeOnClick
booleanfalse
inset
booleanfalse
indicator
"start" | "end""start"
disabled
booleanfalse
AtributoDescrição
data-slot="dropdown-menu-checkbox-item"Seleciona os itens com checkbox no CSS.
data-checkedPresente quando marcado.
data-uncheckedPresente quando não marcado.
data-highlightedPresente enquanto o item está destacado pelo ponteiro ou pelo teclado.
data-disabledPresente quando o item está desabilitado.
data-chosenPresente no item que acabou de ser clicado, enquanto ele pisca.
data-insetPresente quando inset está definido.
PropTipoPadrão
value
Value–
defaultValue
Value–
onValueChange
(value: Value, details) => void–
disabled
booleanfalse
PropTipoPadrão
value
Value–
closeOnClick
booleanfalse
inset
booleanfalse
indicator
"start" | "end""start"
disabled
booleanfalse
AtributoDescrição
data-slot="dropdown-menu-radio-item"Seleciona os itens radio no CSS.
data-checkedPresente no item selecionado.
data-uncheckedPresente nos outros itens.
data-highlightedPresente enquanto o item está destacado pelo ponteiro ou pelo teclado.
data-disabledPresente quando o item está desabilitado.
data-chosenPresente no item que acabou de ser clicado, enquanto ele pisca.
data-insetPresente quando inset está definido.

Agrupa itens relacionados. Um <DropdownMenuLabel /> dentro dele se torna o nome acessível do grupo.

PropTipoPadrão
inset
booleanfalse
render
ReactElement | (props, state) => ReactElement<div>

Um divisor entre grupos, anunciado como separator.

PropTipoPadrão
dirOs atalhos mantêm a ordem das teclas em menus da direita para a esquerda.
"ltr" | "rtl""ltr"
PropTipoPadrão
open
boolean–
defaultOpen
booleanfalse
onOpenChange
(open: boolean, details) => void–
closeParentOnEscFecha o menu inteiro, não apenas este submenu, com Escape.
booleanfalse
PropTipoPadrão
inset
booleanfalse
disabled
booleanfalse
delayMilissegundos antes de o hover abrir o submenu.
number100
closeDelay
number0
AtributoDescrição
data-slot="dropdown-menu-sub-trigger"Seleciona os gatilhos de submenu no CSS.
data-popup-openPresente enquanto o seu submenu está aberto.
data-highlightedPresente enquanto destacado.
PropTipoPadrão
sideOffset
number0
alignOffset
number-4
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescrição
data-slot="dropdown-menu-sub-content"Seleciona os submenus no CSS.
data-openPresente enquanto o menu está aberto.
data-starting-stylePresente enquanto o menu anima a entrada.
data-ending-stylePresente enquanto o menu anima a saída.
data-sideO lado em que o menu se fixou após o tratamento de colisões.
data-alignO alinhamento em que ele se fixou.
data-chosenPresente depois que um item é clicado, então o menu espera o piscar de confirmação do item antes de fechar.
--available-heightEspaço restante no viewport. Menus longos rolam dentro dele.
--transform-originDe onde a animação de escala cresce, ao lado do gatilho.

Usado em blocos

Blocos que se baseiam em Dropdown menu.