R

ReflexKR

Documentación de Reflex

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

El taller de componentes: los 100 componentes de Reflex
Superposiciones

Dropdown Menu

rx.menu.rootrx.menu.triggerrx.menu.contentrx.menu.itemrx.menu.separatorrx.menu.subrx.menu.sub_triggerrx.menu.sub_content

Documentación oficial

Pedido del taller

Pon un botón «⋯» al final de cada fila de la lista de archivos que, al pulsarlo, despliegue un menú para duplicar, mover o cambiar el estado.

Un menú desplegable es una lista de opciones que se despliega bajo un botón al pulsarlo. Sirve para aligerar la pantalla reuniendo detrás de un solo botón las acciones que se usan poco. rx.menu y rx.dropdown_menu son lo mismo; esta página usa la forma corta, rx.menu.

  • menu.root: envuelve todas las partes del menú.
  • menu.trigger: envuelve el elemento que abre y cierra el menú al pulsarlo.
  • menu.content: el panel que se despliega al abrirse.
  • menu.item: una opción del menú. Con shortcut puedes añadir a la derecha el texto de un atajo de teclado.
  • menu.separator: una línea que separa opciones.
  • menu.sub: envuelve todas las partes de un submenú. Dentro van menu.sub_trigger (la opción que abre el submenú) y menu.sub_content (el panel secundario que se despliega).
rx.menu.root(
    rx.menu.trigger(
        rx.button("Options", variant="soft"),
    ),
    rx.menu.content(
        rx.menu.item("Edit", shortcut="⌘ E"),
        rx.menu.item("Duplicate", shortcut="⌘ D"),
        rx.menu.separator(),
        rx.menu.item("Archive", shortcut="⌘ N"),
        rx.menu.sub(
            rx.menu.sub_trigger("More"),
            rx.menu.sub_content(
                rx.menu.item("Move to project…"),
                rx.menu.item("Move to folder…"),
                rx.menu.separator(),
                rx.menu.item("Advanced options…"),
            ),
        ),
        rx.menu.separator(),
        rx.menu.item("Share"),
        rx.menu.item("Add to favorites"),
        rx.menu.separator(),
        rx.menu.item("Delete", shortcut="⌘ ⌫", color="red"),
    ),
)

Prueba las propiedades

El aspecto y la posición del panel van en menu.content. Cambia un valor y pulsa Options para comprobarlo.

Prueba las propiedades

variant

size

color

high_contrast

side

align

disabled

modal

rx.menu.root(
    rx.menu.trigger(rx.button("Options", variant="soft")),
    rx.menu.content(
        rx.menu.item("Edit", shortcut="⌘ E"),
        rx.menu.item("Duplicate", shortcut="⌘ D", disabled=False),
        rx.menu.separator(),
        rx.menu.item("Delete", shortcut="⌘ ⌫", color_scheme="red"),
        variant="solid",
        size="2",
        color_scheme="indigo",
        high_contrast=False,
        side="bottom",
        align="start",
    ),
    modal=True,
)
variant="solid" pinta con fuerza la opción bajo el ratón y "soft" la pinta suave. Las opciones disabled no se pueden elegir.
PartePropiedadValoresUso
rootopen · default_openTrue / FalseDecidir la apertura desde el State (controlado) · abierto al principio
rootmodalTrue / FalseTrue por defecto. Bloquea la interacción exterior y el scroll mientras está abierto
rooton_open_changeEventoAl abrirse o cerrarse. Pasa el nuevo valor de apertura (bool)
contentvariant · size"solid" "soft" · "1" "2"Tipo de resaltado, tamaño
contentcolor_scheme · high_contrastNombre de color · boolColor con que se pintan las opciones, contraste
contentside · align · side_offset · align_offsetPosiciónLado hacia el que se despliega y alineación
contentloopTrue / FalseVolver del final al principio con el teclado
contentavoid_collisions · collision_padding · sticky · hide_when_detached-Comportamiento en el borde de la pantalla (igual que Popover)
itemshortcutCadenaTexto de atajo que se ve a la derecha (solo se muestra)
itemcolor_scheme · disabled · text_valueColor · bool · cadenaColor de la opción, impedir elegirla, texto para buscar tecleando
itemon_selectEventoAl elegir la opción con el ratón o el teclado
subopen · default_open · on_open_change-Apertura del submenú
sub_triggerdisabled · text_value-La opción que abre el submenú
sub_contentloop · side_offset · align_offset, etc.-Posición del panel secundario

content y sub_content tienen los eventos on_escape_key_down, on_pointer_down_outside, on_focus_outside y on_interact_outside, y content además tiene on_close_auto_focus.

Al elegir una opción: on_select

Cuando se pulsa una opción con el ratón o se elige con el teclado, se llama a on_select y el menú se cierra. Si pasas el valor elegido como argumento del handler, no hace falta un handler distinto para cada opción. Como en el ejemplo del diálogo de la documentación oficial, on_click también se dispara con ratón y teclado, pero el evento pensado para elegir opciones es on_select.

Selected: nothing yet

class DropdownPickState(rx.State):
    last: str = "nothing yet"

    @rx.event
    def pick(self, name: str):
        self.last = name


rx.flex(
    rx.menu.root(
        rx.menu.trigger(rx.button("Sort by", rx.icon("chevron-down"), variant="outline")),
        rx.menu.content(
            rx.menu.item("Name", on_select=DropdownPickState.pick("Name")),
            rx.menu.item("Date added", on_select=DropdownPickState.pick("Date added")),
            rx.menu.item("Size", on_select=DropdownPickState.pick("Size")),
            rx.menu.separator(),
            rx.menu.item("Custom…", disabled=True),
        ),
    ),
    rx.text("Selected: ", rx.text.strong(DropdownPickState.last)),
    spacing="3",
    align="center",
    wrap="wrap",
)

Al abrirse y cerrarse: on_open_change

El on_open_change de menu.root pasa True cuando se abre y False cuando se cierra.

Opened or closed: 0 times

Dropdown menu open: no

class DropdownMenuEventState(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: ", DropdownMenuEventState.num_opens, " times"),
    rx.text("Dropdown menu open: ", rx.cond(DropdownMenuEventState.opened, "yes", "no")),
    rx.menu.root(
        rx.menu.trigger(
            rx.button("Options", variant="soft", size="2"),
        ),
        rx.menu.content(
            rx.menu.item("Edit", shortcut="⌘ E"),
            rx.menu.item("Duplicate", shortcut="⌘ D"),
            rx.menu.separator(),
            rx.menu.item("Archive", shortcut="⌘ N"),
            rx.menu.separator(),
            rx.menu.item("Delete", shortcut="⌘ ⌫", color="red"),
        ),
        on_open_change=DropdownMenuEventState.count_opens,
    ),
    direction="column",
    spacing="3",
    align="start",
)

Abrir un diálogo desde el menú

Si metes un rx.dialog.root entero dentro de una opción del menú, el diálogo desaparece en cuanto el menú se cierra. Como el diálogo está dentro del menú, solo existe mientras el menú está abierto.

rx.menu.root(
    rx.menu.trigger(rx.icon("ellipsis-vertical")),
    rx.menu.content(
        rx.menu.item(
            rx.dialog.root(  # así no funciona
                rx.dialog.trigger(rx.text("Edit")),
                rx.dialog.content(...),
            ),
        ),
    ),
)

En su lugar, pon los diálogos fuera del menú y recuerda qué ventana está abierta con una sola cadena en el State. Las opciones solo cambian ese valor, y cada diálogo se abre con open=State.valor == "nombre". Al cerrarse, on_open_change devuelve el valor a la cadena vacía.

class DropdownDialogState(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.menu.root(
        rx.menu.trigger(rx.icon_button(rx.icon("menu"), variant="soft", aria_label="Menu")),
        rx.menu.content(
            rx.menu.item("Delete", on_click=DropdownDialogState.set_which_dialog_open("delete")),
            rx.menu.item("Settings", on_click=DropdownDialogState.set_which_dialog_open("settings")),
        ),
    ),
    rx.cond(
        DropdownDialogState.which_dialog_open,
        rx.text(DropdownDialogState.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=DropdownDialogState.delete),
                ),
                rx.spacer(),
                rx.alert_dialog.cancel(rx.button("Cancel")),
            ),
        ),
        open=DropdownDialogState.which_dialog_open == "delete",
        on_open_change=DropdownDialogState.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=DropdownDialogState.save_settings),
            ),
        ),
        open=DropdownDialogState.which_dialog_open == "settings",
        on_open_change=DropdownDialogState.set_which_dialog_open(""),
    ),
    align="center",
)

Completar el pedido

El menú va en el botón «⋯» al final de la fila. Mover y cambiar el estado se agrupan en submenús, y el valor elegido entra en el State con on_select para cambiar el texto de la fila. Como el botón está en el extremo derecho de la fila, el panel se alinea con align="end".

Q3 report

Inbox · Draft

class DropdownRowState(rx.State):
    status: str = "Draft"
    folder: str = "Inbox"

    @rx.event
    def set_status(self, value: str):
        self.status = value

    @rx.event
    def move(self, folder: str):
        self.folder = folder

    @rx.event
    def duplicate(self):
        return rx.toast("Duplicated Q3 report")


rx.flex(
    rx.icon("file-text", color=rx.color("gray", 10)),
    rx.box(
        rx.text("Q3 report", weight="bold"),
        rx.text(DropdownRowState.folder, " · ", DropdownRowState.status, size="1", color_scheme="gray"),
    ),
    rx.spacer(),
    rx.menu.root(
        rx.menu.trigger(rx.icon_button(rx.icon("ellipsis"), variant="ghost", color_scheme="gray", aria_label="More actions")),
        rx.menu.content(
            rx.menu.item("Duplicate", shortcut="⌘ D", on_select=DropdownRowState.duplicate),
            rx.menu.sub(
                rx.menu.sub_trigger("Move to"),
                rx.menu.sub_content(
                    rx.menu.item("Inbox", on_select=DropdownRowState.move("Inbox")),
                    rx.menu.item("Projects", on_select=DropdownRowState.move("Projects")),
                    rx.menu.item("Archive", on_select=DropdownRowState.move("Archive")),
                ),
            ),
            rx.menu.sub(
                rx.menu.sub_trigger("Status"),
                rx.menu.sub_content(
                    rx.menu.item("Draft", on_select=DropdownRowState.set_status("Draft")),
                    rx.menu.item("Published", on_select=DropdownRowState.set_status("Published")),
                ),
            ),
            rx.menu.separator(),
            rx.menu.item("Delete", shortcut="⌘ ⌫", color_scheme="red", disabled=True),
            align="end",
        ),
    ),
    spacing="3",
    align="center",
    padding="8px 12px",
    border=f"1px solid {rx.color('gray', 5)}",
    border_radius="8px",
    max_width="420px",
)

Notas del taller

  • rx.menu = rx.dropdown_menu. root > trigger + content > item · separator · sub(sub_trigger, sub_content).

  • Al elegir una opción se llama a on_select y el menú se cierra. El valor se pasa como argumento del handler.

  • shortcut solo muestra el texto. El atajo de teclado real hay que programarlo aparte.

  • Aspecto y posición en content (variant, size, color_scheme, side, align).

  • Para abrir un diálogo desde el menú, pon el diálogo fuera del menú y ábrelo desde el State.

← Anterior

Superposiciones · Drawer

Siguiente →

Superposiciones · Context Menu

Recibe avisos de nuevas lecciones

Te avisaremos por correo cuando este curso tenga lecciones nuevas o se abra un curso de continuación.

Datos: tu correo electrónico · Finalidad: avisos de nuevas lecciones o de la publicación de este curso · Conservación: hasta que pidas que lo borremos. No lo usamos para nada más ni lo compartimos. Responde a cualquier aviso y lo borraremos enseguida.

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.

Se muestra como texto plano, sin enlaces ni formato.

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
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex