New API to define triggers (#1820)

This commit is contained in:
Thomas Brandého 2023-09-21 18:47:22 +02:00 committed by GitHub
parent 84bae0dc7d
commit 211dc15995
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 513 additions and 316 deletions

View File

@ -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)

View File

@ -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)),
]

View File

@ -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>

View File

@ -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]
}
/***

View File

@ -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"),

View File

@ -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

View File

@ -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]:

View File

@ -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:

View File

@ -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

View File

@ -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],
}

View File

@ -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"}

View File

@ -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.
"""
...

View File

@ -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],
}

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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]:

View File

@ -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

View File

@ -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
@ -12,7 +12,7 @@ from reflex.event import EventHandler, EventChain, EventSpec
class NumberInput(ChakraComponent):
@overload
@classmethod
def create(cls, *children, value: Optional[Union[Var[int], int]] = None, allow_mouse_wheel: Optional[Union[Var[bool], bool]] = None, clamped_value_on_blur: Optional[Union[Var[bool], bool]] = None, default_value: Optional[Union[Var[int], int]] = None, error_border_color: Optional[Union[Var[str], str]] = None, focus_border_color: Optional[Union[Var[str], str]] = None, focus_input_on_change: Optional[Union[Var[bool], bool]] = None, input_mode: Optional[Union[Var[str], str]] = None, is_disabled: Optional[Union[Var[bool], bool]] = None, is_invalid: Optional[Union[Var[bool], bool]] = None, is_read_only: Optional[Union[Var[bool], bool]] = None, is_required: Optional[Union[Var[bool], bool]] = None, is_valid_character: Optional[Union[Var[str], str]] = None, keep_within_range: Optional[Union[Var[bool], bool]] = None, max_: Optional[Union[Var[int], int]] = None, min_: Optional[Union[Var[int], int]] = None, variant: Optional[Union[Var[str], str]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_change: 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) -> "NumberInput": # type: ignore
def create(cls, *children, value: Optional[Union[Var[Number], Number]] = None, allow_mouse_wheel: Optional[Union[Var[bool], bool]] = None, clamped_value_on_blur: Optional[Union[Var[bool], bool]] = None, default_value: Optional[Union[Var[Number], Number]] = None, error_border_color: Optional[Union[Var[str], str]] = None, focus_border_color: Optional[Union[Var[str], str]] = None, focus_input_on_change: Optional[Union[Var[bool], bool]] = None, input_mode: Optional[Union[Var[str], str]] = None, is_disabled: Optional[Union[Var[bool], bool]] = None, is_invalid: Optional[Union[Var[bool], bool]] = None, is_read_only: Optional[Union[Var[bool], bool]] = None, is_required: Optional[Union[Var[bool], bool]] = None, is_valid_character: Optional[Union[Var[str], str]] = None, keep_within_range: Optional[Union[Var[bool], bool]] = None, max_: Optional[Union[Var[Number], Number]] = None, min_: Optional[Union[Var[Number], Number]] = None, variant: Optional[Union[Var[str], str]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_change: 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) -> "NumberInput": # type: ignore
"""Create a number input component.
If no children are provided, a default stepper will be used.

View File

@ -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):

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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:

View File

@ -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],
}

View File

@ -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

View File

@ -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):

View File

@ -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:

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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: [],
}

View File

@ -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

View File

@ -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"

View File

@ -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 call_event_fn(fn: Callable, arg: Var) -> list[EventSpec]:
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.
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",
}

View File

@ -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 "",
")",
]

View File

@ -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.

View File

@ -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(", ")

View File

@ -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"]
)
assert (
'onError={_e => Event([E("ev_state.on_error", {})], _e)}'
'onLoad={(_e) => addEvents([Event("ev_state.on_load", {})], (_e))}'
in render_dict["props"]
)
assert (
'onError={(_e) => addEvents([Event("ev_state.on_error", {})], (_e))}'
in render_dict["props"]
)

View File

@ -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):

View File

@ -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"})'
)

View File

@ -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}"),