Browse Source

New API to define triggers (#1820)

Thomas Brandého 1 year ago
parent
commit
211dc15995
53 changed files with 510 additions and 313 deletions
  1. 2 2
      reflex/.templates/jinja/web/pages/index.js.jinja2
  2. 2 2
      reflex/.templates/jinja/web/utils/context.js.jinja2
  3. 2 2
      reflex/.templates/web/pages/_app.js
  4. 6 6
      reflex/.templates/web/utils/state.js
  5. 1 1
      reflex/compiler/compiler.py
  6. 0 1
      reflex/components/__init__.py
  7. 9 2
      reflex/components/base/script.py
  8. 83 37
      reflex/components/component.py
  9. 0 1
      reflex/components/forms/__init__.py
  10. 6 4
      reflex/components/forms/checkbox.py
  11. 0 25
      reflex/components/forms/copytoclipboard.py
  12. 0 28
      reflex/components/forms/copytoclipboard.pyi
  13. 9 7
      reflex/components/forms/editable.py
  14. 7 3
      reflex/components/forms/form.py
  15. 1 1
      reflex/components/forms/form.pyi
  16. 10 8
      reflex/components/forms/input.py
  17. 1 1
      reflex/components/forms/input.pyi
  18. 11 9
      reflex/components/forms/multiselect.py
  19. 5 4
      reflex/components/forms/numberinput.py
  20. 1 1
      reflex/components/forms/numberinput.pyi
  21. 7 5
      reflex/components/forms/pininput.py
  22. 7 6
      reflex/components/forms/radio.py
  23. 8 6
      reflex/components/forms/rangeslider.py
  24. 8 7
      reflex/components/forms/select.py
  25. 9 7
      reflex/components/forms/slider.py
  26. 7 4
      reflex/components/forms/switch.py
  27. 11 8
      reflex/components/forms/textarea.py
  28. 6 4
      reflex/components/forms/upload.py
  29. 1 1
      reflex/components/graphing/plotly.pyi
  30. 7 3
      reflex/components/media/avatar.py
  31. 1 1
      reflex/components/media/avatar.pyi
  32. 7 3
      reflex/components/media/image.py
  33. 9 7
      reflex/components/overlay/alertdialog.py
  34. 1 1
      reflex/components/overlay/alertdialog.pyi
  35. 9 7
      reflex/components/overlay/drawer.py
  36. 1 1
      reflex/components/overlay/drawer.pyi
  37. 8 3
      reflex/components/overlay/menu.py
  38. 1 1
      reflex/components/overlay/menu.pyi
  39. 10 8
      reflex/components/overlay/modal.py
  40. 1 1
      reflex/components/overlay/modal.pyi
  41. 9 4
      reflex/components/overlay/popover.py
  42. 1 1
      reflex/components/overlay/popover.pyi
  43. 8 3
      reflex/components/overlay/tooltip.py
  44. 1 1
      reflex/components/overlay/tooltip.pyi
  45. 30 3
      reflex/constants.py
  46. 67 33
      reflex/event.py
  47. 17 4
      reflex/utils/format.py
  48. 3 0
      reflex/utils/types.py
  49. 1 1
      scripts/pyi_generator.py
  50. 4 3
      tests/components/base/test_script.py
  51. 63 7
      tests/components/test_component.py
  52. 24 20
      tests/test_event.py
  53. 7 4
      tests/utils/test_utils.py

+ 2 - 2
reflex/.templates/jinja/web/pages/index.js.jinja2

@@ -14,7 +14,7 @@ export default function Component() {
   const focusRef = useRef();
   
   // Main event loop.
-  const [Event, connectError] = useContext(EventLoopContext)
+  const [addEvents, connectError] = useContext(EventLoopContext)
 
   // Set focus to the specified element.
   useEffect(() => {
@@ -25,7 +25,7 @@ export default function Component() {
 
   // Route after the initial page hydration.
   useEffect(() => {
-    const change_complete = () => Event(initialEvents.map((e) => ({...e})))
+    const change_complete = () => addEvents(initialEvents.map((e) => ({...e})))
     {{const.router}}.events.on('routeChangeComplete', change_complete)
     return () => {
       {{const.router}}.events.off('routeChangeComplete', change_complete)

+ 2 - 2
reflex/.templates/jinja/web/utils/context.js.jinja2

@@ -1,10 +1,10 @@
 import { createContext } from "react"
-import { E, hydrateClientStorage } from "/utils/state.js"
+import { Event, hydrateClientStorage } from "/utils/state.js"
 
 export const initialState = {{ initial_state|json_dumps }}
 export const StateContext = createContext(null);
 export const EventLoopContext = createContext(null);
 export const clientStorage = {{ client_storage|json_dumps }}
 export const initialEvents = [
-    E('{{state_name}}.{{const.hydrate}}', hydrateClientStorage(clientStorage)),
+    Event('{{state_name}}.{{const.hydrate}}', hydrateClientStorage(clientStorage)),
 ]

+ 2 - 2
reflex/.templates/web/pages/_app.js

@@ -15,13 +15,13 @@ const GlobalStyles = css`
 `;
 
 function EventLoopProvider({ children }) {
-  const [state, Event, connectError] = useEventLoop(
+  const [state, addEvents, connectError] = useEventLoop(
     initialState,
     initialEvents,
     clientStorage,
   )
   return (
-    <EventLoopContext.Provider value={[Event, connectError]}>
+    <EventLoopContext.Provider value={[addEvents, connectError]}>
       <StateContext.Provider value={state}>
         {children}
       </StateContext.Provider>

+ 6 - 6
reflex/.templates/web/utils/state.js

@@ -364,7 +364,7 @@ export const uploadFiles = async (handler, files) => {
  * @param handler The client handler to process event.
  * @returns The event object.
  */
-export const E = (name, payload = {}, handler = null) => {
+export const Event = (name, payload = {}, handler = null) => {
   return { name, payload, handler };
 };
 
@@ -440,9 +440,9 @@ const applyClientStorageDelta = (client_storage, delta) => {
  * @param initial_events The initial app events.
  * @param client_storage The client storage object from context.js
  *
- * @returns [state, Event, connectError] -
+ * @returns [state, addEvents, connectError] -
  *   state is a reactive dict,
- *   Event is used to queue an event, and
+ *   addEvents is used to queue an event, and
  *   connectError is a reactive js error from the websocket connection (or null if connected).
  */
 export const useEventLoop = (
@@ -456,7 +456,7 @@ export const useEventLoop = (
   const [connectError, setConnectError] = useState(null)
 
   // Function to add new events to the event queue.
-  const Event = (events, _e) => {
+  const addEvents = (events, _e) => {
     preventDefault(_e);
     queueEvents(events, socket)
   }
@@ -465,7 +465,7 @@ export const useEventLoop = (
   // initial state hydrate
   useEffect(() => {
     if (router.isReady && !sentHydrate.current) {
-      Event(initial_events.map((e) => ({ ...e })))
+      addEvents(initial_events.map((e) => ({ ...e })))
       sentHydrate.current = true
     }
   }, [router.isReady])
@@ -488,7 +488,7 @@ export const useEventLoop = (
       }
     })()
   })
-  return [state, Event, connectError]
+  return [state, addEvents, connectError]
 }
 
 /***

+ 1 - 1
reflex/compiler/compiler.py

@@ -25,7 +25,7 @@ DEFAULT_IMPORTS: imports.ImportDict = {
     "next/router": {ImportVar(tag="useRouter")},
     f"/{constants.STATE_PATH}": {
         ImportVar(tag="uploadFiles"),
-        ImportVar(tag="E"),
+        ImportVar(tag="Event"),
         ImportVar(tag="isTrue"),
         ImportVar(tag="spreadArraysOrObjects"),
         ImportVar(tag="preventDefault"),

+ 0 - 1
reflex/components/__init__.py

@@ -93,7 +93,6 @@ button = Button.create
 button_group = ButtonGroup.create
 checkbox = Checkbox.create
 checkbox_group = CheckboxGroup.create
-copy_to_clipboard = CopyToClipboard.create
 date_picker = DatePicker.create
 date_time_picker = DateTimePicker.create
 debounce_input = DebounceInput.create

+ 9 - 2
reflex/components/base/script.py

@@ -4,6 +4,8 @@ https://nextjs.org/docs/app/api-reference/components/script
 """
 from __future__ import annotations
 
+from typing import Any, Union
+
 from reflex.components.component import Component
 from reflex.event import EventChain
 from reflex.vars import BaseVar, Var
@@ -57,13 +59,18 @@ class Script(Component):
             raise ValueError("Must provide inline script or `src` prop.")
         return super().create(*children, **props)
 
-    def get_triggers(self) -> set[str]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers for the component.
 
         Returns:
             The event triggers.
         """
-        return super().get_triggers() | {"on_load", "on_ready", "on_error"}
+        return {
+            **super().get_event_triggers(),
+            "on_load": lambda: [],
+            "on_ready": lambda: [],
+            "on_error": lambda: [],
+        }
 
 
 def client_side(javascript_code) -> Var[EventChain]:

+ 83 - 37
reflex/components/component.py

@@ -10,9 +10,8 @@ from typing import Any, Callable, Dict, List, Optional, Set, Type, Union
 from reflex import constants
 from reflex.base import Base
 from reflex.components.tags import Tag
+from reflex.constants import EventTriggers
 from reflex.event import (
-    EVENT_ARG,
-    EVENT_TRIGGERS,
     EventChain,
     EventHandler,
     EventSpec,
@@ -21,7 +20,7 @@ from reflex.event import (
     get_handler_args,
 )
 from reflex.style import Style
-from reflex.utils import format, imports, types
+from reflex.utils import console, format, imports, types
 from reflex.vars import BaseVar, ImportVar, Var
 
 
@@ -126,7 +125,7 @@ class Component(Base, ABC):
 
         # Get the component fields, triggers, and props.
         fields = self.get_fields()
-        triggers = self.get_triggers()
+        triggers = self.get_event_triggers().keys()
         props = self.get_props()
 
         # Add any events triggers.
@@ -220,8 +219,7 @@ class Component(Base, ABC):
             ValueError: If the value is not a valid event chain.
         """
         # Check if the trigger is a controlled event.
-        controlled_triggers = self.get_controlled_triggers()
-        is_controlled_event = event_trigger in controlled_triggers
+        triggers = self.get_event_triggers()
 
         # If it's an event chain var, return it.
         if isinstance(value, Var):
@@ -229,27 +227,28 @@ class Component(Base, ABC):
                 raise ValueError(f"Invalid event chain: {value}")
             return value
 
-        arg = controlled_triggers.get(event_trigger, EVENT_ARG)
+        arg_spec = triggers.get(event_trigger, lambda: [])
 
+        wrapped = False
         # If the input is a single event handler, wrap it in a list.
         if isinstance(value, (EventHandler, EventSpec)):
+            wrapped = True
             value = [value]
 
         # If the input is a list of event handlers, create an event chain.
         if isinstance(value, List):
+            if not wrapped:
+                console.deprecate(
+                    feature_name="EventChain",
+                    reason="to avoid confusion, only use yield API",
+                    deprecation_version="0.2.8",
+                    removal_version="0.2.9",
+                )
             events = []
             for v in value:
                 if isinstance(v, EventHandler):
                     # Call the event handler to get the event.
-                    event = call_event_handler(v, arg)
-
-                    # Check that the event handler takes no args if it's uncontrolled.
-                    if not is_controlled_event and (
-                        event.args is not None and len(event.args) > 0
-                    ):
-                        raise ValueError(
-                            f"Event handler: {v.fn} for uncontrolled event {event_trigger} should not take any args."
-                        )
+                    event = call_event_handler(v, arg_spec)  # type: ignore
 
                     # Add the event to the chain.
                     events.append(event)
@@ -258,45 +257,93 @@ class Component(Base, ABC):
                     events.append(v)
                 elif isinstance(v, Callable):
                     # Call the lambda to get the event chain.
-                    events.extend(call_event_fn(v, arg))
+                    events.extend(call_event_fn(v, arg_spec))  # type: ignore
                 else:
                     raise ValueError(f"Invalid event: {v}")
 
         # If the input is a callable, create an event chain.
         elif isinstance(value, Callable):
-            events = call_event_fn(value, arg)
+            events = call_event_fn(value, arg_spec)  # type: ignore
 
         # Otherwise, raise an error.
         else:
             raise ValueError(f"Invalid event chain: {value}")
 
         # Add args to the event specs if necessary.
-        if is_controlled_event:
-            events = [
-                EventSpec(
-                    handler=e.handler,
-                    args=get_handler_args(e, arg),
-                )
-                for e in events
-            ]
+        events = [
+            EventSpec(
+                handler=e.handler,
+                args=get_handler_args(e),
+                client_handler_name=e.client_handler_name,
+            )
+            for e in events
+        ]
 
         # Return the event chain.
-        return EventChain(events=events)
+        if isinstance(arg_spec, Var):
+            return EventChain(events=events, args_spec=None)
+        else:
+            return EventChain(events=events, args_spec=arg_spec)  # type: ignore
 
-    def get_triggers(self) -> Set[str]:
+    def get_event_triggers(self) -> Dict[str, Any]:
         """Get the event triggers for the component.
 
         Returns:
             The event triggers.
         """
-        return (
-            EVENT_TRIGGERS
-            | set(self.get_controlled_triggers())
-            | set((constants.ON_MOUNT, constants.ON_UNMOUNT))
-        )
+        deprecated_triggers = self.get_triggers()
+        if deprecated_triggers:
+            console.deprecate(
+                feature_name=f"get_triggers ({self.__class__.__name__})",
+                reason="replaced by get_event_triggers",
+                deprecation_version="0.2.8",
+                removal_version="0.2.9",
+            )
+            deprecated_triggers = {
+                trigger: lambda: [] for trigger in deprecated_triggers
+            }
+        else:
+            deprecated_triggers = {}
+
+        deprecated_controlled_triggers = self.get_controlled_triggers()
+        if deprecated_controlled_triggers:
+            console.deprecate(
+                feature_name=f"get_controlled_triggers ({self.__class__.__name__})",
+                reason="replaced by get_event_triggers",
+                deprecation_version="0.2.8",
+                removal_version="0.2.9",
+            )
+
+        return {
+            EventTriggers.ON_FOCUS: lambda: [],
+            EventTriggers.ON_BLUR: lambda: [],
+            EventTriggers.ON_CLICK: lambda: [],
+            EventTriggers.ON_CONTEXT_MENU: lambda: [],
+            EventTriggers.ON_DOUBLE_CLICK: lambda: [],
+            EventTriggers.ON_MOUSE_DOWN: lambda: [],
+            EventTriggers.ON_MOUSE_ENTER: lambda: [],
+            EventTriggers.ON_MOUSE_LEAVE: lambda: [],
+            EventTriggers.ON_MOUSE_MOVE: lambda: [],
+            EventTriggers.ON_MOUSE_OUT: lambda: [],
+            EventTriggers.ON_MOUSE_OVER: lambda: [],
+            EventTriggers.ON_MOUSE_UP: lambda: [],
+            EventTriggers.ON_SCROLL: lambda: [],
+            EventTriggers.ON_MOUNT: lambda: [],
+            EventTriggers.ON_UNMOUNT: lambda: [],
+            **deprecated_triggers,
+            **deprecated_controlled_triggers,
+        }
+
+    def get_triggers(self) -> Set[str]:
+        """Get the triggers for non controlled events [DEPRECATED].
+
+        Returns:
+            A set of non controlled triggers.
+        """
+        return set()
 
     def get_controlled_triggers(self) -> Dict[str, Var]:
-        """Get the event triggers that pass the component's value to the handler.
+        """Get the event triggers that pass the component's value to the handler [DEPRECATED].
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
@@ -436,7 +483,6 @@ class Component(Base, ABC):
             The dictionary for template of component.
         """
         tag = self._render()
-
         rendered_dict = dict(
             tag.add_props(
                 **self.event_triggers,
@@ -577,8 +623,8 @@ class Component(Base, ABC):
         """
         # pop on_mount and on_unmount from event_triggers since these are handled by
         # hooks, not as actually props in the component
-        on_mount = self.event_triggers.pop(constants.ON_MOUNT, None)
-        on_unmount = self.event_triggers.pop(constants.ON_UNMOUNT, None)
+        on_mount = self.event_triggers.pop(EventTriggers.ON_MOUNT, None)
+        on_unmount = self.event_triggers.pop(EventTriggers.ON_UNMOUNT, None)
         if on_mount:
             on_mount = format.format_event_chain(on_mount)
         if on_unmount:

+ 0 - 1
reflex/components/forms/__init__.py

@@ -8,7 +8,6 @@ from .colormodeswitch import (
     ColorModeSwitch,
     color_mode_cond,
 )
-from .copytoclipboard import CopyToClipboard
 from .date_picker import DatePicker
 from .date_time_picker import DateTimePicker
 from .debounce import DebounceInput

+ 6 - 4
reflex/components/forms/checkbox.py

@@ -1,9 +1,10 @@
 """A checkbox component."""
+from __future__ import annotations
 
-from typing import Dict
+from typing import Any, Union
 
-from reflex.components.component import EVENT_ARG
 from reflex.components.libs.chakra import ChakraComponent
+from reflex.constants import EventTriggers
 from reflex.vars import Var
 
 
@@ -48,14 +49,15 @@ class Checkbox(ChakraComponent):
     # The spacing between the checkbox and its label text (0.5rem)
     spacing: Var[str]
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_change": EVENT_ARG.target.checked,
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: lambda e0: [e0.target.checked],
         }
 
 

+ 0 - 25
reflex/components/forms/copytoclipboard.py

@@ -1,25 +0,0 @@
-"""A copy to clipboard component."""
-
-from typing import Set
-
-from reflex.components import Component
-from reflex.vars import Var
-
-
-class CopyToClipboard(Component):
-    """Component to copy text to clipboard."""
-
-    library = "react-copy-to-clipboard"
-
-    tag = "CopyToClipboard"
-
-    # The text to copy when clicked.
-    text: Var[str]
-
-    def get_controlled_triggers(self) -> Set[str]:
-        """Get the event triggers that pass the component's value to the handler.
-
-        Returns:
-            The controlled event triggers.
-        """
-        return {"on_copy"}

+ 0 - 28
reflex/components/forms/copytoclipboard.pyi

@@ -1,28 +0,0 @@
-"""Stub file for copytoclipboard.py"""
-# ------------------- DO NOT EDIT ----------------------
-# This file was generated by `scripts/pyi_generator.py`!
-# ------------------------------------------------------
-
-from typing import Optional, Set, Union, overload
-from reflex.components.component import Component
-from reflex.vars import Var, BaseVar, ComputedVar
-from reflex.event import EventHandler, EventChain, EventSpec
-
-class CopyToClipboard(Component):
-    @overload
-    @classmethod
-    def create(cls, *children, text: Optional[Union[Var[str], str]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_copy: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "CopyToClipboard":  # type: ignore
-        """Create the component.
-
-        Args:
-            *children: The children of the component.
-            text: The text to copy when clicked.
-            **props: The props of the component.
-
-        Returns:
-            The component.
-
-        Raises:
-            TypeError: If an invalid child is passed.
-        """
-        ...

+ 9 - 7
reflex/components/forms/editable.py

@@ -1,9 +1,10 @@
 """An editable component."""
+from __future__ import annotations
 
-from typing import Dict
+from typing import Any, Union
 
 from reflex.components.libs.chakra import ChakraComponent
-from reflex.event import EVENT_ARG
+from reflex.constants import EventTriggers
 from reflex.vars import Var
 
 
@@ -36,17 +37,18 @@ class Editable(ChakraComponent):
     # The initial value of the Editable in both edit and preview mode.
     default_value: Var[str]
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_change": EVENT_ARG,
-            "on_edit": EVENT_ARG,
-            "on_submit": EVENT_ARG,
-            "on_cancel": EVENT_ARG,
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: lambda e0: [e0],
+            EventTriggers.ON_EDIT: lambda e0: [e0],
+            EventTriggers.ON_SUBMIT: lambda e0: [e0],
+            EventTriggers.ON_CANCEL: lambda e0: [e0],
         }
 
 

+ 7 - 3
reflex/components/forms/form.py

@@ -1,9 +1,10 @@
 """Form components."""
 
-from typing import Dict
+from typing import Any, Dict
 
 from reflex.components.component import Component
 from reflex.components.libs.chakra import ChakraComponent
+from reflex.constants import EventTriggers
 from reflex.vars import Var
 
 
@@ -15,7 +16,7 @@ class Form(ChakraComponent):
     # What the form renders to.
     as_: Var[str] = "form"  # type: ignore
 
-    def get_controlled_triggers(self) -> Dict[str, Dict]:
+    def get_event_triggers(self) -> Dict[str, Any]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
@@ -33,7 +34,10 @@ class Form(ChakraComponent):
             else:
                 form_refs[ref[4:]] = Var.create(f"getRefValue({ref})", is_local=False)
 
-        return {"on_submit": form_refs}
+        return {
+            **super().get_event_triggers(),
+            EventTriggers.ON_SUBMIT: lambda e0: [form_refs],
+        }
 
 
 class FormControl(ChakraComponent):

+ 1 - 1
reflex/components/forms/form.pyi

@@ -3,7 +3,7 @@
 # This file was generated by `scripts/pyi_generator.py`!
 # ------------------------------------------------------
 
-from typing import Dict, Optional, Union, overload
+from typing import Any, Dict, Optional, Union, overload
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.components.component import Component
 from reflex.vars import Var, BaseVar, ComputedVar

+ 10 - 8
reflex/components/forms/input.py

@@ -1,10 +1,11 @@
 """An input component."""
 
-from typing import Dict
+from typing import Any, Dict
 
-from reflex.components.component import EVENT_ARG, Component
+from reflex.components.component import Component
 from reflex.components.forms.debounce import DebounceInput
 from reflex.components.libs.chakra import ChakraComponent
+from reflex.constants import EventTriggers
 from reflex.utils import imports
 from reflex.vars import ImportVar, Var
 
@@ -56,18 +57,19 @@ class Input(ChakraComponent):
             {"/utils/state": {ImportVar(tag="set_val")}},
         )
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> Dict[str, Any]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_change": EVENT_ARG.target.value,
-            "on_focus": EVENT_ARG.target.value,
-            "on_blur": EVENT_ARG.target.value,
-            "on_key_down": EVENT_ARG.key,
-            "on_key_up": EVENT_ARG.key,
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: lambda e0: [e0.target.value],
+            EventTriggers.ON_FOCUS: lambda e0: [e0.target.value],
+            EventTriggers.ON_BLUR: lambda e0: [e0.target.value],
+            EventTriggers.ON_KEY_DOWN: lambda e0: [e0.key],
+            EventTriggers.ON_KEY_UP: lambda e0: [e0.key],
         }
 
     @classmethod

+ 1 - 1
reflex/components/forms/input.pyi

@@ -3,7 +3,7 @@
 # This file was generated by `scripts/pyi_generator.py`!
 # ------------------------------------------------------
 
-from typing import Dict, Optional, Union, overload
+from typing import Any, Dict, Optional, Union, overload
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.components.component import Component
 from reflex.vars import Var, BaseVar, ComputedVar

+ 11 - 9
reflex/components/forms/multiselect.py

@@ -1,10 +1,11 @@
 """Provides a feature-rich Select and some (not all) related components."""
+from __future__ import annotations
 
 from typing import Any, Dict, List, Optional, Set, Union
 
 from reflex.base import Base
 from reflex.components.component import Component
-from reflex.event import EVENT_ARG
+from reflex.constants import EventTriggers
 from reflex.vars import Var
 
 
@@ -298,19 +299,20 @@ class Select(Component):
     # How the options should be displayed in the menu.
     menu_position: Var[str] = "fixed"  # type: ignore
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
-        # A normal select returns the value.
-        value = EVENT_ARG.value
-
-        # Multi-select returns a list of values.
-        if self.is_multi:
-            value = Var.create_safe(f"{EVENT_ARG}.map(e => e.value)", is_local=True)
-        return {"on_change": value}
+        return {
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: (
+                lambda e0: [Var.create_safe(f"{e0}.map(e => e.value)", is_local=True)]
+                if self.is_multi
+                else lambda e0: [e0]
+            ),
+        }
 
     @classmethod
     def get_initial_props(cls) -> Set[str]:

+ 5 - 4
reflex/components/forms/numberinput.py

@@ -1,11 +1,11 @@
 """A number input component."""
 
 from numbers import Number
-from typing import Dict
+from typing import Any, Dict
 
 from reflex.components.component import Component
 from reflex.components.libs.chakra import ChakraComponent
-from reflex.event import EVENT_ARG
+from reflex.constants import EventTriggers
 from reflex.vars import Var
 
 
@@ -65,14 +65,15 @@ class NumberInput(ChakraComponent):
     # "outline" | "filled" | "flushed" | "unstyled"
     variant: Var[str]
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> Dict[str, Any]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_change": EVENT_ARG,
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: lambda e0: [e0],
         }
 
     @classmethod

File diff suppressed because it is too large
+ 1 - 1
reflex/components/forms/numberinput.pyi


+ 7 - 5
reflex/components/forms/pininput.py

@@ -1,11 +1,12 @@
 """A pin input component."""
+from __future__ import annotations
 
-from typing import Dict, Optional
+from typing import Any, Optional, Union
 
 from reflex.components.component import Component
 from reflex.components.layout import Foreach
 from reflex.components.libs.chakra import ChakraComponent
-from reflex.event import EVENT_ARG
+from reflex.constants import EventTriggers
 from reflex.utils import format
 from reflex.vars import Var
 
@@ -57,15 +58,16 @@ class PinInput(ChakraComponent):
     # "outline" | "flushed" | "filled" | "unstyled"
     variant: Var[str]
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_change": EVENT_ARG,
-            "on_complete": EVENT_ARG,
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: lambda e0: [e0],
+            EventTriggers.ON_COMPLETE: lambda e0: [e0],
         }
 
     def get_ref(self):

+ 7 - 6
reflex/components/forms/radio.py

@@ -1,14 +1,14 @@
 """A radio component."""
 
 
-from typing import Any, Dict, List
+from typing import Any, Dict, List, Union
 
 from reflex.components.component import Component
 from reflex.components.layout.foreach import Foreach
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.components.typography.text import Text
-from reflex.event import EVENT_ARG
-from reflex.utils import types
+from reflex.constants import EventTriggers
+from reflex.utils.types import _issubclass
 from reflex.vars import Var
 
 
@@ -23,14 +23,15 @@ class RadioGroup(ChakraComponent):
     # The default value.
     default_value: Var[Any]
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> Dict[str, Union[Var, Any]]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_change": EVENT_ARG,
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: lambda e0: [e0],
         }
 
     @classmethod
@@ -49,7 +50,7 @@ class RadioGroup(ChakraComponent):
         if (
             len(children) == 1
             and isinstance(children[0], Var)
-            and types._issubclass(children[0].type_, List)
+            and _issubclass(children[0].type_, List)
         ):
             children = [Foreach.create(children[0], lambda item: Radio.create(item))]
         return super().create(*children, **props)

+ 8 - 6
reflex/components/forms/rangeslider.py

@@ -1,10 +1,11 @@
 """A range slider component."""
+from __future__ import annotations
 
-from typing import Dict, List, Optional
+from typing import Any, List, Optional, Union
 
 from reflex.components.component import Component
 from reflex.components.libs.chakra import ChakraComponent
-from reflex.event import EVENT_ARG
+from reflex.constants import EventTriggers
 from reflex.utils import format
 from reflex.vars import Var
 
@@ -44,16 +45,17 @@ class RangeSlider(ChakraComponent):
     # The minimum distance between slider thumbs. Useful for preventing the thumbs from being too close together.
     min_steps_between_thumbs: Var[int]
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_change": EVENT_ARG,
-            "on_change_end": EVENT_ARG,
-            "on_change_start": EVENT_ARG,
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: lambda e0: [e0],
+            EventTriggers.ON_CHANGE_END: lambda e0: [e0],
+            EventTriggers.ON_CHANGE_START: lambda e0: [e0],
         }
 
     def get_ref(self):

+ 8 - 7
reflex/components/forms/select.py

@@ -1,12 +1,13 @@
 """A select component."""
 
-from typing import Any, Dict, List
+from typing import Any, Dict, List, Union
 
-from reflex.components.component import EVENT_ARG, Component
+from reflex.components.component import Component
 from reflex.components.layout.foreach import Foreach
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.components.typography.text import Text
-from reflex.utils import types
+from reflex.constants import EventTriggers
+from reflex.utils.types import _issubclass
 from reflex.vars import Var
 
 
@@ -45,15 +46,15 @@ class Select(ChakraComponent):
     # The size of the select.
     size: Var[str]
 
-    @classmethod
-    def get_controlled_triggers(cls) -> Dict[str, Var]:
+    def get_event_triggers(self) -> Dict[str, Union[Var, Any]]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_change": EVENT_ARG.target.value,
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: lambda e0: [e0.target.value],
         }
 
     @classmethod
@@ -75,7 +76,7 @@ class Select(ChakraComponent):
         if (
             len(children) == 1
             and isinstance(children[0], Var)
-            and types._issubclass(children[0].type_, List)
+            and _issubclass(children[0].type_, List)
         ):
             children = [Foreach.create(children[0], lambda item: Option.create(item))]
         return super().create(*children, **props)

+ 9 - 7
reflex/components/forms/slider.py

@@ -1,10 +1,11 @@
 """A slider component."""
+from __future__ import annotations
 
-from typing import Dict
+from typing import Any, Union
 
 from reflex.components.component import Component
 from reflex.components.libs.chakra import ChakraComponent
-from reflex.event import EVENT_ARG
+from reflex.constants import EventTriggers
 from reflex.vars import Var
 
 
@@ -64,17 +65,18 @@ class Slider(ChakraComponent):
     # Maximum width of the slider.
     max_w: Var[str]
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_change": EVENT_ARG,
-            "on_change_end": EVENT_ARG,
-            "on_change_start": EVENT_ARG,
-        }
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: lambda e0: [e0],
+            EventTriggers.ON_CHANGE_END: lambda e0: [e0],
+            EventTriggers.ON_CHANGE_START: lambda e0: [e0],
+        }  # type: ignore
 
     @classmethod
     def create(cls, *children, **props) -> Component:

+ 7 - 4
reflex/components/forms/switch.py

@@ -1,8 +1,10 @@
 """A switch component."""
-from typing import Dict
+from __future__ import annotations
+
+from typing import Any, Union
 
-from reflex.components.component import EVENT_ARG
 from reflex.components.libs.chakra import ChakraComponent
+from reflex.constants import EventTriggers
 from reflex.vars import Var
 
 
@@ -41,12 +43,13 @@ class Switch(ChakraComponent):
     # The color scheme of the switch (e.g. "blue", "green", "red", etc.)
     color_scheme: Var[str]
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_change": EVENT_ARG.target.checked,
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: lambda e0: [e0.target.checked],
         }

+ 11 - 8
reflex/components/forms/textarea.py

@@ -1,10 +1,12 @@
 """A textarea component."""
+from __future__ import annotations
 
-from typing import Dict
+from typing import Any, Union
 
-from reflex.components.component import EVENT_ARG, Component
+from reflex.components.component import Component
 from reflex.components.forms.debounce import DebounceInput
 from reflex.components.libs.chakra import ChakraComponent
+from reflex.constants import EventTriggers
 from reflex.vars import Var
 
 
@@ -43,18 +45,19 @@ class TextArea(ChakraComponent):
     # "outline" | "filled" | "flushed" | "unstyled"
     variant: Var[str]
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_change": EVENT_ARG.target.value,
-            "on_focus": EVENT_ARG.target.value,
-            "on_blur": EVENT_ARG.target.value,
-            "on_key_down": EVENT_ARG.key,
-            "on_key_up": EVENT_ARG.key,
+            **super().get_event_triggers(),
+            EventTriggers.ON_CHANGE: lambda e0: [e0.target.value],
+            EventTriggers.ON_FOCUS: lambda e0: [e0.target.value],
+            EventTriggers.ON_BLUR: lambda e0: [e0.target.value],
+            EventTriggers.ON_KEY_DOWN: lambda e0: [e0.key],
+            EventTriggers.ON_KEY_UP: lambda e0: [e0.key],
         }
 
     @classmethod

+ 6 - 4
reflex/components/forms/upload.py

@@ -1,11 +1,12 @@
 """A file upload component."""
 from __future__ import annotations
 
-from typing import Dict, List, Optional
+from typing import Any, Dict, List, Optional, Union
 
-from reflex.components.component import EVENT_ARG, Component
+from reflex.components.component import Component
 from reflex.components.forms.input import Input
 from reflex.components.layout.box import Box
+from reflex.constants import EventTriggers
 from reflex.event import EventChain
 from reflex.vars import BaseVar, Var
 
@@ -89,14 +90,15 @@ class Upload(Component):
         # Create the component.
         return super().create(zone, on_drop=upload_file, **upload_props)
 
-    def get_controlled_triggers(self) -> Dict[str, Var]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers that pass the component's value to the handler.
 
         Returns:
             A dict mapping the event trigger to the var that is passed to the handler.
         """
         return {
-            "on_drop": EVENT_ARG,
+            **super().get_event_triggers(),
+            EventTriggers.ON_DROP: lambda e0: [e0],
         }
 
     def _render(self):

+ 1 - 1
reflex/components/graphing/plotly.pyi

@@ -31,7 +31,7 @@ class PlotlyLib(NoSSRComponent):
 class Plotly(PlotlyLib):
     @overload
     @classmethod
-    def create(cls, *children, data: Optional[Union[Var[Any], Any]] = None, layout: Optional[Union[Var[Dict], Dict]] = None, width: Optional[Union[Var[str], str]] = None, height: Optional[Union[Var[str], str]] = None, use_resize_handler: Optional[Union[Var[bool], bool]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "Plotly":  # type: ignore
+    def create(cls, *children, data: Optional[Union[Var[Figure], Figure]] = None, layout: Optional[Union[Var[Dict], Dict]] = None, width: Optional[Union[Var[str], str]] = None, height: Optional[Union[Var[str], str]] = None, use_resize_handler: Optional[Union[Var[bool], bool]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "Plotly":  # type: ignore
         """Create the component.
 
         Args:

+ 7 - 3
reflex/components/media/avatar.py

@@ -1,6 +1,7 @@
 """Avatar components."""
+from __future__ import annotations
 
-from typing import Set
+from typing import Any, Union
 
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.vars import Var
@@ -35,13 +36,16 @@ class Avatar(ChakraComponent):
     # "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "full"
     size: Var[str]
 
-    def get_triggers(self) -> Set[str]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers for the component.
 
         Returns:
             The event triggers.
         """
-        return super().get_triggers() | {"on_error"}
+        return {
+            **super().get_event_triggers(),
+            "on_error": lambda: [],
+        }
 
 
 class AvatarBadge(ChakraComponent):

+ 1 - 1
reflex/components/media/avatar.pyi

@@ -3,7 +3,7 @@
 # This file was generated by `scripts/pyi_generator.py`!
 # ------------------------------------------------------
 
-from typing import Optional, Set, Union, overload
+from typing import Dict, Optional, Union, overload
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.components.component import Component
 from reflex.vars import Var, BaseVar, ComputedVar

+ 7 - 3
reflex/components/media/image.py

@@ -3,7 +3,7 @@ from __future__ import annotations
 
 import base64
 import io
-from typing import Any, Optional
+from typing import Any, Optional, Union
 
 from reflex.components.component import Component
 from reflex.components.libs.chakra import ChakraComponent
@@ -53,13 +53,17 @@ class Image(ChakraComponent):
     # Learn more _[here](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)_
     src_set: Var[str]
 
-    def get_triggers(self) -> set[str]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers for the component.
 
         Returns:
             The event triggers.
         """
-        return super().get_triggers() | {"on_error", "on_load"}
+        return {
+            **super().get_event_triggers(),
+            "on_error": lambda: [],
+            "on_load": lambda: [],
+        }
 
     def _render(self) -> Tag:
         self.src.is_string = True

+ 9 - 7
reflex/components/overlay/alertdialog.py

@@ -1,6 +1,7 @@
 """Alert dialog components."""
+from __future__ import annotations
 
-from typing import Set
+from typing import Any, Union
 
 from reflex.components.component import Component
 from reflex.components.libs.chakra import ChakraComponent
@@ -52,17 +53,18 @@ class AlertDialog(ChakraComponent):
     # If true, the siblings of the modal will have `aria-hidden` set to true so that screen readers can only see the modal. This is commonly known as making the other elements **inert**
     use_inert: Var[bool]
 
-    def get_triggers(self) -> Set[str]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers for the component.
 
         Returns:
             The event triggers.
         """
-        return super().get_triggers() | {
-            "on_close",
-            "on_close_complete",
-            "on_esc",
-            "on_overlay_click",
+        return {
+            **super().get_event_triggers(),
+            "on_close": lambda: [],
+            "on_close_complete": lambda: [],
+            "on_esc": lambda: [],
+            "on_overlay_click": lambda: [],
         }
 
     @classmethod

+ 1 - 1
reflex/components/overlay/alertdialog.pyi

@@ -3,7 +3,7 @@
 # This file was generated by `scripts/pyi_generator.py`!
 # ------------------------------------------------------
 
-from typing import Optional, Set, Union, overload
+from typing import Dict, Optional, Union, overload
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.components.component import Component
 from reflex.vars import Var, BaseVar, ComputedVar

+ 9 - 7
reflex/components/overlay/drawer.py

@@ -1,6 +1,7 @@
 """Container to stack elements with spacing."""
+from __future__ import annotations
 
-from typing import Set
+from typing import Any, Union
 
 from reflex.components.component import Component
 from reflex.components.libs.chakra import ChakraComponent
@@ -58,17 +59,18 @@ class Drawer(ChakraComponent):
     # Variant of drawer
     variant: Var[str]
 
-    def get_triggers(self) -> Set[str]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers for the component.
 
         Returns:
             The event triggers.
         """
-        return super().get_triggers() | {
-            "on_close",
-            "on_close_complete",
-            "on_esc",
-            "on_overlay_click",
+        return {
+            **super().get_event_triggers(),
+            "on_close": lambda: [],
+            "on_close_complete": lambda: [],
+            "on_esc": lambda: [],
+            "on_overlay_click": lambda: [],
         }
 
     @classmethod

+ 1 - 1
reflex/components/overlay/drawer.pyi

@@ -3,7 +3,7 @@
 # This file was generated by `scripts/pyi_generator.py`!
 # ------------------------------------------------------
 
-from typing import Optional, Set, Union, overload
+from typing import Dict, Optional, Union, overload
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.components.component import Component
 from reflex.vars import Var, BaseVar, ComputedVar

+ 8 - 3
reflex/components/overlay/menu.py

@@ -1,6 +1,7 @@
 """Menu components."""
+from __future__ import annotations
 
-from typing import List, Set
+from typing import Any, List, Union
 
 from reflex.components.component import Component
 from reflex.components.libs.chakra import ChakraComponent
@@ -60,13 +61,17 @@ class Menu(ChakraComponent):
     # The CSS positioning strategy to use. ("fixed" | "absolute")
     strategy: Var[str]
 
-    def get_triggers(self) -> Set[str]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers for the component.
 
         Returns:
             The event triggers.
         """
-        return super().get_triggers() | {"on_close", "on_open"}
+        return {
+            **super().get_event_triggers(),
+            "on_close": lambda: [],
+            "on_open": lambda: [],
+        }
 
     @classmethod
     def create(cls, *children, button=None, items=None, **props) -> Component:

+ 1 - 1
reflex/components/overlay/menu.pyi

@@ -3,7 +3,7 @@
 # This file was generated by `scripts/pyi_generator.py`!
 # ------------------------------------------------------
 
-from typing import List, Optional, Set, Union, overload
+from typing import Dict, List, Optional, Union, overload
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.components.component import Component
 from reflex.vars import Var, BaseVar, ComputedVar

+ 10 - 8
reflex/components/overlay/modal.py

@@ -1,6 +1,7 @@
 """Modal components."""
+from __future__ import annotations
 
-from typing import Optional, Set, Union
+from typing import Any, Optional, Union
 
 from reflex.components.component import Component
 from reflex.components.libs.chakra import ChakraComponent
@@ -52,17 +53,18 @@ class Modal(ChakraComponent):
     # A11y: If true, the siblings of the modal will have `aria-hidden` set to true so that screen readers can only see the modal. This is commonly known as making the other elements **inert**
     use_inert: Var[bool]
 
-    def get_triggers(self) -> Set[str]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers for the component.
 
         Returns:
             The event triggers.
         """
-        return super().get_triggers() | {
-            "on_close",
-            "on_close_complete",
-            "on_esc",
-            "on_overlay_click",
+        return {
+            **super().get_event_triggers(),
+            "on_close": lambda: [],
+            "on_close_complete": lambda: [],
+            "on_esc": lambda: [],
+            "on_overlay_click": lambda: [],
         }
 
     @classmethod
@@ -73,7 +75,7 @@ class Modal(ChakraComponent):
         body: Optional[Union[Component, str]] = None,
         footer: Optional[Union[Component, str]] = None,
         close_button: Optional[Component] = None,
-        **props
+        **props,
     ) -> Component:
         """Create a modal component.
 

+ 1 - 1
reflex/components/overlay/modal.pyi

@@ -3,7 +3,7 @@
 # This file was generated by `scripts/pyi_generator.py`!
 # ------------------------------------------------------
 
-from typing import Optional, Set, Union, overload
+from typing import Dict, Optional, Union, overload
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.components.component import Component
 from reflex.vars import Var, BaseVar, ComputedVar

+ 9 - 4
reflex/components/overlay/popover.py

@@ -1,6 +1,7 @@
 """Popover components."""
+from __future__ import annotations
 
-from typing import Set
+from typing import Any, Union
 
 from reflex.components.component import Component
 from reflex.components.libs.chakra import ChakraComponent
@@ -75,13 +76,17 @@ class Popover(ChakraComponent):
     # The interaction that triggers the popover. hover - means the popover will open when you hover with mouse or focus with keyboard on the popover trigger click - means the popover will open on click or press Enter to Space on keyboard ("click" | "hover")
     trigger: Var[str]
 
-    def get_triggers(self) -> Set[str]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers for the component.
 
         Returns:
             The event triggers.
         """
-        return super().get_triggers() | {"on_close", "on_open"}
+        return {
+            **super().get_event_triggers(),
+            "on_close": lambda: [],
+            "on_open": lambda: [],
+        }
 
     @classmethod
     def create(
@@ -92,7 +97,7 @@ class Popover(ChakraComponent):
         body=None,
         footer=None,
         use_close_button=False,
-        **props
+        **props,
     ) -> Component:
         """Create a popover component.
 

+ 1 - 1
reflex/components/overlay/popover.pyi

@@ -3,7 +3,7 @@
 # This file was generated by `scripts/pyi_generator.py`!
 # ------------------------------------------------------
 
-from typing import Optional, Set, Union, overload
+from typing import Dict, Optional, Union, overload
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.components.component import Component
 from reflex.vars import Var, BaseVar, ComputedVar

+ 8 - 3
reflex/components/overlay/tooltip.py

@@ -1,6 +1,7 @@
 """Tooltip components."""
+from __future__ import annotations
 
-from typing import Set
+from typing import Any, Union
 
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.vars import Var
@@ -62,10 +63,14 @@ class Tooltip(ChakraComponent):
     # If true, the tooltip will wrap its children in a `<span/>` with `tabIndex=0`
     should_wrap_children: Var[bool]
 
-    def get_triggers(self) -> Set[str]:
+    def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
         """Get the event triggers for the component.
 
         Returns:
             The event triggers.
         """
-        return super().get_triggers() | {"on_close", "on_open"}
+        return {
+            **super().get_event_triggers(),
+            "on_close": lambda: [],
+            "on_open": lambda: [],
+        }

+ 1 - 1
reflex/components/overlay/tooltip.pyi

@@ -3,7 +3,7 @@
 # This file was generated by `scripts/pyi_generator.py`!
 # ------------------------------------------------------
 
-from typing import Optional, Set, Union, overload
+from typing import Dict, Optional, Union, overload
 from reflex.components.libs.chakra import ChakraComponent
 from reflex.components.component import Component
 from reflex.vars import Var, BaseVar, ComputedVar

+ 30 - 3
reflex/constants.py

@@ -407,9 +407,36 @@ ALEMBIC_CONFIG = os.environ.get("ALEMBIC_CONFIG", "alembic.ini")
 COOKIES = "cookies"
 LOCAL_STORAGE = "local_storage"
 
-# Names of event handlers on all components mapped to useEffect
-ON_MOUNT = "on_mount"
-ON_UNMOUNT = "on_unmount"
+
+class EventTriggers(SimpleNamespace):
+    """All trigger names used in Reflex."""
+
+    ON_FOCUS = "on_focus"
+    ON_BLUR = "on_blur"
+    ON_CANCEL = "on_cancel"
+    ON_CLICK = "on_click"
+    ON_CHANGE = "on_change"
+    ON_CHANGE_END = "on_change_end"
+    ON_CHANGE_START = "on_change_start"
+    ON_COMPLETE = "on_complete"
+    ON_CONTEXT_MENU = "on_context_menu"
+    ON_DOUBLE_CLICK = "on_double_click"
+    ON_DROP = "on_drop"
+    ON_EDIT = "on_edit"
+    ON_KEY_DOWN = "on_key_down"
+    ON_KEY_UP = "on_key_up"
+    ON_MOUSE_DOWN = "on_mouse_down"
+    ON_MOUSE_ENTER = "on_mouse_enter"
+    ON_MOUSE_LEAVE = "on_mouse_leave"
+    ON_MOUSE_MOVE = "on_mouse_move"
+    ON_MOUSE_OUT = "on_mouse_out"
+    ON_MOUSE_OVER = "on_mouse_over"
+    ON_MOUSE_UP = "on_mouse_up"
+    ON_SCROLL = "on_scroll"
+    ON_SUBMIT = "on_submit"
+    ON_MOUNT = "on_mount"
+    ON_UNMOUNT = "on_unmount"
+
 
 # If this env var is set to "yes", App.compile will be a no-op
 SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"

+ 67 - 33
reflex/event.py

@@ -2,11 +2,12 @@
 from __future__ import annotations
 
 import inspect
-from typing import Any, Callable, Dict, List, Optional, Tuple
+from typing import Any, Callable, Dict, List, Optional, Tuple, Union
 
 from reflex import constants
 from reflex.base import Base
-from reflex.utils import format
+from reflex.utils import console, format
+from reflex.utils.types import ArgsSpec
 from reflex.vars import BaseVar, Var
 
 
@@ -109,6 +110,8 @@ class EventChain(Base):
 
     events: List[EventSpec]
 
+    args_spec: Optional[ArgsSpec]
+
 
 class Target(Base):
     """A Javascript event target."""
@@ -383,7 +386,9 @@ def get_hydrate_event(state) -> str:
     return get_event(state, constants.HYDRATE)
 
 
-def call_event_handler(event_handler: EventHandler, arg: Var) -> EventSpec:
+def call_event_handler(
+    event_handler: EventHandler, arg_spec: Union[Var, ArgsSpec]
+) -> EventSpec:
     """Call an event handler to get the event spec.
 
     This function will inspect the function signature of the event handler.
@@ -392,21 +397,66 @@ def call_event_handler(event_handler: EventHandler, arg: Var) -> EventSpec:
 
     Args:
         event_handler: The event handler.
-        arg: The argument to pass to the event handler.
+        arg_spec: The lambda that define the argument(s) to pass to the event handler.
+
+    Raises:
+        ValueError: if number of arguments expected by event_handler doesn't match the spec.
 
     Returns:
         The event spec from calling the event handler.
     """
     args = inspect.getfullargspec(event_handler.fn).args
+
+    # handle new API using lambda to define triggers
+    if isinstance(arg_spec, ArgsSpec):
+        parsed_args = parse_args_spec(arg_spec)
+
+        if len(args) == len(["self", *parsed_args]):
+            return event_handler(*parsed_args)  # type: ignore
+        else:
+            source = inspect.getsource(arg_spec)
+            raise ValueError(
+                f"number of arguments in {event_handler.fn.__name__} "
+                f"doesn't match the definition '{source.strip().strip(',')}'"
+            )
+    else:
+        console.deprecate(
+            feature_name="EVENT_ARG API for triggers",
+            reason="Replaced by new API using lambda allow arbitrary number of args",
+            deprecation_version="0.2.8",
+            removal_version="0.2.9",
+        )
     if len(args) == 1:
         return event_handler()
     assert (
         len(args) == 2
     ), f"Event handler {event_handler.fn} must have 1 or 2 arguments."
-    return event_handler(arg)
+    return event_handler(arg_spec)
+
+
+def parse_args_spec(arg_spec: ArgsSpec):
+    """Parse the args provided in the ArgsSpec of an event trigger.
 
+    Args:
+        arg_spec: The spec of the args.
 
-def call_event_fn(fn: Callable, arg: Var) -> list[EventSpec]:
+    Returns:
+        The parsed args.
+    """
+    spec = inspect.getfullargspec(arg_spec)
+    return arg_spec(
+        *[
+            BaseVar(
+                name=f"_{l_arg}",
+                type_=spec.annotations.get(l_arg, FrontendEvent),
+                is_local=True,
+            )
+            for l_arg in spec.args
+        ]
+    )
+
+
+def call_event_fn(fn: Callable, arg: Union[Var, ArgsSpec]) -> list[EventSpec]:
     """Call a function to a list of event specs.
 
     The function should return either a single EventSpec or a list of EventSpecs.
@@ -429,13 +479,16 @@ def call_event_fn(fn: Callable, arg: Var) -> list[EventSpec]:
     # Get the args of the lambda.
     args = inspect.getfullargspec(fn).args
 
-    # Call the lambda.
-    if len(args) == 0:
-        out = fn()
-    elif len(args) == 1:
-        out = fn(arg)
+    if isinstance(arg, ArgsSpec):
+        out = fn(*parse_args_spec(arg))
     else:
-        raise ValueError(f"Lambda {fn} must have 0 or 1 arguments.")
+        # Call the lambda.
+        if len(args) == 0:
+            out = fn()
+        elif len(args) == 1:
+            out = fn(arg)
+        else:
+            raise ValueError(f"Lambda {fn} must have 0 or 1 arguments.")
 
     # Convert the output to a list.
     if not isinstance(out, List):
@@ -449,7 +502,7 @@ def call_event_fn(fn: Callable, arg: Var) -> list[EventSpec]:
             if len(args) == 0:
                 e = e()
             elif len(args) == 1:
-                e = e(arg)
+                e = e(arg)  # type: ignore
 
         # Make sure the event spec is valid.
         if not isinstance(e, EventSpec):
@@ -462,12 +515,11 @@ def call_event_fn(fn: Callable, arg: Var) -> list[EventSpec]:
     return events
 
 
-def get_handler_args(event_spec: EventSpec, arg: Var) -> tuple[tuple[Var, Var], ...]:
+def get_handler_args(event_spec: EventSpec) -> tuple[tuple[Var, Var], ...]:
     """Get the handler args for the given event spec.
 
     Args:
         event_spec: The event spec.
-        arg: The controlled event argument.
 
     Returns:
         The handler args.
@@ -539,21 +591,3 @@ def get_fn_signature(fn: Callable) -> inspect.Signature:
         "state", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=Any
     )
     return signature.replace(parameters=(new_param, *signature.parameters.values()))
-
-
-# A set of common event triggers.
-EVENT_TRIGGERS: set[str] = {
-    "on_focus",
-    "on_blur",
-    "on_click",
-    "on_context_menu",
-    "on_double_click",
-    "on_mouse_down",
-    "on_mouse_enter",
-    "on_mouse_leave",
-    "on_mouse_move",
-    "on_mouse_out",
-    "on_mouse_over",
-    "on_mouse_up",
-    "on_scroll",
-}

+ 17 - 4
reflex/utils/format.py

@@ -2,6 +2,7 @@
 
 from __future__ import annotations
 
+import inspect
 import json
 import os
 import os.path as op
@@ -300,9 +301,21 @@ def format_prop(
 
         # Handle event props.
         elif isinstance(prop, EventChain):
+            if prop.args_spec is None:
+                arg_def = f"{EVENT_ARG}"
+            else:
+                sig = inspect.signature(prop.args_spec)
+                if sig.parameters:
+                    arg_def = ",".join(f"_{p}" for p in sig.parameters)
+                    arg_def = f"({arg_def})"
+                else:
+                    # add a default argument for addEvents if none were specified in prop.args_spec
+                    # used to trigger the preventDefault() on the event.
+                    arg_def = "(_e)"
+
             chain = ",".join([format_event(event) for event in prop.events])
-            event = f"Event([{chain}], {EVENT_ARG})"
-            prop = f"{EVENT_ARG} => {event}"
+            event = f"addEvents([{chain}], {arg_def})"
+            prop = f"{arg_def} => {event}"
 
         # Handle other types.
         elif isinstance(prop, str):
@@ -414,7 +427,7 @@ def format_event(event_spec: EventSpec) -> str:
 
     if event_spec.client_handler_name:
         event_args.append(wrap(event_spec.client_handler_name, '"'))
-    return f"E({', '.join(event_args)})"
+    return f"Event({', '.join(event_args)})"
 
 
 def format_event_chain(
@@ -450,7 +463,7 @@ def format_event_chain(
     chain = ",".join([format_event(event) for event in event_chain.events])
     return "".join(
         [
-            f"Event([{chain}]",
+            f"addEvents([{chain}]",
             f", {format_var(event_arg)}" if event_arg else "",
             ")",
         ]

+ 3 - 0
reflex/utils/types.py

@@ -4,6 +4,7 @@ from __future__ import annotations
 
 import contextlib
 import typing
+from types import LambdaType
 from typing import Any, Callable, Type, Union, _GenericAlias  # type: ignore
 
 from reflex.base import Base
@@ -17,6 +18,8 @@ PrimitiveType = Union[int, float, bool, str, list, dict, set, tuple]
 StateVar = Union[PrimitiveType, Base, None]
 StateIterVar = Union[list, set, tuple]
 
+ArgsSpec = LambdaType
+
 
 def get_args(alias: _GenericAlias) -> tuple[Type, ...]:
     """Get the arguments of a type alias.

+ 1 - 1
scripts/pyi_generator.py

@@ -123,7 +123,7 @@ class PyiGenerator:
                 continue
             definition += f"{name}: {_get_type_hint(value)} = None, "
 
-        for trigger in sorted(_class().get_triggers()):
+        for trigger in sorted(_class().get_event_triggers().keys()):
             definition += f"{trigger}: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, "
 
         definition = definition.rstrip(", ")

+ 4 - 3
tests/components/base/test_script.py

@@ -57,13 +57,14 @@ def test_script_event_handler():
     )
     render_dict = component.render()
     assert (
-        'onReady={_e => Event([E("ev_state.on_ready", {})], _e)}'
+        'onReady={(_e) => addEvents([Event("ev_state.on_ready", {})], (_e))}'
         in render_dict["props"]
     )
     assert (
-        'onLoad={_e => Event([E("ev_state.on_load", {})], _e)}' in render_dict["props"]
+        'onLoad={(_e) => addEvents([Event("ev_state.on_load", {})], (_e))}'
+        in render_dict["props"]
     )
     assert (
-        'onError={_e => Event([E("ev_state.on_error", {})], _e)}'
+        'onError={(_e) => addEvents([Event("ev_state.on_error", {})], (_e))}'
         in render_dict["props"]
     )

+ 63 - 7
tests/components/test_component.py

@@ -1,12 +1,13 @@
-from typing import Dict, List, Type
+from typing import Any, Dict, List, Type
 
 import pytest
 
 import reflex as rx
+from reflex.base import Base
 from reflex.components.component import Component, CustomComponent, custom_component
 from reflex.components.layout.box import Box
-from reflex.constants import ON_MOUNT, ON_UNMOUNT
-from reflex.event import EVENT_ARG, EVENT_TRIGGERS, EventHandler
+from reflex.constants import EventTriggers
+from reflex.event import EVENT_ARG, EventHandler
 from reflex.state import State
 from reflex.style import Style
 from reflex.utils import imports
@@ -371,16 +372,71 @@ def test_get_controlled_triggers(component1, component2):
     assert set(component2().get_controlled_triggers()) == {"on_open", "on_close"}
 
 
-def test_get_triggers(component1, component2):
+def test_get_event_triggers(component1, component2):
     """Test that we can get the triggers of a component.
 
     Args:
         component1: A test component.
         component2: A test component.
     """
-    default_triggers = {ON_MOUNT, ON_UNMOUNT} | EVENT_TRIGGERS
-    assert component1().get_triggers() == default_triggers
-    assert component2().get_triggers() == {"on_open", "on_close"} | default_triggers
+    default_triggers = {
+        EventTriggers.ON_FOCUS,
+        EventTriggers.ON_BLUR,
+        EventTriggers.ON_CLICK,
+        EventTriggers.ON_CONTEXT_MENU,
+        EventTriggers.ON_DOUBLE_CLICK,
+        EventTriggers.ON_MOUSE_DOWN,
+        EventTriggers.ON_MOUSE_ENTER,
+        EventTriggers.ON_MOUSE_LEAVE,
+        EventTriggers.ON_MOUSE_MOVE,
+        EventTriggers.ON_MOUSE_OUT,
+        EventTriggers.ON_MOUSE_OVER,
+        EventTriggers.ON_MOUSE_UP,
+        EventTriggers.ON_SCROLL,
+        EventTriggers.ON_MOUNT,
+        EventTriggers.ON_UNMOUNT,
+    }
+    assert set(component1().get_event_triggers().keys()) == default_triggers
+    assert (
+        component2().get_event_triggers().keys()
+        == {"on_open", "on_close"} | default_triggers
+    )
+
+
+class C1State(State):
+    """State for testing C1 component."""
+
+    def mock_handler(self, _e, _bravo, _charlie):
+        """Mock handler."""
+        pass
+
+
+def test_component_event_trigger_arbitrary_args():
+    """Test that we can define arbitrary types for the args of an event trigger."""
+
+    class Obj(Base):
+        custom: int = 0
+
+    def on_foo_spec(_e, alpha: str, bravo: Dict[str, Any], charlie: Obj):
+        return [_e.target.value, bravo["nested"], charlie.custom + 42]
+
+    class C1(Component):
+        library = "/local"
+        tag = "C1"
+
+        def get_event_triggers(self) -> Dict[str, Any]:
+            return {
+                **super().get_event_triggers(),
+                "on_foo": on_foo_spec,
+            }
+
+    comp = C1.create(on_foo=C1State.mock_handler)
+
+    assert comp.render()["props"][0] == (
+        "onFoo={(__e,_alpha,_bravo,_charlie) => addEvents("
+        '[Event("c1_state.mock_handler", {_e:__e.target.value,_bravo:_bravo["nested"],_charlie:(_charlie.custom + 42)})], '
+        "(__e,_alpha,_bravo,_charlie))}"
+    )
 
 
 def test_create_custom_component(my_component):

+ 24 - 20
tests/test_event.py

@@ -48,7 +48,7 @@ def test_call_event_handler():
 
     assert event_spec.handler == handler
     assert event_spec.args == ()
-    assert format.format_event(event_spec) == 'E("test_fn", {})'
+    assert format.format_event(event_spec) == 'Event("test_fn", {})'
 
     handler = EventHandler(fn=test_fn_with_args)
     event_spec = handler(make_var("first"), make_var("second"))
@@ -61,14 +61,14 @@ def test_call_event_handler():
     assert event_spec.args[1][1].equals(Var.create_safe("second"))
     assert (
         format.format_event(event_spec)
-        == 'E("test_fn_with_args", {arg1:first,arg2:second})'
+        == 'Event("test_fn_with_args", {arg1:first,arg2:second})'
     )
 
     # Passing args as strings should format differently.
     event_spec = handler("first", "second")  # type: ignore
     assert (
         format.format_event(event_spec)
-        == 'E("test_fn_with_args", {arg1:"first",arg2:"second"})'
+        == 'Event("test_fn_with_args", {arg1:"first",arg2:"second"})'
     )
 
     first, second = 123, "456"
@@ -76,7 +76,7 @@ def test_call_event_handler():
     event_spec = handler(first, second)  # type: ignore
     assert (
         format.format_event(event_spec)
-        == 'E("test_fn_with_args", {arg1:123,arg2:"456"})'
+        == 'Event("test_fn_with_args", {arg1:123,arg2:"456"})'
     )
 
     assert event_spec.handler == handler
@@ -126,9 +126,9 @@ def test_event_redirect():
     assert spec.handler.fn.__qualname__ == "_redirect"
     assert spec.args[0][0].equals(Var.create_safe("path"))
     assert spec.args[0][1].equals(Var.create_safe("/path"))
-    assert format.format_event(spec) == 'E("_redirect", {path:"/path"})'
+    assert format.format_event(spec) == 'Event("_redirect", {path:"/path"})'
     spec = event.redirect(Var.create_safe("path"))
-    assert format.format_event(spec) == 'E("_redirect", {path:path})'
+    assert format.format_event(spec) == 'Event("_redirect", {path:path})'
 
 
 def test_event_console_log():
@@ -138,9 +138,9 @@ def test_event_console_log():
     assert spec.handler.fn.__qualname__ == "_console"
     assert spec.args[0][0].equals(Var.create_safe("message"))
     assert spec.args[0][1].equals(Var.create_safe("message"))
-    assert format.format_event(spec) == 'E("_console", {message:"message"})'
+    assert format.format_event(spec) == 'Event("_console", {message:"message"})'
     spec = event.console_log(Var.create_safe("message"))
-    assert format.format_event(spec) == 'E("_console", {message:message})'
+    assert format.format_event(spec) == 'Event("_console", {message:message})'
 
 
 def test_event_window_alert():
@@ -150,9 +150,9 @@ def test_event_window_alert():
     assert spec.handler.fn.__qualname__ == "_alert"
     assert spec.args[0][0].equals(Var.create_safe("message"))
     assert spec.args[0][1].equals(Var.create_safe("message"))
-    assert format.format_event(spec) == 'E("_alert", {message:"message"})'
+    assert format.format_event(spec) == 'Event("_alert", {message:"message"})'
     spec = event.window_alert(Var.create_safe("message"))
-    assert format.format_event(spec) == 'E("_alert", {message:message})'
+    assert format.format_event(spec) == 'Event("_alert", {message:message})'
 
 
 def test_set_focus():
@@ -162,9 +162,9 @@ def test_set_focus():
     assert spec.handler.fn.__qualname__ == "_set_focus"
     assert spec.args[0][0].equals(Var.create_safe("ref"))
     assert spec.args[0][1].equals(Var.create_safe("ref_input1"))
-    assert format.format_event(spec) == 'E("_set_focus", {ref:ref_input1})'
+    assert format.format_event(spec) == 'Event("_set_focus", {ref:ref_input1})'
     spec = event.set_focus("input1")
-    assert format.format_event(spec) == 'E("_set_focus", {ref:ref_input1})'
+    assert format.format_event(spec) == 'Event("_set_focus", {ref:ref_input1})'
 
 
 def test_set_value():
@@ -176,10 +176,11 @@ def test_set_value():
     assert spec.args[0][1].equals(Var.create_safe("ref_input1"))
     assert spec.args[1][0].equals(Var.create_safe("value"))
     assert spec.args[1][1].equals(Var.create_safe(""))
-    assert format.format_event(spec) == 'E("_set_value", {ref:ref_input1,value:""})'
+    assert format.format_event(spec) == 'Event("_set_value", {ref:ref_input1,value:""})'
     spec = event.set_value("input1", Var.create_safe("message"))
     assert (
-        format.format_event(spec) == 'E("_set_value", {ref:ref_input1,value:message})'
+        format.format_event(spec)
+        == 'Event("_set_value", {ref:ref_input1,value:message})'
     )
 
 
@@ -194,7 +195,7 @@ def test_set_cookie():
     assert spec.args[1][1].equals(Var.create_safe("testvalue"))
     assert (
         format.format_event(spec)
-        == 'E("_set_cookie", {key:"testkey",value:"testvalue"})'
+        == 'Event("_set_cookie", {key:"testkey",value:"testvalue"})'
     )
 
 
@@ -208,7 +209,8 @@ def test_remove_cookie():
     assert spec.args[1][0].equals(Var.create_safe("options"))
     assert spec.args[1][1].equals(Var.create_safe({}))
     assert (
-        format.format_event(spec) == 'E("_remove_cookie", {key:"testkey",options:{}})'
+        format.format_event(spec)
+        == 'Event("_remove_cookie", {key:"testkey",options:{}})'
     )
 
 
@@ -229,7 +231,7 @@ def test_remove_cookie_with_options():
     assert spec.args[1][1].equals(Var.create_safe(options))
     assert (
         format.format_event(spec)
-        == f'E("_remove_cookie", {{key:"testkey",options:{json.dumps(options)}}})'
+        == f'Event("_remove_cookie", {{key:"testkey",options:{json.dumps(options)}}})'
     )
 
 
@@ -244,7 +246,7 @@ def test_set_local_storage():
     assert spec.args[1][1].equals(Var.create_safe("testvalue"))
     assert (
         format.format_event(spec)
-        == 'E("_set_local_storage", {key:"testkey",value:"testvalue"})'
+        == 'Event("_set_local_storage", {key:"testkey",value:"testvalue"})'
     )
 
 
@@ -254,7 +256,7 @@ def test_clear_local_storage():
     assert isinstance(spec, EventSpec)
     assert spec.handler.fn.__qualname__ == "_clear_local_storage"
     assert not spec.args
-    assert format.format_event(spec) == 'E("_clear_local_storage", {})'
+    assert format.format_event(spec) == 'Event("_clear_local_storage", {})'
 
 
 def test_remove_local_storage():
@@ -264,4 +266,6 @@ def test_remove_local_storage():
     assert spec.handler.fn.__qualname__ == "_remove_local_storage"
     assert spec.args[0][0].equals(Var.create_safe("key"))
     assert spec.args[0][1].equals(Var.create_safe("testkey"))
-    assert format.format_event(spec) == 'E("_remove_local_storage", {key:"testkey"})'
+    assert (
+        format.format_event(spec) == 'Event("_remove_local_storage", {key:"testkey"})'
+    )

+ 7 - 4
tests/utils/test_utils.py

@@ -312,8 +312,10 @@ def test_format_route(route: str, format_case: bool, expected: bool):
             r'{{"a": "foo \"{ \"bar\" }\" baz", "b": val}}',
         ),
         (
-            EventChain(events=[EventSpec(handler=EventHandler(fn=mock_event))]),
-            '{_e => Event([E("mock_event", {})], _e)}',
+            EventChain(
+                events=[EventSpec(handler=EventHandler(fn=mock_event))], args_spec=None
+            ),
+            '{_e => addEvents([Event("mock_event", {})], _e)}',
         ),
         (
             EventChain(
@@ -322,9 +324,10 @@ def test_format_route(route: str, format_case: bool, expected: bool):
                         handler=EventHandler(fn=mock_event),
                         args=((Var.create_safe("arg"), EVENT_ARG.target.value),),
                     )
-                ]
+                ],
+                args_spec=None,
             ),
-            '{_e => Event([E("mock_event", {arg:_e.target.value})], _e)}',
+            '{_e => addEvents([Event("mock_event", {arg:_e.target.value})], _e)}',
         ),
         ({"a": "red", "b": "blue"}, '{{"a": "red", "b": "blue"}}'),
         (BaseVar(name="var", type_="int"), "{var}"),

Some files were not shown because too many files changed in this diff