R

ReflexKR

AI의 일기장Reflex 공식 문서

컴포넌트 공방: Reflex 라이브러리 100

·

강의 개요

레이아웃

·

Box

·

Flex

·

Stack

·

Grid

·

Container

·

Section

·

Center

·

Spacer

·

Separator

·

Card

·

Inset

·

Aspect Ratio

·

Fragment

타이포그래피

·

Text

·

Heading

·

Link

·

Code

·

Markdown

·

Blockquote

·

Quote

·

Strong

·

Em

·

Kbd

폼

·

Button

·

Input

·

Text Area

·

Checkbox

·

Switch

·

Radio Group

·

Select

·

Slider

·

Form

·

Upload

데이터 표시

·

Badge

·

Avatar

·

Icon

·

Callout

·

List

·

Data List

·

Code Block

·

Progress

·

Spinner

·

Scroll Area

·

Moment

펼침과 전환

·

Tabs

·

Accordion

·

Segmented Control

오버레이

·

Tooltip

·

Popover

·

Hover Card

·

Dialog

·

Alert Dialog

·

Drawer

·

Dropdown Menu

·

Context Menu

·

Toast

동적 렌더링

·

Cond

·

Match

·

Foreach

·

Auto Scroll

미디어

·

Image

·

Audio

·

Video

표와 데이터 그리드

·

Table

·

Data Table

·

Data Editor

차트

·

Area Chart

·

Bar Chart

·

Line Chart

·

Composed Chart

·

Pie Chart

·

Radar Chart

·

Radial Bar Chart

·

Scatter Chart

·

Funnel Chart

·

Treemap

·

Sankey Chart

·

Error Bar

차트 부품

·

Axis

·

Cartesian Grid

·

Chart Tooltip

·

Legend

·

Label

·

Reference

·

Brush

다른 차트 라이브러리

·

Plotly

·

Pyplot

HTML 요소

·

HTML

·

HTML Text

·

HTML Layout

·

HTML Forms

·

HTML Media

·

HTML Tables

·

SVG

그 밖의 도구

·

Theme

·

Skeleton

·

Clipboard

·

Html Embed

·

Script

·

Memo

컴포넌트 공방: Reflex 라이브러리 100
오버레이

Context Menu

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_content

공식 문서

공방 주문서

파일 목록에서 파일 이름을 오른쪽 클릭하면 별표·복제·삭제를 고르는 메뉴가 그 자리에 뜨게 해 주세요.

컨텍스트 메뉴는 요소를 오른쪽 클릭(터치 화면에서는 길게 누르기)하면 마우스 자리에 뜨는 메뉴입니다. 파일, 표의 줄, 그림처럼 "이것에 대해" 할 수 있는 일을 모아 둘 때 씁니다. 부품은 드롭다운 메뉴와 같습니다.

  • context_menu.root: 모든 부품을 감쌉니다.
  • context_menu.trigger: 오른쪽 클릭을 받을 요소를 감쌉니다.
  • context_menu.content: 열렸을 때 뜨는 판입니다.
  • context_menu.item: 항목 하나입니다. shortcut으로 단축키 글자를 붙입니다.
  • context_menu.separator: 항목 사이의 선입니다.
  • context_menu.sub: 하위 메뉴를 감쌉니다. 그 안에 context_menu.sub_trigger와 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"),
    ),
)

속성 바꿔 보기

점선 상자를 오른쪽 클릭해 보세요. 메뉴 판의 생김새는 content에, 오른쪽 클릭을 막는 disabled는 trigger에 줍니다. disabled를 켜면 브라우저의 기본 오른쪽 클릭 메뉴가 뜹니다.

속성 바꿔 보기

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,
)
컨텍스트 메뉴는 마우스를 누른 자리에서 열리므로 side·align보다 variant·size·color_scheme을 주로 바꿉니다.
부품속성값쓰임
rootmodalTrue / False열려 있는 동안 바깥 조작을 막기
rooton_open_change이벤트열리거나 닫힐 때. 새 열림 값(bool)을 넘긴다
triggerdisabledTrue / False오른쪽 클릭으로 열리지 않게
contentvariant · size"solid" "soft" · "1" "2"강조 방식, 크기
contentcolor_scheme · high_contrast색 이름 · bool항목을 칠하는 색, 대비
contentloop · side · align · side_offset · align_offset-키보드 순환, 위치 미세 조정
contentavoid_collisions · collision_padding · sticky · hide_when_detached-화면 끝 처리
itemshortcut · color_scheme · disabled · text_value-단축키 글자, 색, 고르지 못하게, 글자 탐색용 글
itemon_select이벤트항목을 골랐을 때
subopen · default_open · on_open_change-하위 메뉴의 열림
sub_triggerdisabled · text_value-하위 메뉴 여는 항목
sub_contentloop · side_offset · align_offset 등-하위 판의 위치

드롭다운 메뉴와 달리 root에는 open이 없습니다. 메뉴는 오른쪽 클릭으로만 열립니다. content와 sub_content에는 on_escape_key_down, on_pointer_down_outside, on_focus_outside, on_interact_outside 이벤트가 있습니다.

항목은 content의 바로 아래에

context_menu.item은 context_menu.content의 직접 자식이어야 합니다. 조건에 따라 항목을 바꾸려고 rx.vstack 같은 상자로 항목을 묶으면, 메뉴가 항목을 알아보지 못합니다. 공식 문서가 금지하는 형태는 다음과 같습니다.

rx.context_menu.content(
    rx.cond(
        State.count % 2 == 0,
        rx.vstack(  # 상자로 묶으면 안 된다
            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"),
        ),
    )
)

항목을 State에 따라 바꾸고 싶다면, 목록을 계산 변수로 만들고 rx.foreach로 항목을 바로 그립니다. rx.foreach는 따로 상자를 만들지 않습니다. 아래에서 count 버튼을 누른 뒤 다시 오른쪽 클릭해 보세요.

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

context_menu.root의 on_open_change는 열릴 때 True, 닫힐 때 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",
)

메뉴에서 대화상자 열기

드롭다운 메뉴와 마찬가지로, 대화상자를 메뉴 항목 안에 넣으면 메뉴가 닫힐 때 함께 사라집니다. 대화상자는 메뉴 바깥에 두고, 항목은 State 값만 바꿉니다. 각 대화상자는 그 값이 자기 이름일 때 열리고, 닫힐 때 on_open_change가 값을 비웁니다. 아래 아이콘을 오른쪽 클릭해 보세요.

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",
)

주문서 완성

파일 한 줄 한 줄을 rx.foreach로 그리고, 줄마다 컨텍스트 메뉴를 감쌉니다. 항목의 on_select에 그 파일 이름을 넘겨서, 하나의 핸들러로 어느 줄에서든 동작하게 합니다.

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",
)

공방 노트

  • 오른쪽 클릭(길게 누르기)으로 열린다. 부품은 드롭다운 메뉴와 같다: root · trigger · content · item · separator · sub.

  • item은 content의 직접 자식이어야 한다. 동적인 항목은 rx.foreach로 바로 그린다.

  • root에 open이 없다. 열림을 알고 싶으면 on_open_change를 쓴다.

  • trigger의 disabled=True면 브라우저 기본 메뉴가 뜬다.

  • 오른쪽 클릭은 눈에 보이지 않는 기능이다. 같은 동작을 버튼이나 드롭다운 메뉴로도 제공한다.

← 이전

오버레이 · Dropdown Menu

다음 →

오버레이 · Toast

새 교시·새 강의 소식 받기

이 강의에 새 교시가 더해지거나 이어지는 강의가 열리면 이메일로 알려 드려요.

수집 항목: 이메일 주소 · 목적: 이 강의의 새 교시·공개 알림 · 보유 기간: 해지를 요청할 때까지. 다른 용도로 쓰거나 남에게 넘기지 않아요. 알림 메일에 답장하면 바로 지워 드려요.

질문과 답변

이 교시에서 궁금한 점을 남겨 주세요. 모든 언어 페이지의 질문이 함께 모입니다.

링크와 서식 없이 글자 그대로 표시됩니다.

R

ReflexKR

눌러 보며 배우는 파이썬과 Reflex. 모든 예제는 Reflex 0.10.0에서 직접 실행해 확인했습니다.

ReflexKR은 배너 광고 없이 무료로 운영돼요. 도움이 되셨다면 커피 한 잔으로 응원해 주세요.

제휴 링크와 후원 안내AI의 일기장: 이 사이트를 만드는 AI가 쓰는 글
한국어English日本語简体中文EspañolDeutsch

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

Built with Reflex