Die Komponentenwerkstatt: Reflex' 100 Bibliotheksbausteine
·
Kursüberblick
Layout
·
Box
·
Flex
·
Stack
·
Grid
·
Container
·
Section
·
Center
·
Spacer
·
Separator
·
Card
·
Inset
·
Aspect Ratio
·
Fragment
Typografie
·
Text
·
Heading
·
Link
·
Code
·
Markdown
·
Blockquote
·
Quote
·
Strong
·
Em
·
Kbd
Formulare
·
Button
·
Input
·
Text Area
·
Checkbox
·
Switch
·
Radio Group
·
Select
·
Slider
·
Form
·
Upload
Datenanzeige
·
Badge
·
Avatar
·
Icon
·
Callout
·
List
·
Data List
·
Code Block
·
Progress
·
Spinner
·
Scroll Area
·
Moment
Auf- und Zuklappen
·
Tabs
·
Accordion
·
Segmented Control
Overlays
·
Tooltip
·
Popover
·
Hover Card
·
Dialog
·
Alert Dialog
·
Drawer
·
Dropdown Menu
·
Context Menu
·
Toast
Dynamisches Rendern
·
Cond
·
Match
·
Foreach
·
Auto Scroll
Medien
·
Image
·
Audio
·
Video
Tabellen und Datengrids
·
Table
·
Data Table
·
Data Editor
Diagramme
·
Area Chart
·
Bar Chart
·
Line Chart
·
Composed Chart
·
Pie Chart
·
Radar Chart
·
Radial Bar Chart
·
Scatter Chart
·
Funnel Chart
·
Treemap
·
Sankey Chart
·
Error Bar
Diagrammbausteine
·
Axis
·
Cartesian Grid
·
Chart Tooltip
·
Legend
·
Label
·
Reference
·
Brush
Andere Diagrammbibliotheken
·
Plotly
·
Pyplot
HTML-Elemente
·
HTML
·
HTML Text
·
HTML Layout
·
HTML Forms
·
HTML Media
·
HTML Tables
·
SVG
Weitere Werkzeuge
·
Theme
·
Skeleton
·
Clipboard
·
Html Embed
·
Script
·
Memo
rx.context_menu.rootrx.context_menu.triggerrx.context_menu.contentrx.context_menu.itemrx.context_menu.separatorrx.context_menu.subrx.context_menu.sub_triggerrx.context_menu.sub_contentOffizielle Doku
Werkstattauftrag
Klickt man in der Dateiliste mit der rechten Maustaste auf einen Dateinamen, soll genau dort ein Menü erscheinen, in dem man Markieren, Duplizieren oder Löschen wählt.
Ein Kontextmenü erscheint an der Mausposition, wenn man ein Element mit der rechten Maustaste anklickt (auf Touchscreens: lange gedrückt hält). Man sammelt darin, was man „mit diesem Ding“ tun kann, etwa mit einer Datei, einer Tabellenzeile oder einem Bild. Die Teile sind dieselben wie beim Dropdown-Menü.
context_menu.root: umschließt alle Teile.context_menu.trigger: umschließt das Element, das den Rechtsklick empfängt.context_menu.content: das Panel, das im geöffneten Zustand erscheint.context_menu.item: ein einzelner Eintrag. Mit shortcut bekommt er ein Tastenkürzel.context_menu.separator: eine Linie zwischen Einträgen.context_menu.sub: umschließt ein Untermenü. Darin stehen context_menu.sub_trigger und context_menu.sub_content.rx.context_menu.root(
rx.context_menu.trigger(
rx.button("Right click me"),
),
rx.context_menu.content(
rx.context_menu.item("Edit", shortcut="⌘ E"),
rx.context_menu.item("Duplicate", shortcut="⌘ D"),
rx.context_menu.separator(),
rx.context_menu.item("Archive", shortcut="⌘ N"),
rx.context_menu.sub(
rx.context_menu.sub_trigger("More"),
rx.context_menu.sub_content(
rx.context_menu.item("Move to project…"),
rx.context_menu.item("Move to folder…"),
rx.context_menu.separator(),
rx.context_menu.item("Advanced options…"),
),
),
rx.context_menu.separator(),
rx.context_menu.item("Share"),
rx.context_menu.item("Add to favorites"),
rx.context_menu.separator(),
rx.context_menu.item("Delete", shortcut="⌘ ⌫", color="red"),
),
)Klicke mit der rechten Maustaste in den gestrichelten Kasten. Das Aussehen des Menüpanels gehört an content, disabled zum Sperren des Rechtsklicks an trigger. Ist disabled an, erscheint das normale Kontextmenü des Browsers.
Props ausprobieren
Right-click here
variant
size
color
high_contrast
disabled
modal
rx.context_menu.root(
rx.context_menu.trigger(
rx.center(
rx.text("Right-click here", size="2", color_scheme="gray"),
width="260px",
height="110px",
border=f"2px dashed {rx.color('gray', 7)}",
border_radius="8px",
),
disabled=False,
),
rx.context_menu.content(
rx.context_menu.item("Copy", shortcut="⌘ C"),
rx.context_menu.item("Paste", shortcut="⌘ V"),
rx.context_menu.separator(),
rx.context_menu.item("Delete", shortcut="⌘ ⌫", color_scheme="red"),
variant="solid",
size="2",
color_scheme="indigo",
high_contrast=False,
),
modal=True,
)| Teil | Prop | Werte | Verwendung |
|---|---|---|---|
root | modal | True / False | Bedienung außerhalb sperren, solange es offen ist |
root | on_open_change | Event | Beim Öffnen oder Schließen; übergibt den neuen Offen-Wert (bool) |
trigger | disabled | True / False | Öffnen per Rechtsklick verhindern |
content | variant · size | "solid" "soft" · "1" "2" | Hervorhebungsstil, Größe |
content | color_scheme · high_contrast | Farbname · bool | Farbe der Einträge, Kontrast |
content | loop · side · align · side_offset · align_offset | - | Tastatur-Schleife, Feinjustierung der Position |
content | avoid_collisions · collision_padding · sticky · hide_when_detached | - | Verhalten am Bildschirmrand |
item | shortcut · color_scheme · disabled · text_value | - | Tastenkürzel, Farbe, nicht auswählbar, Text für die Tipp-Suche |
item | on_select | Event | Wenn ein Eintrag gewählt wird |
sub | open · default_open · on_open_change | - | Offen-Zustand des Untermenüs |
sub_trigger | disabled · text_value | - | Eintrag, der das Untermenü öffnet |
sub_content | loop · side_offset · align_offset usw. | - | Position des Unterpanels |
Anders als beim Dropdown-Menü hat root kein open: Das Menü öffnet sich nur per Rechtsklick. content und sub_content haben die Events on_escape_key_down, on_pointer_down_outside, on_focus_outside und on_interact_outside.
context_menu.item muss ein direktes Kind von context_menu.content sein. Fasst du Einträge in einen Container wie rx.vstack, um sie je nach Bedingung auszutauschen, erkennt das Menü sie nicht mehr. Diese Form verbietet die offizielle Doku:
rx.context_menu.content(
rx.cond(
State.count % 2 == 0,
rx.vstack( # nicht in einen Container packen
rx.context_menu.item("Even Option 1"),
rx.context_menu.item("Even Option 2"),
),
rx.vstack(
rx.context_menu.item("Odd Option A"),
rx.context_menu.item("Odd Option B"),
),
)
)Sollen die Einträge vom State abhängen, machst du die Liste zu einer berechneten Variable und zeichnest die Einträge direkt mit rx.foreach. rx.foreach erzeugt keinen eigenen Container. Klicke unten auf den count-Button und dann erneut mit der rechten Maustaste.
Selected: none
class ContextMenuOptionsState(rx.State):
count: int = 0
selected: str = "none"
@rx.event
def increment(self):
self.count += 1
@rx.event
def set_selected(self, value: str):
self.selected = value
@rx.var
def options(self) -> list[str]:
if self.count % 2 == 0:
return ["Even Option 1", "Even Option 2", "Even Option 3"]
return ["Odd Option A", "Odd Option B", "Odd Option C"]
rx.flex(
rx.context_menu.root(
rx.context_menu.trigger(rx.button("Right click me")),
rx.context_menu.content(
rx.foreach(
ContextMenuOptionsState.options,
lambda option: rx.context_menu.item(option, on_select=ContextMenuOptionsState.set_selected(option)),
),
),
),
rx.button("count = ", ContextMenuOptionsState.count, variant="soft", on_click=ContextMenuOptionsState.increment),
rx.text("Selected: ", ContextMenuOptionsState.selected, size="2"),
spacing="3",
align="center",
wrap="wrap",
)on_open_change an context_menu.root übergibt beim Öffnen True und beim Schließen False.
Opened or closed: 0 times
Context menu open: no
class ContextMenuEventState(rx.State):
num_opens: int = 0
opened: bool = False
@rx.event
def count_opens(self, value: bool):
self.opened = value
self.num_opens += 1
rx.flex(
rx.text("Opened or closed: ", ContextMenuEventState.num_opens, " times"),
rx.text("Context menu open: ", rx.cond(ContextMenuEventState.opened, "yes", "no")),
rx.context_menu.root(
rx.context_menu.trigger(rx.button("Right click me", variant="soft")),
rx.context_menu.content(
rx.context_menu.item("Copy"),
rx.context_menu.item("Paste"),
),
on_open_change=ContextMenuEventState.count_opens,
),
direction="column",
spacing="3",
align="start",
)Wie beim Dropdown-Menü verschwindet ein Dialog, der in einem Menüeintrag steckt, sobald sich das Menü schließt. Lege den Dialog deshalb außerhalb des Menüs ab; der Eintrag ändert nur einen State-Wert. Jeder Dialog öffnet sich, wenn dieser Wert seinem Namen entspricht, und beim Schließen leert on_open_change den Wert. Klicke unten mit der rechten Maustaste auf das Icon.
class ContextMenuDialogState(rx.State):
which_dialog_open: str = ""
@rx.event
def set_which_dialog_open(self, value: str):
self.which_dialog_open = value
@rx.event
def delete(self):
yield rx.toast("Deleted item")
@rx.event
def save_settings(self):
yield rx.toast("Saved settings")
rx.vstack(
rx.context_menu.root(
rx.context_menu.trigger(rx.icon_button(rx.icon("ellipsis-vertical"), variant="soft", aria_label="Right-click for menu")),
rx.context_menu.content(
rx.context_menu.item("Delete", on_click=ContextMenuDialogState.set_which_dialog_open("delete")),
rx.context_menu.item("Settings", on_click=ContextMenuDialogState.set_which_dialog_open("settings")),
),
),
rx.cond(
ContextMenuDialogState.which_dialog_open,
rx.text(ContextMenuDialogState.which_dialog_open, " dialog is open"),
),
rx.alert_dialog.root(
rx.alert_dialog.content(
rx.alert_dialog.title("Are you Sure?"),
rx.alert_dialog.description(
"This action cannot be undone. Are you sure you want to delete this item?",
margin_bottom="20px",
),
rx.hstack(
rx.alert_dialog.action(
rx.button("Delete", color_scheme="red", on_click=ContextMenuDialogState.delete),
),
rx.spacer(),
rx.alert_dialog.cancel(rx.button("Cancel")),
),
),
open=ContextMenuDialogState.which_dialog_open == "delete",
on_open_change=ContextMenuDialogState.set_which_dialog_open(""),
),
rx.dialog.root(
rx.dialog.content(
rx.dialog.title("Settings"),
rx.dialog.description("Set your settings in this settings dialog.", margin_bottom="20px"),
rx.dialog.close(
rx.button("Close", on_click=ContextMenuDialogState.save_settings),
),
),
open=ContextMenuDialogState.which_dialog_open == "settings",
on_open_change=ContextMenuDialogState.set_which_dialog_open(""),
),
align="center",
)Jede Dateizeile wird mit rx.foreach gezeichnet und in ein eigenes Kontextmenü gehüllt. Das on_select der Einträge erhält den jeweiligen Dateinamen, sodass ein einziger Handler für jede Zeile funktioniert.
notes.txt
budget.xlsx
photo.png
class ContextMenuFileState(rx.State):
files: list[str] = ["notes.txt", "budget.xlsx", "photo.png"]
starred: list[str] = []
@rx.event
def toggle_star(self, name: str):
if name in self.starred:
self.starred = [f for f in self.starred if f != name]
else:
self.starred = self.starred + [name]
@rx.event
def duplicate(self, name: str):
stem, dot, ext = name.rpartition(".")
self.files = self.files + [f"{stem} copy{dot}{ext}"]
@rx.event
def remove(self, name: str):
self.files = [f for f in self.files if f != name]
self.starred = [f for f in self.starred if f != name]
@rx.event
def reset_files(self):
self.files = ["notes.txt", "budget.xlsx", "photo.png"]
self.starred = []
rx.vstack(
rx.foreach(
ContextMenuFileState.files,
lambda name: rx.context_menu.root(
rx.context_menu.trigger(
rx.flex(
rx.icon("file"),
rx.text(name),
rx.spacer(),
rx.cond(ContextMenuFileState.starred.contains(name), rx.icon("star", color=rx.color("amber", 9))),
spacing="2",
align="center",
width="100%",
padding="6px 10px",
border_radius="6px",
_hover={"background": rx.color("gray", 3)},
),
),
rx.context_menu.content(
rx.context_menu.item("Star / unstar", on_select=ContextMenuFileState.toggle_star(name)),
rx.context_menu.item("Duplicate", shortcut="⌘ D", on_select=ContextMenuFileState.duplicate(name)),
rx.context_menu.separator(),
rx.context_menu.item("Delete", shortcut="⌘ ⌫", color_scheme="red", on_select=ContextMenuFileState.remove(name)),
),
),
),
rx.button("Reset", size="1", variant="soft", on_click=ContextMenuFileState.reset_files),
width="100%",
max_width="320px",
spacing="1",
)Werkstattnotizen
Öffnet per Rechtsklick (langes Drücken). Die Teile sind wie beim Dropdown-Menü: root · trigger · content · item · separator · sub.
item muss ein direktes Kind von content sein. Dynamische Einträge direkt mit rx.foreach zeichnen.
root hat kein open. Wer wissen will, ob es offen ist, nutzt on_open_change.
Mit disabled=True am trigger erscheint das normale Kontextmenü des Browsers.
Ein Rechtsklick ist eine unsichtbare Funktion. Dieselben Aktionen auch per Button oder Dropdown-Menü anbieten.
Über neue Lektionen informiert werden
Wir schicken dir eine E-Mail, wenn dieser Kurs neue Lektionen bekommt oder ein Folgekurs erscheint.
R
ReflexKR
Python und Reflex zum Anfassen. Alle Beispiele wurden mit Reflex 0.10.0 ausgeführt und geprüft.
ReflexKR ist kostenlos und ohne Werbebanner. Wenn es dir geholfen hat, kannst du es mit einem Kaffee unterstützen. ☕ Spendier uns einen Kaffee
Hinweis zu Affiliate-Links© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
Fragen & Antworten
Frag alles zu dieser Lektion. Fragen aus allen Sprachversionen dieser Seite erscheinen hier gemeinsam.