Skip to content

Commit 3154987

Browse files
authored
add window_event_listener (#5568)
* add window_event_listener * add storage and popstate event listener
1 parent 9c6cb12 commit 3154987

4 files changed

Lines changed: 109 additions & 2 deletions

File tree

pyi_hashes.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"reflex/__init__.pyi": "c69e4120c505941af8087b9c18df6121",
2+
"reflex/__init__.pyi": "8f9482b205f5a33a59f748006ded637b",
33
"reflex/components/__init__.pyi": "ac05995852baa81062ba3d18fbc489fb",
44
"reflex/components/base/__init__.pyi": "16e47bf19e0d62835a605baa3d039c5a",
55
"reflex/components/base/app_wrap.pyi": "ae600e2cc9d70f2ce613bdd6c1da3b16",
@@ -11,7 +11,7 @@
1111
"reflex/components/base/meta.pyi": "0445c66fbc32671c795640ef1a4827e9",
1212
"reflex/components/base/script.pyi": "43a0e21f257b10d2c76ed359284a9d80",
1313
"reflex/components/base/strict_mode.pyi": "d169c575d676c73edc6a3f593badfd1f",
14-
"reflex/components/core/__init__.pyi": "6419485660830fe0af9c9c5715a4ed03",
14+
"reflex/components/core/__init__.pyi": "007170b97e58bdf28b2aee381d91c0c7",
1515
"reflex/components/core/auto_scroll.pyi": "c628ed503c7bfcee0dd05cf48d5b763d",
1616
"reflex/components/core/banner.pyi": "407352aa1833b80b21d30647ec7717d8",
1717
"reflex/components/core/client_side_routing.pyi": "c3d38a1de89cfcd76735a1559e99ed05",
@@ -21,6 +21,7 @@
2121
"reflex/components/core/html.pyi": "faf9bb353ef4784e7f17ac97c93ef697",
2222
"reflex/components/core/sticky.pyi": "cdf17e6cd287e7300acd25669701d117",
2323
"reflex/components/core/upload.pyi": "f9be9b74d97d841b53b963d8704d5809",
24+
"reflex/components/core/window_events.pyi": "04be3d73886d12774498004b712c136e",
2425
"reflex/components/datadisplay/__init__.pyi": "52755871369acbfd3a96b46b9a11d32e",
2526
"reflex/components/datadisplay/code.pyi": "3787ca724cae7b29d57ea03f981b8c22",
2627
"reflex/components/datadisplay/dataeditor.pyi": "23f777b8a46eff2afd95035dd5fc51a7",

reflex/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@
249249
"upload",
250250
],
251251
"components.core.auto_scroll": ["auto_scroll"],
252+
"components.core.window_events": ["window_event_listener"],
252253
}
253254

254255
COMPONENTS_BASE_MAPPING: dict = {

reflex/components/core/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"selected_files",
5151
],
5252
"auto_scroll": ["auto_scroll"],
53+
"window_events": ["WindowEventListener", "window_event_listener"],
5354
}
5455

5556
__getattr__, __dir__, __all__ = lazy_loader.attach(
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"""Window event listener component for Reflex."""
2+
3+
import reflex as rx
4+
from reflex.components.base.fragment import Fragment
5+
from reflex.constants.compiler import Hooks
6+
from reflex.event import key_event, no_args_event_spec
7+
from reflex.vars.base import Var, VarData
8+
from reflex.vars.object import ObjectVar
9+
10+
11+
def _on_resize_spec() -> tuple[Var[int], Var[int]]:
12+
"""Args spec for the on_resize event trigger.
13+
14+
Returns:
15+
A tuple containing window width and height variables.
16+
"""
17+
return (Var("window.innerWidth"), Var("window.innerHeight"))
18+
19+
20+
def _on_scroll_spec() -> tuple[Var[float], Var[float]]:
21+
"""Args spec for the on_scroll event trigger.
22+
23+
Returns:
24+
A tuple containing window scroll X and Y position variables.
25+
"""
26+
return (Var("window.scrollX"), Var("window.scrollY"))
27+
28+
29+
def _on_visibility_change_spec() -> tuple[Var[bool]]:
30+
"""Args spec for the on_visibility_change event trigger.
31+
32+
Returns:
33+
A tuple containing the document hidden state variable.
34+
"""
35+
return (Var("document.hidden"),)
36+
37+
38+
def _on_storage_spec(e: ObjectVar) -> tuple[Var[str], Var[str], Var[str], Var[str]]:
39+
"""Args spec for the on_storage event trigger.
40+
41+
Args:
42+
e: The storage event.
43+
44+
Returns:
45+
A tuple containing key, old value, new value, and URL variables.
46+
"""
47+
return (e.key.to(str), e.oldValue.to(str), e.newValue.to(str), e.url.to(str))
48+
49+
50+
class WindowEventListener(Fragment):
51+
"""A component that listens for window events."""
52+
53+
# Event handlers
54+
on_resize: rx.EventHandler[_on_resize_spec]
55+
on_scroll: rx.EventHandler[_on_scroll_spec]
56+
on_focus: rx.EventHandler[no_args_event_spec]
57+
on_blur: rx.EventHandler[no_args_event_spec]
58+
on_visibility_change: rx.EventHandler[_on_visibility_change_spec]
59+
on_before_unload: rx.EventHandler[no_args_event_spec]
60+
on_key_down: rx.EventHandler[key_event]
61+
on_popstate: rx.EventHandler[no_args_event_spec]
62+
on_storage: rx.EventHandler[_on_storage_spec]
63+
64+
def _exclude_props(self) -> list[str]:
65+
"""Exclude event handler props from being passed to Fragment.
66+
67+
Returns:
68+
List of prop names to exclude from the Fragment.
69+
"""
70+
return [*super()._exclude_props(), *self.event_triggers.keys()]
71+
72+
def add_hooks(self) -> list[str | Var[str]]:
73+
"""Add hooks to register window event listeners.
74+
75+
Returns:
76+
The hooks to add to the component.
77+
"""
78+
hooks = []
79+
80+
for prop_name, event_trigger in self.event_triggers.items():
81+
# Get JS event name: remove on_ prefix and underscores
82+
event_name = prop_name.removeprefix("on_").replace("_", "")
83+
84+
hook_expr = f"""
85+
useEffect(() => {{
86+
if (typeof window === 'undefined') return;
87+
88+
window.addEventListener('{event_name}', {event_trigger});
89+
return () => window.removeEventListener('{event_name}', {event_trigger});
90+
}}, []);
91+
"""
92+
93+
hooks.append(
94+
Var(
95+
hook_expr,
96+
_var_type="str",
97+
_var_data=VarData(position=Hooks.HookPosition.POST_TRIGGER),
98+
)
99+
)
100+
101+
return hooks
102+
103+
104+
window_event_listener = WindowEventListener.create

0 commit comments

Comments
 (0)