From e745671d8684eddab12f4a54be2a6706d8d83090 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 9 Oct 2024 18:43:13 -0700 Subject: [PATCH] add type hinting to events --- reflex/components/core/clipboard.py | 6 ++--- reflex/components/core/debounce.py | 6 ++--- reflex/components/datadisplay/dataeditor.py | 16 ++++++------ reflex/components/el/elements/forms.py | 13 ++++++++-- reflex/components/moment/moment.py | 4 +-- .../components/radix/primitives/accordion.py | 16 ++++++++++-- reflex/components/radix/primitives/drawer.py | 14 +++++----- reflex/components/radix/primitives/slider.py | 20 +++++++++++--- .../radix/themes/components/alert_dialog.py | 10 +++---- .../radix/themes/components/checkbox.py | 6 ++--- .../radix/themes/components/context_menu.py | 22 ++++++++-------- .../radix/themes/components/dialog.py | 14 +++++----- .../radix/themes/components/dropdown_menu.py | 26 +++++++++---------- .../radix/themes/components/hover_card.py | 4 +-- .../radix/themes/components/popover.py | 16 ++++++------ .../radix/themes/components/radio_cards.py | 4 +-- .../radix/themes/components/radio_group.py | 4 +-- .../themes/components/segmented_control.py | 16 ++++++++++-- .../radix/themes/components/select.py | 11 ++++---- .../radix/themes/components/slider.py | 20 +++++++++++--- .../radix/themes/components/switch.py | 4 +-- .../radix/themes/components/tabs.py | 4 +-- .../radix/themes/components/tooltip.py | 8 +++--- .../components/react_player/react_player.py | 10 +++---- reflex/event.py | 26 +++++++++++++++++++ reflex/utils/types.py | 17 ++++++------ tests/units/components/test_component.py | 2 +- .../test_component_future_annotations.py | 2 +- 28 files changed, 204 insertions(+), 117 deletions(-) diff --git a/reflex/components/core/clipboard.py b/reflex/components/core/clipboard.py index 1d7ce7c14..88aa2d145 100644 --- a/reflex/components/core/clipboard.py +++ b/reflex/components/core/clipboard.py @@ -2,11 +2,11 @@ from __future__ import annotations -from typing import Dict, List, Union +from typing import Dict, List, Tuple, Union from reflex.components.base.fragment import Fragment from reflex.components.tags.tag import Tag -from reflex.event import EventChain, EventHandler +from reflex.event import EventChain, EventHandler, identity_event from reflex.utils.format import format_prop, wrap from reflex.utils.imports import ImportVar from reflex.vars import get_unique_variable_name @@ -20,7 +20,7 @@ class Clipboard(Fragment): targets: Var[List[str]] # Called when the user pastes data into the document. Data is a list of tuples of (mime_type, data). Binary types will be base64 encoded as a data uri. - on_paste: EventHandler[lambda data: [data]] + on_paste: EventHandler[identity_event(List[Tuple[str, str]])] # Save the original event actions for the on_paste event. on_paste_event_actions: Var[Dict[str, Union[bool, int]]] diff --git a/reflex/components/core/debounce.py b/reflex/components/core/debounce.py index a8f20f08a..d30e80f08 100644 --- a/reflex/components/core/debounce.py +++ b/reflex/components/core/debounce.py @@ -6,7 +6,6 @@ from typing import Any, Type, Union from reflex.components.component import Component from reflex.constants import EventTriggers -from reflex.event import EventHandler from reflex.vars import VarData from reflex.vars.base import Var @@ -45,9 +44,6 @@ class DebounceInput(Component): # The element to wrap element: Var[Type[Component]] - # Fired when the input value changes - on_change: EventHandler[lambda e0: [e0.value]] - @classmethod def create(cls, *children: Component, **props: Any) -> Component: """Create a DebounceInput component. @@ -123,6 +119,8 @@ class DebounceInput(Component): ), ) + print(f"{props=}") + component = super().create(**props) component._get_style = child._get_style component.event_triggers.update(child.event_triggers) diff --git a/reflex/components/datadisplay/dataeditor.py b/reflex/components/datadisplay/dataeditor.py index ab84ad3f7..01255dd14 100644 --- a/reflex/components/datadisplay/dataeditor.py +++ b/reflex/components/datadisplay/dataeditor.py @@ -3,12 +3,12 @@ from __future__ import annotations from enum import Enum -from typing import Any, Dict, List, Literal, Optional, Union +from typing import Any, Dict, List, Literal, Optional, Tuple, Union from reflex.base import Base from reflex.components.component import Component, NoSSRComponent from reflex.components.literals import LiteralRowMarker -from reflex.event import EventHandler, empty_event +from reflex.event import EventHandler, empty_event, identity_event from reflex.utils import console, format, types from reflex.utils.imports import ImportDict, ImportVar from reflex.utils.serializers import serializer @@ -223,13 +223,13 @@ class DataEditor(NoSSRComponent): theme: Var[Union[DataEditorTheme, Dict]] # Fired when a cell is activated. - on_cell_activated: EventHandler[lambda pos: [pos]] + on_cell_activated: EventHandler[identity_event(Tuple[int, int])] # Fired when a cell is clicked. - on_cell_clicked: EventHandler[lambda pos: [pos]] + on_cell_clicked: EventHandler[identity_event(Tuple[int, int])] # Fired when a cell is right-clicked. - on_cell_context_menu: EventHandler[lambda pos: [pos]] + on_cell_context_menu: EventHandler[identity_event(Tuple[int, int])] # Fired when a cell is edited. on_cell_edited: EventHandler[on_edit_spec] @@ -244,16 +244,16 @@ class DataEditor(NoSSRComponent): on_group_header_renamed: EventHandler[lambda idx, val: [idx, val]] # Fired when a header is clicked. - on_header_clicked: EventHandler[lambda pos: [pos]] + on_header_clicked: EventHandler[identity_event(Tuple[int, int])] # Fired when a header is right-clicked. - on_header_context_menu: EventHandler[lambda pos: [pos]] + on_header_context_menu: EventHandler[identity_event(Tuple[int, int])] # Fired when a header menu item is clicked. on_header_menu_click: EventHandler[lambda col, pos: [col, pos]] # Fired when an item is hovered. - on_item_hovered: EventHandler[lambda pos: [pos]] + on_item_hovered: EventHandler[identity_event(Tuple[int, int])] # Fired when a selection is deleted. on_delete: EventHandler[lambda selection: [selection]] diff --git a/reflex/components/el/elements/forms.py b/reflex/components/el/elements/forms.py index 05168e66c..a343991d5 100644 --- a/reflex/components/el/elements/forms.py +++ b/reflex/components/el/elements/forms.py @@ -3,7 +3,7 @@ from __future__ import annotations from hashlib import md5 -from typing import Any, Dict, Iterator, Set, Union +from typing import Any, Dict, Iterator, Set, Tuple, Union from jinja2 import Environment @@ -102,6 +102,15 @@ class Fieldset(Element): name: Var[Union[str, int, bool]] +def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]: + """Event handler spec for the on_submit event. + + Returns: + The event handler spec. + """ + return (FORM_DATA,) + + class Form(BaseHTML): """Display the form element.""" @@ -141,7 +150,7 @@ class Form(BaseHTML): handle_submit_unique_name: Var[str] # Fired when the form is submitted - on_submit: EventHandler[lambda e0: [FORM_DATA]] + on_submit: EventHandler[on_submit_event_spec] @classmethod def create(cls, *children, **props): diff --git a/reflex/components/moment/moment.py b/reflex/components/moment/moment.py index da9949235..51b09d55d 100644 --- a/reflex/components/moment/moment.py +++ b/reflex/components/moment/moment.py @@ -4,7 +4,7 @@ import dataclasses from typing import List, Optional from reflex.components.component import Component, NoSSRComponent -from reflex.event import EventHandler +from reflex.event import EventHandler, identity_event from reflex.utils.imports import ImportDict from reflex.vars.base import Var @@ -93,7 +93,7 @@ class Moment(NoSSRComponent): tz: Var[str] # Fires when the date changes. - on_change: EventHandler[lambda date: [date]] + on_change: EventHandler[identity_event(str)] def add_imports(self) -> ImportDict: """Add the imports for the Moment component. diff --git a/reflex/components/radix/primitives/accordion.py b/reflex/components/radix/primitives/accordion.py index 40cbfa2a7..bbcecb1d8 100644 --- a/reflex/components/radix/primitives/accordion.py +++ b/reflex/components/radix/primitives/accordion.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, List, Literal, Optional, Union +from typing import Any, List, Literal, Optional, Tuple, Union from reflex.components.component import Component, ComponentNamespace from reflex.components.core.colors import color @@ -71,6 +71,18 @@ class AccordionComponent(RadixPrimitiveComponent): return ["color_scheme", "variant"] +def on_value_change(value: Var[str | List[str]]) -> Tuple[Var[str | List[str]]]: + """Handle the on_value_change event. + + Args: + value: The value of the event. + + Returns: + The value of the event. + """ + return (value,) + + class AccordionRoot(AccordionComponent): """An accordion component.""" @@ -114,7 +126,7 @@ class AccordionRoot(AccordionComponent): _valid_children: List[str] = ["AccordionItem"] # Fired when the opened the accordions changes. - on_value_change: EventHandler[lambda e0: [e0]] + on_value_change: EventHandler[on_value_change] def _exclude_props(self) -> list[str]: return super()._exclude_props() + [ diff --git a/reflex/components/radix/primitives/drawer.py b/reflex/components/radix/primitives/drawer.py index f0efa7e18..6fe4d10dd 100644 --- a/reflex/components/radix/primitives/drawer.py +++ b/reflex/components/radix/primitives/drawer.py @@ -10,7 +10,7 @@ from reflex.components.component import Component, ComponentNamespace from reflex.components.radix.primitives.base import RadixPrimitiveComponent from reflex.components.radix.themes.base import Theme from reflex.components.radix.themes.layout.flex import Flex -from reflex.event import EventHandler +from reflex.event import EventHandler, empty_event, identity_event from reflex.utils import console from reflex.vars.base import Var @@ -61,7 +61,7 @@ class DrawerRoot(DrawerComponent): preventScrollRestoration: Var[bool] # Fires when the drawer is opened. - on_open_change: EventHandler[lambda e0: [e0]] + on_open_change: EventHandler[identity_event(bool)] class DrawerTrigger(DrawerComponent): @@ -129,19 +129,19 @@ class DrawerContent(DrawerComponent): return {"css": base_style} # Fired when the drawer content is opened. Deprecated. - on_open_auto_focus: EventHandler[lambda e0: []] + on_open_auto_focus: EventHandler[empty_event] # Fired when the drawer content is closed. Deprecated. - on_close_auto_focus: EventHandler[lambda e0: []] + on_close_auto_focus: EventHandler[empty_event] # Fired when the escape key is pressed. Deprecated. - on_escape_key_down: EventHandler[lambda e0: []] + on_escape_key_down: EventHandler[empty_event] # Fired when the pointer is down outside the drawer content. Deprecated. - on_pointer_down_outside: EventHandler[lambda e0: []] + on_pointer_down_outside: EventHandler[empty_event] # Fired when interacting outside the drawer content. Deprecated. - on_interact_outside: EventHandler[lambda e0: []] + on_interact_outside: EventHandler[empty_event] @classmethod def create(cls, *children, **props): diff --git a/reflex/components/radix/primitives/slider.py b/reflex/components/radix/primitives/slider.py index dd3108f0e..10a0079a4 100644 --- a/reflex/components/radix/primitives/slider.py +++ b/reflex/components/radix/primitives/slider.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, List, Literal +from typing import Any, List, Literal, Tuple from reflex.components.component import Component, ComponentNamespace from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName @@ -19,6 +19,20 @@ class SliderComponent(RadixPrimitiveComponentWithClassName): library = "@radix-ui/react-slider@^1.1.2" +def on_value_event_spec( + value: Var[List[int]], +) -> Tuple[Var[List[int]]]: + """Event handler spec for the value event. + + Args: + value: The value of the event. + + Returns: + The event handler spec. + """ + return (value,) # type: ignore + + class SliderRoot(SliderComponent): """The Slider component comtaining all slider parts.""" @@ -48,10 +62,10 @@ class SliderRoot(SliderComponent): min_steps_between_thumbs: Var[int] # Fired when the value of a thumb changes. - on_value_change: EventHandler[lambda e0: [e0]] + on_value_change: EventHandler[on_value_event_spec] # Fired when a thumb is released. - on_value_commit: EventHandler[lambda e0: [e0]] + on_value_commit: EventHandler[on_value_event_spec] def add_style(self) -> dict[str, Any] | None: """Add style to the component. diff --git a/reflex/components/radix/themes/components/alert_dialog.py b/reflex/components/radix/themes/components/alert_dialog.py index ca876b4c3..f3c8ec319 100644 --- a/reflex/components/radix/themes/components/alert_dialog.py +++ b/reflex/components/radix/themes/components/alert_dialog.py @@ -5,7 +5,7 @@ from typing import Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements -from reflex.event import EventHandler +from reflex.event import EventHandler, empty_event, identity_event from reflex.vars.base import Var from ..base import RadixThemesComponent, RadixThemesTriggerComponent @@ -22,7 +22,7 @@ class AlertDialogRoot(RadixThemesComponent): open: Var[bool] # Fired when the open state changes. - on_open_change: EventHandler[lambda e0: [e0]] + on_open_change: EventHandler[identity_event(bool)] class AlertDialogTrigger(RadixThemesTriggerComponent): @@ -43,13 +43,13 @@ class AlertDialogContent(elements.Div, RadixThemesComponent): force_mount: Var[bool] # Fired when the dialog is opened. - on_open_auto_focus: EventHandler[lambda e0: [e0]] + on_open_auto_focus: EventHandler[empty_event] # Fired when the dialog is closed. - on_close_auto_focus: EventHandler[lambda e0: [e0]] + on_close_auto_focus: EventHandler[empty_event] # Fired when the escape key is pressed. - on_escape_key_down: EventHandler[lambda e0: [e0]] + on_escape_key_down: EventHandler[empty_event] class AlertDialogTitle(RadixThemesComponent): diff --git a/reflex/components/radix/themes/components/checkbox.py b/reflex/components/radix/themes/components/checkbox.py index 6ba1b04da..2944b1f11 100644 --- a/reflex/components/radix/themes/components/checkbox.py +++ b/reflex/components/radix/themes/components/checkbox.py @@ -6,7 +6,7 @@ from reflex.components.component import Component, ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.layout.flex import Flex from reflex.components.radix.themes.typography.text import Text -from reflex.event import EventHandler +from reflex.event import EventHandler, identity_event from reflex.vars.base import LiteralVar, Var from ..base import ( @@ -61,7 +61,7 @@ class Checkbox(RadixThemesComponent): _rename_props = {"onChange": "onCheckedChange"} # Fired when the checkbox is checked or unchecked. - on_change: EventHandler[lambda e0: [e0]] + on_change: EventHandler[identity_event(bool)] class HighLevelCheckbox(RadixThemesComponent): @@ -112,7 +112,7 @@ class HighLevelCheckbox(RadixThemesComponent): _rename_props = {"onChange": "onCheckedChange"} # Fired when the checkbox is checked or unchecked. - on_change: EventHandler[lambda e0: [e0]] + on_change: EventHandler[identity_event(bool)] @classmethod def create(cls, text: Var[str] = LiteralVar.create(""), **props) -> Component: diff --git a/reflex/components/radix/themes/components/context_menu.py b/reflex/components/radix/themes/components/context_menu.py index c82f8e714..3eb54a457 100644 --- a/reflex/components/radix/themes/components/context_menu.py +++ b/reflex/components/radix/themes/components/context_menu.py @@ -4,7 +4,7 @@ from typing import List, Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive -from reflex.event import EventHandler +from reflex.event import EventHandler, empty_event, identity_event from reflex.vars.base import Var from ..base import ( @@ -24,7 +24,7 @@ class ContextMenuRoot(RadixThemesComponent): _invalid_children: List[str] = ["ContextMenuItem"] # Fired when the open state changes. - on_open_change: EventHandler[lambda e0: [e0]] + on_open_change: EventHandler[identity_event(bool)] class ContextMenuTrigger(RadixThemesComponent): @@ -64,19 +64,19 @@ class ContextMenuContent(RadixThemesComponent): avoid_collisions: Var[bool] # Fired when the context menu is closed. - on_close_auto_focus: EventHandler[lambda e0: [e0]] + on_close_auto_focus: EventHandler[empty_event] # Fired when the escape key is pressed. - on_escape_key_down: EventHandler[lambda e0: [e0]] + on_escape_key_down: EventHandler[empty_event] # Fired when a pointer down event happens outside the context menu. - on_pointer_down_outside: EventHandler[lambda e0: [e0]] + on_pointer_down_outside: EventHandler[empty_event] # Fired when focus moves outside the context menu. - on_focus_outside: EventHandler[lambda e0: [e0]] + on_focus_outside: EventHandler[empty_event] # Fired when interacting outside the context menu. - on_interact_outside: EventHandler[lambda e0: [e0]] + on_interact_outside: EventHandler[empty_event] class ContextMenuSub(RadixThemesComponent): @@ -107,16 +107,16 @@ class ContextMenuSubContent(RadixThemesComponent): _valid_parents: List[str] = ["ContextMenuSub"] # Fired when the escape key is pressed. - on_escape_key_down: EventHandler[lambda e0: [e0]] + on_escape_key_down: EventHandler[empty_event] # Fired when a pointer down event happens outside the context menu. - on_pointer_down_outside: EventHandler[lambda e0: [e0]] + on_pointer_down_outside: EventHandler[empty_event] # Fired when focus moves outside the context menu. - on_focus_outside: EventHandler[lambda e0: [e0]] + on_focus_outside: EventHandler[empty_event] # Fired when interacting outside the context menu. - on_interact_outside: EventHandler[lambda e0: [e0]] + on_interact_outside: EventHandler[empty_event] class ContextMenuItem(RadixThemesComponent): diff --git a/reflex/components/radix/themes/components/dialog.py b/reflex/components/radix/themes/components/dialog.py index 951e8506d..e8da506ed 100644 --- a/reflex/components/radix/themes/components/dialog.py +++ b/reflex/components/radix/themes/components/dialog.py @@ -5,7 +5,7 @@ from typing import Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements -from reflex.event import EventHandler +from reflex.event import EventHandler, empty_event, identity_event from reflex.vars.base import Var from ..base import ( @@ -23,7 +23,7 @@ class DialogRoot(RadixThemesComponent): open: Var[bool] # Fired when the open state changes. - on_open_change: EventHandler[lambda e0: [e0]] + on_open_change: EventHandler[identity_event(bool)] class DialogTrigger(RadixThemesTriggerComponent): @@ -47,19 +47,19 @@ class DialogContent(elements.Div, RadixThemesComponent): size: Var[Responsive[Literal["1", "2", "3", "4"]]] # Fired when the dialog is opened. - on_open_auto_focus: EventHandler[lambda e0: [e0]] + on_open_auto_focus: EventHandler[empty_event] # Fired when the dialog is closed. - on_close_auto_focus: EventHandler[lambda e0: [e0]] + on_close_auto_focus: EventHandler[empty_event] # Fired when the escape key is pressed. - on_escape_key_down: EventHandler[lambda e0: [e0]] + on_escape_key_down: EventHandler[empty_event] # Fired when the pointer is down outside the dialog. - on_pointer_down_outside: EventHandler[lambda e0: [e0]] + on_pointer_down_outside: EventHandler[empty_event] # Fired when the pointer interacts outside the dialog. - on_interact_outside: EventHandler[lambda e0: [e0]] + on_interact_outside: EventHandler[empty_event] class DialogDescription(RadixThemesComponent): diff --git a/reflex/components/radix/themes/components/dropdown_menu.py b/reflex/components/radix/themes/components/dropdown_menu.py index c853619dd..885e8df35 100644 --- a/reflex/components/radix/themes/components/dropdown_menu.py +++ b/reflex/components/radix/themes/components/dropdown_menu.py @@ -4,7 +4,7 @@ from typing import Dict, List, Literal, Union from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive -from reflex.event import EventHandler +from reflex.event import EventHandler, empty_event, identity_event from reflex.vars.base import Var from ..base import ( @@ -50,7 +50,7 @@ class DropdownMenuRoot(RadixThemesComponent): _invalid_children: List[str] = ["DropdownMenuItem"] # Fired when the open state changes. - on_open_change: EventHandler[lambda e0: [e0]] + on_open_change: EventHandler[identity_event(bool)] class DropdownMenuTrigger(RadixThemesTriggerComponent): @@ -120,19 +120,19 @@ class DropdownMenuContent(RadixThemesComponent): hide_when_detached: Var[bool] # Fired when the dialog is closed. - on_close_auto_focus: EventHandler[lambda e0: [e0]] + on_close_auto_focus: EventHandler[empty_event] # Fired when the escape key is pressed. - on_escape_key_down: EventHandler[lambda e0: [e0]] + on_escape_key_down: EventHandler[empty_event] # Fired when the pointer is down outside the dialog. - on_pointer_down_outside: EventHandler[lambda e0: [e0]] + on_pointer_down_outside: EventHandler[empty_event] # Fired when focus moves outside the dialog. - on_focus_outside: EventHandler[lambda e0: [e0]] + on_focus_outside: EventHandler[empty_event] # Fired when the pointer interacts outside the dialog. - on_interact_outside: EventHandler[lambda e0: [e0]] + on_interact_outside: EventHandler[empty_event] class DropdownMenuSubTrigger(RadixThemesTriggerComponent): @@ -164,7 +164,7 @@ class DropdownMenuSub(RadixThemesComponent): default_open: Var[bool] # Fired when the open state changes. - on_open_change: EventHandler[lambda e0: [e0]] + on_open_change: EventHandler[identity_event(bool)] class DropdownMenuSubContent(RadixThemesComponent): @@ -205,16 +205,16 @@ class DropdownMenuSubContent(RadixThemesComponent): _valid_parents: List[str] = ["DropdownMenuSub"] # Fired when the escape key is pressed. - on_escape_key_down: EventHandler[lambda e0: [e0]] + on_escape_key_down: EventHandler[empty_event] # Fired when the pointer is down outside the dialog. - on_pointer_down_outside: EventHandler[lambda e0: [e0]] + on_pointer_down_outside: EventHandler[empty_event] # Fired when focus moves outside the dialog. - on_focus_outside: EventHandler[lambda e0: [e0]] + on_focus_outside: EventHandler[empty_event] # Fired when the pointer interacts outside the dialog. - on_interact_outside: EventHandler[lambda e0: [e0]] + on_interact_outside: EventHandler[empty_event] class DropdownMenuItem(RadixThemesComponent): @@ -240,7 +240,7 @@ class DropdownMenuItem(RadixThemesComponent): _valid_parents: List[str] = ["DropdownMenuContent", "DropdownMenuSubContent"] # Fired when the item is selected. - on_select: EventHandler[lambda e0: []] + on_select: EventHandler[empty_event] class DropdownMenuSeparator(RadixThemesComponent): diff --git a/reflex/components/radix/themes/components/hover_card.py b/reflex/components/radix/themes/components/hover_card.py index d67a0396a..e76184795 100644 --- a/reflex/components/radix/themes/components/hover_card.py +++ b/reflex/components/radix/themes/components/hover_card.py @@ -5,7 +5,7 @@ from typing import Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements -from reflex.event import EventHandler +from reflex.event import EventHandler, identity_event from reflex.vars.base import Var from ..base import ( @@ -32,7 +32,7 @@ class HoverCardRoot(RadixThemesComponent): close_delay: Var[int] # Fired when the open state changes. - on_open_change: EventHandler[lambda e0: [e0]] + on_open_change: EventHandler[identity_event(bool)] class HoverCardTrigger(RadixThemesTriggerComponent): diff --git a/reflex/components/radix/themes/components/popover.py b/reflex/components/radix/themes/components/popover.py index 0297b2d99..2535a8a22 100644 --- a/reflex/components/radix/themes/components/popover.py +++ b/reflex/components/radix/themes/components/popover.py @@ -5,7 +5,7 @@ from typing import Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements -from reflex.event import EventHandler +from reflex.event import EventHandler, empty_event, identity_event from reflex.vars.base import Var from ..base import ( @@ -26,7 +26,7 @@ class PopoverRoot(RadixThemesComponent): modal: Var[bool] # Fired when the open state changes. - on_open_change: EventHandler[lambda e0: [e0]] + on_open_change: EventHandler[identity_event(bool)] class PopoverTrigger(RadixThemesTriggerComponent): @@ -59,22 +59,22 @@ class PopoverContent(elements.Div, RadixThemesComponent): avoid_collisions: Var[bool] # Fired when the dialog is opened. - on_open_auto_focus: EventHandler[lambda e0: [e0]] + on_open_auto_focus: EventHandler[empty_event] # Fired when the dialog is closed. - on_close_auto_focus: EventHandler[lambda e0: [e0]] + on_close_auto_focus: EventHandler[empty_event] # Fired when the escape key is pressed. - on_escape_key_down: EventHandler[lambda e0: [e0]] + on_escape_key_down: EventHandler[empty_event] # Fired when the pointer is down outside the dialog. - on_pointer_down_outside: EventHandler[lambda e0: [e0]] + on_pointer_down_outside: EventHandler[empty_event] # Fired when focus moves outside the dialog. - on_focus_outside: EventHandler[lambda e0: [e0]] + on_focus_outside: EventHandler[empty_event] # Fired when the pointer interacts outside the dialog. - on_interact_outside: EventHandler[lambda e0: [e0]] + on_interact_outside: EventHandler[empty_event] class PopoverClose(RadixThemesTriggerComponent): diff --git a/reflex/components/radix/themes/components/radio_cards.py b/reflex/components/radix/themes/components/radio_cards.py index 4c1a92aef..e0aa2a749 100644 --- a/reflex/components/radix/themes/components/radio_cards.py +++ b/reflex/components/radix/themes/components/radio_cards.py @@ -4,7 +4,7 @@ from types import SimpleNamespace from typing import Literal, Union from reflex.components.core.breakpoints import Responsive -from reflex.event import EventHandler +from reflex.event import EventHandler, identity_event from reflex.vars.base import Var from ..base import LiteralAccentColor, RadixThemesComponent @@ -65,7 +65,7 @@ class RadioCardsRoot(RadixThemesComponent): loop: Var[bool] # Event handler called when the value changes. - on_value_change: EventHandler[lambda e0: [e0]] + on_value_change: EventHandler[identity_event(str)] class RadioCardsItem(RadixThemesComponent): diff --git a/reflex/components/radix/themes/components/radio_group.py b/reflex/components/radix/themes/components/radio_group.py index dcc74e040..95d33033b 100644 --- a/reflex/components/radix/themes/components/radio_group.py +++ b/reflex/components/radix/themes/components/radio_group.py @@ -9,7 +9,7 @@ from reflex.components.component import Component, ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.layout.flex import Flex from reflex.components.radix.themes.typography.text import Text -from reflex.event import EventHandler +from reflex.event import EventHandler, identity_event from reflex.utils import types from reflex.vars.base import LiteralVar, Var from reflex.vars.sequence import StringVar @@ -59,7 +59,7 @@ class RadioGroupRoot(RadixThemesComponent): _rename_props = {"onChange": "onValueChange"} # Fired when the value of the radio group changes. - on_change: EventHandler[lambda e0: [e0]] + on_change: EventHandler[identity_event(str)] class RadioGroupItem(RadixThemesComponent): diff --git a/reflex/components/radix/themes/components/segmented_control.py b/reflex/components/radix/themes/components/segmented_control.py index 21b50f03e..22725eaa6 100644 --- a/reflex/components/radix/themes/components/segmented_control.py +++ b/reflex/components/radix/themes/components/segmented_control.py @@ -3,7 +3,7 @@ from __future__ import annotations from types import SimpleNamespace -from typing import List, Literal, Union +from typing import List, Literal, Tuple, Union from reflex.components.core.breakpoints import Responsive from reflex.event import EventHandler @@ -12,6 +12,18 @@ from reflex.vars.base import Var from ..base import LiteralAccentColor, RadixThemesComponent +def on_value_change(value: Var[str | List[str]]) -> Tuple[Var[str | List[str]]]: + """Handle the on_value_change event. + + Args: + value: The value of the event. + + Returns: + The value of the event. + """ + return (value,) + + class SegmentedControlRoot(RadixThemesComponent): """Root element for a SegmentedControl component.""" @@ -39,7 +51,7 @@ class SegmentedControlRoot(RadixThemesComponent): value: Var[Union[str, List[str]]] # Handles the `onChange` event for the SegmentedControl component. - on_change: EventHandler[lambda e0: [e0]] + on_change: EventHandler[on_value_change] _rename_props = {"onChange": "onValueChange"} diff --git a/reflex/components/radix/themes/components/select.py b/reflex/components/radix/themes/components/select.py index 9017ab5c7..47a1eaf3f 100644 --- a/reflex/components/radix/themes/components/select.py +++ b/reflex/components/radix/themes/components/select.py @@ -5,6 +5,7 @@ from typing import List, Literal, Union import reflex as rx from reflex.components.component import Component, ComponentNamespace from reflex.components.core.breakpoints import Responsive +from reflex.event import empty_event, identity_event from reflex.vars.base import Var from ..base import ( @@ -47,10 +48,10 @@ class SelectRoot(RadixThemesComponent): _rename_props = {"onChange": "onValueChange"} # Fired when the value of the select changes. - on_change: rx.EventHandler[lambda e0: [e0]] + on_change: rx.EventHandler[identity_event(str)] # Fired when the select is opened or closed. - on_open_change: rx.EventHandler[lambda e0: [e0]] + on_open_change: rx.EventHandler[identity_event(bool)] class SelectTrigger(RadixThemesComponent): @@ -103,13 +104,13 @@ class SelectContent(RadixThemesComponent): align_offset: Var[int] # Fired when the select content is closed. - on_close_auto_focus: rx.EventHandler[lambda e0: [e0]] + on_close_auto_focus: rx.EventHandler[empty_event] # Fired when the escape key is pressed. - on_escape_key_down: rx.EventHandler[lambda e0: [e0]] + on_escape_key_down: rx.EventHandler[empty_event] # Fired when a pointer down event happens outside the select content. - on_pointer_down_outside: rx.EventHandler[lambda e0: [e0]] + on_pointer_down_outside: rx.EventHandler[empty_event] class SelectGroup(RadixThemesComponent): diff --git a/reflex/components/radix/themes/components/slider.py b/reflex/components/radix/themes/components/slider.py index 3cf2e172d..559eada31 100644 --- a/reflex/components/radix/themes/components/slider.py +++ b/reflex/components/radix/themes/components/slider.py @@ -1,6 +1,6 @@ """Interactive components provided by @radix-ui/themes.""" -from typing import List, Literal, Optional, Union +from typing import List, Literal, Optional, Tuple, Union from reflex.components.component import Component from reflex.components.core.breakpoints import Responsive @@ -13,6 +13,20 @@ from ..base import ( ) +def on_value_event_spec( + value: Var[List[int | float]], +) -> Tuple[Var[List[int | float]]]: + """Event handler spec for the value event. + + Args: + value: The value of the event. + + Returns: + The event handler spec. + """ + return (value,) # type: ignore + + class Slider(RadixThemesComponent): """Provides user selection from a range of values.""" @@ -64,10 +78,10 @@ class Slider(RadixThemesComponent): _rename_props = {"onChange": "onValueChange"} # Fired when the value of the slider changes. - on_change: EventHandler[lambda e0: [e0]] + on_change: EventHandler[on_value_event_spec] # Fired when a thumb is released after being dragged. - on_value_commit: EventHandler[lambda e0: [e0]] + on_value_commit: EventHandler[on_value_event_spec] @classmethod def create( diff --git a/reflex/components/radix/themes/components/switch.py b/reflex/components/radix/themes/components/switch.py index 56951bc74..13be32d83 100644 --- a/reflex/components/radix/themes/components/switch.py +++ b/reflex/components/radix/themes/components/switch.py @@ -3,7 +3,7 @@ from typing import Literal from reflex.components.core.breakpoints import Responsive -from reflex.event import EventHandler +from reflex.event import EventHandler, identity_event from reflex.vars.base import Var from ..base import ( @@ -59,7 +59,7 @@ class Switch(RadixThemesComponent): _rename_props = {"onChange": "onCheckedChange"} # Fired when the value of the switch changes - on_change: EventHandler[lambda checked: [checked]] + on_change: EventHandler[identity_event(bool)] switch = Switch.create diff --git a/reflex/components/radix/themes/components/tabs.py b/reflex/components/radix/themes/components/tabs.py index 5560d44b0..12359b528 100644 --- a/reflex/components/radix/themes/components/tabs.py +++ b/reflex/components/radix/themes/components/tabs.py @@ -7,7 +7,7 @@ from typing import Any, Dict, List, Literal from reflex.components.component import Component, ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.core.colors import color -from reflex.event import EventHandler +from reflex.event import EventHandler, identity_event from reflex.vars.base import Var from ..base import ( @@ -42,7 +42,7 @@ class TabsRoot(RadixThemesComponent): _rename_props = {"onChange": "onValueChange"} # Fired when the value of the tabs changes. - on_change: EventHandler[lambda e0: [e0]] + on_change: EventHandler[identity_event(str)] def add_style(self) -> Dict[str, Any] | None: """Add style for the component. diff --git a/reflex/components/radix/themes/components/tooltip.py b/reflex/components/radix/themes/components/tooltip.py index 7fd181465..ac35c86d1 100644 --- a/reflex/components/radix/themes/components/tooltip.py +++ b/reflex/components/radix/themes/components/tooltip.py @@ -3,7 +3,7 @@ from typing import Dict, Literal, Union from reflex.components.component import Component -from reflex.event import EventHandler +from reflex.event import EventHandler, empty_event, identity_event from reflex.utils import format from reflex.vars.base import Var @@ -85,13 +85,13 @@ class Tooltip(RadixThemesComponent): aria_label: Var[str] # Fired when the open state changes. - on_open_change: EventHandler[lambda e0: [e0]] + on_open_change: EventHandler[identity_event(bool)] # Fired when the escape key is pressed. - on_escape_key_down: EventHandler[lambda e0: []] + on_escape_key_down: EventHandler[empty_event] # Fired when the pointer is down outside the tooltip. - on_pointer_down_outside: EventHandler[lambda e0: []] + on_pointer_down_outside: EventHandler[empty_event] @classmethod def create(cls, *children, **props) -> Component: diff --git a/reflex/components/react_player/react_player.py b/reflex/components/react_player/react_player.py index 9da878b64..7ad45b093 100644 --- a/reflex/components/react_player/react_player.py +++ b/reflex/components/react_player/react_player.py @@ -3,7 +3,7 @@ from __future__ import annotations from reflex.components.component import NoSSRComponent -from reflex.event import EventHandler, empty_event +from reflex.event import EventHandler, empty_event, identity_event from reflex.vars.base import Var @@ -58,7 +58,7 @@ class ReactPlayer(NoSSRComponent): on_progress: EventHandler[lambda progress: [progress]] # Callback containing duration of the media, in seconds. - on_duration: EventHandler[lambda seconds: [seconds]] + on_duration: EventHandler[identity_event(float)] # Called when media is paused. on_pause: EventHandler[empty_event] @@ -70,13 +70,13 @@ class ReactPlayer(NoSSRComponent): on_buffer_end: EventHandler[empty_event] # Called when media seeks with seconds parameter. - on_seek: EventHandler[lambda seconds: [seconds]] + on_seek: EventHandler[identity_event(float)] # Called when playback rate of the player changed. Only supported by YouTube, Vimeo (if enabled), Wistia, and file paths. - on_playback_rate_change: EventHandler[lambda e0: []] + on_playback_rate_change: EventHandler[empty_event] # Called when playback quality of the player changed. Only supported by YouTube (if enabled). - on_playback_quality_change: EventHandler[lambda e0: []] + on_playback_quality_change: EventHandler[empty_event] # Called when media finishes playing. Does not fire when loop is set to true. on_ended: EventHandler[empty_event] diff --git a/reflex/event.py b/reflex/event.py index d2bebaa3f..c26adfba3 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -17,6 +17,7 @@ from typing import ( Optional, Tuple, Type, + TypeVar, Union, get_type_hints, ) @@ -458,6 +459,31 @@ def empty_event() -> Tuple[()]: return tuple() # type: ignore +T = TypeVar("T") + + +def identity_event(event_type: Type[T]) -> Callable[[Var[T]], Tuple[Var[T]]]: + """A helper function that returns the input event as output.""" + + def inner(ev: Var[T]) -> Tuple[Var[T]]: + return (ev,) + + inner.__signature__ = inspect.signature(inner).replace( # type: ignore + parameters=[ + inspect.Parameter( + "ev", + kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, + annotation=Var[event_type], + ) + ], + return_annotation=Tuple[Var[event_type]], + ) + inner.__annotations__["ev"] = Var[event_type] + inner.__annotations__["return"] = Tuple[Var[event_type]] + + return inner + + @dataclasses.dataclass( init=True, frozen=True, diff --git a/reflex/utils/types.py b/reflex/utils/types.py index 6bedf5b61..61c32d13e 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -18,6 +18,7 @@ from typing import ( List, Literal, Optional, + Sequence, Tuple, Type, Union, @@ -102,14 +103,14 @@ if TYPE_CHECKING: # ArgsSpec = Callable[[Var], list[Var]] ArgsSpec = ( - Callable[[], List[Var]] - | Callable[[Var], List[Var]] - | Callable[[Var, Var], List[Var]] - | Callable[[Var, Var, Var], List[Var]] - | Callable[[Var, Var, Var, Var], List[Var]] - | Callable[[Var, Var, Var, Var, Var], List[Var]] - | Callable[[Var, Var, Var, Var, Var, Var], List[Var]] - | Callable[[Var, Var, Var, Var, Var, Var, Var], List[Var]] + Callable[[], Sequence[Var]] + | Callable[[Var], Sequence[Var]] + | Callable[[Var, Var], Sequence[Var]] + | Callable[[Var, Var, Var], Sequence[Var]] + | Callable[[Var, Var, Var, Var], Sequence[Var]] + | Callable[[Var, Var, Var, Var, Var], Sequence[Var]] + | Callable[[Var, Var, Var, Var, Var, Var], Sequence[Var]] + | Callable[[Var, Var, Var, Var, Var, Var, Var], Sequence[Var]] ) else: ArgsSpec = Callable[..., List[Any]] diff --git a/tests/units/components/test_component.py b/tests/units/components/test_component.py index b54ce1bbe..049a119c0 100644 --- a/tests/units/components/test_component.py +++ b/tests/units/components/test_component.py @@ -1794,7 +1794,7 @@ def test_custom_component_declare_event_handlers_in_fields(): class TestComponent(Component): on_a: EventHandler[lambda e0: [e0]] on_b: EventHandler[input_event] - on_c: EventHandler[lambda e0: []] + on_c: EventHandler[empty_event] on_d: EventHandler[empty_event] on_e: EventHandler on_f: EventHandler[lambda a, b, c: [c, b, a]] diff --git a/tests/units/components/test_component_future_annotations.py b/tests/units/components/test_component_future_annotations.py index 791fef7c9..44ec52c16 100644 --- a/tests/units/components/test_component_future_annotations.py +++ b/tests/units/components/test_component_future_annotations.py @@ -26,7 +26,7 @@ def test_custom_component_declare_event_handlers_in_fields(): class TestComponent(Component): on_a: EventHandler[lambda e0: [e0]] on_b: EventHandler[input_event] - on_c: EventHandler[lambda e0: []] + on_c: EventHandler[empty_event] on_d: EventHandler[empty_event] custom_component = ReferenceComponent.create()