Context Menu
Akcje dla konkretnego obiektu, otwierane prawym przyciskiem myszy lub długim przytrzymaniem dokładnie tam, gdzie jest kursor — skrót, nigdy jedyna droga.
import {
ContextMenu,
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuGroup,
ContextMenuItem,
ContextMenuLabel,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
export default function ContextMenuDemo() {
return (
<ContextMenu>
<ContextMenuTrigger
// Focusable, so Shift+F10 or the Menu key opens it from the keyboard.
tabIndex={0}
className="flex h-40 w-full max-w-sm items-center justify-center rounded-xl border border-dashed border-border-strong bg-card text-sm text-muted-foreground focus-ring"
>
Kliknij tutaj prawym przyciskiem
</ContextMenuTrigger>
<ContextMenuContent className="w-60">
<ContextMenuGroup>
<ContextMenuItem>
Wstecz
<ContextMenuShortcut>⌘[</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem disabled>
Dalej
<ContextMenuShortcut>⌘]</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
Załaduj ponownie
<ContextMenuShortcut>⌘R</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger>Więcej narzędzi</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuGroup>
<ContextMenuItem>Zapisz stronę jako…</ContextMenuItem>
<ContextMenuItem>Utwórz skrót…</ContextMenuItem>
<ContextMenuItem>Narzędzia dla deweloperów</ContextMenuItem>
</ContextMenuGroup>
</ContextMenuSubContent>
</ContextMenuSub>
</ContextMenuGroup>
<ContextMenuSeparator />
<ContextMenuGroup>
<ContextMenuCheckboxItem defaultChecked>
Pokaż pasek zakładek
</ContextMenuCheckboxItem>
<ContextMenuCheckboxItem>Pokaż pełne adresy URL</ContextMenuCheckboxItem>
</ContextMenuGroup>
<ContextMenuSeparator />
<ContextMenuGroup>
<ContextMenuLabel inset>Profil</ContextMenuLabel>
<ContextMenuRadioGroup defaultValue="work">
<ContextMenuRadioItem value="personal">
Osobisty
</ContextMenuRadioItem>
<ContextMenuRadioItem value="work">Służbowy</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuGroup>
</ContextMenuContent>
</ContextMenu>
)
}Instalacja
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/context-menu.jsonUżycie
import {
ContextMenu,
ContextMenuContent,
ContextMenuGroup,
ContextMenuItem,
ContextMenuTrigger,
} from "@/components/ui/context-menu"<ContextMenu>
<ContextMenuTrigger className="rounded-xl border border-dashed p-10">
Right-click here
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuGroup>
<ContextMenuItem>Rename</ContextMenuItem>
<ContextMenuItem>Duplicate</ContextMenuItem>
</ContextMenuGroup>
</ContextMenuContent>
</ContextMenu>ContextMenuTrigger wyznacza obszar, który reaguje na kliknięcie prawym przyciskiem myszy (albo długie przytrzymanie na ekranie dotykowym). Menu otwiera się w miejscu kursora. Pozycje, grupy, podmenu, pozycje z polem wyboru i pozycje radiowe działają dokładnie tak samo jak w Dropdown Menu.
Przykłady
Menu w stylu przeglądarki
Wszystko, co może zawierać menu kontekstowe: skróty, wyłączoną pozycję, podmenu, pozycje z polem wyboru i grupę radiową z etykietą.
import {
ContextMenu,
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuGroup,
ContextMenuItem,
ContextMenuLabel,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
export default function ContextMenuDemo() {
return (
<ContextMenu>
<ContextMenuTrigger
// Focusable, so Shift+F10 or the Menu key opens it from the keyboard.
tabIndex={0}
className="flex h-40 w-full max-w-sm items-center justify-center rounded-xl border border-dashed border-border-strong bg-card text-sm text-muted-foreground focus-ring"
>
Kliknij tutaj prawym przyciskiem
</ContextMenuTrigger>
<ContextMenuContent className="w-60">
<ContextMenuGroup>
<ContextMenuItem>
Wstecz
<ContextMenuShortcut>⌘[</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem disabled>
Dalej
<ContextMenuShortcut>⌘]</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
Załaduj ponownie
<ContextMenuShortcut>⌘R</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger>Więcej narzędzi</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuGroup>
<ContextMenuItem>Zapisz stronę jako…</ContextMenuItem>
<ContextMenuItem>Utwórz skrót…</ContextMenuItem>
<ContextMenuItem>Narzędzia dla deweloperów</ContextMenuItem>
</ContextMenuGroup>
</ContextMenuSubContent>
</ContextMenuSub>
</ContextMenuGroup>
<ContextMenuSeparator />
<ContextMenuGroup>
<ContextMenuCheckboxItem defaultChecked>
Pokaż pasek zakładek
</ContextMenuCheckboxItem>
<ContextMenuCheckboxItem>Pokaż pełne adresy URL</ContextMenuCheckboxItem>
</ContextMenuGroup>
<ContextMenuSeparator />
<ContextMenuGroup>
<ContextMenuLabel inset>Profil</ContextMenuLabel>
<ContextMenuRadioGroup defaultValue="work">
<ContextMenuRadioItem value="personal">
Osobisty
</ContextMenuRadioItem>
<ContextMenuRadioItem value="work">Służbowy</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuGroup>
</ContextMenuContent>
</ContextMenu>
)
}Elementy listy
Daj każdemu wierszowi własny ContextMenu, żeby akcje zawsze dotyczyły obiektu pod kursorem. Gdy menu jest otwarte, jego wyzwalacz dostaje atrybut data-popup-open — użyj go, żeby wiersz pozostał wyróżniony i było widać, na czym działa menu.
import {
CopyIcon,
DownloadIcon,
FileTextIcon,
FolderInputIcon,
LinkIcon,
PencilIcon,
Share2Icon,
StarIcon,
Trash2Icon,
} from "lucide-react"
import {
ContextMenu,
ContextMenuContent,
ContextMenuGroup,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
const files = [
{ name: "Brand guidelines.pdf", meta: "2,4 MB · Edytowano dziś" },
{ name: "Q3 roadmap.fig", meta: "18,1 MB · Edytowano wczoraj" },
{ name: "Launch checklist.md", meta: "12 KB · Edytowano 4 marca" },
]
export default function ContextMenuFileItem() {
return (
<div className="flex w-full max-w-md flex-col gap-1 rounded-xl border bg-card p-1.5 shadow-raised">
{files.map((file) => (
<ContextMenu key={file.name}>
<ContextMenuTrigger
tabIndex={0}
className="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm focus-ring transition-colors duration-fast hover:bg-accent data-popup-open:bg-accent"
>
<span className="flex size-9 shrink-0 items-center justify-center rounded-lg border bg-surface text-muted-foreground">
<FileTextIcon className="size-4" />
</span>
<span className="flex min-w-0 flex-col">
<span className="truncate font-medium text-foreground">
{file.name}
</span>
<span className="text-body-xs text-muted-foreground">
{file.meta}
</span>
</span>
</ContextMenuTrigger>
<ContextMenuContent className="w-56">
<ContextMenuGroup>
<ContextMenuItem>
<PencilIcon />
Zmień nazwę
<ContextMenuShortcut>↵</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
<CopyIcon />
Duplikuj
<ContextMenuShortcut>⌘D</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
<StarIcon />
Oznacz gwiazdką
</ContextMenuItem>
<ContextMenuItem>
<FolderInputIcon />
Przenieś do…
</ContextMenuItem>
</ContextMenuGroup>
<ContextMenuSeparator />
<ContextMenuGroup>
<ContextMenuSub>
<ContextMenuSubTrigger>
<Share2Icon />
Udostępnij
</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-44">
<ContextMenuGroup>
<ContextMenuItem>
<LinkIcon />
Kopiuj link
</ContextMenuItem>
<ContextMenuItem>
<DownloadIcon />
Pobierz
</ContextMenuItem>
</ContextMenuGroup>
</ContextMenuSubContent>
</ContextMenuSub>
</ContextMenuGroup>
<ContextMenuSeparator />
<ContextMenuGroup>
<ContextMenuItem variant="destructive">
<Trash2Icon />
Przenieś do kosza
<ContextMenuShortcut>⌘⌫</ContextMenuShortcut>
</ContextMenuItem>
</ContextMenuGroup>
</ContextMenuContent>
</ContextMenu>
))}
</div>
)
}Obiekty na płótnie
W narzędziach projektowych, edytorach i mapach polecenia dotyczące pojedynczych obiektów opierają się na menu kontekstowym. Używaj dokładnie tego samego słownictwa co w głównych menu aplikacji i pokazuj te same skróty.
import {
BringToFrontIcon,
ClipboardPasteIcon,
CopyIcon,
GroupIcon,
LockIcon,
ScissorsIcon,
SendToBackIcon,
} from "lucide-react"
import {
ContextMenu,
ContextMenuContent,
ContextMenuGroup,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
export default function ContextMenuCanvas() {
return (
<ContextMenu>
<ContextMenuTrigger
// Focusable, so Shift+F10 or the Menu key opens it from the keyboard.
tabIndex={0}
className="relative flex h-52 w-full max-w-md items-center justify-center overflow-hidden rounded-xl border bg-surface focus-ring"
>
<div className="flex size-28 items-center justify-center rounded-lg border bg-card text-sm font-medium shadow-floating">
Ramka 1
</div>
</ContextMenuTrigger>
<ContextMenuContent className="w-56">
<ContextMenuGroup>
<ContextMenuItem>
<ScissorsIcon />
Wytnij
<ContextMenuShortcut>⌘X</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
<CopyIcon />
Kopiuj
<ContextMenuShortcut>⌘C</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
<ClipboardPasteIcon />
Wklej tutaj
<ContextMenuShortcut>⌘V</ContextMenuShortcut>
</ContextMenuItem>
</ContextMenuGroup>
<ContextMenuSeparator />
<ContextMenuGroup>
<ContextMenuItem>
<BringToFrontIcon />
Przenieś na wierzch
<ContextMenuShortcut>]</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
<SendToBackIcon />
Przenieś na spód
<ContextMenuShortcut>[</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
<GroupIcon />
Grupuj zaznaczenie
<ContextMenuShortcut>⌘G</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
<LockIcon />
Zablokuj
<ContextMenuShortcut>⇧⌘L</ContextMenuShortcut>
</ContextMenuItem>
</ContextMenuGroup>
</ContextMenuContent>
</ContextMenu>
)
}Wytyczne
Kiedy używać
- Aby przyspieszyć akcje na obiektach, którymi użytkownicy manipulują bezpośrednio: plikach, wierszach, warstwach na płótnie, wiadomościach.
- W gęstych narzędziach obsługiwanych głównie myszą, w których widoczne przyciski akcji zagraciłyby interfejs.
Kiedy nie używać
- Gdy ma być jedynym sposobem dotarcia do akcji. Kliknięcie prawym przyciskiem trudno odkryć, na większości urządzeń dotykowych w ogóle nie istnieje, a z klawiatury wywołuje się je niewygodnie. Każda pozycja musi być dostępna także z widocznej kontrolki — Dropdown Menu pod przyciskiem
⋯, paska narzędzi albo widoku szczegółów obiektu. - Tam, gdzie ludzie oczekują menu przeglądarki — np. na tekście, który chcą skopiować, albo na linkach, które chcą otworzyć w nowej karcie.
Odzwierciedlaj widoczne menu
Menu kontekstowe to skrót do poleceń, które już istnieją. Używaj tych samych etykiet, kolejności i skrótów co w menu ⋯ danego wiersza albo w pasku menu aplikacji, żeby ludzie uczyli się tego słownictwa tylko raz. Najczęstsze akcje umieszczaj na początku, a destrukcyjne — na końcu, za separatorem.
Im krócej, tym lepiej
Menu kontekstowe czyta się przy kursorze i w pośpiechu. Staraj się zmieścić w mniej niż dziesięciu pozycjach w dwóch–trzech grupach, unikaj więcej niż jednego poziomu podmenu i nigdy nie umieszczaj w menu dłuższych treści.
Dostępność
Context Menu opiera się na komponencie Context Menu z Base UI, który po otwarciu realizuje wzorzec WAI-ARIA menu (APG opisuje menu i paski menu na jednej stronie).
| Klawisz | Działanie |
|---|---|
ShiftF10 | Gdy fokus jest w obszarze wyzwalacza, otwiera menu — to systemowy klawiaturowy odpowiednik kliknięcia prawym przyciskiem. To samo robi klawisz menu kontekstowego (ContextMenu) na klawiaturach dla Windows. |
↓↑ | Przenosi fokus między pozycjami, zapętlając się na końcach listy. |
HomeEnd | Przenosi fokus na pierwszą lub ostatnią pozycję. |
→← | Otwiera lub zamyka podmenu. |
EnterSpacja | Aktywuje pozycję z fokusem. |
A–Z | Przenosi fokus na następną pozycję zaczynającą się od wpisanych znaków. |
Esc | Zamyka menu. |
- Dotyk. Długie przytrzymanie wyzwalacza otwiera menu, więc te same akcje działają na telefonach i tabletach.
- Równoważność. Menu kontekstowe trudno odkryć za pomocą technologii wspomagających, dlatego widoczna alternatywa nie jest opcjonalna — to dzięki niej akcje są w ogóle dostępne.
Dokumentacja API
ContextMenu
Komponent główny. Przyjmuje propsy ContextMenu.Root z Base UI.
ContextMenuTrigger
Obszar, który nasłuchuje kliknięcia prawym przyciskiem i długiego przytrzymania. Renderuje <div> z klasą select-none; stylizuj go jak każdy kontener. Gdy jego menu jest otwarte, dostaje atrybut data-popup-open.
ContextMenuContent
ContextMenuItem
Pozostałe części
ContextMenuCheckboxItem, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut i ContextMenuGroup mają takie samo API jak odpowiadające im części Dropdown Menu. ContextMenuPortal jest eksportowany na potrzeby własnych kompozycji; ContextMenuContent renderuje go już sam.