123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- """Define event classes to connect the frontend and backend."""
- from __future__ import annotations
- import inspect
- from typing import Any, Callable, Dict, List, Set, Tuple
- from pynecone import utils
- from pynecone.base import Base
- from pynecone.var import BaseVar, Var
- class Event(Base):
- """An event that describes any state change in the app."""
- # The token to specify the client that the event is for.
- token: str
- # The event name.
- name: str
- # The routing data where event occurred
- router_data: Dict[str, Any] = {}
- # The event payload.
- payload: Dict[str, Any] = {}
- class EventHandler(Base):
- """An event handler responds to an event to update the state."""
- # The function to call in response to the event.
- fn: Callable
- class Config:
- """The Pydantic config."""
- # Needed to allow serialization of Callable.
- frozen = True
- def __call__(self, *args: Var) -> EventSpec:
- """Pass arguments to the handler to get an event spec.
- This method configures event handlers that take in arguments.
- Args:
- *args: The arguments to pass to the handler.
- Returns:
- The event spec, containing both the function and args.
- Raises:
- TypeError: If the arguments are invalid.
- """
- # Get the function args.
- fn_args = inspect.getfullargspec(self.fn).args[1:]
- # Construct the payload.
- values = []
- for arg in args:
- # If it is a Var, add the full name.
- if isinstance(arg, Var):
- values.append(arg.full_name)
- continue
- if isinstance(arg, FileUpload):
- return EventSpec(handler=self, upload=True)
- # Otherwise, convert to JSON.
- try:
- values.append(utils.json_dumps(arg))
- except TypeError as e:
- raise TypeError(
- f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
- ) from e
- payload = tuple(zip(fn_args, values))
- # Return the event spec.
- return EventSpec(handler=self, args=payload)
- class EventSpec(Base):
- """An event specification.
- Whereas an Event object is passed during runtime, a spec is used
- during compile time to outline the structure of an event.
- """
- # The event handler.
- handler: EventHandler
- # The local arguments on the frontend.
- local_args: Tuple[str, ...] = ()
- # The arguments to pass to the function.
- args: Tuple[Any, ...] = ()
- # Whether to upload files.
- upload: bool = False
- class Config:
- """The Pydantic config."""
- # Required to allow tuple fields.
- frozen = True
- class EventChain(Base):
- """Container for a chain of events that will be executed in order."""
- events: List[EventSpec]
- class Target(Base):
- """A Javascript event target."""
- checked: bool = False
- value: Any = None
- class FrontendEvent(Base):
- """A Javascript event."""
- target: Target = Target()
- key: str = ""
- # The default event argument.
- EVENT_ARG = BaseVar(name="_e", type_=FrontendEvent, is_local=True)
- class FileUpload(Base):
- """Class to represent a file upload."""
- pass
- # Special server-side events.
- def redirect(path: str) -> EventSpec:
- """Redirect to a new path.
- Args:
- path: The path to redirect to.
- Returns:
- An event to redirect to the path.
- """
- def fn():
- return None
- fn.__qualname__ = "_redirect"
- return EventSpec(
- handler=EventHandler(fn=fn),
- args=(("path", path),),
- )
- def console_log(message: str) -> EventSpec:
- """Do a console.log on the browser.
- Args:
- message: The message to log.
- Returns:
- An event to log the message.
- """
- def fn():
- return None
- fn.__qualname__ = "_console"
- return EventSpec(
- handler=EventHandler(fn=fn),
- args=(("message", message),),
- )
- def window_alert(message: str) -> EventSpec:
- """Create a window alert on the browser.
- Args:
- message: The message to alert.
- Returns:
- An event to alert the message.
- """
- def fn():
- return None
- fn.__qualname__ = "_alert"
- return EventSpec(
- handler=EventHandler(fn=fn),
- args=(("message", message),),
- )
- # 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",
- }
|