|
@@ -1,6 +1,7 @@
|
|
|
from __future__ import annotations
|
|
|
|
|
|
import re
|
|
|
+import warnings
|
|
|
from copy import deepcopy
|
|
|
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
|
|
|
|
|
@@ -38,7 +39,7 @@ class Element(Visibility):
|
|
|
self._classes: List[str] = []
|
|
|
self._style: Dict[str, str] = {}
|
|
|
self._props: Dict[str, Any] = {}
|
|
|
- self._event_listeners: List[EventListener] = []
|
|
|
+ self._event_listeners: Dict[str, EventListener] = {}
|
|
|
self._text: str = ''
|
|
|
self.slots: Dict[str, Slot] = {}
|
|
|
self.default_slot = self.add_slot('default')
|
|
@@ -71,64 +72,23 @@ class Element(Visibility):
|
|
|
def __exit__(self, *_):
|
|
|
self.default_slot.__exit__(*_)
|
|
|
|
|
|
- def _collect_event_dict(self) -> Dict[str, Dict]:
|
|
|
- events: Dict[str, Dict] = {}
|
|
|
- for listener in self._event_listeners:
|
|
|
- words = listener.type.split('.')
|
|
|
- type = words.pop(0)
|
|
|
- specials = [w for w in words if w in {'capture', 'once', 'passive'}]
|
|
|
- modifiers = [w for w in words if w in {'stop', 'prevent', 'self', 'ctrl', 'shift', 'alt', 'meta'}]
|
|
|
- keys = [w for w in words if w not in specials + modifiers]
|
|
|
- events[listener.type] = {
|
|
|
- 'listener_type': listener.type,
|
|
|
- 'type': type,
|
|
|
- 'specials': specials,
|
|
|
- 'modifiers': modifiers,
|
|
|
- 'keys': keys,
|
|
|
- 'args': list(set(events.get(listener.type, {}).get('args', []) + listener.args)),
|
|
|
- 'throttle': min(events.get(listener.type, {}).get('throttle', float('inf')), listener.throttle),
|
|
|
- }
|
|
|
- return events
|
|
|
-
|
|
|
def _collect_slot_dict(self) -> Dict[str, List[int]]:
|
|
|
return {
|
|
|
name: {'template': slot.template, 'ids': [child.id for child in slot.children]}
|
|
|
for name, slot in self.slots.items()
|
|
|
}
|
|
|
|
|
|
- def _to_dict(self, *keys: str) -> Dict:
|
|
|
- if not keys:
|
|
|
- return {
|
|
|
- 'id': self.id,
|
|
|
- 'tag': self.tag,
|
|
|
- 'class': self._classes,
|
|
|
- 'style': self._style,
|
|
|
- 'props': self._props,
|
|
|
- 'text': self._text,
|
|
|
- 'slots': self._collect_slot_dict(),
|
|
|
- 'events': self._collect_event_dict(),
|
|
|
- }
|
|
|
- dict_: Dict[str, Any] = {}
|
|
|
- for key in keys:
|
|
|
- if key == 'id':
|
|
|
- dict_['id'] = self.id
|
|
|
- elif key == 'tag':
|
|
|
- dict_['tag'] = self.tag
|
|
|
- elif key == 'class':
|
|
|
- dict_['class'] = self._classes
|
|
|
- elif key == 'style':
|
|
|
- dict_['style'] = self._style
|
|
|
- elif key == 'props':
|
|
|
- dict_['props'] = self._props
|
|
|
- elif key == 'text':
|
|
|
- dict_['text'] = self._text
|
|
|
- elif key == 'slots':
|
|
|
- dict_['slots'] = self._collect_slot_dict()
|
|
|
- elif key == 'events':
|
|
|
- dict_['events'] = self._collect_event_dict()
|
|
|
- else:
|
|
|
- raise ValueError(f'Unknown key {key}')
|
|
|
- return dict_
|
|
|
+ def _to_dict(self) -> Dict[str, Any]:
|
|
|
+ return {
|
|
|
+ 'id': self.id,
|
|
|
+ 'tag': self.tag,
|
|
|
+ 'class': self._classes,
|
|
|
+ 'style': self._style,
|
|
|
+ 'props': self._props,
|
|
|
+ 'text': self._text,
|
|
|
+ 'slots': self._collect_slot_dict(),
|
|
|
+ 'events': [listener.to_dict() for listener in self._event_listeners.values()],
|
|
|
+ }
|
|
|
|
|
|
def classes(self, add: Optional[str] = None, *, remove: Optional[str] = None, replace: Optional[str] = None) \
|
|
|
-> Self:
|
|
@@ -237,15 +197,17 @@ class Element(Visibility):
|
|
|
:param throttle: minimum time (in seconds) between event occurrences (default: 0.0)
|
|
|
"""
|
|
|
if handler:
|
|
|
- args = args if args is not None else ['*']
|
|
|
+ if args and '*' in args:
|
|
|
+ url = f'https://github.com/zauberzeug/nicegui/issues/644'
|
|
|
+ warnings.warn(DeprecationWarning(f'Event args "*" is deprecated, omit this parameter instead ({url})'))
|
|
|
+ args = None
|
|
|
listener = EventListener(element_id=self.id, type=type, args=args, handler=handler, throttle=throttle)
|
|
|
- self._event_listeners.append(listener)
|
|
|
+ self._event_listeners[listener.id] = listener
|
|
|
return self
|
|
|
|
|
|
def _handle_event(self, msg: Dict) -> None:
|
|
|
- for listener in self._event_listeners:
|
|
|
- if listener.type == msg['type']:
|
|
|
- events.handle_event(listener.handler, msg, sender=self)
|
|
|
+ listener = self._event_listeners[msg['listener_id']]
|
|
|
+ events.handle_event(listener.handler, msg, sender=self)
|
|
|
|
|
|
def update(self) -> None:
|
|
|
"""Update the element on the client side."""
|