El taller de componentes: los 100 componentes de Reflex
·
Resumen del curso
Diseño
·
Box
·
Flex
·
Stack
·
Grid
·
Container
·
Section
·
Center
·
Spacer
·
Separator
·
Card
·
Inset
·
Aspect Ratio
·
Fragment
Tipografía
·
Text
·
Heading
·
Link
·
Code
·
Markdown
·
Blockquote
·
Quote
·
Strong
·
Em
·
Kbd
Formularios
·
Button
·
Input
·
Text Area
·
Checkbox
·
Switch
·
Radio Group
·
Select
·
Slider
·
Form
·
Upload
Visualización de datos
·
Badge
·
Avatar
·
Icon
·
Callout
·
List
·
Data List
·
Code Block
·
Progress
·
Spinner
·
Scroll Area
·
Moment
Mostrar y ocultar
·
Tabs
·
Accordion
·
Segmented Control
Superposiciones
·
Tooltip
·
Popover
·
Hover Card
·
Dialog
·
Alert Dialog
·
Drawer
·
Dropdown Menu
·
Context Menu
·
Toast
Renderizado dinámico
·
Cond
·
Match
·
Foreach
·
Auto Scroll
Multimedia
·
Image
·
Audio
·
Video
Tablas y cuadrículas de datos
·
Table
·
Data Table
·
Data Editor
Gráficos
·
Area Chart
·
Bar Chart
·
Line Chart
·
Composed Chart
·
Pie Chart
·
Radar Chart
·
Radial Bar Chart
·
Scatter Chart
·
Funnel Chart
·
Treemap
·
Sankey Chart
·
Error Bar
Piezas de gráficos
·
Axis
·
Cartesian Grid
·
Chart Tooltip
·
Legend
·
Label
·
Reference
·
Brush
Otras bibliotecas de gráficos
·
Plotly
·
Pyplot
Elementos HTML
·
HTML
·
HTML Text
·
HTML Layout
·
HTML Forms
·
HTML Media
·
HTML Tables
·
SVG
Otras herramientas
·
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_contentDocumentación oficial
Pedido del taller
En la lista de archivos, al hacer clic derecho sobre el nombre de un archivo, abre ahí mismo un menú para marcarlo con estrella, duplicarlo o borrarlo.
Un menú contextual es un menú que aparece en la posición del ratón al hacer clic derecho sobre un elemento (en pantallas táctiles, al mantenerlo pulsado). Sirve para reunir lo que se puede hacer «con esto»: un archivo, una fila de una tabla, una imagen. Sus partes son las mismas que las del menú desplegable.
context_menu.root: envuelve todas las partes.context_menu.trigger: envuelve el elemento que recibe el clic derecho.context_menu.content: el panel que aparece al abrirse.context_menu.item: una opción. Con shortcut se le añade el texto de un atajo.context_menu.separator: una línea entre opciones.context_menu.sub: envuelve un submenú. Dentro van context_menu.sub_trigger y 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"),
),
)Haz clic derecho en el recuadro de puntos. El aspecto del panel va en content, y disabled, que impide el clic derecho, va en trigger. Con disabled activado aparece el menú contextual normal del navegador.
Prueba las propiedades
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,
)| Parte | Propiedad | Valores | Uso |
|---|---|---|---|
root | modal | True / False | Bloquear la interacción exterior mientras está abierto |
root | on_open_change | Evento | Al abrirse o cerrarse. Pasa el nuevo valor de apertura (bool) |
trigger | disabled | True / False | Que no se abra con el clic derecho |
content | variant · size | "solid" "soft" · "1" "2" | Tipo de resaltado, tamaño |
content | color_scheme · high_contrast | Nombre de color · bool | Color con que se pintan las opciones, contraste |
content | loop · side · align · side_offset · align_offset | - | Recorrido circular con el teclado, ajuste fino de la posición |
content | avoid_collisions · collision_padding · sticky · hide_when_detached | - | Comportamiento en el borde de la pantalla |
item | shortcut · color_scheme · disabled · text_value | - | Texto de atajo, color, impedir elegirla, texto para buscar tecleando |
item | on_select | Evento | Al elegir la opción |
sub | open · default_open · on_open_change | - | Apertura del submenú |
sub_trigger | disabled · text_value | - | La opción que abre el submenú |
sub_content | loop · side_offset · align_offset, etc. | - | Posición del panel secundario |
A diferencia del menú desplegable, root no tiene open: el menú solo se abre con el clic derecho. content y sub_content tienen los eventos on_escape_key_down, on_pointer_down_outside, on_focus_outside y on_interact_outside.
context_menu.item tiene que ser hijo directo de context_menu.content. Si, para cambiar las opciones según una condición, las agrupas en una caja como rx.vstack, el menú deja de reconocerlas. Esta es la forma que la documentación oficial prohíbe.
rx.context_menu.content(
rx.cond(
State.count % 2 == 0,
rx.vstack( # no las agrupes en una caja
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"),
),
)
)Si quieres que las opciones cambien según el State, convierte la lista en una variable calculada y dibuja las opciones directamente con rx.foreach, que no crea ninguna caja extra. Abajo, pulsa el botón count y vuelve a hacer clic derecho.
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",
)El on_open_change de context_menu.root pasa True cuando se abre y False cuando se cierra.
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",
)Igual que con el menú desplegable, si metes un diálogo dentro de una opción, desaparece cuando el menú se cierra. Pon los diálogos fuera del menú y haz que las opciones solo cambien un valor del State. Cada diálogo se abre cuando ese valor coincide con su nombre, y al cerrarse on_open_change vacía el valor. Haz clic derecho en el icono de abajo.
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",
)Cada fila de archivo se dibuja con rx.foreach y va envuelta en su propio menú contextual. El on_select de cada opción recibe el nombre de ese archivo, de modo que un solo handler funciona en cualquier fila.
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",
)Notas del taller
Se abre con clic derecho (o pulsación larga). Las partes son las del menú desplegable: root · trigger · content · item · separator · sub.
item debe ser hijo directo de content. Las opciones dinámicas se dibujan directamente con rx.foreach.
root no tiene open. Para saber si está abierto, usa on_open_change.
Con disabled=True en trigger aparece el menú normal del navegador.
El clic derecho es una función invisible. Ofrece las mismas acciones también con botones o un menú desplegable.
Recibe avisos de nuevas lecciones
Te avisaremos por correo cuando este curso tenga lecciones nuevas o se abra un curso de continuación.
R
ReflexKR
Aprende Python y Reflex haciendo. Todos los ejemplos se ejecutaron y verificaron en Reflex 0.10.0.
ReflexKR es gratis y sin banners publicitarios. Si te ayudó, puedes apoyarlo con un café. ☕ Invítanos un café
Aviso de afiliados© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
Preguntas y respuestas
Pregunta lo que quieras sobre esta lección. Aquí se reúnen las preguntas de todas las versiones de idioma de esta página.