11import sys
22import logging
3- import inspect
43
5- from ..utils .defaults import TrameDefault
6- from ..utils .formatter import to_pretty_html
4+ from trame_client .utils .defaults import TrameDefault
5+ from trame_client .utils .formatter import to_pretty_html
6+ from trame_common .obj .component import TrameComponent
77
88AVAILABLE_DIRECTIVES = [
99 ("v_text" , "v-text" ),
2525 ("v_memo" , "v-memo" ),
2626 ("v_cloak" , "v-cloak" ),
2727]
28+ KEY_ALIAS = [
29+ "enter" ,
30+ "tab" ,
31+ "delete" ,
32+ "esc" ,
33+ "space" ,
34+ "left" ,
35+ "up" ,
36+ "right" ,
37+ "down" ,
38+ ]
39+ KEY_MODIFIER = ["ctrl" , "alt" , "shift" , "meta" ]
40+ MOUSE_BUTTONS = ["left" , "right" , "middle" ]
41+ V_ON_MODIFIER = [
42+ "stop" ,
43+ "prevent" ,
44+ "capture" ,
45+ "self" ,
46+ "once" ,
47+ "left" ,
48+ "right" ,
49+ "middle" ,
50+ "passive" ,
51+ ] # *KEY_ALIAS
52+ V_ON_TYPE_MOUSE = [
53+ "click" ,
54+ "dblclick" ,
55+ "mousedown" ,
56+ "mouseup" ,
57+ "mousemove" ,
58+ "mouseover" ,
59+ "mouseout" ,
60+ "mousewheel" ,
61+ ]
62+ V_ON_TYPE_KEYBOARD = [
63+ "keydown" ,
64+ "keyup" ,
65+ "keypress" ,
66+ ]
67+ V_ON_TYPE_FORM = [
68+ "submit" ,
69+ "input" ,
70+ "change" ,
71+ "focus" ,
72+ "blur" ,
73+ ]
74+ V_ON_TYPE_TOUCH = [
75+ "touchstart" ,
76+ "touchmove" ,
77+ "touchend" ,
78+ "touchcancel" ,
79+ ]
80+ V_ON_TYPE_UI = [
81+ "scroll" ,
82+ "resize" ,
83+ "select" ,
84+ ]
85+ V_ON_TYPE_ANIM = [
86+ "animationstart" ,
87+ "animationend" ,
88+ "animationiteration" ,
89+ ]
90+ V_ON_TYPE_TRANSITION = [
91+ "transitionstart" ,
92+ "transitionend" ,
93+ "transitioncancel" ,
94+ ]
95+
2896
2997SHARED_ATTRIBUTES = [
3098 "accesskey" ,
57125 ["key" , ":key" ],
58126]
59127
128+ # !all modifiers should go through v_on_click_left_stop...
60129SHARED_EVENTS = [
61- "click" ,
62- "mousedown" ,
63- "mouseup" ,
64- "mouseenter" ,
65- "mouseleave" ,
130+ * V_ON_TYPE_MOUSE ,
131+ * V_ON_TYPE_KEYBOARD ,
132+ * V_ON_TYPE_FORM ,
133+ * V_ON_TYPE_TOUCH ,
134+ * V_ON_TYPE_UI ,
135+ * V_ON_TYPE_ANIM ,
136+ * V_ON_TYPE_TRANSITION ,
66137 "contextmenu" ,
67138]
68139
69140logger = logging .getLogger (__name__ )
70141
71142
72- def can_be_decorated (x ):
73- return inspect .ismethod (x ) or inspect .isfunction (x )
74-
75-
76143def py2js_key (key ):
77144 return key .replace ("_" , "-" )
78145
@@ -217,99 +284,33 @@ def __call__(self, layout=None, **kwargs):
217284 HTML_CTX .add_child (self )
218285
219286
220- class TrameComponent :
221- """
222- Base trame class that has access to a trame server instance
223- on which we provide simple accessor and method decoration capabilities.
224- """
225-
226- def __init__ (self , server , ctx_name = None , ** _ ):
227- """
228- Initialize TrameComponent with its server.
229-
230- Keyword arguments:
231- server -- the server to link to (default None)
232- ctx_name -- name to use to bind current instance to server.context (default None)
233- """
234- self ._server = server
235-
236- if ctx_name :
237- self .ctx [ctx_name ] = self
238-
239- self ._bind_annotated_methods ()
240-
241- @property
242- def server (self ):
243- """Return the associated trame server instance"""
244- return self ._server
245-
246- @property
247- def state (self ):
248- """Return the associated server state"""
249- return self .server .state
250-
251- @property
252- def ctrl (self ):
253- """Return the associated server controller"""
254- return self .server .controller
255-
256- @property
257- def ctx (self ):
258- """Return the associated server context"""
259- return self .server .context
260-
261- def _bind_annotated_methods (self ):
262- # Look for method decorator
263- for k in inspect .getmembers (self .__class__ , can_be_decorated ):
264- fn = getattr (self , k [0 ])
265-
266- # Handle @state.change
267- s_translator = self .state .translator
268- if "_trame_state_change" in fn .__dict__ :
269- state_change_names = fn .__dict__ ["_trame_state_change" ]
270- logger .debug (
271- f"state.change({ [f'{ s_translator .translate_key (v )} ' for v in state_change_names ]} )({ k [0 ]} )"
272- )
273- self .state .change (* [f"{ v } " for v in state_change_names ])(fn )
274-
275- # Handle @trigger
276- if "_trame_trigger_names" in fn .__dict__ :
277- trigger_names = fn .__dict__ ["_trame_trigger_names" ]
278- for trigger_name in trigger_names :
279- logger .debug (f"trigger({ trigger_name } )({ k [0 ]} )" )
280- self .server .trigger (f"{ trigger_name } " )(fn )
281-
282- # Handle @ctrl.[add, once, add_task, set]
283- if "_trame_controller" in fn .__dict__ :
284- actions = fn .__dict__ ["_trame_controller" ]
285- for action in actions :
286- name = action .get ("name" )
287- method = action .get ("method" )
288- decorate = getattr (self .ctrl , method )
289- logger .debug (f"ctrl.{ method } ({ name } )({ k [0 ]} )" )
290- decorate (name )(fn )
291-
292- def _unbind_annotated_methods (self ):
293- # Look for method decorator
294- for k in inspect .getmembers (self .__class__ , can_be_decorated ):
295- fn = getattr (self , k [0 ])
296-
297- # Handle @state.change
298- methods_to_detach = {}
299- if "_trame_state_change" in fn .__dict__ :
300- methods_to_detach .add (fn )
301-
302- if methods_to_detach :
303- for fn_list in self .state ._change_callbacks .values ():
304- to_remove = set (fn_list ) | methods_to_detach
305- for fn in to_remove :
306- fn_list .remove (fn )
307-
308- # Handle @trigger
309- # TODO
310-
311- # Handle @ctrl
312- # TODO
287+ def _event_value_processing (server , js_key , value ):
288+ if isinstance (value , str ):
289+ translated_value = server .state .translator .translate_js_expression (
290+ server .state , value
291+ )
292+ return f'{ js_key } ="{ translated_value } "'
293+ elif callable (value ):
294+ trigger_name = server .trigger_name (value )
295+ return f"{ js_key } =\" trigger('{ trigger_name } ')\" "
296+ elif isinstance (value , tuple ):
297+ trigger_name = value [0 ]
298+ if callable (trigger_name ):
299+ trigger_name = server .trigger_name (trigger_name )
300+ if len (value ) == 1 :
301+ return f"{ js_key } =\" trigger('{ trigger_name } ')\" "
302+ if len (value ) == 2 :
303+ translated_value = server .state .translator .translate_js_expression (
304+ server .state , value [1 ]
305+ )
306+ return f"{ js_key } =\" trigger('{ trigger_name } ', { translated_value } )\" "
307+ if len (value ) == 3 :
308+ translated_value = server .state .translator .translate_js_expression (
309+ server .state , value [1 ]
310+ )
311+ # We don't want to translate kwargs as we may change keys rather than just values
312+ return f"{ js_key } =\" trigger('{ trigger_name } ', { translated_value } , { value [2 ]} )\" "
313+ return False
313314
314315
315316class AbstractElement (TrameComponent ):
@@ -608,6 +609,7 @@ def events(self, *names):
608609 :param names: The names events to process
609610 :type names: *str
610611 """
612+ processed_event = set ()
611613 for _name in names :
612614 js_key = None
613615 name = _name
@@ -623,48 +625,44 @@ def events(self, *names):
623625 if value is None :
624626 continue
625627
626- if isinstance (value , str ):
627- translated_value = (
628- self .server .state .translator .translate_js_expression (
629- self .server .state , value
630- )
628+ attribute = _event_value_processing (self .server , js_key , value )
629+ if attribute is None :
630+ # no match
631+ pass
632+ elif isinstance (attribute , str ):
633+ self ._attributes [name ] = attribute
634+ processed_event .add (name )
635+ else :
636+ print (
637+ "Error: Don't know how to handle event name "
638+ f"'{ name } ' with value '{ value } ' in { self .__class__ } ::{ self ._elem_name } "
631639 )
632- self ._attributes [name ] = f'{ js_key } ="{ translated_value } "'
633- elif callable (value ):
634- trigger_name = self .server .trigger_name (value )
635- self ._attributes [name ] = f"{ js_key } =\" trigger('{ trigger_name } ')\" "
636- elif isinstance (value , tuple ):
637- trigger_name = value [0 ]
638- if callable (trigger_name ):
639- trigger_name = self .server .trigger_name (trigger_name )
640- if len (value ) == 1 :
641- self ._attributes [name ] = (
642- f"{ js_key } =\" trigger('{ trigger_name } ')\" "
643- )
644- if len (value ) == 2 :
645- translated_value = (
646- self .server .state .translator .translate_js_expression (
647- self .server .state , value [1 ]
648- )
649- )
650- self ._attributes [name ] = (
651- f"{ js_key } =\" trigger('{ trigger_name } ', { translated_value } )\" "
652- )
653- if len (value ) == 3 :
654- translated_value = (
655- self .server .state .translator .translate_js_expression (
656- self .server .state , value [1 ]
657- )
658- )
659- # We don't want to translate kwargs as we may change keys rather than just values
660- self ._attributes [name ] = (
661- f"{ js_key } =\" trigger('{ trigger_name } ', { translated_value } , { value [2 ]} )\" "
662- )
640+
641+ # process v_on_....
642+ for key_name in self ._py_attr :
643+ if key_name in processed_event :
644+ continue
645+
646+ if key_name .startswith ("v_on_" ):
647+ tokens = key_name .split ("_" )[2 :]
648+ js_key = f"@{ '.' .join (tokens )} "
649+ value = self ._py_attr [key_name ]
650+
651+ if value is None :
652+ continue
653+
654+ attribute = _event_value_processing (self .server , js_key , value )
655+ if attribute is None :
656+ # no match
657+ pass
658+ elif isinstance (attribute , str ):
659+ self ._attributes [name ] = attribute
663660 else :
664661 print (
665662 "Error: Don't know how to handle event name "
666663 f"'{ name } ' with value '{ value } ' in { self .__class__ } ::{ self ._elem_name } "
667664 )
665+
668666 return self
669667
670668 def clear (self ):
0 commit comments