instaui 0.1.8__py3-none-any.whl → 0.1.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. instaui/_helper/observable_helper.py +11 -1
  2. instaui/arco/components/select.py +4 -1
  3. instaui/arco/static/instaui-arco.css +1 -1
  4. instaui/arco/static/instaui-arco.js +55771 -55771
  5. instaui/components/echarts/echarts.js +7 -5
  6. instaui/components/echarts/echarts.py +1 -1
  7. instaui/components/element.py +12 -22
  8. instaui/components/grid.py +63 -11
  9. instaui/components/html/paragraph.py +10 -0
  10. instaui/components/html/select.py +0 -5
  11. instaui/components/html/table.py +1 -1
  12. instaui/components/label.py +5 -0
  13. instaui/components/row.py +0 -3
  14. instaui/components/shiki_code/static/shiki-style.css +179 -179
  15. instaui/event/js_event.py +24 -0
  16. instaui/event/vue_event.py +66 -0
  17. instaui/event/web_event.py +36 -25
  18. instaui/experimental/__init__.py +1 -2
  19. instaui/handlers/_utils.py +27 -5
  20. instaui/shadcn_classless/static/shadcn-classless.css +403 -403
  21. instaui/spa_router/_file_base_utils.py +20 -16
  22. instaui/static/insta-ui.css +1 -1
  23. instaui/static/insta-ui.esm-browser.prod.js +3683 -3663
  24. instaui/static/insta-ui.js.map +1 -1
  25. instaui/systems/func_system.py +15 -0
  26. instaui/template/webview_template.py +0 -2
  27. instaui/ui/__init__.py +5 -1
  28. instaui/ui/__init__.pyi +5 -1
  29. instaui/ui_functions/ui_page.py +3 -3
  30. instaui/vars/js_computed.py +25 -1
  31. instaui/vars/state.py +15 -0
  32. instaui/vars/web_computed.py +42 -0
  33. instaui/watch/js_watch.py +37 -1
  34. instaui/watch/web_watch.py +53 -0
  35. instaui/webview/__init__.py +1 -0
  36. instaui/webview/index.py +0 -1
  37. instaui-0.1.10.dist-info/METADATA +153 -0
  38. {instaui-0.1.8.dist-info → instaui-0.1.10.dist-info}/RECORD +70 -73
  39. {instaui-0.1.8.dist-info → instaui-0.1.10.dist-info}/WHEEL +1 -1
  40. instaui/experimental/link_sql/__init__.py +0 -3
  41. instaui/experimental/link_sql/_base.py +0 -23
  42. instaui/experimental/link_sql/_duckdb.py +0 -221
  43. instaui/experimental/link_sql/_types.py +0 -15
  44. instaui/experimental/link_sql/data_source.js +0 -50
  45. instaui-0.1.8.dist-info/METADATA +0 -160
  46. {instaui-0.1.8.dist-info → instaui-0.1.10.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,66 @@
1
+ import typing
2
+ from instaui.common.jsonable import Jsonable
3
+ from instaui.vars.mixin_types.observable import ObservableMixin
4
+ from .event_mixin import EventMixin
5
+
6
+
7
+ class VueEvent(Jsonable, EventMixin):
8
+ """
9
+ Create an event object that can be bound to a UI component's event listener.
10
+
11
+ This function generates a callable event handler with optional contextual bindings.
12
+ The event logic is defined via a code string, which can reference bound variables.
13
+
14
+ Args:
15
+ code (str): A string containing the executable logic for the event handler.
16
+ Typically contains a function body or expression that utilizes bound variables.
17
+ bindings (typing.Optional[typing.Dict[str, typing.Any]], optional): A dictionary mapping variable names to values that should be available in the
18
+ event handler's context. If None, no additional bindings are created.. Defaults to None.
19
+
20
+ Example:
21
+ .. code-block:: python
22
+ a = ui.state(1)
23
+
24
+ event = ui.vue_event(bindings={"a": a}, code=r'''()=> { a.value +=1}''')
25
+
26
+ html.span(a)
27
+ html.button("plus").on("click", event)
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ *,
33
+ code: str,
34
+ bindings: typing.Optional[typing.Dict[str, typing.Any]] = None,
35
+ ):
36
+ self.code = code
37
+ self._bindings = bindings
38
+
39
+ if bindings:
40
+ bindData = [
41
+ int(not isinstance(v, ObservableMixin)) for v in bindings.values()
42
+ ]
43
+
44
+ if sum(bindData) > 0:
45
+ self.bindData = bindData
46
+
47
+ self.bind = {
48
+ k: typing.cast(ObservableMixin, v)._to_observable_config()
49
+ if isinstance(v, ObservableMixin)
50
+ else v
51
+ for k, v in bindings.items()
52
+ }
53
+
54
+ def copy_with_extends(self, extends: typing.Dict):
55
+ raise NotImplementedError("VueEvent does not support extends")
56
+
57
+ def event_type(self):
58
+ return "vue"
59
+
60
+ def _to_json_dict(self):
61
+ data = super()._to_json_dict()
62
+ data["type"] = self.event_type()
63
+ return data
64
+
65
+
66
+ vue_event = VueEvent
@@ -20,8 +20,8 @@ class WebEvent(Jsonable, EventMixin, typing.Generic[P, R]):
20
20
  def __init__(
21
21
  self,
22
22
  fn: typing.Callable[P, R],
23
- inputs: typing.List[CanInputMixin],
24
- outputs: typing.List[CanOutputMixin],
23
+ inputs: typing.Sequence[CanInputMixin],
24
+ outputs: typing.Sequence[CanOutputMixin],
25
25
  ):
26
26
  self._inputs = inputs
27
27
  self._outputs = outputs
@@ -36,7 +36,7 @@ class WebEvent(Jsonable, EventMixin, typing.Generic[P, R]):
36
36
  def copy_with_extends(self, extends: typing.Sequence[CanInputMixin]):
37
37
  return WebEvent(
38
38
  fn=self._fn,
39
- inputs=self._inputs + list(extends),
39
+ inputs=list(self._inputs) + list(extends),
40
40
  outputs=self._outputs,
41
41
  )
42
42
 
@@ -76,32 +76,43 @@ class WebEvent(Jsonable, EventMixin, typing.Generic[P, R]):
76
76
  return data
77
77
 
78
78
 
79
- @typing.overload
80
- def ui_event(fn: typing.Callable[P, R]) -> WebEvent[P, R]: ...
79
+ def event(
80
+ *,
81
+ inputs: typing.Optional[typing.Sequence] = None,
82
+ outputs: typing.Optional[typing.Sequence] = None,
83
+ ):
84
+ """
85
+ Creates an event handler decorator for binding reactive logic to component events.
81
86
 
87
+ Args:
88
+ inputs (typing.Optional[typing.Sequence], optional): Reactive sources (state objects, computed properties)
89
+ that should be accessible during event handling.
90
+ These values will be passed to the decorated function
91
+ when the event fires.
92
+ outputs (typing.Optional[typing.Sequence], optional): Targets (state variables, UI elements) that should
93
+ update when this handler executes. Used for coordinating
94
+ interface updates after the event is processed.
82
95
 
83
- @typing.overload
84
- def ui_event(
85
- *,
86
- inputs: typing.Optional[typing.Union[_T_input, typing.Sequence[_T_input]]] = None,
87
- outputs: typing.Optional[
88
- typing.Union[_T_output, typing.Sequence[_T_output]]
89
- ] = None,
90
- ) -> typing.Callable[[typing.Callable[P, R]], WebEvent[P, R]]: ...
96
+ # Example:
97
+ .. code-block:: python
98
+ from instaui import ui, html
99
+
100
+ a = ui.state(0)
91
101
 
102
+ @ui.event(inputs=[a], outputs=[a])
103
+ def plus_one(a):
104
+ return a + 1
92
105
 
93
- def ui_event(
94
- fn: typing.Optional[typing.Callable[P, R]] = None, *, inputs=None, outputs=None
95
- ) -> typing.Union[
96
- WebEvent[P, R], typing.Callable[[typing.Callable[P, R]], WebEvent[P, R]]
97
- ]:
98
- inputs = [inputs] if isinstance(inputs, CanInputMixin) else inputs
99
- outputs = [outputs] if isinstance(outputs, CanOutputMixin) else outputs
100
- if fn is None:
106
+ html.button("click me").on_click(plus_one)
107
+ html.paragraph(a)
101
108
 
102
- def wrapper(fn: typing.Callable[P, R]):
103
- return WebEvent(fn, inputs=inputs or [], outputs=outputs or [])
109
+ """
104
110
 
105
- return wrapper
111
+ def wrapper(func: typing.Callable[P, R]):
112
+ return WebEvent(
113
+ func,
114
+ inputs or [],
115
+ outputs=outputs or [],
116
+ )
106
117
 
107
- return WebEvent(fn, inputs=inputs or [], outputs=outputs or [])
118
+ return wrapper
@@ -1,4 +1,3 @@
1
1
  from .debug import list_all_bindables
2
- from instaui.vars.state import state
3
2
 
4
- __all__ = ["list_all_bindables", "state"]
3
+ __all__ = ["list_all_bindables"]
@@ -23,17 +23,20 @@ class HandlerInfo:
23
23
  fn: Callable
24
24
  fn_location_info: str
25
25
  outputs_binding_count: int = 0
26
- hanlder_param_converters: List[pydantic_system.TypeAdapterProtocol] = field(
26
+ handler_param_converters: List[pydantic_system.TypeAdapterProtocol] = field(
27
27
  default_factory=list
28
28
  )
29
+ is_last_param_args: bool = False
29
30
 
30
31
  def get_handler_args(self, input_values: List):
32
+ real_param_converters = _try_expand_params_converters(
33
+ self.handler_param_converters, input_values, self.is_last_param_args
34
+ )
35
+
31
36
  try:
32
37
  return [
33
38
  param_converter.to_python_value(value)
34
- for param_converter, value in zip(
35
- self.hanlder_param_converters, input_values
36
- )
39
+ for param_converter, value in zip(real_param_converters, input_values)
37
40
  ]
38
41
  except pydantic_core._pydantic_core.ValidationError as e:
39
42
  raise ValueError(f"invalid input[{self.fn_location_info}]: {e}") from None
@@ -49,6 +52,7 @@ class HandlerInfo:
49
52
  ):
50
53
  custom_type_adapter_map = custom_type_adapter_map or {}
51
54
  params_infos = func_system.get_fn_params_infos(handler)
55
+ is_last_param_args = func_system.is_last_param_args(handler)
52
56
  param_converters = [
53
57
  custom_type_adapter_map.get(
54
58
  idx, pydantic_system.create_type_adapter(param_type)
@@ -62,5 +66,23 @@ class HandlerInfo:
62
66
  handler,
63
67
  f'File "{file}", line {lineno}',
64
68
  outputs_binding_count,
65
- hanlder_param_converters=param_converters,
69
+ handler_param_converters=param_converters,
70
+ is_last_param_args=is_last_param_args,
66
71
  )
72
+
73
+
74
+ def _try_expand_params_converters(
75
+ old_param_converters: List[pydantic_system.TypeAdapterProtocol],
76
+ input_values: List,
77
+ is_last_param_args: bool,
78
+ ):
79
+ if not is_last_param_args:
80
+ return old_param_converters
81
+
82
+ diff = len(input_values) - len(old_param_converters)
83
+ if diff == 0:
84
+ return old_param_converters
85
+
86
+ arg_param_converters = [old_param_converters[-1]] * diff
87
+
88
+ return [*old_param_converters[:], *arg_param_converters]