Context menu
Ein Menü mit Aktionen per Rechtsklick oder langem Drücken, mit Untermenüs, Checkbox- und Radio-Einträgen sowie Halte-Feedback auf Touch-Geräten.
Last action: Nothing yet
pnpm dlx shadcn@latest add https://hextaui.com/r/context-menu.jsonFügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.
Füge die Theme-Tokens zu deinem globalen CSS hinzu, falls du das noch nicht getan hast.
Installiere die Abhängigkeiten.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnKopiere den folgenden Code und füge ihn in dein Projekt ein.
components/ui/context-menu.tsx Passe die Importpfade an dein Projekt-Setup an.
Dateiliste
Gib jeder Zeile ihr eigenes Menü. Die geöffnete Zeile behält eine Hervorhebung, und ein destruktiver Eintrag übergibt zur Bestätigung an einen Alert Dialog.
Kontrolliert
Übergib open und onOpenChange, um den State selbst zu führen. Das zweite Argument nennt den Grund der Änderung, etwa trigger-press, outside-press oder escape-key. Einträge mit closeOnClick={false} halten es offen.
Deaktiviert
Ein deaktiviertes <ContextMenu /> gibt den Bereich an das eigene Menü des Browsers zurück. Deaktivierte Einträge bleiben sichtbar, werden aber von der Tastatur übersprungen.
Halte-Feedback
Auf Touchscreens öffnet sich das Menü nach einem langen Druck. Solange der Finger gehalten wird, schrumpft der Bereich leicht, damit man merkt, dass der Druck erkannt wurde. Bewegt sich der Finger, wird abgebrochen. Setze holdFeedback={false}, um es abzuschalten.
Langer Inhalt
Lange Labels werden innerhalb einer maximalen Breite von 20 rem umgebrochen, und hohe Menüs scrollen im verbleibenden Platz des Viewports.
Verschachtelte Untermenüs
Untermenüs öffnen sich bei Hover oder mit den Pfeiltasten, in beliebiger Tiefe. Ein deaktivierter Untermenü-Trigger öffnet sich nie.
In einem Sheet
Das Menü liegt über anderen Overlays, und Escape schließt nur das Menü, nicht das Sheet dahinter.
Als anderes Element rendern
Nutze render, um den Trigger zu einem beliebigen Element zu machen, etwa einer Figure, oder um einen Eintrag in einen Link zu verwandeln.
Rechts nach links
Das Menü liest die Richtung des Triggers, sodass sich Untermenüs nach links öffnen und die Pfeiltasten umkehren.
| Taste | Aktion |
|---|---|
| ↓ | Hebt den nächsten Eintrag hervor, am Ende wird umgebrochen. |
| ↑ | Hebt den vorherigen Eintrag hervor, am Anfang wird umgebrochen. |
| Home | Hebt den ersten Eintrag hervor. |
| End | Hebt den letzten Eintrag hervor. |
| EnterSpace | Führt den hervorgehobenen Eintrag aus. Checkbox- und Radio-Einträge schalten um und halten das Menü offen. |
| → | Öffnet das hervorgehobene Untermenü und wechselt hinein. ← in Rechts-nach-links-Layouts. |
| ← | Schließt das aktuelle Untermenü und kehrt zu seinem Trigger zurück. → in Rechts-nach-links-Layouts. |
| Esc | Schließt das aktuelle Menü. In einem Untermenü schließt sich nur dieses Untermenü. |
| A–Z | Hebt den nächsten Eintrag hervor, der mit diesem Buchstaben beginnt. |
- Ein Kontextmenü ist eine Abkürzung. Mache jede Aktion darin auch auf anderem Weg erreichbar, etwa über einen sichtbaren Button oder ein Dropdown-Menü, da viele nie rechtsklicken oder lange drücken.
- Browser lösen das Kontextmenü-Event auch bei Shift+F10 und der Menütaste auf einem fokussierten Element aus, sodass ein fokussierbares Element im Trigger Tastaturnutzern das Öffnen ermöglicht.
- Kürzel in
<ContextMenuShortcut />sind nur Labels. Binde die Tasten selbst. - Bei reduzierter Bewegung entfallen das Halte-Feedback und das Blinken der Einträge, und das Menü blendet nur ein und aus.
Basiert auf dem Base UI Context Menu. Jeder Teil akzeptiert die Props der Primitive, die er umschließt, sowie eine className-Funktion, die den Zustand des Teils erhält.
| Prop | Typ | Standard |
|---|---|---|
open | boolean | – |
defaultOpen | boolean | false |
onOpenChangedetails.reason gibt an, was die Änderung ausgelöst hat. | (open: boolean, details) => void | – |
onOpenChangeCompleteLäuft, nachdem die Öffnen- oder Schließen-Animation beendet ist. | (open: boolean) => void | – |
disabledZeigt stattdessen das native Menü des Browsers. | boolean | false |
loopFocusDie Pfeiltasten-Navigation an den Enden umbrechen. | boolean | true |
highlightItemOnHover | boolean | true |
actionsRefDas Menü imperativ schließen. | RefObject<{ close, unmount }> | – |
| Prop | Typ | Standard |
|---|---|---|
holdFeedbackVerkleinert den Bereich leicht, solange auf Touchscreens ein langer Druck gehalten wird. | boolean | true |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Beschreibung |
|---|---|
data-slot="context-menu-trigger" | Den Trigger in CSS ansprechen. |
data-popup-open | Vorhanden, solange das Menü geöffnet ist. |
data-holding | Vorhanden, solange ein langer Druck gehalten wird. |
data-pressed | Vorhanden, solange der Trigger gedrückt ist. |
| Prop | Typ | Standard |
|---|---|---|
sideBevorzugte Seite, relativ zum Zeiger. | "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end" | "bottom" |
align | "start" | "center" | "end" | "start" |
sideOffset | number | 0 |
alignOffset | number | 0 |
collisionPaddingAbstand, der zwischen dem Menü und den Rändern des Viewports bleibt. | number | { top, right, bottom, left } | – |
collisionAvoidanceWie das Menü umklappt oder sich verschiebt, wenn es überlaufen würde. | CollisionAvoidance | – |
anchorAn etwas anderem als dem Zeiger positionieren. | Element | VirtualElement | RefObject | – |
finalFocusWohin der Fokus nach dem Schließen des Menüs geht. | boolean | RefObject | (closeType) => HTMLElement | boolean | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Beschreibung |
|---|---|
data-slot="context-menu-content" | Das Menü-Popup. |
data-open | Vorhanden, solange geöffnet. |
data-starting-style | Vorhanden, während das Menü eingeblendet wird. |
data-ending-style | Vorhanden, während das Menü ausgeblendet wird. |
data-side | Die Seite, auf der es nach Kollisionen platziert wurde. |
data-chosen | Vorhanden, nachdem ein Eintrag geklickt wurde. Das Ausblenden wartet auf das Blinken. |
--transform-origin | Der Punkt, von dem aus die Skalierungsanimation wächst. |
--available-height | Verbleibender Platz im Viewport. Begrenzt die Menühöhe. |
| Prop | Typ | Standard |
|---|---|---|
variant | "default" | "destructive" | "default" |
insetRückt das Label ein, damit es mit Checkbox-Einträgen fluchtet. | boolean | false |
onClickLäuft bei Klick, Enter oder Space. Das Menü schließt sich nach kurzem Blinken. | (event) => void | – |
closeOnClick | boolean | true |
disabled | boolean | false |
labelText für die Typeahead-Suche, wenn die Kinder kein reiner Text sind. | string | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Beschreibung |
|---|---|
data-slot="context-menu-item" | Items in CSS ansprechen. |
data-variant | Die aktuelle Variante. |
data-highlighted | Vorhanden am Eintrag unter dem Zeiger oder mit Tastaturfokus. |
data-disabled | Vorhanden, wenn das Item deaktiviert ist. |
data-inset | Vorhanden, wenn inset gesetzt ist. |
data-chosen | Vorhanden am geklickten Eintrag, solange er blinkt. |
| Prop | Typ | Standard |
|---|---|---|
checked | boolean | – |
defaultChecked | boolean | false |
onCheckedChange | (checked: boolean, details) => void | – |
closeOnClick | boolean | false |
inset | boolean | false |
disabled | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Beschreibung |
|---|---|
data-slot="context-menu-checkbox-item" | Checkbox-Einträge in CSS ansprechen. |
data-checked | Vorhanden, wenn aktiviert. |
data-unchecked | Vorhanden, wenn nicht aktiviert. |
data-highlighted | Vorhanden am Eintrag unter dem Zeiger oder mit Tastaturfokus. |
data-disabled | Vorhanden, wenn das Item deaktiviert ist. |
data-inset | Vorhanden, wenn inset gesetzt ist. |
data-chosen | Vorhanden am geklickten Eintrag, solange er blinkt. |
| Prop | Typ | Standard |
|---|---|---|
value | any | – |
defaultValue | any | – |
onValueChange | (value: any, details) => void | – |
disabled | boolean | false |
| Prop | Typ | Standard |
|---|---|---|
value | any | – |
closeOnClick | boolean | false |
inset | boolean | false |
disabled | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Beschreibung |
|---|---|
data-slot="context-menu-radio-item" | Radio-Einträge in CSS ansprechen. |
data-checked | Vorhanden, wenn ausgewählt. |
data-highlighted | Vorhanden am Eintrag unter dem Zeiger oder mit Tastaturfokus. |
data-disabled | Vorhanden, wenn das Item deaktiviert ist. |
data-inset | Vorhanden, wenn inset gesetzt ist. |
data-chosen | Vorhanden am geklickten Eintrag, solange er blinkt. |
| Prop | Typ | Standard |
|---|---|---|
inset | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
In <ContextMenuGroup /> oder <ContextMenuRadioGroup /> beschriftet es die Gruppe für assistive Technologien. Anderswo ist es eine einfache Überschrift.
| Prop | Typ | Standard |
|---|---|---|
open | boolean | – |
defaultOpen | boolean | false |
onOpenChange | (open: boolean, details) => void | – |
disabled | boolean | false |
closeParentOnEscDas ganze Menü mit Escape schließen, nicht nur dieses Untermenü. | boolean | false |
| Prop | Typ | Standard |
|---|---|---|
inset | boolean | false |
openOnHover | boolean | true |
delayMillisekunden Hover, bevor sich das Untermenü öffnet. | number | 100 |
closeDelay | number | 0 |
disabled | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Beschreibung |
|---|---|
data-slot="context-menu-sub-trigger" | Untermenü-Trigger in CSS ansprechen. |
data-popup-open | Vorhanden, solange sein Untermenü geöffnet ist. |
data-highlighted | Vorhanden, solange hervorgehoben. |
data-disabled | Vorhanden, wenn deaktiviert. |
| Prop | Typ | Standard |
|---|---|---|
sideÖffnet sich standardmäßig zum Inline-Ende hin. | "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end" | – |
align | "start" | "center" | "end" | – |
sideOffset | number | 0 |
alignOffset | number | -4 |
collisionPaddingAbstand, der zwischen dem Menü und den Rändern des Viewports bleibt. | number | { top, right, bottom, left } | – |
collisionAvoidanceWie das Menü umklappt oder sich verschiebt, wenn es überlaufen würde. | CollisionAvoidance | – |
anchorAn etwas anderem als dem Zeiger positionieren. | Element | VirtualElement | RefObject | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Beschreibung |
|---|---|
data-slot="context-menu-sub-content" | Das Untermenü-Popup. Nimmt dieselben State-Attribute wie das Menü. |
| Prop | Typ | Standard |
|---|---|---|
dirHält Kürzel wie ⇧⌘S in Rechts-nach-links-Menüs in der richtigen Reihenfolge. | "ltr" | "rtl" | "ltr" |
| Attribut | Beschreibung |
|---|---|
data-slot="context-menu-shortcut" | Das Kürzel-Label. |
<ContextMenuGroup /> gruppiert zusammengehörige Einträge unter einem Label. <ContextMenuSeparator /> zeichnet eine Trennlinie. Beide akzeptieren render und className und tragen context-menu-group und context-menu-separator als ihr data-slot.
- Alert dialogEin Bestätigungsdialog für destruktive oder wichtige Aktionen, der auf asynchrone Arbeit wartet und auf Smartphones zum Bottom Sheet wird.
- CommandEine durchsuchbare Liste von Aktionen, inline oder als ⌘K-Palette, mit Seiten, Tastenkürzeln und hervorgehobenen Treffern.
- DialogEin Fenster über der Seite für Formulare und fokussierte Aufgaben, mit fixiertem Header und Footer, Verschachtelung und einem wischbaren Bottom Sheet auf Smartphones.
- DrawerEin Panel, das von jeder Kante hereingleitet und deinem Finger folgt, mit Snap-Punkten, einem funktionierenden Griff und verschachtelten Drawern, die sich stapeln.
- Dropdown menuEin Menü mit Aktionen und Optionen hinter einem Button, mit Gruppen, Untermenüs, Checkbox- und Radio-Einträgen sowie Tastenkürzeln.
- Hover cardEine Vorschaukarte, die sich öffnet, wenn ein Link mit der Maus berührt oder fokussiert wird, für Inhalte, die sehende Nutzer kurz überfliegen können.
In Blocks verwendet
Blocks, die auf Context menu aufbauen.