New API to define triggers (#1820)
This commit is contained in:
parent
84bae0dc7d
commit
211dc15995
@ -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)
|
||||
|
@ -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)),
|
||||
]
|
@ -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>
|
||||
|
@ -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]
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -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"),
|
||||
|
@ -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
|
||||
|
@ -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]:
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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],
|
||||
}
|
||||
|
||||
|
||||
|
@ -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"}
|
@ -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.
|
||||
"""
|
||||
...
|
@ -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],
|
||||
}
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]:
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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],
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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: [],
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
100
reflex/event.py
100
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 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",
|
||||
}
|
||||
|
@ -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 "",
|
||||
")",
|
||||
]
|
||||
|
@ -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.
|
||||
|
@ -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(", ")
|
||||
|
@ -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"]
|
||||
)
|
||||
|
@ -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):
|
||||
|
@ -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"})'
|
||||
)
|
||||
|
@ -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}"),
|
||||
|
Loading…
Reference in New Issue
Block a user