Wrapping extra components inside of the Context Menu Component (partial fix for #4262) label, group, radio, radio_group

This commit is contained in:
slackroo 2025-02-17 17:52:22 +05:30
parent 6848915883
commit 05e68c2b27
9 changed files with 414 additions and 17 deletions

View File

@ -15,7 +15,7 @@ repos:
rev: v2.3.0 rev: v2.3.0
hooks: hooks:
- id: codespell - id: codespell
args: ["reflex"] args: ["reflex", "te"]
# Run pyi check before pyright because pyright can fail if pyi files are wrong. # Run pyi check before pyright because pyright can fail if pyi files are wrong.
- repo: local - repo: local

View File

@ -35,11 +35,11 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None
for base in bases: for base in bases:
if not reload and getattr(base, field_name, None): if not reload and getattr(base, field_name, None):
pass pass
except TypeError as te: except TypeError as te: # codespell:ignore te
raise VarNameError( raise VarNameError(
f'State var "{field_name}" in {base} has been shadowed by a substate var; ' f'State var "{field_name}" in {base} has been shadowed by a substate var; '
f'use a different field name instead".' f'use a different field name instead".'
) from te ) from te # codespell:ignore te
# monkeypatch pydantic validate_field_name method to skip validating # monkeypatch pydantic validate_field_name method to skip validating

View File

@ -180,7 +180,7 @@ def save_error(error: Exception) -> str:
timestamp = datetime.now().strftime("%Y-%m-%d__%H-%M-%S") timestamp = datetime.now().strftime("%Y-%m-%d__%H-%M-%S")
constants.Reflex.LOGS_DIR.mkdir(parents=True, exist_ok=True) constants.Reflex.LOGS_DIR.mkdir(parents=True, exist_ok=True)
log_path = constants.Reflex.LOGS_DIR / f"error_{timestamp}.log" log_path = constants.Reflex.LOGS_DIR / f"error_{timestamp}.log"
traceback.TracebackException.from_exception(error).print(file=log_path.open("w+")) traceback.TracebackException.from_exception(error)
return str(log_path) return str(log_path)

View File

@ -10,6 +10,7 @@ from reflex.vars.base import Var
from ..base import LiteralAccentColor, RadixThemesComponent from ..base import LiteralAccentColor, RadixThemesComponent
from .checkbox import Checkbox from .checkbox import Checkbox
from .radio_group import HighLevelRadioGroup
LiteralDirType = Literal["ltr", "rtl"] LiteralDirType = Literal["ltr", "rtl"]
@ -226,7 +227,11 @@ class ContextMenuItem(RadixThemesComponent):
# Optional text used for typeahead purposes. By default the typeahead behavior will use the content of the item. Use this when the content is complex, or you have non-textual content inside. # Optional text used for typeahead purposes. By default the typeahead behavior will use the content of the item. Use this when the content is complex, or you have non-textual content inside.
text_value: Var[str] text_value: Var[str]
_valid_parents: List[str] = ["ContextMenuContent", "ContextMenuSubContent"] _valid_parents: List[str] = [
"ContextMenuContent",
"ContextMenuSubContent",
"ContextMenuGroup",
]
# Fired when the item is selected. # Fired when the item is selected.
on_select: EventHandler[no_args_event_spec] on_select: EventHandler[no_args_event_spec]
@ -247,6 +252,75 @@ class ContextMenuCheckbox(Checkbox):
shortcut: Var[str] shortcut: Var[str]
class ContextMenuLabel(RadixThemesComponent):
"""The component that contains the label."""
tag = "ContextMenu.Label"
# Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False.
as_child: Var[bool]
class ContextMenuGroup(RadixThemesComponent):
"""The component that contains the group."""
tag = "ContextMenu.Group"
# Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False.
as_child: Var[bool]
_valid_parents: List[str] = ["ContextMenuContent", "ContextMenuSubContent"]
class ContextMenuRadioGroup(RadixThemesComponent):
"""The component that contains context menu radio items."""
tag = "ContextMenu.RadioGroup"
# Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False.
as_child: Var[bool]
# The value of the selected item in the group.
value: Var[str]
# Props to rename
_rename_props = {"onChange": "onValueChange"}
# Fired when the value of the radio group changes.
on_change: EventHandler[passthrough_event_spec(str)]
_valid_parents: List[str] = [
"ContextMenuRadioItem",
"ContextMenuSubContent",
"ContextMenuContent",
"ContextMenuSub",
]
class ContextMenuRadioItem(HighLevelRadioGroup):
"""The component that contains context menu radio items."""
tag = "ContextMenu.RadioItem"
# Override theme color for Dropdown Menu Content
color_scheme: Var[LiteralAccentColor]
# Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False.
as_child: Var[bool]
# The unique value of the item.
value: Var[str]
# When true, prevents the user from interacting with the item.
disabled: Var[bool]
# Event handler called when the user selects an item (via mouse or keyboard). Calling event.preventDefault in this handler will prevent the context menu from closing when selecting that item.
on_select: EventHandler[no_args_event_spec]
# Optional text used for typeahead purposes. By default the typeahead behavior will use the .textContent of the item. Use this when the content is complex, or you have non-textual content inside.
text_value: Var[str]
class ContextMenu(ComponentNamespace): class ContextMenu(ComponentNamespace):
"""Menu representing a set of actions, displayed at the origin of a pointer right-click or long-press.""" """Menu representing a set of actions, displayed at the origin of a pointer right-click or long-press."""
@ -259,6 +333,10 @@ class ContextMenu(ComponentNamespace):
item = staticmethod(ContextMenuItem.create) item = staticmethod(ContextMenuItem.create)
separator = staticmethod(ContextMenuSeparator.create) separator = staticmethod(ContextMenuSeparator.create)
checkbox = staticmethod(ContextMenuCheckbox.create) checkbox = staticmethod(ContextMenuCheckbox.create)
label = staticmethod(ContextMenuLabel.create)
group = staticmethod(ContextMenuGroup.create)
radio_group = staticmethod(ContextMenuRadioGroup.create)
radio = staticmethod(ContextMenuRadioItem.create)
context_menu = ContextMenu() context_menu = ContextMenu()

View File

@ -3,7 +3,7 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Dict, Literal, Optional, Union, overload from typing import Any, Dict, List, Literal, Optional, Union, overload
from reflex.components.component import ComponentNamespace from reflex.components.component import ComponentNamespace
from reflex.components.core.breakpoints import Breakpoints from reflex.components.core.breakpoints import Breakpoints
@ -13,6 +13,7 @@ from reflex.vars.base import Var
from ..base import RadixThemesComponent from ..base import RadixThemesComponent
from .checkbox import Checkbox from .checkbox import Checkbox
from .radio_group import HighLevelRadioGroup
LiteralDirType = Literal["ltr", "rtl"] LiteralDirType = Literal["ltr", "rtl"]
LiteralSizeType = Literal["1", "2"] LiteralSizeType = Literal["1", "2"]
@ -820,6 +821,320 @@ class ContextMenuCheckbox(Checkbox):
""" """
... ...
class ContextMenuLabel(RadixThemesComponent):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
as_child: Optional[Union[Var[bool], bool]] = None,
style: Optional[Style] = None,
key: Optional[Any] = None,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
on_blur: Optional[EventType[()]] = None,
on_click: Optional[EventType[()]] = None,
on_context_menu: Optional[EventType[()]] = None,
on_double_click: Optional[EventType[()]] = None,
on_focus: Optional[EventType[()]] = None,
on_mount: Optional[EventType[()]] = None,
on_mouse_down: Optional[EventType[()]] = None,
on_mouse_enter: Optional[EventType[()]] = None,
on_mouse_leave: Optional[EventType[()]] = None,
on_mouse_move: Optional[EventType[()]] = None,
on_mouse_out: Optional[EventType[()]] = None,
on_mouse_over: Optional[EventType[()]] = None,
on_mouse_up: Optional[EventType[()]] = None,
on_scroll: Optional[EventType[()]] = None,
on_unmount: Optional[EventType[()]] = None,
**props,
) -> "ContextMenuLabel":
"""Create a new component instance.
Will prepend "RadixThemes" to the component tag to avoid conflicts with
other UI libraries for common names, like Text and Button.
Args:
*children: Child components.
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded
custom_attrs: custom attribute
**props: Component properties.
Returns:
A new component instance.
"""
...
class ContextMenuGroup(RadixThemesComponent):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
as_child: Optional[Union[Var[bool], bool]] = None,
style: Optional[Style] = None,
key: Optional[Any] = None,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
on_blur: Optional[EventType[()]] = None,
on_click: Optional[EventType[()]] = None,
on_context_menu: Optional[EventType[()]] = None,
on_double_click: Optional[EventType[()]] = None,
on_focus: Optional[EventType[()]] = None,
on_mount: Optional[EventType[()]] = None,
on_mouse_down: Optional[EventType[()]] = None,
on_mouse_enter: Optional[EventType[()]] = None,
on_mouse_leave: Optional[EventType[()]] = None,
on_mouse_move: Optional[EventType[()]] = None,
on_mouse_out: Optional[EventType[()]] = None,
on_mouse_over: Optional[EventType[()]] = None,
on_mouse_up: Optional[EventType[()]] = None,
on_scroll: Optional[EventType[()]] = None,
on_unmount: Optional[EventType[()]] = None,
**props,
) -> "ContextMenuGroup":
"""Create a new component instance.
Will prepend "RadixThemes" to the component tag to avoid conflicts with
other UI libraries for common names, like Text and Button.
Args:
*children: Child components.
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded
custom_attrs: custom attribute
**props: Component properties.
Returns:
A new component instance.
"""
...
class ContextMenuRadioGroup(RadixThemesComponent):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
as_child: Optional[Union[Var[bool], bool]] = None,
value: Optional[Union[Var[str], str]] = None,
style: Optional[Style] = None,
key: Optional[Any] = None,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
on_blur: Optional[EventType[()]] = None,
on_change: Optional[Union[EventType[()], EventType[str]]] = None,
on_click: Optional[EventType[()]] = None,
on_context_menu: Optional[EventType[()]] = None,
on_double_click: Optional[EventType[()]] = None,
on_focus: Optional[EventType[()]] = None,
on_mount: Optional[EventType[()]] = None,
on_mouse_down: Optional[EventType[()]] = None,
on_mouse_enter: Optional[EventType[()]] = None,
on_mouse_leave: Optional[EventType[()]] = None,
on_mouse_move: Optional[EventType[()]] = None,
on_mouse_out: Optional[EventType[()]] = None,
on_mouse_over: Optional[EventType[()]] = None,
on_mouse_up: Optional[EventType[()]] = None,
on_scroll: Optional[EventType[()]] = None,
on_unmount: Optional[EventType[()]] = None,
**props,
) -> "ContextMenuRadioGroup":
"""Create a new component instance.
Will prepend "RadixThemes" to the component tag to avoid conflicts with
other UI libraries for common names, like Text and Button.
Args:
*children: Child components.
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False.
value: The value of the selected item in the group.
on_change: Fired when the value of the radio group changes.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded
custom_attrs: custom attribute
**props: Component properties.
Returns:
A new component instance.
"""
...
class ContextMenuRadioItem(HighLevelRadioGroup):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
color_scheme: Optional[
Union[
Literal[
"amber",
"blue",
"bronze",
"brown",
"crimson",
"cyan",
"gold",
"grass",
"gray",
"green",
"indigo",
"iris",
"jade",
"lime",
"mint",
"orange",
"pink",
"plum",
"purple",
"red",
"ruby",
"sky",
"teal",
"tomato",
"violet",
"yellow",
],
Var[
Literal[
"amber",
"blue",
"bronze",
"brown",
"crimson",
"cyan",
"gold",
"grass",
"gray",
"green",
"indigo",
"iris",
"jade",
"lime",
"mint",
"orange",
"pink",
"plum",
"purple",
"red",
"ruby",
"sky",
"teal",
"tomato",
"violet",
"yellow",
]
],
]
] = None,
as_child: Optional[Union[Var[bool], bool]] = None,
value: Optional[Union[Var[str], str]] = None,
disabled: Optional[Union[Var[bool], bool]] = None,
text_value: Optional[Union[Var[str], str]] = None,
items: Optional[Union[List[str], Var[List[str]]]] = None,
direction: Optional[
Union[
Literal["column", "column-reverse", "row", "row-reverse"],
Var[Literal["column", "column-reverse", "row", "row-reverse"]],
]
] = None,
spacing: Optional[
Union[
Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
Var[Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]],
]
] = None,
size: Optional[
Union[Literal["1", "2", "3"], Var[Literal["1", "2", "3"]]]
] = None,
variant: Optional[
Union[
Literal["classic", "soft", "surface"],
Var[Literal["classic", "soft", "surface"]],
]
] = None,
high_contrast: Optional[Union[Var[bool], bool]] = None,
default_value: Optional[Union[Var[str], str]] = None,
name: Optional[Union[Var[str], str]] = None,
required: Optional[Union[Var[bool], bool]] = None,
style: Optional[Style] = None,
key: Optional[Any] = None,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
on_blur: Optional[EventType[()]] = None,
on_click: Optional[EventType[()]] = None,
on_context_menu: Optional[EventType[()]] = None,
on_double_click: Optional[EventType[()]] = None,
on_focus: Optional[EventType[()]] = None,
on_mount: Optional[EventType[()]] = None,
on_mouse_down: Optional[EventType[()]] = None,
on_mouse_enter: Optional[EventType[()]] = None,
on_mouse_leave: Optional[EventType[()]] = None,
on_mouse_move: Optional[EventType[()]] = None,
on_mouse_out: Optional[EventType[()]] = None,
on_mouse_over: Optional[EventType[()]] = None,
on_mouse_up: Optional[EventType[()]] = None,
on_scroll: Optional[EventType[()]] = None,
on_select: Optional[EventType[()]] = None,
on_unmount: Optional[EventType[()]] = None,
**props,
) -> "ContextMenuRadioItem":
"""Create a radio group component.
Args:
items: The items of the radio group.
color_scheme: The color of the radio group
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False.
value: The controlled value of the radio item to check. Should be used in conjunction with on_change.
disabled: Whether the radio group is disabled
on_select: Event handler called when the user selects an item (via mouse or keyboard). Calling event.preventDefault in this handler will prevent the context menu from closing when selecting that item.
text_value: Optional text used for typeahead purposes. By default the typeahead behavior will use the .textContent of the item. Use this when the content is complex, or you have non-textual content inside.
items: The items of the radio group.
direction: The direction of the radio group.
spacing: The gap between the items of the radio group.
size: The size of the radio group.
variant: The variant of the radio group
high_contrast: Whether to render the radio group with higher contrast color against background
default_value: The initial value of checked radio item. Should be used in conjunction with on_change.
name: The name of the group. Submitted with its owning form as part of a name/value pair.
required: Whether the radio group is required
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded
custom_attrs: custom attribute
**props: Additional properties to apply to the accordion item.
Returns:
The created radio group component.
Raises:
TypeError: If the type of items is invalid.
"""
...
class ContextMenu(ComponentNamespace): class ContextMenu(ComponentNamespace):
root = staticmethod(ContextMenuRoot.create) root = staticmethod(ContextMenuRoot.create)
trigger = staticmethod(ContextMenuTrigger.create) trigger = staticmethod(ContextMenuTrigger.create)
@ -830,5 +1145,9 @@ class ContextMenu(ComponentNamespace):
item = staticmethod(ContextMenuItem.create) item = staticmethod(ContextMenuItem.create)
separator = staticmethod(ContextMenuSeparator.create) separator = staticmethod(ContextMenuSeparator.create)
checkbox = staticmethod(ContextMenuCheckbox.create) checkbox = staticmethod(ContextMenuCheckbox.create)
label = staticmethod(ContextMenuLabel.create)
group = staticmethod(ContextMenuGroup.create)
radio_group = staticmethod(ContextMenuRadioGroup.create)
radio = staticmethod(ContextMenuRadioItem.create)
context_menu = ContextMenu() context_menu = ContextMenu()

View File

@ -599,7 +599,6 @@ def no_args_event_spec() -> Tuple[()]:
stop_propagation = EventChain(events=[], args_spec=no_args_event_spec).stop_propagation stop_propagation = EventChain(events=[], args_spec=no_args_event_spec).stop_propagation
prevent_default = EventChain(events=[], args_spec=no_args_event_spec).prevent_default prevent_default = EventChain(events=[], args_spec=no_args_event_spec).prevent_default
T = TypeVar("T") T = TypeVar("T")
U = TypeVar("U") U = TypeVar("U")
@ -1300,10 +1299,10 @@ def call_event_handler(
compare_result = typehint_issubclass( compare_result = typehint_issubclass(
args_types_without_vars[i], type_hints_of_provided_callback[arg] args_types_without_vars[i], type_hints_of_provided_callback[arg]
) )
except TypeError as te: except TypeError as te: # codespell:ignore te
raise TypeError( raise TypeError(
f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_callback.fn.__qualname__} provided for {key}." f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_callback.fn.__qualname__} provided for {key}."
) from te ) from te # codespell:ignore te
if compare_result: if compare_result:
type_match_found[arg] = True type_match_found[arg] = True
@ -1887,7 +1886,6 @@ class LambdaEventCallback(Protocol[Unpack[P]]):
ARGS = TypeVarTuple("ARGS") ARGS = TypeVarTuple("ARGS")
LAMBDA_OR_STATE = TypeAliasType( LAMBDA_OR_STATE = TypeAliasType(
"LAMBDA_OR_STATE", "LAMBDA_OR_STATE",
LambdaEventCallback[Unpack[ARGS]] | EventCallback[Unpack[ARGS]], LambdaEventCallback[Unpack[ARGS]] | EventCallback[Unpack[ARGS]],
@ -1910,7 +1908,6 @@ EventType = TypeAliasType(
"EventType", ItemOrList[IndividualEventType[Unpack[ARGS]]], type_params=(ARGS,) "EventType", ItemOrList[IndividualEventType[Unpack[ARGS]]], type_params=(ARGS,)
) )
if TYPE_CHECKING: if TYPE_CHECKING:
from reflex.state import BaseState from reflex.state import BaseState

View File

@ -6,14 +6,15 @@ import asyncio
import dataclasses import dataclasses
import multiprocessing import multiprocessing
import platform import platform
import sys
import warnings import warnings
from contextlib import suppress from contextlib import suppress
from reflex.config import environment from reflex.config import environment
try: if sys.version_info >= (3, 11):
from datetime import UTC, datetime from datetime import UTC, datetime
except ImportError: else:
from datetime import datetime from datetime import datetime
UTC = None UTC = None

View File

@ -516,10 +516,12 @@ def _issubclass(cls: GenericType, cls_check: GenericType, instance: Any = None)
# Check if the types match. # Check if the types match.
try: try:
return cls_check_base == Any or issubclass(cls_base, cls_check_base) return cls_check_base == Any or issubclass(cls_base, cls_check_base)
except TypeError as te: except TypeError as te: # codespell:ignore te
# These errors typically arise from bad annotations and are hard to # These errors typically arise from bad annotations and are hard to
# debug without knowing the type that we tried to compare. # debug without knowing the type that we tried to compare.
raise TypeError(f"Invalid type for issubclass: {cls_base}") from te raise TypeError(
f"Invalid type for issubclass: {cls_base}"
) from te # codespell:ignore te
def does_obj_satisfy_typed_dict(obj: Any, cls: GenericType) -> bool: def does_obj_satisfy_typed_dict(obj: Any, cls: GenericType) -> bool:

View File

@ -239,8 +239,8 @@ class DependencyTracker:
""" """
# Get the original source code and eval it to get the Var. # Get the original source code and eval it to get the Var.
module = inspect.getmodule(self.func) module = inspect.getmodule(self.func)
positions0 = self._getting_var_instructions[0].positions positions0 = self._getting_var_instructions[0].positions # type: ignore[attr-defined]
positions1 = self._getting_var_instructions[-1].positions positions1 = self._getting_var_instructions[-1].positions # type: ignore[attr-defined]
if module is None or positions0 is None or positions1 is None: if module is None or positions0 is None or positions1 is None:
raise VarValueError( raise VarValueError(
f"Cannot determine the source code for the var in {self.func!r}." f"Cannot determine the source code for the var in {self.func!r}."