1212
1313P = typing_extensions .ParamSpec ("P" )
1414
15-
16- def _widget_from_signature (classname , base_class : Type [widgets .Widget ], func : Callable [..., None ], event_prefix : str ) -> Type [widgets .Widget ]:
15+ default_to_json = widgets .widget_serialization ["to_json" ]
16+ default_from_json = widgets .widget_serialization ["from_json" ]
17+
18+
19+ def _widget_from_signature (
20+ classname ,
21+ base_class : Type [widgets .Widget ],
22+ func : Callable [..., None ],
23+ event_prefix : str ,
24+ tags : Dict [str , Any ],
25+ to_json : Dict [str , Callable [[Any , widgets .Widget ], Any ]],
26+ from_json : Dict [str , Callable [[Any , widgets .Widget ], Any ]],
27+ ) -> Type [widgets .Widget ]:
1728 classprops : Dict [str , Any ] = {}
1829
1930 parameters = inspect .signature (func ).parameters
@@ -38,15 +49,23 @@ def event_handler(self, data, buffers=None, event_name=event_name, param=param):
3849 trait = traitlets .Any ()
3950 else :
4051 trait = traitlets .Any (default_value = param .default )
41- classprops [name ] = trait .tag (sync = True , ** widgets .widget_serialization )
52+ tag = dict (sync = True , to_json = to_json .get (name , default_to_json ), from_json = from_json .get (name , default_from_json ))
53+ tag .update (** tags .get (name , {}))
54+ classprops [name ] = trait .tag (** tag )
4255 # maps event_foo to a callable
4356 classprops ["_event_callbacks" ] = traitlets .Dict (default_value = {})
4457
4558 widget_class = type (classname , (base_class ,), classprops )
4659 return widget_class
4760
4861
49- def _widget_vue (vue_path : str , vuetify = True ) -> Callable [[Callable [P , None ]], Type [v .VuetifyTemplate ]]:
62+ def _widget_vue (
63+ vue_path : str ,
64+ vuetify = True ,
65+ to_json : Dict [str , Callable [[Any , widgets .Widget ], Any ]] = {},
66+ from_json : Dict [str , Callable [[Any , widgets .Widget ], Any ]] = {},
67+ tags : Dict [str , Any ] = {},
68+ ) -> Callable [[Callable [P , None ]], Type [v .VuetifyTemplate ]]:
5069 def decorator (func : Callable [P , None ]):
5170 class VuetifyWidgetSolara (v .VuetifyTemplate ):
5271 template_file = (os .path .abspath (inspect .getfile (func )), vue_path )
@@ -55,14 +74,20 @@ class VueWidgetSolara(vue.VueTemplate):
5574 template_file = (os .path .abspath (inspect .getfile (func )), vue_path )
5675
5776 base_class = VuetifyWidgetSolara if vuetify else VueWidgetSolara
58- widget_class = _widget_from_signature ("VueWidgetSolaraSub" , base_class , func , "vue_" )
77+ widget_class = _widget_from_signature ("VueWidgetSolaraSub" , base_class , func , "vue_" , to_json = to_json , from_json = from_json , tags = tags )
5978
6079 return widget_class
6180
6281 return decorator
6382
6483
65- def component_vue (vue_path : str , vuetify = True ) -> Callable [[Callable [P , None ]], Callable [P , solara .Element ]]:
84+ def component_vue (
85+ vue_path : str ,
86+ vuetify = True ,
87+ tags : Dict [str , Any ] = {},
88+ to_json : Dict [str , Callable [[Any , widgets .Widget ], Any ]] = {},
89+ from_json : Dict [str , Callable [[Any , widgets .Widget ], Any ]] = {},
90+ ) -> Callable [[Callable [P , None ]], Callable [P , solara .Element ]]:
6691 """Decorator to create a component backed by a Vue template.
6792
6893 Although many components can be made from the Python side, sometimes it is easier to write components using Vue directly.
@@ -74,14 +99,19 @@ def component_vue(vue_path: str, vuetify=True) -> Callable[[Callable[P, None]],
7499 are assumed by refer to the same vue property, with `on_foo` being the event handler when `foo` changes from
75100 the vue template.
76101
77- Arguments or the form `event_foo` should be callbacks that can be called from the vue template. They are
102+ Arguments of the form `event_foo` should be callbacks that can be called from the vue template. They are
78103 available as the function `foo` in the vue template.
79104
80105 [See the vue v2 api](https://v2.vuejs.org/v2/api/) for more information on how to use Vue, like `watch`,
81106 `methods` and lifecycle hooks such as `mounted` and `destroyed`.
82107
83108 See the [Vue component example](/documentation/examples/general/vue_component) for an example of how to use this decorator.
84109
110+ The underlying trait can be passed extra arguments by passing a dictionary to the `tags` argument.
111+ The most common case is to pass a custom serializer and deserializer for the trait, for which we added the
112+ strictly typed `to_json` and `from_json` arguments.
113+ Otherwise pass a dictionary to the `tags` argument, see the example below for more details.
114+
85115
86116 ## Examples
87117
@@ -105,6 +135,28 @@ def MyDateComponent(month: int, event_date_clicked: Callable):
105135 pass
106136 ```
107137
138+ ## Example with custom serializer and deserializer
139+
140+ ```python
141+ import solara
142+
143+ def to_json_datetime(value: datetime.date, widget: widgets.Widget) -> str:
144+ return value.isoformat()
145+
146+ def from_json_datetime(value: str, widget: widgets.Widget) -> datetime.date:
147+ return datetime.date.fromisoformat(value)
148+
149+ @solara.component_vue("my_date_component.vue", to_json={"month": to_json_datetime}, from_json={"month": from_json_datetime})
150+ def MyDateComponent(month: datetime.date, event_date_clicked: Callable):
151+ pass
152+
153+ # the following will be the same, except that it is less strictly typed
154+ @solara.component_vue("my_date_component.vue", tags={"month": {"to_json": to_json_datetime, "from_json": from_json_datetime}})
155+ def MyDateComponentSame(month: datetime.date, event_date_clicked: Callable):
156+ pass
157+
158+ ```
159+
108160 ## Arguments
109161
110162 * `vue_path`: The path to the Vue template file.
@@ -113,7 +165,7 @@ def MyDateComponent(month: int, event_date_clicked: Callable):
113165 """
114166
115167 def decorator (func : Callable [P , None ]):
116- VueWidgetSolaraSub = _widget_vue (vue_path , vuetify = vuetify )(func )
168+ VueWidgetSolaraSub = _widget_vue (vue_path , vuetify = vuetify , to_json = to_json , from_json = from_json , tags = tags )(func )
117169
118170 def wrapper (* args , ** kwargs ):
119171 event_callbacks = {}
0 commit comments