Przejdź do treści

Dropdown Menu

Stabilny

Lista akcji lub opcji rozwijana z wyzwalacza, dzięki której polecenia drugorzędne są o jedno kliknięcie stąd i nie zagracają interfejsu.

Instalacja

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/dropdown-menu.json

Użycie

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
<DropdownMenu>
  <DropdownMenuTrigger render={<Button variant="outline" />}>
    Options
  </DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuGroup>
      <DropdownMenuLabel>My account</DropdownMenuLabel>
      <DropdownMenuItem>
        Profile
        <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
      </DropdownMenuItem>
      <DropdownMenuItem>Settings</DropdownMenuItem>
    </DropdownMenuGroup>
    <DropdownMenuSeparator />
    <DropdownMenuGroup>
      <DropdownMenuItem>Log out</DropdownMenuItem>
    </DropdownMenuGroup>
  </DropdownMenuContent>
</DropdownMenu>

Wyzwalacz renderuje dowolny element przez render — zwykle Button. Pozycje zawsze umieszczaj w DropdownMenuGroup, żeby czytniki ekranu ogłaszały etykietę grupy, a separatory miały co oddzielać.

Przykłady

Menu konta

Najczęstsze menu: na górze informacja, kim jesteś, pośrodku akcje konta, a na końcu wylogowanie. Skróty są wyrównane do prawej krawędzi i mają stonowany kolor, więc łatwo je wyłapać wzrokiem, a przy tym nie konkurują z etykietami.

Pozycje z polem wyboru

Używaj DropdownMenuCheckboxItem do niezależnych ustawień typu włącz/wyłącz, takich jak widoczne kolumny. Kliknięcie takiej pozycji nie zamyka menu (closeOnClick ma domyślnie wartość false), więc za jednym razem można przełączyć kilka z nich.

Grupa radiowa

DropdownMenuRadioGroup grupuje wzajemnie wykluczające się opcje — kolejność sortowania, gęstość, tryb widoku. Pokaż bieżący wybór w etykiecie wyzwalacza, żeby stan był widoczny także przy zamkniętym menu.

Zagnieżdżaj powiązane akcje za pomocą DropdownMenuSub. Podmenu otwiera się po najechaniu, z krótkim opóźnieniem, a z klawiatury — klawiszem . Ogranicz zagnieżdżanie do jednego poziomu — drugi poziom to znak, że menu wykonuje pracę strony.

Akcje wiersza

Ikonowy przycisk w wariancie ghost ze znakiem to przyjęte miejsce na akcje pojedynczego elementu listy lub tabeli. Wyrównaj menu do końca wyzwalacza (align="end"), żeby otwierało się w stronę treści, i nadaj wyzwalaczowi aria-label, który nazywa element, na którym działa.

Planowanie kwartalneEdytowano 2 godz. temu
Aktywne

Wyłączone pozycje

Wyłącz pozycję, gdy akcja istnieje, ale jest chwilowo niedostępna, i wyjaśnij w etykiecie dlaczego. Jeśli akcja w ogóle nie ma zastosowania w bieżącym kontekście, usuń ją.

Wytyczne

Kiedy używać

  • Aby zebrać drugorzędne akcje pod jednym wyzwalaczem: akcje wiersza, nadmiarowe akcje z paska narzędzi, menu konta.
  • Aby udostępnić kilka ustawień widoku — przełączniki i wybory jednokrotne — bez osobnej strony ustawień.

Kiedy nie używać

  • Do wyboru wartości, która staje się częścią formularza — użyj Select albo Combobox.
  • Do głównej akcji widoku — niech będzie widoczna jako Button.
  • Do nawigacji po witrynie lub aplikacji z rozbudowanymi panelami — użyj Navigation Menu.
  • Do akcji dotyczących obszaru, odkrywanych prawym przyciskiem myszy — użyj Context Menu i zawsze udostępnij te same akcje w widocznym miejscu.

Treść pozycji

Zaczynaj każdą pozycję od czasownika albo od rzeczownika, który nazywa miejsce docelowe: Zmień nazwę, Duplikuj, Ustawienia. Wielką literą pisz tylko pierwsze słowo, mieść pozycje w jednej linii i dodawaj wielokropek (…) tylko wtedy, gdy pozycja otwiera okno, które prosi o dodatkowe dane, zanim cokolwiek się stanie.

Ikony są opcjonalne, ale w obrębie grupy obowiązuje zasada „wszystkie albo żadna”: kolumna etykiet, z których część ma ikony, a część nie, wygląda na zepsutą. Gdy reszta menu ma ikony lub wskaźniki, dodaj inset do pozycji, które ich nie mają.

Zmień nazwę
Duplikuj
Archiwizuj
Usuń
Dobrze.Czasowniki na początku, spójne ikony i jedna pozycja destrukcyjna na końcu.
Zmiana nazwy
Usuń na zawsze
Zrób kopię tego
Wybierz kraj…
Źle.Niespójne sformułowania, akcja destrukcyjna ukryta w środku i menu, które wykonuje pracę formularza.

Akcje destrukcyjne

Umieszczaj pozycje destrukcyjne na końcu, oddzielone od reszty, i używaj dla nich variant="destructive". Jeśli akcji nie da się cofnąć, pozycja powinna otwierać Alert Dialog, zamiast działać od razu. Pozycja destrukcyjna nigdy nie może być pierwszą, na którą trafia fokus klawiatury.

Skróty klawiszowe

DropdownMenuShortcut to podpowiedź, a nie przypisanie klawisza — właściwą obsługę skrótu zarejestruj gdzie indziej. Pokazuj skróty tylko przy akcjach, które naprawdę je mają, używaj symboli platformy (⌘ ⇧ ⌥ ⌃ ⌫ ↵) w przyjętej kolejności (⌃ ⌥ ⇧ ⌘) i nie pokazuj skrótu przy wyłączonej pozycji.

Rozmiar i grupowanie

Siedem do dziesięciu pozycji to wygodne maksimum. Grupuj powiązane pozycje i oddzielaj grupy za pomocą DropdownMenuSeparator; gdy przeznaczenie grupy nie jest oczywiste, nadaj jej etykietę DropdownMenuLabel. Menu jest co najmniej tak szerokie jak wyzwalacz, nigdy nie obcina etykiet i rozszerza się do szerokości najdłuższej pozycji.

Dostępność

Dropdown Menu, oparty na Base UI, realizuje wzorzec WAI-ARIA menu button. Wyzwalacz otrzymuje aria-haspopuparia-expanded, pozycje — rolę menuitem, menuitemcheckbox lub menuitemradio; po otwarciu fokus przechodzi do menu, a po zamknięciu wraca do wyzwalacza.

KlawiszDziałanie
EnterSpacja
Na wyzwalaczu: otwiera menu i przenosi fokus na pierwszą pozycję.
Na wyzwalaczu: otwiera menu i przenosi fokus na ostatnią pozycję.
Przenosi fokus między pozycjami. Z ostatniej pozycji fokus przechodzi na pierwszą.
HomeEnd
Przenosi fokus na pierwszą lub ostatnią pozycję.
Na wyzwalaczu podmenu: otwiera podmenu i przenosi fokus na jego pierwszą pozycję.
W podmenu: zamyka je i przywraca fokus do jego wyzwalacza.
EnterSpacja
Aktywuje pozycję z fokusem. Pozycje z polem wyboru i pozycje radiowe przełączają się bez zamykania menu.
A–Z
Wyszukiwanie przez wpisywanie: przenosi fokus na następną pozycję, której etykieta zaczyna się od wpisanych znaków.
Esc
Zamyka menu i przywraca fokus do wyzwalacza.
  • Wyzwalacze z samą ikoną potrzebują aria-label, który nazywa zarówno akcję, jak i obiekt: Akcje: Planowanie kwartalne, a nie Więcej.
  • Wyszukiwanie przez wpisywanie dopasowuje tekst pozycji. Gdy treść pozycji nie jest zwykłym tekstem, ustaw w propie label to, co wpisaliby użytkownicy.
  • Podświetlenie a najechanie. Pozycje podświetlają się tak samo po najechaniu kursorem, jak przy fokusie z klawiatury (data-highlighted), więc aktywna jest zawsze tylko jedna pozycja.

Dokumentacja API

Komponent główny. Przyjmuje wszystkie propsy Menu.Root z Base UI.

PropTypDomyślnie
open

Kontrolowany stan otwarcia.

booleanBrak wartości domyślnej
defaultOpen

Początkowy stan otwarcia w trybie niekontrolowanym.

booleanfalse
onOpenChange

Wywoływana, gdy menu się otwiera lub zamyka.

(open: boolean, details) => voidBrak wartości domyślnej
modal

Czy reszta strony jest nieaktywna, gdy menu jest otwarte.

booleantrue
loopFocus

Czy fokus przesuwany strzałkami przechodzi z ostatniej pozycji na pierwszą.

booleantrue
disabled

Ignoruje interakcję użytkownika z wyzwalaczem.

booleanfalse

Element, który otwiera menu. Użyj render, żeby podać Button.

PropTypDomyślnie
render

Element do wyrenderowania, zwykle <Button />.

ReactElement | (props, state) => ReactElementBrak wartości domyślnej
openOnHover

Otwiera menu także po najechaniu na wyzwalacz.

booleanfalse
delay

Opóźnienie otwarcia po najechaniu, w ms. Wymaga openOnHover.

number100
disabled

Wyłącza wyzwalacz.

booleanfalse

Popup renderowany w portalu i pozycjonowany względem wyzwalacza.

PropTypDomyślnie
side

Preferowana strona wyzwalacza. Automatycznie zmienia się na przeciwną, żeby menu pozostało widoczne.

"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end""bottom"
align

Wyrównanie względem wyzwalacza po wybranej stronie.

"start" | "center" | "end""start"
sideOffset

Odstęp między wyzwalaczem a menu w px.

number6
alignOffset

Przesunięcie wzdłuż osi wyrównania w px.

number0
PropTypDomyślnie
variant

Pozycje destrukcyjne mają kolor danger w tekście, ikonie i podświetleniu.

"default" | "destructive""default"
inset

Dodaje odstęp na początku, żeby wyrównać pozycję z pozycjami, które mają ikony lub wskaźniki.

booleanfalse
onClick

Wywoływana, gdy pozycja zostanie aktywowana myszą lub klawiaturą.

(event) => voidBrak wartości domyślnej
closeOnClick

Czy aktywowanie pozycji zamyka menu.

booleantrue
disabled

Pomija pozycję podczas nawigacji klawiaturą i ignoruje kliknięcia.

booleanfalse
label

Tekst do wyszukiwania przez wpisywanie, gdy treść pozycji nie jest zwykłym tekstem.

stringBrak wartości domyślnej
PropTypDomyślnie
checked

Kontrolowany stan zaznaczenia.

booleanBrak wartości domyślnej
defaultChecked

Początkowy stan zaznaczenia w trybie niekontrolowanym.

booleanfalse
onCheckedChange

Wywoływana, gdy pozycja zostanie przełączona.

(checked: boolean, details) => voidBrak wartości domyślnej
closeOnClick

Czy przełączenie zamyka menu.

booleanfalse
inset

Zachowany dla spójności API; pozycje z polem wyboru zawsze rezerwują miejsce na wskaźnik.

booleanfalse
PropTypDomyślnie
value

Kontrolowana wybrana wartość.

anyBrak wartości domyślnej
defaultValue

Początkowo wybrana wartość w trybie niekontrolowanym.

anyBrak wartości domyślnej
onValueChange

Wywoływana, gdy zmienia się wybór.

(value: any, details) => voidBrak wartości domyślnej
disabled

Wyłącza wszystkie pozycje w grupie.

booleanfalse
PropTypDomyślnie
valuewymagany

Wartość, którą wybiera ta pozycja.

anyBrak wartości domyślnej
closeOnClick

Czy wybranie pozycji zamyka menu.

booleanfalse
disabled

Pomija pozycję i ignoruje jej wybór.

booleanfalse

DropdownMenuSub otacza zagnieżdżone menu. DropdownMenuSubTrigger przyjmuje inset, disabled, openOnHover (domyślnie true), delay (domyślnie 100) i closeDelay (domyślnie 0). DropdownMenuSubContent przyjmuje te same propsy pozycjonowania co DropdownMenuContent, z wartościami domyślnymi side="right", align="start", alignOffset={-5}sideOffset={2}.

DropdownMenuLabel nadaje etykietę otaczającej ją grupie DropdownMenuGroup i przyjmuje inset. DropdownMenuSeparator rysuje cienką linię między grupami. DropdownMenuShortcut to czysto prezentacyjny <span> dosunięty do końcowej krawędzi pozycji.