Browse Source

Add type hinting to dataeditor events (#4210)

Khaleel Al-Adhami 6 months ago
parent
commit
c103ab5e28
29 changed files with 342 additions and 71 deletions
  1. 3 1
      reflex/components/base/error_boundary.py
  2. 1 1
      reflex/components/base/error_boundary.pyi
  3. 1 1
      reflex/components/core/clipboard.pyi
  4. 82 6
      reflex/components/datadisplay/dataeditor.py
  5. 66 12
      reflex/components/datadisplay/dataeditor.pyi
  6. 1 1
      reflex/components/moment/moment.pyi
  7. 2 2
      reflex/components/radix/primitives/drawer.pyi
  8. 1 1
      reflex/components/radix/themes/color_mode.pyi
  9. 1 1
      reflex/components/radix/themes/components/alert_dialog.pyi
  10. 3 3
      reflex/components/radix/themes/components/checkbox.pyi
  11. 1 1
      reflex/components/radix/themes/components/context_menu.pyi
  12. 2 2
      reflex/components/radix/themes/components/dialog.pyi
  13. 2 2
      reflex/components/radix/themes/components/dropdown_menu.pyi
  14. 2 2
      reflex/components/radix/themes/components/hover_card.pyi
  15. 1 1
      reflex/components/radix/themes/components/popover.pyi
  16. 1 1
      reflex/components/radix/themes/components/radio_cards.pyi
  17. 1 1
      reflex/components/radix/themes/components/radio_group.pyi
  18. 6 6
      reflex/components/radix/themes/components/select.pyi
  19. 1 1
      reflex/components/radix/themes/components/switch.pyi
  20. 2 2
      reflex/components/radix/themes/components/tabs.pyi
  21. 1 1
      reflex/components/radix/themes/components/tooltip.pyi
  22. 2 2
      reflex/components/react_player/audio.pyi
  23. 2 2
      reflex/components/react_player/react_player.pyi
  24. 2 2
      reflex/components/react_player/video.pyi
  25. 4 4
      reflex/components/suneditor/editor.pyi
  26. 73 9
      reflex/event.py
  27. 1 1
      reflex/experimental/layout.pyi
  28. 76 2
      reflex/utils/pyi_generator.py
  29. 1 0
      tests/integration/test_lifespan.py

+ 3 - 1
reflex/components/base/error_boundary.py

@@ -12,7 +12,9 @@ from reflex.state import FrontendEventExceptionState
 from reflex.vars.base import Var
 from reflex.vars.base import Var
 
 
 
 
-def on_error_spec(error: Var, info: Var[Dict[str, str]]) -> Tuple[Var[str], Var[str]]:
+def on_error_spec(
+    error: Var[Dict[str, str]], info: Var[Dict[str, str]]
+) -> Tuple[Var[str], Var[str]]:
     """The spec for the on_error event handler.
     """The spec for the on_error event handler.
 
 
     Args:
     Args:

+ 1 - 1
reflex/components/base/error_boundary.pyi

@@ -11,7 +11,7 @@ from reflex.style import Style
 from reflex.vars.base import Var
 from reflex.vars.base import Var
 
 
 def on_error_spec(
 def on_error_spec(
-    error: Var, info: Var[Dict[str, str]]
+    error: Var[Dict[str, str]], info: Var[Dict[str, str]]
 ) -> Tuple[Var[str], Var[str]]: ...
 ) -> Tuple[Var[str], Var[str]]: ...
 
 
 class ErrorBoundary(Component):
 class ErrorBoundary(Component):

+ 1 - 1
reflex/components/core/clipboard.pyi

@@ -40,7 +40,7 @@ class Clipboard(Fragment):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_paste: Optional[EventType] = None,
+        on_paste: Optional[EventType[list[tuple[str, str]]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 82 - 6
reflex/components/datadisplay/dataeditor.py

@@ -5,6 +5,8 @@ from __future__ import annotations
 from enum import Enum
 from enum import Enum
 from typing import Any, Dict, List, Literal, Optional, Tuple, Union
 from typing import Any, Dict, List, Literal, Optional, Tuple, Union
 
 
+from typing_extensions import TypedDict
+
 from reflex.base import Base
 from reflex.base import Base
 from reflex.components.component import Component, NoSSRComponent
 from reflex.components.component import Component, NoSSRComponent
 from reflex.components.literals import LiteralRowMarker
 from reflex.components.literals import LiteralRowMarker
@@ -120,6 +122,78 @@ def on_edit_spec(pos, data: dict[str, Any]):
     return [pos, data]
     return [pos, data]
 
 
 
 
+class Bounds(TypedDict):
+    """The bounds of the group header."""
+
+    x: int
+    y: int
+    width: int
+    height: int
+
+
+class CompatSelection(TypedDict):
+    """The selection."""
+
+    items: list
+
+
+class Rectangle(TypedDict):
+    """The bounds of the group header."""
+
+    x: int
+    y: int
+    width: int
+    height: int
+
+
+class GridSelectionCurrent(TypedDict):
+    """The current selection."""
+
+    cell: list[int]
+    range: Rectangle
+    rangeStack: list[Rectangle]
+
+
+class GridSelection(TypedDict):
+    """The grid selection."""
+
+    current: Optional[GridSelectionCurrent]
+    columns: CompatSelection
+    rows: CompatSelection
+
+
+class GroupHeaderClickedEventArgs(TypedDict):
+    """The arguments for the group header clicked event."""
+
+    kind: str
+    group: str
+    location: list[int]
+    bounds: Bounds
+    isEdge: bool
+    shiftKey: bool
+    ctrlKey: bool
+    metaKey: bool
+    isTouch: bool
+    localEventX: int
+    localEventY: int
+    button: int
+    buttons: int
+    scrollEdge: list[int]
+
+
+class GridCell(TypedDict):
+    """The grid cell."""
+
+    span: Optional[List[int]]
+
+
+class GridColumn(TypedDict):
+    """The grid column."""
+
+    title: str
+    group: Optional[str]
+
+
 class DataEditor(NoSSRComponent):
 class DataEditor(NoSSRComponent):
     """The DataEditor Component."""
     """The DataEditor Component."""
 
 
@@ -238,10 +312,12 @@ class DataEditor(NoSSRComponent):
     on_group_header_clicked: EventHandler[on_edit_spec]
     on_group_header_clicked: EventHandler[on_edit_spec]
 
 
     # Fired when a group header is right-clicked.
     # Fired when a group header is right-clicked.
-    on_group_header_context_menu: EventHandler[lambda grp_idx, data: [grp_idx, data]]
+    on_group_header_context_menu: EventHandler[
+        identity_event(int, GroupHeaderClickedEventArgs)
+    ]
 
 
     # Fired when a group header is renamed.
     # Fired when a group header is renamed.
-    on_group_header_renamed: EventHandler[lambda idx, val: [idx, val]]
+    on_group_header_renamed: EventHandler[identity_event(str, str)]
 
 
     # Fired when a header is clicked.
     # Fired when a header is clicked.
     on_header_clicked: EventHandler[identity_event(Tuple[int, int])]
     on_header_clicked: EventHandler[identity_event(Tuple[int, int])]
@@ -250,16 +326,16 @@ class DataEditor(NoSSRComponent):
     on_header_context_menu: EventHandler[identity_event(Tuple[int, int])]
     on_header_context_menu: EventHandler[identity_event(Tuple[int, int])]
 
 
     # Fired when a header menu item is clicked.
     # Fired when a header menu item is clicked.
-    on_header_menu_click: EventHandler[lambda col, pos: [col, pos]]
+    on_header_menu_click: EventHandler[identity_event(int, Rectangle)]
 
 
     # Fired when an item is hovered.
     # Fired when an item is hovered.
     on_item_hovered: EventHandler[identity_event(Tuple[int, int])]
     on_item_hovered: EventHandler[identity_event(Tuple[int, int])]
 
 
     # Fired when a selection is deleted.
     # Fired when a selection is deleted.
-    on_delete: EventHandler[lambda selection: [selection]]
+    on_delete: EventHandler[identity_event(GridSelection)]
 
 
     # Fired when editing is finished.
     # Fired when editing is finished.
-    on_finished_editing: EventHandler[lambda new_value, movement: [new_value, movement]]
+    on_finished_editing: EventHandler[identity_event(Union[GridCell, None], list[int])]
 
 
     # Fired when a row is appended.
     # Fired when a row is appended.
     on_row_appended: EventHandler[empty_event]
     on_row_appended: EventHandler[empty_event]
@@ -268,7 +344,7 @@ class DataEditor(NoSSRComponent):
     on_selection_cleared: EventHandler[empty_event]
     on_selection_cleared: EventHandler[empty_event]
 
 
     # Fired when a column is resized.
     # Fired when a column is resized.
-    on_column_resize: EventHandler[lambda col, width: [col, width]]
+    on_column_resize: EventHandler[identity_event(GridColumn, int)]
 
 
     def add_imports(self) -> ImportDict:
     def add_imports(self) -> ImportDict:
         """Add imports for the component.
         """Add imports for the component.

+ 66 - 12
reflex/components/datadisplay/dataeditor.pyi

@@ -6,6 +6,8 @@
 from enum import Enum
 from enum import Enum
 from typing import Any, Dict, List, Literal, Optional, Union, overload
 from typing import Any, Dict, List, Literal, Optional, Union, overload
 
 
+from typing_extensions import TypedDict
+
 from reflex.base import Base
 from reflex.base import Base
 from reflex.components.component import NoSSRComponent
 from reflex.components.component import NoSSRComponent
 from reflex.event import EventType
 from reflex.event import EventType
@@ -78,6 +80,54 @@ class DataEditorTheme(Base):
 
 
 def on_edit_spec(pos, data: dict[str, Any]): ...
 def on_edit_spec(pos, data: dict[str, Any]): ...
 
 
+class Bounds(TypedDict):
+    x: int
+    y: int
+    width: int
+    height: int
+
+class CompatSelection(TypedDict):
+    items: list
+
+class Rectangle(TypedDict):
+    x: int
+    y: int
+    width: int
+    height: int
+
+class GridSelectionCurrent(TypedDict):
+    cell: list[int]
+    range: Rectangle
+    rangeStack: list[Rectangle]
+
+class GridSelection(TypedDict):
+    current: Optional[GridSelectionCurrent]
+    columns: CompatSelection
+    rows: CompatSelection
+
+class GroupHeaderClickedEventArgs(TypedDict):
+    kind: str
+    group: str
+    location: list[int]
+    bounds: Bounds
+    isEdge: bool
+    shiftKey: bool
+    ctrlKey: bool
+    metaKey: bool
+    isTouch: bool
+    localEventX: int
+    localEventY: int
+    button: int
+    buttons: int
+    scrollEdge: list[int]
+
+class GridCell(TypedDict):
+    span: Optional[List[int]]
+
+class GridColumn(TypedDict):
+    title: str
+    group: Optional[str]
+
 class DataEditor(NoSSRComponent):
 class DataEditor(NoSSRComponent):
     def add_imports(self) -> ImportDict: ...
     def add_imports(self) -> ImportDict: ...
     def add_hooks(self) -> list[str]: ...
     def add_hooks(self) -> list[str]: ...
@@ -136,24 +186,28 @@ class DataEditor(NoSSRComponent):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_cell_activated: Optional[EventType] = None,
-        on_cell_clicked: Optional[EventType] = None,
-        on_cell_context_menu: Optional[EventType] = None,
+        on_cell_activated: Optional[EventType[tuple[int, int]]] = None,
+        on_cell_clicked: Optional[EventType[tuple[int, int]]] = None,
+        on_cell_context_menu: Optional[EventType[tuple[int, int]]] = None,
         on_cell_edited: Optional[EventType] = None,
         on_cell_edited: Optional[EventType] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
-        on_column_resize: Optional[EventType] = None,
+        on_column_resize: Optional[EventType[GridColumn, int]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
-        on_delete: Optional[EventType] = None,
+        on_delete: Optional[EventType[GridSelection]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
-        on_finished_editing: Optional[EventType] = None,
+        on_finished_editing: Optional[
+            EventType[Union[GridCell, None], list[int]]
+        ] = None,
         on_focus: Optional[EventType[[]]] = None,
         on_focus: Optional[EventType[[]]] = None,
         on_group_header_clicked: Optional[EventType] = None,
         on_group_header_clicked: Optional[EventType] = None,
-        on_group_header_context_menu: Optional[EventType] = None,
-        on_group_header_renamed: Optional[EventType] = None,
-        on_header_clicked: Optional[EventType] = None,
-        on_header_context_menu: Optional[EventType] = None,
-        on_header_menu_click: Optional[EventType] = None,
-        on_item_hovered: Optional[EventType] = None,
+        on_group_header_context_menu: Optional[
+            EventType[int, GroupHeaderClickedEventArgs]
+        ] = None,
+        on_group_header_renamed: Optional[EventType[str, str]] = None,
+        on_header_clicked: Optional[EventType[tuple[int, int]]] = None,
+        on_header_context_menu: Optional[EventType[tuple[int, int]]] = None,
+        on_header_menu_click: Optional[EventType[int, Rectangle]] = None,
+        on_item_hovered: Optional[EventType[tuple[int, int]]] = None,
         on_mount: Optional[EventType[[]]] = None,
         on_mount: Optional[EventType[[]]] = None,
         on_mouse_down: Optional[EventType[[]]] = None,
         on_mouse_down: Optional[EventType[[]]] = None,
         on_mouse_enter: Optional[EventType[[]]] = None,
         on_mouse_enter: Optional[EventType[[]]] = None,

+ 1 - 1
reflex/components/moment/moment.pyi

@@ -58,7 +58,7 @@ class Moment(NoSSRComponent):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[str]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,

+ 2 - 2
reflex/components/radix/primitives/drawer.pyi

@@ -101,7 +101,7 @@ class DrawerRoot(DrawerComponent):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,
@@ -511,7 +511,7 @@ class Drawer(ComponentNamespace):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 1 - 1
reflex/components/radix/themes/color_mode.pyi

@@ -383,7 +383,7 @@ class ColorModeSwitch(Switch):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[bool]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,

+ 1 - 1
reflex/components/radix/themes/components/alert_dialog.pyi

@@ -42,7 +42,7 @@ class AlertDialogRoot(RadixThemesComponent):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 3 - 3
reflex/components/radix/themes/components/checkbox.pyi

@@ -116,7 +116,7 @@ class Checkbox(RadixThemesComponent):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[bool]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
@@ -263,7 +263,7 @@ class HighLevelCheckbox(RadixThemesComponent):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[bool]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
@@ -407,7 +407,7 @@ class CheckboxNamespace(ComponentNamespace):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[bool]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,

+ 1 - 1
reflex/components/radix/themes/components/context_menu.pyi

@@ -39,7 +39,7 @@ class ContextMenuRoot(RadixThemesComponent):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 2 - 2
reflex/components/radix/themes/components/dialog.pyi

@@ -40,7 +40,7 @@ class DialogRoot(RadixThemesComponent):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,
@@ -382,7 +382,7 @@ class Dialog(ComponentNamespace):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 2 - 2
reflex/components/radix/themes/components/dropdown_menu.pyi

@@ -49,7 +49,7 @@ class DropdownMenuRoot(RadixThemesComponent):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,
@@ -363,7 +363,7 @@ class DropdownMenuSub(RadixThemesComponent):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 2 - 2
reflex/components/radix/themes/components/hover_card.pyi

@@ -43,7 +43,7 @@ class HoverCardRoot(RadixThemesComponent):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,
@@ -256,7 +256,7 @@ class HoverCard(ComponentNamespace):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 1 - 1
reflex/components/radix/themes/components/popover.pyi

@@ -41,7 +41,7 @@ class PopoverRoot(RadixThemesComponent):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 1 - 1
reflex/components/radix/themes/components/radio_cards.pyi

@@ -177,7 +177,7 @@ class RadioCardsRoot(RadixThemesComponent):
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
-        on_value_change: Optional[EventType] = None,
+        on_value_change: Optional[EventType[str]] = None,
         **props,
         **props,
     ) -> "RadioCardsRoot":
     ) -> "RadioCardsRoot":
         """Create a new component instance.
         """Create a new component instance.

+ 1 - 1
reflex/components/radix/themes/components/radio_group.pyi

@@ -113,7 +113,7 @@ class RadioGroupRoot(RadixThemesComponent):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[str]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,

+ 6 - 6
reflex/components/radix/themes/components/select.pyi

@@ -44,7 +44,7 @@ class SelectRoot(RadixThemesComponent):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[str]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
@@ -57,7 +57,7 @@ class SelectRoot(RadixThemesComponent):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,
@@ -680,7 +680,7 @@ class HighLevelSelect(SelectRoot):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[str]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
@@ -693,7 +693,7 @@ class HighLevelSelect(SelectRoot):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,
@@ -854,7 +854,7 @@ class Select(ComponentNamespace):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[str]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
@@ -867,7 +867,7 @@ class Select(ComponentNamespace):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 1 - 1
reflex/components/radix/themes/components/switch.pyi

@@ -119,7 +119,7 @@ class Switch(RadixThemesComponent):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[bool]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,

+ 2 - 2
reflex/components/radix/themes/components/tabs.pyi

@@ -41,7 +41,7 @@ class TabsRoot(RadixThemesComponent):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[str]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
@@ -340,7 +340,7 @@ class Tabs(ComponentNamespace):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[[]]] = None,
         on_blur: Optional[EventType[[]]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[str]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,

+ 1 - 1
reflex/components/radix/themes/components/tooltip.pyi

@@ -76,7 +76,7 @@ class Tooltip(RadixThemesComponent):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_pointer_down_outside: Optional[EventType[[]]] = None,
         on_pointer_down_outside: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,

+ 2 - 2
reflex/components/react_player/audio.pyi

@@ -41,7 +41,7 @@ class Audio(ReactPlayer):
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_disable_pip: Optional[EventType[[]]] = None,
         on_disable_pip: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
-        on_duration: Optional[EventType] = None,
+        on_duration: Optional[EventType[float]] = None,
         on_enable_pip: Optional[EventType[[]]] = None,
         on_enable_pip: Optional[EventType[[]]] = None,
         on_ended: Optional[EventType[[]]] = None,
         on_ended: Optional[EventType[[]]] = None,
         on_error: Optional[EventType[[]]] = None,
         on_error: Optional[EventType[[]]] = None,
@@ -61,7 +61,7 @@ class Audio(ReactPlayer):
         on_progress: Optional[EventType] = None,
         on_progress: Optional[EventType] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
-        on_seek: Optional[EventType] = None,
+        on_seek: Optional[EventType[float]] = None,
         on_start: Optional[EventType[[]]] = None,
         on_start: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 2 - 2
reflex/components/react_player/react_player.pyi

@@ -39,7 +39,7 @@ class ReactPlayer(NoSSRComponent):
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_disable_pip: Optional[EventType[[]]] = None,
         on_disable_pip: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
-        on_duration: Optional[EventType] = None,
+        on_duration: Optional[EventType[float]] = None,
         on_enable_pip: Optional[EventType[[]]] = None,
         on_enable_pip: Optional[EventType[[]]] = None,
         on_ended: Optional[EventType[[]]] = None,
         on_ended: Optional[EventType[[]]] = None,
         on_error: Optional[EventType[[]]] = None,
         on_error: Optional[EventType[[]]] = None,
@@ -59,7 +59,7 @@ class ReactPlayer(NoSSRComponent):
         on_progress: Optional[EventType] = None,
         on_progress: Optional[EventType] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
-        on_seek: Optional[EventType] = None,
+        on_seek: Optional[EventType[float]] = None,
         on_start: Optional[EventType[[]]] = None,
         on_start: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 2 - 2
reflex/components/react_player/video.pyi

@@ -41,7 +41,7 @@ class Video(ReactPlayer):
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_disable_pip: Optional[EventType[[]]] = None,
         on_disable_pip: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
-        on_duration: Optional[EventType] = None,
+        on_duration: Optional[EventType[float]] = None,
         on_enable_pip: Optional[EventType[[]]] = None,
         on_enable_pip: Optional[EventType[[]]] = None,
         on_ended: Optional[EventType[[]]] = None,
         on_ended: Optional[EventType[[]]] = None,
         on_error: Optional[EventType[[]]] = None,
         on_error: Optional[EventType[[]]] = None,
@@ -61,7 +61,7 @@ class Video(ReactPlayer):
         on_progress: Optional[EventType] = None,
         on_progress: Optional[EventType] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
-        on_seek: Optional[EventType] = None,
+        on_seek: Optional[EventType[float]] = None,
         on_start: Optional[EventType[[]]] = None,
         on_start: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 4 - 4
reflex/components/suneditor/editor.pyi

@@ -128,7 +128,7 @@ class Editor(NoSSRComponent):
         autofocus: Optional[bool] = None,
         autofocus: Optional[bool] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
         on_blur: Optional[EventType[str]] = None,
         on_blur: Optional[EventType[str]] = None,
-        on_change: Optional[EventType] = None,
+        on_change: Optional[EventType[str]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_copy: Optional[EventType[[]]] = None,
         on_copy: Optional[EventType[[]]] = None,
@@ -136,7 +136,7 @@ class Editor(NoSSRComponent):
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_focus: Optional[EventType[[]]] = None,
         on_focus: Optional[EventType[[]]] = None,
         on_input: Optional[EventType[[]]] = None,
         on_input: Optional[EventType[[]]] = None,
-        on_load: Optional[EventType] = None,
+        on_load: Optional[EventType[bool]] = None,
         on_mount: Optional[EventType[[]]] = None,
         on_mount: Optional[EventType[[]]] = None,
         on_mouse_down: Optional[EventType[[]]] = None,
         on_mouse_down: Optional[EventType[[]]] = None,
         on_mouse_enter: Optional[EventType[[]]] = None,
         on_mouse_enter: Optional[EventType[[]]] = None,
@@ -148,8 +148,8 @@ class Editor(NoSSRComponent):
         on_paste: Optional[EventType[str, bool]] = None,
         on_paste: Optional[EventType[str, bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
-        toggle_code_view: Optional[EventType] = None,
-        toggle_full_screen: Optional[EventType] = None,
+        toggle_code_view: Optional[EventType[bool]] = None,
+        toggle_full_screen: Optional[EventType[bool]] = None,
         **props,
         **props,
     ) -> "Editor":
     ) -> "Editor":
         """Create an instance of Editor. No children allowed.
         """Create an instance of Editor. No children allowed.

+ 73 - 9
reflex/event.py

@@ -24,7 +24,7 @@ from typing import (
     overload,
     overload,
 )
 )
 
 
-from typing_extensions import ParamSpec, get_args, get_origin
+from typing_extensions import ParamSpec, Protocol, get_args, get_origin
 
 
 from reflex import constants
 from reflex import constants
 from reflex.utils import console, format
 from reflex.utils import console, format
@@ -465,33 +465,97 @@ prevent_default = EventChain(events=[], args_spec=empty_event).prevent_default
 
 
 
 
 T = TypeVar("T")
 T = TypeVar("T")
+U = TypeVar("U")
 
 
 
 
-def identity_event(event_type: Type[T]) -> Callable[[Var[T]], Tuple[Var[T]]]:
+# def identity_event(event_type: Type[T]) -> Callable[[Var[T]], Tuple[Var[T]]]:
+#     """A helper function that returns the input event as output.
+
+#     Args:
+#         event_type: The type of the event.
+
+#     Returns:
+#         A 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
+
+
+class IdentityEventReturn(Generic[T], Protocol):
+    """Protocol for an identity event return."""
+
+    def __call__(self, *values: Var[T]) -> Tuple[Var[T], ...]:
+        """Return the input values.
+
+        Args:
+            *values: The values to return.
+
+        Returns:
+            The input values.
+        """
+        return values
+
+
+@overload
+def identity_event(event_type: Type[T], /) -> Callable[[Var[T]], Tuple[Var[T]]]: ...  # type: ignore
+
+
+@overload
+def identity_event(
+    event_type_1: Type[T], event_type2: Type[U], /
+) -> Callable[[Var[T], Var[U]], Tuple[Var[T], Var[U]]]: ...
+
+
+@overload
+def identity_event(*event_types: Type[T]) -> IdentityEventReturn[T]: ...
+
+
+def identity_event(*event_types: Type[T]) -> IdentityEventReturn[T]:  # type: ignore
     """A helper function that returns the input event as output.
     """A helper function that returns the input event as output.
 
 
     Args:
     Args:
-        event_type: The type of the event.
+        *event_types: The types of the events.
 
 
     Returns:
     Returns:
         A function that returns the input event as output.
         A function that returns the input event as output.
     """
     """
 
 
-    def inner(ev: Var[T]) -> Tuple[Var[T]]:
-        return (ev,)
+    def inner(*values: Var[T]) -> Tuple[Var[T], ...]:
+        return values
+
+    inner_type = tuple(Var[event_type] for event_type in event_types)
+    return_annotation = Tuple[inner_type]  # type: ignore
 
 
     inner.__signature__ = inspect.signature(inner).replace(  # type: ignore
     inner.__signature__ = inspect.signature(inner).replace(  # type: ignore
         parameters=[
         parameters=[
             inspect.Parameter(
             inspect.Parameter(
-                "ev",
+                f"ev_{i}",
                 kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
                 kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
                 annotation=Var[event_type],
                 annotation=Var[event_type],
             )
             )
+            for i, event_type in enumerate(event_types)
         ],
         ],
-        return_annotation=Tuple[Var[event_type]],
+        return_annotation=return_annotation,
     )
     )
-    inner.__annotations__["ev"] = Var[event_type]
-    inner.__annotations__["return"] = Tuple[Var[event_type]]
+    for i, event_type in enumerate(event_types):
+        inner.__annotations__[f"ev_{i}"] = Var[event_type]
+    inner.__annotations__["return"] = return_annotation
 
 
     return inner
     return inner
 
 

+ 1 - 1
reflex/experimental/layout.pyi

@@ -129,7 +129,7 @@ class DrawerSidebar(DrawerRoot):
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_out: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_over: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
         on_mouse_up: Optional[EventType[[]]] = None,
-        on_open_change: Optional[EventType] = None,
+        on_open_change: Optional[EventType[bool]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         on_unmount: Optional[EventType[[]]] = None,
         **props,
         **props,

+ 76 - 2
reflex/utils/pyi_generator.py

@@ -16,7 +16,7 @@ from itertools import chain
 from multiprocessing import Pool, cpu_count
 from multiprocessing import Pool, cpu_count
 from pathlib import Path
 from pathlib import Path
 from types import ModuleType, SimpleNamespace
 from types import ModuleType, SimpleNamespace
-from typing import Any, Callable, Iterable, Type, get_args
+from typing import Any, Callable, Iterable, Type, get_args, get_origin
 
 
 from reflex.components.component import Component
 from reflex.components.component import Component
 from reflex.utils import types as rx_types
 from reflex.utils import types as rx_types
@@ -372,6 +372,53 @@ def _extract_class_props_as_ast_nodes(
     return kwargs
     return kwargs
 
 
 
 
+def type_to_ast(typ) -> ast.AST:
+    """Converts any type annotation into its AST representation.
+    Handles nested generic types, unions, etc.
+
+    Args:
+        typ: The type annotation to convert.
+
+    Returns:
+        The AST representation of the type annotation.
+    """
+    if typ is type(None):
+        return ast.Name(id="None")
+
+    origin = get_origin(typ)
+
+    # Handle plain types (int, str, custom classes, etc.)
+    if origin is None:
+        if hasattr(typ, "__name__"):
+            return ast.Name(id=typ.__name__)
+        elif hasattr(typ, "_name"):
+            return ast.Name(id=typ._name)
+        return ast.Name(id=str(typ))
+
+    # Get the base type name (List, Dict, Optional, etc.)
+    base_name = origin._name if hasattr(origin, "_name") else origin.__name__
+
+    # Get type arguments
+    args = get_args(typ)
+
+    # Handle empty type arguments
+    if not args:
+        return ast.Name(id=base_name)
+
+    # Convert all type arguments recursively
+    arg_nodes = [type_to_ast(arg) for arg in args]
+
+    # Special case for single-argument types (like List[T] or Optional[T])
+    if len(arg_nodes) == 1:
+        slice_value = arg_nodes[0]
+    else:
+        slice_value = ast.Tuple(elts=arg_nodes, ctx=ast.Load())
+
+    return ast.Subscript(
+        value=ast.Name(id=base_name), slice=ast.Index(value=slice_value), ctx=ast.Load()
+    )
+
+
 def _get_parent_imports(func):
 def _get_parent_imports(func):
     _imports = {"reflex.vars": ["Var"]}
     _imports = {"reflex.vars": ["Var"]}
     for type_hint in inspect.get_annotations(func).values():
     for type_hint in inspect.get_annotations(func).values():
@@ -430,13 +477,40 @@ def _generate_component_create_functiondef(
     def figure_out_return_type(annotation: Any):
     def figure_out_return_type(annotation: Any):
         if inspect.isclass(annotation) and issubclass(annotation, inspect._empty):
         if inspect.isclass(annotation) and issubclass(annotation, inspect._empty):
             return ast.Name(id="Optional[EventType]")
             return ast.Name(id="Optional[EventType]")
+
+        if not isinstance(annotation, str) and get_origin(annotation) is tuple:
+            arguments = get_args(annotation)
+
+            arguments_without_var = [
+                get_args(argument)[0] if get_origin(argument) == Var else argument
+                for argument in arguments
+            ]
+
+            # Convert each argument type to its AST representation
+            type_args = [type_to_ast(arg) for arg in arguments_without_var]
+
+            # Join the type arguments with commas for EventType
+            args_str = ", ".join(ast.unparse(arg) for arg in type_args)
+
+            # Create EventType using the joined string
+            event_type = ast.Name(id=f"EventType[{args_str}]")
+
+            # Wrap in Optional
+            optional_type = ast.Subscript(
+                value=ast.Name(id="Optional"),
+                slice=ast.Index(value=event_type),
+                ctx=ast.Load(),
+            )
+
+            return ast.Name(id=ast.unparse(optional_type))
+
         if isinstance(annotation, str) and annotation.startswith("Tuple["):
         if isinstance(annotation, str) and annotation.startswith("Tuple["):
             inside_of_tuple = annotation.removeprefix("Tuple[").removesuffix("]")
             inside_of_tuple = annotation.removeprefix("Tuple[").removesuffix("]")
 
 
             if inside_of_tuple == "()":
             if inside_of_tuple == "()":
                 return ast.Name(id="Optional[EventType[[]]]")
                 return ast.Name(id="Optional[EventType[[]]]")
 
 
-            arguments: list[str] = [""]
+            arguments = [""]
 
 
             bracket_count = 0
             bracket_count = 0
 
 

+ 1 - 0
tests/integration/test_lifespan.py

@@ -51,6 +51,7 @@ def LifespanApp():
         def context_global(self) -> int:
         def context_global(self) -> int:
             return lifespan_context_global
             return lifespan_context_global
 
 
+        @rx.event
         def tick(self, date):
         def tick(self, date):
             pass
             pass