ex4nicegui 0.6.7__py3-none-any.whl → 0.6.9__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 (75) hide show
  1. ex4nicegui/__init__.py +12 -6
  2. ex4nicegui/gsap/__init__.py +11 -0
  3. ex4nicegui/helper/__init__.py +4 -0
  4. ex4nicegui/helper/client_instance_locker.py +31 -0
  5. ex4nicegui/reactive/__init__.py +4 -0
  6. ex4nicegui/reactive/base.py +426 -0
  7. ex4nicegui/reactive/deferredTask.py +3 -2
  8. ex4nicegui/reactive/local_file_picker.py +1 -2
  9. ex4nicegui/reactive/mixins/backgroundColor.py +41 -0
  10. ex4nicegui/reactive/mixins/disableable.py +44 -0
  11. ex4nicegui/reactive/mixins/textColor.py +66 -0
  12. ex4nicegui/reactive/officials/aggrid.py +4 -4
  13. ex4nicegui/reactive/officials/base.py +4 -351
  14. ex4nicegui/reactive/officials/button.py +36 -3
  15. ex4nicegui/reactive/officials/checkbox.py +2 -3
  16. ex4nicegui/reactive/officials/chip.py +102 -0
  17. ex4nicegui/reactive/officials/circular_progress.py +6 -5
  18. ex4nicegui/reactive/officials/color_picker.py +3 -4
  19. ex4nicegui/reactive/officials/column.py +1 -1
  20. ex4nicegui/reactive/officials/date.py +2 -3
  21. ex4nicegui/reactive/officials/drawer.py +2 -3
  22. ex4nicegui/reactive/officials/echarts.py +2 -3
  23. ex4nicegui/reactive/officials/expansion.py +2 -3
  24. ex4nicegui/reactive/officials/grid.py +4 -3
  25. ex4nicegui/reactive/officials/html.py +1 -3
  26. ex4nicegui/reactive/officials/icon.py +5 -9
  27. ex4nicegui/reactive/officials/image.py +2 -4
  28. ex4nicegui/reactive/officials/input.py +4 -6
  29. ex4nicegui/reactive/officials/knob.py +6 -4
  30. ex4nicegui/reactive/officials/label.py +3 -10
  31. ex4nicegui/reactive/officials/linear_progress.py +5 -9
  32. ex4nicegui/reactive/officials/number.py +3 -6
  33. ex4nicegui/reactive/officials/radio.py +3 -5
  34. ex4nicegui/reactive/officials/row.py +1 -1
  35. ex4nicegui/reactive/officials/select.py +3 -5
  36. ex4nicegui/reactive/officials/slider.py +4 -6
  37. ex4nicegui/reactive/officials/switch.py +2 -4
  38. ex4nicegui/reactive/officials/tab.py +1 -1
  39. ex4nicegui/reactive/officials/tab_panel.py +1 -1
  40. ex4nicegui/reactive/officials/tab_panels.py +109 -3
  41. ex4nicegui/reactive/officials/table.py +7 -6
  42. ex4nicegui/reactive/officials/tabs.py +1 -1
  43. ex4nicegui/reactive/officials/textarea.py +3 -5
  44. ex4nicegui/reactive/officials/upload.py +2 -2
  45. ex4nicegui/reactive/q_pagination.py +2 -2
  46. ex4nicegui/reactive/scopedStyle.js +55 -0
  47. ex4nicegui/reactive/scopedStyle.py +25 -0
  48. ex4nicegui/reactive/services/pandas_service.py +31 -0
  49. ex4nicegui/reactive/{utils.py → services/reactive_service.py} +9 -67
  50. ex4nicegui/reactive/systems/color_system.py +157 -0
  51. ex4nicegui/reactive/systems/object_system.py +33 -0
  52. ex4nicegui/reactive/systems/reactive_system.py +21 -0
  53. ex4nicegui/reactive/useMouse/UseMouse.py +4 -4
  54. ex4nicegui/reactive/usePagination.py +1 -1
  55. ex4nicegui/reactive/vfor.py +1 -2
  56. ex4nicegui/reactive/vmodel.py +1 -1
  57. ex4nicegui/utils/apiEffect.py +5 -1
  58. ex4nicegui/utils/effect.py +3 -2
  59. ex4nicegui/utils/refComputed.py +147 -0
  60. ex4nicegui/utils/refWrapper.py +57 -0
  61. ex4nicegui/utils/scheduler.py +20 -4
  62. ex4nicegui/utils/signals.py +51 -192
  63. ex4nicegui/utils/types.py +16 -0
  64. ex4nicegui/version.py +3 -0
  65. {ex4nicegui-0.6.7.dist-info → ex4nicegui-0.6.9.dist-info}/METADATA +61 -4
  66. {ex4nicegui-0.6.7.dist-info → ex4nicegui-0.6.9.dist-info}/RECORD +68 -58
  67. ex4nicegui/reactive/EChartsComponent/__init__.py +0 -0
  68. ex4nicegui/reactive/UseDraggable/__init__.py +0 -0
  69. ex4nicegui/reactive/dropZone/__init__.py +0 -0
  70. ex4nicegui/reactive/mermaid/__init__.py +0 -0
  71. ex4nicegui/reactive/officials/__init__.py +0 -1
  72. ex4nicegui/reactive/officials/utils.py +0 -11
  73. ex4nicegui/reactive/useMouse/__init__.py +0 -0
  74. {ex4nicegui-0.6.7.dist-info → ex4nicegui-0.6.9.dist-info}/LICENSE +0 -0
  75. {ex4nicegui-0.6.7.dist-info → ex4nicegui-0.6.9.dist-info}/WHEEL +0 -0
@@ -0,0 +1,147 @@
1
+ from functools import partial
2
+ import types
3
+ import signe
4
+ from .clientScope import _CLIENT_SCOPE_MANAGER
5
+ from typing import (
6
+ Any,
7
+ Dict,
8
+ Protocol,
9
+ Type,
10
+ TypeVar,
11
+ Generic,
12
+ overload,
13
+ Optional,
14
+ Callable,
15
+ cast,
16
+ Union,
17
+ )
18
+ from .scheduler import get_uiScheduler
19
+ from .types import (
20
+ ReadonlyRef,
21
+ DescReadonlyRef,
22
+ )
23
+
24
+
25
+ T = TypeVar("T", covariant=True)
26
+
27
+
28
+ class TInstanceCall(Protocol[T]):
29
+ def __call__(_, self) -> T: # type: ignore
30
+ ...
31
+
32
+
33
+ @overload
34
+ def ref_computed(
35
+ fn: Union[Callable[[], T], TInstanceCall[T]],
36
+ *,
37
+ desc="",
38
+ debug_trigger: Optional[Callable[..., None]] = None,
39
+ priority_level: int = 1,
40
+ debug_name: Optional[str] = None,
41
+ ) -> ReadonlyRef[T]:
42
+ """Takes a getter function and returns a readonly reactive ref object for the returned value from the getter. It can also take an object with get and set functions to create a writable ref object.
43
+
44
+ @see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#ref_computed
45
+ @中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#ref_computed
46
+
47
+
48
+ Args:
49
+ fn (Callable[[], T]): _description_
50
+ desc (str, optional): _description_. Defaults to "".
51
+ debug_trigger (Optional[Callable[..., None]], optional): _description_. Defaults to None.
52
+ priority_level (int, optional): _description_. Defaults to 1.
53
+ debug_name (Optional[str], optional): _description_. Defaults to None.
54
+
55
+ """
56
+ ...
57
+
58
+
59
+ @overload
60
+ def ref_computed(
61
+ fn=None,
62
+ *,
63
+ desc="",
64
+ debug_trigger: Optional[Callable[..., None]] = None,
65
+ priority_level: int = 1,
66
+ debug_name: Optional[str] = None,
67
+ ) -> Callable[[Callable[..., T]], ReadonlyRef[T]]:
68
+ ...
69
+
70
+
71
+ def ref_computed(
72
+ fn: Optional[Union[Callable[[], T], TInstanceCall[T]]] = None,
73
+ *,
74
+ desc="",
75
+ debug_trigger: Optional[Callable[..., None]] = None,
76
+ priority_level: int = 1,
77
+ debug_name: Optional[str] = None,
78
+ ) -> Union[ReadonlyRef[T], Callable[[Callable[..., T]], ReadonlyRef[T]]]:
79
+ kws = {
80
+ "debug_trigger": debug_trigger,
81
+ "priority_level": priority_level,
82
+ "debug_name": debug_name,
83
+ }
84
+
85
+ if fn:
86
+ if _helpers.is_class_define_method(fn):
87
+ return cast(
88
+ ref_computed_method[T],
89
+ ref_computed_method(fn, computed_args=kws), # type: ignore
90
+ ) # type: ignore
91
+
92
+ getter = signe.Computed(
93
+ cast(Callable[[], T], fn),
94
+ **kws,
95
+ scope=_CLIENT_SCOPE_MANAGER.get_current_scope(),
96
+ scheduler=get_uiScheduler(),
97
+ )
98
+ return cast(DescReadonlyRef[T], getter)
99
+
100
+ else:
101
+
102
+ def wrap(fn: Callable[[], T]):
103
+ return ref_computed(fn, **kws)
104
+
105
+ return wrap
106
+
107
+
108
+ class ref_computed_method(Generic[T]):
109
+ __isabstractmethod__: bool
110
+
111
+ def __init__(self, fget: Callable[[Any], T], computed_args: Dict) -> None:
112
+ self._fget = fget
113
+ self._computed_args = computed_args
114
+
115
+ def __set_name__(self, owner, name):
116
+ _helpers.add_computed_to_instance(owner, name, self._fget, self._computed_args)
117
+
118
+
119
+ class _helpers:
120
+ @staticmethod
121
+ def is_class_define_method(fn: Callable):
122
+ has_name = hasattr(fn, "__name__")
123
+ qualname_prefix = f".<locals>.{fn.__name__}" if has_name else ""
124
+
125
+ return (
126
+ hasattr(fn, "__qualname__")
127
+ and has_name
128
+ and "." in fn.__qualname__
129
+ and qualname_prefix != fn.__qualname__[-len(qualname_prefix) :]
130
+ and (isinstance(fn, types.FunctionType))
131
+ )
132
+
133
+ @staticmethod
134
+ def add_computed_to_instance(
135
+ cls_type: Type, attr_name: str, fn: Callable, computed_args: Dict
136
+ ):
137
+ """
138
+ Add an attribute to an instance of a class.
139
+ """
140
+ original_init = cls_type.__init__
141
+
142
+ def new_init(self, *args, **kwargs):
143
+ original_init(self, *args, **kwargs)
144
+ setattr(self, attr_name, ref_computed(partial(fn, self), **computed_args))
145
+
146
+ cls_type.__init__ = new_init
147
+ return cls_type
@@ -0,0 +1,57 @@
1
+ import signe
2
+ from typing import (
3
+ TypeVar,
4
+ Generic,
5
+ Optional,
6
+ Callable,
7
+ cast,
8
+ )
9
+ import warnings
10
+ from .types import (
11
+ TGetterOrReadonlyRef,
12
+ )
13
+
14
+ T = TypeVar("T")
15
+
16
+
17
+ class RefWrapper(Generic[T]):
18
+ __slot__ = ("_getter_fn", "_setter_fn", "")
19
+
20
+ def __init__(
21
+ self,
22
+ getter_or_ref: TGetterOrReadonlyRef[T],
23
+ setter_or_ref: Optional[Callable[[T], None]] = None,
24
+ ):
25
+ if signe.is_signal(getter_or_ref):
26
+ self._getter_fn = lambda: getter_or_ref.value
27
+
28
+ def ref_setter(v):
29
+ getter_or_ref.value = v # type: ignore
30
+
31
+ self._setter_fn = ref_setter
32
+ elif isinstance(getter_or_ref, Callable):
33
+ self._getter_fn = getter_or_ref
34
+ self._setter_fn = setter_or_ref or (lambda x: None)
35
+ else:
36
+ self._getter_fn = lambda: getter_or_ref
37
+ self._setter_fn = lambda x: None
38
+
39
+ self._is_readonly = False
40
+
41
+ @property
42
+ def value(self) -> T:
43
+ return cast(T, self._getter_fn())
44
+
45
+ @value.setter
46
+ def value(self, new_value: T):
47
+ if self._is_readonly:
48
+ warnings.warn("readonly ref cannot be assigned.")
49
+ return
50
+ return self._setter_fn(new_value)
51
+
52
+
53
+ def to_ref_wrapper(
54
+ getter_or_ref: TGetterOrReadonlyRef[T],
55
+ setter_or_ref: Optional[Callable[[T], None]] = None,
56
+ ):
57
+ return RefWrapper(getter_or_ref, setter_or_ref)
@@ -1,15 +1,13 @@
1
1
  from collections import deque
2
2
  import signe
3
- from typing import (
4
- TypeVar,
5
- Callable,
6
- )
3
+ from typing import TypeVar, Callable, Literal
7
4
  from functools import lru_cache
8
5
 
9
6
 
10
7
  T = TypeVar("T")
11
8
 
12
9
  T_JOB_FN = Callable[[], None]
10
+ T_JOB_TYPE = Literal["pre", "post"]
13
11
 
14
12
 
15
13
  class UiScheduler(signe.ExecutionScheduler):
@@ -18,6 +16,7 @@ class UiScheduler(signe.ExecutionScheduler):
18
16
 
19
17
  self._pre_deque: deque[T_JOB_FN] = deque()
20
18
  self._post_deque: deque[T_JOB_FN] = deque()
19
+ self._next_tick_deque: deque[T_JOB_FN] = deque()
21
20
 
22
21
  def pre_job(self, job: T_JOB_FN):
23
22
  self._pre_deque.appendleft(job)
@@ -25,6 +24,9 @@ class UiScheduler(signe.ExecutionScheduler):
25
24
  def post_job(self, job: T_JOB_FN):
26
25
  self._post_deque.appendleft(job)
27
26
 
27
+ def next_tick_job(self, job: T_JOB_FN):
28
+ self._next_tick_deque.appendleft(job)
29
+
28
30
  def run(self):
29
31
  while self._scheduler_fns:
30
32
  super().run()
@@ -33,6 +35,7 @@ class UiScheduler(signe.ExecutionScheduler):
33
35
  try:
34
36
  self.run_pre_deque()
35
37
  self.run_post_deque()
38
+ self.run_next_tick_deque()
36
39
  pass
37
40
  except Exception as e:
38
41
  raise e
@@ -47,7 +50,20 @@ class UiScheduler(signe.ExecutionScheduler):
47
50
  while self._post_deque:
48
51
  self._post_deque.pop()()
49
52
 
53
+ def run_next_tick_deque(self):
54
+ while self._next_tick_deque:
55
+ self._next_tick_deque.pop()()
56
+
50
57
 
51
58
  @lru_cache(maxsize=1)
52
59
  def get_uiScheduler():
53
60
  return UiScheduler()
61
+
62
+
63
+ def next_tick(job: T_JOB_FN):
64
+ """Schedule a job to run on the next tick of the event loop.
65
+
66
+ Args:
67
+ job (T_JOB_FN): The job to run on the next tick.
68
+ """
69
+ get_uiScheduler().next_tick_job(job)
@@ -1,17 +1,11 @@
1
1
  from datetime import date, datetime
2
- from functools import partial
3
- import types
4
- from weakref import WeakValueDictionary
5
2
  import signe
6
- from signe.core.protocols import ComputedResultProtocol
3
+ from signe.core.scope import Scope
7
4
  from .clientScope import _CLIENT_SCOPE_MANAGER
8
5
  from typing import (
9
6
  Any,
10
7
  Dict,
11
- Protocol,
12
8
  TypeVar,
13
- Generic,
14
- overload,
15
9
  Optional,
16
10
  Callable,
17
11
  cast,
@@ -21,85 +15,51 @@ from typing import (
21
15
  from nicegui import ui
22
16
  from .effect import effect
23
17
  from .scheduler import get_uiScheduler
24
- import warnings
18
+ from .types import (
19
+ _TMaybeRef,
20
+ TGetterOrReadonlyRef,
21
+ Ref,
22
+ TReadonlyRef, # noqa: F401
23
+ TRef, # noqa: F401
24
+ DescReadonlyRef, # noqa: F401
25
+ _TMaybeRef as TMaybeRef, # noqa: F401
26
+ )
27
+ from .refWrapper import RefWrapper, to_ref_wrapper # noqa: F401
28
+ from .refComputed import ref_computed # noqa: F401
25
29
 
26
30
  T = TypeVar("T")
27
31
 
28
32
 
29
- TReadonlyRef = ComputedResultProtocol[T]
30
- ReadonlyRef = TReadonlyRef[T]
31
- DescReadonlyRef = TReadonlyRef[T]
32
-
33
- TGetterOrReadonlyRef = signe.TGetter[T]
34
-
35
-
36
33
  is_reactive = signe.is_reactive
34
+ to_raw = signe.to_raw
37
35
 
38
36
 
39
37
  def reactive(obj: T) -> T:
40
38
  return signe.reactive(obj, get_uiScheduler())
41
39
 
42
40
 
43
- class RefWrapper(Generic[T]):
44
- __slot__ = ("_getter_fn", "_setter_fn", "")
45
-
46
- def __init__(
47
- self,
48
- getter_or_ref: TGetterOrReadonlyRef[T],
49
- setter_or_ref: Optional[Callable[[T], None]] = None,
50
- ):
51
- if signe.is_signal(getter_or_ref):
52
- self._getter_fn = lambda: getter_or_ref.value
53
-
54
- def ref_setter(v):
55
- getter_or_ref.value = v # type: ignore
56
-
57
- self._setter_fn = ref_setter
58
- elif isinstance(getter_or_ref, Callable):
59
- self._getter_fn = getter_or_ref
60
- self._setter_fn = setter_or_ref or (lambda x: None)
61
- else:
62
- self._getter_fn = lambda: getter_or_ref
63
- self._setter_fn = lambda x: None
64
-
65
- self._is_readonly = False
66
-
67
- @property
68
- def value(self) -> T:
69
- return cast(T, self._getter_fn())
70
-
71
- @value.setter
72
- def value(self, new_value: T):
73
- if self._is_readonly:
74
- warnings.warn("readonly ref cannot be assigned.")
75
- return
76
- return self._setter_fn(new_value)
77
-
78
-
79
- def to_ref_wrapper(
80
- getter_or_ref: TGetterOrReadonlyRef[T],
81
- setter_or_ref: Optional[Callable[[T], None]] = None,
82
- ):
83
- return RefWrapper(getter_or_ref, setter_or_ref)
84
-
85
-
86
- _TMaybeRef = signe.TMaybeSignal[T]
87
- TRef = signe.TSignal[T]
88
- Ref = TRef[T]
89
-
90
-
91
- to_raw = signe.to_raw
92
-
93
-
94
41
  def is_setter_ref(obj):
95
42
  return isinstance(obj, (signe.Signal, RefWrapper))
96
43
 
97
44
 
98
- def is_ref(obj):
45
+ def is_ref(obj: Any):
46
+ """Checks if a value is a ref object."""
99
47
  return signe.is_signal(obj) or isinstance(obj, (RefWrapper))
100
48
 
101
49
 
102
50
  def to_value(obj: Union[_TMaybeRef[T], RefWrapper]) -> T:
51
+ """unwraps a ref object and returns its inner value.
52
+
53
+ Args:
54
+ obj (Union[_TMaybeRef[T], RefWrapper]): A getter function, an existing ref, or a non-function value.
55
+
56
+ ## Example
57
+ ```python
58
+ to_value(1) # 1
59
+ to_value(lambda: 1) # 1
60
+ to_value(to_ref(1)) # 1
61
+ ```
62
+ """
103
63
  if is_ref(obj):
104
64
  return obj.value # type: ignore
105
65
  if isinstance(obj, Callable):
@@ -144,7 +104,7 @@ def _ref_comp_with_None(old, new):
144
104
  return False
145
105
 
146
106
 
147
- def ref(value: T, is_deep=False):
107
+ def ref(value: T, is_deep=False) -> Ref[T]:
148
108
  comp = False # Default never equal
149
109
 
150
110
  if _is_comp_values(value):
@@ -170,135 +130,33 @@ def deep_ref(value: T) -> Ref[T]:
170
130
  return to_ref(value, is_deep=True)
171
131
 
172
132
 
173
- class TInstanceCall(Protocol[T]):
174
- def __call__(_, self) -> T:
175
- ...
176
-
177
-
178
- @overload
179
- def ref_computed(
180
- fn: Union[Callable[[], T], TInstanceCall[T]],
181
- *,
182
- desc="",
183
- debug_trigger: Optional[Callable[..., None]] = None,
184
- priority_level: int = 1,
185
- debug_name: Optional[str] = None,
186
- ) -> ReadonlyRef[T]:
187
- """Takes a getter function and returns a readonly reactive ref object for the returned value from the getter. It can also take an object with get and set functions to create a writable ref object.
188
-
189
- @see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#ref_computed
190
- @中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#ref_computed
191
-
192
-
193
- Args:
194
- fn (Callable[[], T]): _description_
195
- desc (str, optional): _description_. Defaults to "".
196
- debug_trigger (Optional[Callable[..., None]], optional): _description_. Defaults to None.
197
- priority_level (int, optional): _description_. Defaults to 1.
198
- debug_name (Optional[str], optional): _description_. Defaults to None.
199
-
200
- """
201
- ...
202
-
203
-
204
- @overload
205
- def ref_computed(
206
- fn=None,
207
- *,
208
- desc="",
209
- debug_trigger: Optional[Callable[..., None]] = None,
210
- priority_level: int = 1,
211
- debug_name: Optional[str] = None,
212
- ) -> Callable[[Callable[..., T]], ReadonlyRef[T]]:
213
- ...
214
-
215
-
216
- def ref_computed(
217
- fn: Optional[Union[Callable[[], T], TInstanceCall[T]]] = None,
218
- *,
219
- desc="",
220
- debug_trigger: Optional[Callable[..., None]] = None,
221
- priority_level: int = 1,
222
- debug_name: Optional[str] = None,
223
- ) -> Union[ReadonlyRef[T], Callable[[Callable[..., T]], ReadonlyRef[T]]]:
224
- kws = {
225
- "debug_trigger": debug_trigger,
226
- "priority_level": priority_level,
227
- "debug_name": debug_name,
228
- }
229
-
230
- if fn:
231
- if _is_class_define_method(fn):
232
- return cast(
233
- ref_computed_method[T],
234
- ref_computed_method(fn, computed_args=kws), # type: ignore
235
- ) # type: ignore
236
-
237
- getter = signe.Computed(
238
- cast(Callable[[], T], fn),
239
- **kws,
240
- scope=_CLIENT_SCOPE_MANAGER.get_current_scope(),
241
- scheduler=get_uiScheduler(),
242
- )
243
- return cast(DescReadonlyRef[T], getter)
244
-
245
- else:
246
-
247
- def wrap(fn: Callable[[], T]):
248
- return ref_computed(fn, **kws)
249
-
250
- return wrap
251
-
252
-
253
- def _is_class_define_method(fn: Callable):
254
- has_name = hasattr(fn, "__name__")
255
- qualname_prefix = f".<locals>.{fn.__name__}" if has_name else ""
256
-
257
- return (
258
- hasattr(fn, "__qualname__")
259
- and has_name
260
- and "." in fn.__qualname__
261
- and qualname_prefix != fn.__qualname__[-len(qualname_prefix) :]
262
- and (isinstance(fn, types.FunctionType))
263
- )
264
-
265
-
266
- class ref_computed_method(Generic[T]):
267
- __isabstractmethod__: bool
268
-
269
- def __init__(self, fget: Callable[[Any], T], computed_args: Dict) -> None:
270
- self._fget = fget
271
- self._computed_args = computed_args
272
- self._instance_map: WeakValueDictionary[
273
- int, TReadonlyRef[T]
274
- ] = WeakValueDictionary()
275
-
276
- def __get_computed(self, instance):
277
- ins_id = id(instance)
278
- if ins_id not in self._instance_map:
279
- cp = signe.Computed(
280
- partial(self._fget, instance),
281
- **self._computed_args,
282
- scope=_CLIENT_SCOPE_MANAGER.get_current_scope(),
283
- scheduler=get_uiScheduler(),
284
- capture_parent_effect=False,
285
- )
286
- self._instance_map[ins_id] = cp # type: ignore
287
-
288
- return self._instance_map[ins_id]
289
-
290
- def __get__(self, __instance: Any, __owner: Optional[type] = None):
291
- return cast(TRef[T], self.__get_computed(__instance))
133
+ _T_effect_refreshable_refs = Union[
134
+ TGetterOrReadonlyRef,
135
+ RefWrapper,
136
+ Sequence[TGetterOrReadonlyRef],
137
+ _TMaybeRef,
138
+ Sequence[_TMaybeRef],
139
+ ]
292
140
 
293
141
 
294
142
  class effect_refreshable:
295
- def __init__(self, fn: Callable, refs: Union[TRef, Sequence[TRef]] = []) -> None:
143
+ def __init__(self, fn: Callable, refs: _T_effect_refreshable_refs = []) -> None:
296
144
  self._fn = fn
297
- self._refs = refs if isinstance(refs, Sequence) else [refs]
145
+
146
+ if isinstance(refs, Sequence):
147
+ ref_arg = [ref for ref in refs if self._is_valid_ref(ref)]
148
+ else:
149
+ ref_arg = [refs] if self._is_valid_ref(refs) else []
150
+
151
+ self._refs = ref_arg
298
152
  self()
299
153
 
154
+ @classmethod
155
+ def _is_valid_ref(cls, ref):
156
+ return is_ref(ref) or isinstance(ref, Callable)
157
+
300
158
  @staticmethod
301
- def on(refs: Union[TRef, Sequence[TRef]]):
159
+ def on(refs: _T_effect_refreshable_refs):
302
160
  def warp(
303
161
  fn: Callable,
304
162
  ):
@@ -323,7 +181,7 @@ class effect_refreshable:
323
181
  if len(self._refs) == 0:
324
182
  runner = effect(runner)
325
183
  else:
326
- runner = on(self._refs)(runner)
184
+ runner = on(self._refs)(runner) # type: ignore
327
185
 
328
186
  return runner
329
187
 
@@ -334,6 +192,7 @@ def on(
334
192
  priority_level=1,
335
193
  effect_kws: Optional[Dict[str, Any]] = None,
336
194
  deep: bool = True,
195
+ scope: Optional[Scope] = None,
337
196
  ):
338
197
  """Watches one or more reactive data sources and invokes a callback function when the sources change.
339
198
 
@@ -362,7 +221,7 @@ def on(
362
221
  fn,
363
222
  onchanges=onchanges,
364
223
  effect_kws=effect_kws,
365
- scope=_CLIENT_SCOPE_MANAGER.get_current_scope(),
224
+ scope=scope or _CLIENT_SCOPE_MANAGER.get_current_scope(),
366
225
  deep=deep,
367
226
  scheduler=get_uiScheduler(),
368
227
  )
@@ -0,0 +1,16 @@
1
+ import signe
2
+ from signe.core.protocols import ComputedResultProtocol
3
+ from typing import (
4
+ TypeVar,
5
+ )
6
+
7
+ T = TypeVar("T")
8
+
9
+
10
+ TReadonlyRef = ComputedResultProtocol[T]
11
+ ReadonlyRef = TReadonlyRef[T]
12
+ DescReadonlyRef = TReadonlyRef[T]
13
+ TGetterOrReadonlyRef = signe.TGetter[T]
14
+ _TMaybeRef = signe.TMaybeSignal[T]
15
+ TRef = signe.TSignal[T]
16
+ Ref = TRef[T]
ex4nicegui/version.py ADDED
@@ -0,0 +1,3 @@
1
+ import importlib.metadata
2
+
3
+ __version__: str = importlib.metadata.version("ex4nicegui")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ex4nicegui
3
- Version: 0.6.7
3
+ Version: 0.6.9
4
4
  Summary: Extension library based on nicegui, providing data responsive,BI functionality modules
5
5
  Home-page: https://github.com/CrystalWindSnake/ex4nicegui
6
6
  License: MIT
@@ -604,7 +604,7 @@ rxui.label("bind to ref_computed").bind_classes(
604
604
 
605
605
  ---
606
606
 
607
- 绑定为列表
607
+ 绑定为列表或单个字符串的响应式变量
608
608
 
609
609
  ```python
610
610
  bg_color = to_ref("red")
@@ -617,10 +617,11 @@ rxui.select(["red", "green", "yellow"], label="bg color", value=bg_color)
617
617
  rxui.select(["red", "green", "yellow"], label="text color", value=text_color)
618
618
 
619
619
  rxui.label("binding to arrays").bind_classes([bg_color_class, text_color_class])
620
-
620
+ rxui.label("binding to single string").bind_classes(bg_color_class)
621
621
  ```
622
622
 
623
- 列表中每个元素为返回类名的响应式变量
623
+ - 列表中每个元素为返回类名的响应式变量
624
+
624
625
 
625
626
  ---
626
627
 
@@ -887,6 +888,62 @@ rxui.radio(names, value=current_tab).props("inline")
887
888
 
888
889
  rxui.label(lambda: f"当前 tab 为:{current_tab.value}")
889
890
  ```
891
+ ---
892
+
893
+ ### lazy_tab_panels
894
+
895
+ 懒加载模式下,只有当前激活的 tab 才会渲染。
896
+ ```python
897
+ from ex4nicegui import to_ref, rxui, on, deep_ref
898
+
899
+ current_tab = to_ref("t1")
900
+
901
+ with rxui.tabs(current_tab):
902
+ ui.tab("t1")
903
+ ui.tab("t2")
904
+
905
+ with rxui.lazy_tab_panels(current_tab) as panels:
906
+
907
+ @panels.add_tab_panel("t1")
908
+ def _():
909
+ ui.notify("Hello from t1")
910
+
911
+ @panels.add_tab_panel("t2")
912
+ def _():
913
+ ui.notify("Hello from t2")
914
+
915
+ ```
916
+
917
+ 页面加载后,立刻显示 "Hello from t1"。当切换到 "t2" 页签,才会显示 "Hello from t2"。
918
+
919
+ ---
920
+
921
+ ### scoped_style
922
+
923
+ `scoped_style` 方法允许你创建限定在组件内部的样式。
924
+
925
+ ```python
926
+ # 所有子元素都会有红色轮廓,但排除自身
927
+ with rxui.row().scoped_style("*", "outline: 1px solid red;") as row:
928
+ ui.label("Hello")
929
+ ui.label("World")
930
+
931
+
932
+ # 所有子元素都会有红色轮廓,包括自身
933
+ with rxui.row().scoped_style(":self *", "outline: 1px solid red;") as row:
934
+ ui.label("Hello")
935
+ ui.label("World")
936
+
937
+ # 当鼠标悬停在 row 组件时,所有子元素都会有红色轮廓,但排除自身
938
+ with rxui.row().scoped_style(":hover *", "outline: 1px solid red;") as row:
939
+ ui.label("Hello")
940
+ ui.label("World")
941
+
942
+ # 当鼠标悬停在 row 组件时,所有子元素都会有红色轮廓,包括自身
943
+ with rxui.row().scoped_style(":self:hover *", "outline: 1px solid red;") as row:
944
+ ui.label("Hello")
945
+ ui.label("World")
946
+ ```
890
947
 
891
948
 
892
949
  ---