Skip to content

Commit 326191e

Browse files
committed
remove Events() object
1 parent 793e6b5 commit 326191e

File tree

10 files changed

+121
-222
lines changed

10 files changed

+121
-222
lines changed

src/client/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/idom/__init__.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from . import config, html, log, web
1414
from .core import hooks
1515
from .core.component import Component, component
16-
from .core.events import Events, event
16+
from .core.events import EventHandler, event
1717
from .core.layout import Layout
1818
from .core.vdom import VdomDict, vdom
1919
from .server.prefab import run
@@ -22,21 +22,21 @@
2222

2323

2424
__all__ = [
25-
"config",
26-
"html",
27-
"log",
28-
"web",
29-
"hooks",
30-
"Component",
3125
"component",
32-
"Events",
26+
"Component",
27+
"config",
3328
"event",
34-
"Layout",
35-
"VdomDict",
36-
"vdom",
37-
"run",
38-
"Ref",
39-
"html_to_vdom",
29+
"EventHandler",
30+
"hooks",
4031
"hotswap",
32+
"html_to_vdom",
33+
"html",
34+
"Layout",
35+
"log",
4136
"multiview",
37+
"Ref",
38+
"run",
39+
"vdom",
40+
"VdomDict",
41+
"web",
4242
]

src/idom/core/events.py

Lines changed: 38 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
from __future__ import annotations
66

77
import asyncio
8-
from typing import Any, Callable, Iterator, List, Mapping, Optional, Sequence
8+
from typing import Any, Callable, List, Optional, Sequence
99

1010
from anyio import create_task_group
1111

12-
from idom.core.proto import EventHandlerDict, EventHandlerFunc, EventHandlerType
12+
from idom.core.proto import EventHandlerFunc, EventHandlerType
1313

1414

1515
def event(
@@ -47,7 +47,7 @@ def my_callback(*data):
4747

4848
def setup(function: Callable[..., Any]) -> EventHandler:
4949
return EventHandler(
50-
to_event_handler_function(function),
50+
to_event_handler_function(function, positional_args=True),
5151
stop_propagation,
5252
prevent_default,
5353
)
@@ -84,152 +84,63 @@ def __init__(
8484
prevent_default: bool = False,
8585
target: Optional[str] = None,
8686
) -> None:
87-
self.function = function
87+
self.function = to_event_handler_function(function, positional_args=False)
8888
self.prevent_default = prevent_default
8989
self.stop_propagation = stop_propagation
9090
self.target = target
9191

92+
def __eq__(self, other: Any) -> bool:
93+
for slot in self.__slots__:
94+
if not slot.startswith("_"):
95+
if not hasattr(other, slot):
96+
return False
97+
elif not getattr(other, slot) == getattr(self, slot):
98+
return False
99+
return True
100+
92101
def __repr__(self) -> str:
93102
public_names = [name for name in self.__slots__ if not name.startswith("_")]
94103
items = ", ".join([f"{n}={getattr(self, n)!r}" for n in public_names])
95104
return f"{type(self).__name__}({items})"
96105

97106

98-
async def _no_op(data: List[Any]) -> None:
99-
return None
100-
101-
102-
class Events(Mapping[str, EventHandler]):
103-
"""A container for event handlers.
104-
105-
Assign this object to the ``"eventHandlers"`` field of an element model.
106-
"""
107-
108-
__slots__ = "_handlers"
109-
110-
def __init__(self) -> None:
111-
self._handlers: EventHandlerDict = {}
112-
113-
def on(
114-
self,
115-
event: str,
116-
stop_propagation: Optional[bool] = None,
117-
prevent_default: Optional[bool] = None,
118-
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
119-
"""A decorator for adding an event handler.
120-
121-
Parameters:
122-
event:
123-
The camel-case name of the event, the word "on" is automatically
124-
prepended. So passing "keyDown" would refer to the event "onKeyDown".
125-
stop_propagation:
126-
Block the event from propagating further up the DOM.
127-
prevent_default:
128-
Stops the default action associate with the event from taking place.
129-
130-
Returns:
131-
A decorator which accepts an event handler function as its first argument.
132-
The parameters of the event handler function may indicate event attributes
133-
which should be sent back from the frontend. See :class:`EventHandler` for
134-
more info.
135-
136-
Examples:
137-
Simple "onClick" event handler:
138-
139-
.. code-block:: python
140-
141-
def clickable_element():
142-
events = Events()
143-
144-
@events.on("click")
145-
def handler(event):
146-
# do something on a click event
147-
...
148-
149-
return idom.vdom("button", "hello!", eventHandlers=events)
150-
"""
151-
if not event.startswith("on"):
152-
event = "on" + event[:1].upper() + event[1:]
153-
154-
if event not in self._handlers:
155-
# do this so it's possible to stop event propagation or default behavior
156-
# without making the user have to pass a no op event handler themselves
157-
self._handlers[event] = EventHandler(
158-
_no_op,
159-
stop_propagation,
160-
prevent_default,
161-
)
162-
163-
def setup(function: Callable[..., Any]) -> Callable[..., Any]:
164-
old_handler = self._handlers[event]
165-
166-
if old_handler.function is _no_op:
167-
return EventHandler(
168-
to_event_handler_function(function),
169-
bool(stop_propagation),
170-
bool(prevent_default),
171-
)
172-
173-
new_stop_propagation = (
174-
old_handler.stop_propagation
175-
if stop_propagation is None
176-
else stop_propagation
177-
)
178-
new_prevent_default = (
179-
old_handler.prevent_default
180-
if prevent_default is None
181-
else prevent_default
182-
)
183-
184-
self._handlers[event] = merge_event_handlers(
185-
[
186-
old_handler,
187-
EventHandler(
188-
to_event_handler_function(function),
189-
new_stop_propagation,
190-
new_prevent_default,
191-
),
192-
]
193-
)
194-
195-
return function
196-
197-
return setup
198-
199-
def __contains__(self, key: Any) -> bool:
200-
return key in self._handlers
201-
202-
def __len__(self) -> int:
203-
return len(self._handlers)
204-
205-
def __iter__(self) -> Iterator[str]:
206-
return iter(self._handlers)
207-
208-
def __getitem__(self, key: str) -> EventHandler:
209-
return self._handlers[key]
210-
211-
def __repr__(self) -> str: # pragma: no cover
212-
return repr(self._handlers)
213-
214-
215-
def to_event_handler_function(function: Callable[..., Any]) -> EventHandlerFunc:
107+
def to_event_handler_function(
108+
function: Callable[..., Any],
109+
positional_args: bool = True,
110+
) -> EventHandlerFunc:
216111
"""Make a :data:`~idom.core.proto.EventHandlerFunc` from a function or coroutine
217112
218113
Parameters:
219114
function:
220115
A function or coroutine accepting a number of positional arguments.
116+
positional_args:
117+
Whether to pass the event parameters a positional args or as a list.
221118
"""
222-
if asyncio.iscoroutinefunction(function):
223-
return lambda data: function(*data)
224-
else:
119+
if positional_args:
120+
if asyncio.iscoroutinefunction(function):
121+
122+
async def wrapper(data: List[Any]) -> None:
123+
await function(*data)
124+
125+
else:
126+
127+
async def wrapper(data: List[Any]) -> None:
128+
function(*data)
129+
130+
return wrapper
131+
elif not asyncio.iscoroutinefunction(function):
225132

226133
async def wrapper(data: List[Any]) -> None:
227-
return function(*data)
134+
function(data)
228135

229136
return wrapper
137+
else:
138+
return function
230139

231140

232-
def merge_event_handlers(event_handlers: Sequence[EventHandlerType]) -> EventHandler:
141+
def merge_event_handlers(
142+
event_handlers: Sequence[EventHandlerType],
143+
) -> EventHandlerType:
233144
"""Merge multiple event handlers into one
234145
235146
Raises a ValueError if any handlers have conflicting

src/idom/core/proto.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from typing import (
1010
TYPE_CHECKING,
1111
Any,
12-
Awaitable,
1312
Callable,
1413
Dict,
1514
List,
@@ -71,8 +70,12 @@ def __exit__(
7170
EventHandlerDict = Dict[str, "EventHandlerType"]
7271
"""A dict mapping between event names to their handlers"""
7372

74-
EventHandlerFunc = Callable[[List[Any]], Awaitable[None]]
75-
"""A coroutine which can handle event data"""
73+
74+
class EventHandlerFunc(Protocol):
75+
"""A coroutine which can handle event data"""
76+
77+
async def __call__(self, data: List[Any]) -> None:
78+
...
7679

7780

7881
@runtime_checkable

src/idom/core/vdom.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,15 @@ def separate_attributes_and_event_handlers(
237237
attributes: Mapping[str, Any], event_handlers: EventHandlerMapping
238238
) -> Tuple[Dict[str, Any], EventHandlerDict]:
239239
separated_attributes = {}
240-
separated_event_handlers: Dict[str, List[EventHandler]] = {}
240+
separated_event_handlers: Dict[str, List[EventHandlerType]] = {}
241241

242242
for k, v in event_handlers.items():
243243
separated_event_handlers[k] = [v]
244244

245245
for k, v in attributes.items():
246+
247+
handler: EventHandlerType
248+
246249
if callable(v):
247250
handler = EventHandler(to_event_handler_function(v))
248251
elif (

src/idom/testing.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,12 +279,17 @@ def Child(key):
279279
def __init__(self) -> None:
280280
self.target = uuid4().hex
281281

282-
def use(self, function: Callable[..., Any], *args, **kwargs) -> EventHandler:
282+
def use(
283+
self,
284+
function: Callable[..., Any],
285+
stop_propagation: bool = False,
286+
prevent_default: bool = False,
287+
) -> EventHandler:
283288
return EventHandler(
284289
to_event_handler_function(function),
285-
*args,
286-
target=self.target,
287-
**kwargs,
290+
stop_propagation,
291+
prevent_default,
292+
self.target,
288293
)
289294

290295

src/idom/widgets.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,14 @@ def Input(
5353
attrs = attributes or {}
5454
value, set_value = idom.hooks.use_state(value)
5555

56-
events = idom.Events()
57-
58-
@events.on("change")
5956
def on_change(event: Dict[str, Any]) -> None:
6057
value = event["value"]
6158
set_value(value)
6259
if not value and ignore_empty:
6360
return
6461
callback(value if cast is None else cast(value))
6562

66-
return html.input({"type": type, "value": value, **attrs}, event_handlers=events)
63+
return html.input({**attrs, "type": type, "value": value, "onChange": on_change})
6764

6865

6966
MountFunc = Callable[[ComponentConstructor], None]

0 commit comments

Comments
 (0)