ex4nicegui 0.6.1__py3-none-any.whl → 0.6.3__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.
ex4nicegui/__init__.py CHANGED
@@ -45,4 +45,4 @@ __all__ = [
45
45
  "is_setter_ref",
46
46
  ]
47
47
 
48
- __version__ = "0.6.1"
48
+ __version__ = "0.6.3"
@@ -1,4 +1,4 @@
1
- from .officials.aggrid import AggridBindableUi as aggird
1
+ from .officials.aggrid import AggridBindableUi as aggrid
2
2
  from .officials.button import ButtonBindableUi as button
3
3
  from .officials.card import (
4
4
  CardBindableUi as card,
@@ -46,6 +46,10 @@ from .officials.expansion import ExpansionBindableUi as expansion
46
46
  from .officials.linear_progress import LinearProgressBindableUi as linear_progress
47
47
  from .officials.knob import KnobBindableUi as knob
48
48
  from .officials.circular_progress import CircularProgressBindableUi as circular_progress
49
+ from .officials.tabs import TabsBindableUi as tabs
50
+ from .officials.tab import TabBindableUi as tab
51
+ from .officials.tab_panels import TabPanelsBindableUi as tab_panels
52
+ from .officials.tab_panel import TabPanelBindableUi as tab_panel
49
53
  from .q_pagination import PaginationBindableUi as q_pagination
50
54
 
51
55
  from .local_file_picker import local_file_picker
@@ -63,6 +67,10 @@ pagination = q_pagination
63
67
 
64
68
 
65
69
  __all__ = [
70
+ "tab_panels",
71
+ "tab_panel",
72
+ "tabs",
73
+ "tab",
66
74
  "circular_progress",
67
75
  "knob",
68
76
  "UploadResult",
@@ -76,7 +84,7 @@ __all__ = [
76
84
  "VforStore",
77
85
  "vmodel",
78
86
  "html",
79
- "aggird",
87
+ "aggrid",
80
88
  "button",
81
89
  "card",
82
90
  "card_actions",
@@ -0,0 +1,3 @@
1
+ export default {
2
+ template: '<slot></slot>'
3
+ }
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+ from nicegui.element import Element
3
+
4
+
5
+ class Empty(Element, component="empty.js"):
6
+ pass
@@ -15,7 +15,7 @@ from ex4nicegui.utils.signals import (
15
15
  from ex4nicegui.utils.apiEffect import ui_effect
16
16
  from nicegui import ui
17
17
  from .base import BindableUi
18
- from ex4nicegui.reactive.utils import ParameterClassifier
18
+ from ex4nicegui.reactive.utils import ParameterClassifier, dataframe2col_str
19
19
 
20
20
 
21
21
  class AggridBindableUi(BindableUi[ui.aggrid]):
@@ -48,32 +48,35 @@ class AggridBindableUi(BindableUi[ui.aggrid]):
48
48
  for col in df.columns # type: ignore
49
49
  ]
50
50
 
51
- @staticmethod
51
+ @classmethod
52
52
  def from_pandas(
53
+ cls,
53
54
  df: TMaybeRef,
54
55
  columns_define_fn: Optional[Callable[[str], Dict]] = None,
55
56
  **org_kws,
56
57
  ):
57
58
  columns_define_fn = columns_define_fn or (lambda x: {})
58
- if is_ref(df):
59
+ if is_ref(df) or isinstance(df, Callable):
59
60
 
60
61
  @ref_computed
61
62
  def cp_options():
63
+ copy_df = dataframe2col_str(to_value(df))
62
64
  columnDefs = AggridBindableUi._get_columnDefs_from_dataframe(
63
- df.value, columns_define_fn
65
+ copy_df, columns_define_fn
64
66
  )
65
- rowData = df.value.to_dict("records")
67
+ rowData = copy_df.to_dict("records")
66
68
  data = {"columnDefs": columnDefs, "rowData": rowData}
67
69
  return data
68
70
 
69
- return AggridBindableUi(cp_options, **org_kws)
71
+ return cls(cp_options, **org_kws)
70
72
 
73
+ copy_df = dataframe2col_str(df)
71
74
  columnDefs = AggridBindableUi._get_columnDefs_from_dataframe(
72
- df, columns_define_fn
75
+ copy_df, columns_define_fn
73
76
  )
74
- rowData = df.to_dict("records") # type: ignore
77
+ rowData = copy_df.to_dict("records") # type: ignore
75
78
  options = {"columnDefs": columnDefs, "rowData": rowData}
76
- return AggridBindableUi(options, **org_kws)
79
+ return cls(options, **org_kws)
77
80
 
78
81
  def bind_prop(self, prop: str, ref_ui: ReadonlyRef[Any]):
79
82
  if prop == "options":
@@ -224,7 +224,7 @@ class BindableUi(Generic[TWidget]):
224
224
 
225
225
  return self
226
226
 
227
- def bind_style(self, style: Dict[str, TGetterOrReadonlyRef[str]]):
227
+ def bind_style(self, style: Dict[str, TGetterOrReadonlyRef[Any]]):
228
228
  """data binding is manipulating an element's style
229
229
 
230
230
  @see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#bind-style
@@ -239,11 +239,15 @@ class BindableUi(Generic[TWidget]):
239
239
 
240
240
  @self._ui_effect
241
241
  def _(name=name, ref_obj=ref_obj):
242
- self.element._style[name] = to_value(ref_obj)
242
+ self.element._style[name] = str(to_value(ref_obj))
243
243
  self.element.update()
244
244
 
245
245
  return self
246
246
 
247
+ def update(self):
248
+ """Update the element on the client side."""
249
+ self.element.update()
250
+
247
251
 
248
252
  # class SingleValueBindableUi(BindableUi[TWidget], Generic[T, TWidget]):
249
253
  # def __init__(self, value: TMaybeRef[T], element: TWidget) -> None:
@@ -0,0 +1,57 @@
1
+ from typing import Dict, Optional
2
+ from ex4nicegui.reactive.utils import ParameterClassifier
3
+ from ex4nicegui.utils.signals import (
4
+ to_value,
5
+ _TMaybeRef as TMaybeRef,
6
+ )
7
+ from nicegui import ui
8
+ from .base import BindableUi
9
+
10
+
11
+ class TabBindableUi(BindableUi[ui.tab]):
12
+ def __init__(
13
+ self,
14
+ name: TMaybeRef[str],
15
+ label: Optional[TMaybeRef[str]] = None,
16
+ icon: Optional[TMaybeRef[str]] = None,
17
+ ) -> None:
18
+ pc = ParameterClassifier(
19
+ locals(),
20
+ maybeRefs=["name", "label", "icon"],
21
+ )
22
+
23
+ value_kws = pc.get_values_kws()
24
+ element = ui.tab(**value_kws)
25
+ super().__init__(element)
26
+
27
+ bindings_kws = pc.get_bindings()
28
+ self.__bind_label(bindings_kws, value_kws)
29
+
30
+ if "label" in bindings_kws:
31
+ bindings_kws.pop("label")
32
+
33
+ for key, value in bindings_kws.items():
34
+ self.bind_prop(key, value) # type: ignore
35
+
36
+ # def bind_prop(self, prop: str, ref_ui: TMaybeRef):
37
+ # if prop == "name":
38
+ # return self.bind_name(ref_ui)
39
+
40
+ # return super().bind_prop(prop, ref_ui)
41
+
42
+ def __bind_label(self, binding_kws: Dict, value_kws: Dict):
43
+ name_ref = binding_kws.get("name") or value_kws.get("name")
44
+ label_ref = binding_kws.get("label") or value_kws.get("label")
45
+
46
+ @self._ui_effect
47
+ def _():
48
+ self.element._props["label"] = (
49
+ to_value(label_ref) if label_ref is not None else to_value(name_ref)
50
+ )
51
+ self.element.update()
52
+
53
+ # def bind_name(self, ref_ui: TMaybeRef):
54
+ # @self._ui_effect
55
+ # def _():
56
+ # self.element._props["name"] = to_value(ref_ui)
57
+ # self.element.update()
@@ -0,0 +1,23 @@
1
+ from ex4nicegui.reactive.utils import ParameterClassifier
2
+ from ex4nicegui.utils.signals import (
3
+ _TMaybeRef as TMaybeRef,
4
+ )
5
+ from nicegui import ui
6
+ from .base import BindableUi
7
+
8
+
9
+ class TabPanelBindableUi(BindableUi[ui.tab_panel]):
10
+ def __init__(
11
+ self,
12
+ name: TMaybeRef[str],
13
+ ) -> None:
14
+ pc = ParameterClassifier(
15
+ locals(),
16
+ maybeRefs=["name"],
17
+ )
18
+
19
+ element = ui.tab_panel(**pc.get_values_kws())
20
+ super().__init__(element)
21
+
22
+ for key, value in pc.get_bindings().items():
23
+ self.bind_prop(key, value) # type: ignore
@@ -0,0 +1,46 @@
1
+ from typing import Any, Callable, Optional
2
+ from ex4nicegui.reactive.utils import ParameterClassifier
3
+ from ex4nicegui.utils.signals import (
4
+ to_value,
5
+ _TMaybeRef as TMaybeRef,
6
+ )
7
+ from nicegui import ui
8
+ from .base import BindableUi
9
+
10
+
11
+ class TabPanelsBindableUi(BindableUi[ui.tab_panels]):
12
+ def __init__(
13
+ self,
14
+ value: Optional[TMaybeRef[str]] = None,
15
+ *,
16
+ on_change: Optional[Callable[..., Any]] = None,
17
+ animated: TMaybeRef[bool] = True,
18
+ keep_alive: TMaybeRef[bool] = True,
19
+ ) -> None:
20
+ pc = ParameterClassifier(
21
+ locals(),
22
+ maybeRefs=["value", "animated", "keep_alive"],
23
+ v_model=("value", "on_change"),
24
+ events=["on_change"],
25
+ )
26
+
27
+ element = ui.tab_panels(**pc.get_values_kws())
28
+ super().__init__(element)
29
+
30
+ for key, value in pc.get_bindings().items():
31
+ self.bind_prop(key, value) # type: ignore
32
+
33
+ @property
34
+ def value(self):
35
+ return self.element.value
36
+
37
+ def bind_prop(self, prop: str, ref_ui: TMaybeRef):
38
+ if prop == "value":
39
+ return self.bind_value(ref_ui)
40
+
41
+ return super().bind_prop(prop, ref_ui)
42
+
43
+ def bind_value(self, ref_ui: TMaybeRef):
44
+ @self._ui_effect
45
+ def _():
46
+ self.element.set_value(to_value(ref_ui))
@@ -6,7 +6,7 @@ from typing import (
6
6
  Dict,
7
7
  )
8
8
  from typing_extensions import Literal
9
- from ex4nicegui.reactive.utils import ParameterClassifier
9
+ from ex4nicegui.reactive.utils import ParameterClassifier, dataframe2col_str
10
10
  import ex4nicegui.utils.common as utils_common
11
11
  from ex4nicegui.utils.signals import (
12
12
  ReadonlyRef,
@@ -17,6 +17,7 @@ from ex4nicegui.utils.signals import (
17
17
  to_value,
18
18
  to_raw,
19
19
  on,
20
+ RefWrapper,
20
21
  )
21
22
  from nicegui import ui
22
23
  from .base import BindableUi
@@ -34,8 +35,16 @@ class TableBindableUi(BindableUi[ui.table]):
34
35
  on_select: Optional[Callable[..., Any]] = None,
35
36
  on_pagination_change: Optional[Callable[..., Any]] = None,
36
37
  ) -> None:
38
+ local_args = locals()
39
+ rows_data = local_args.get("rows")
40
+ if isinstance(rows_data, RefWrapper):
41
+ local_args.update(rows=lambda: rows_data.value)
42
+ columns_data = local_args.get("columns") # type: ignore
43
+ if isinstance(columns_data, RefWrapper):
44
+ local_args.update(columns=lambda: columns_data.value)
45
+
37
46
  pc = ParameterClassifier(
38
- locals(),
47
+ local_args,
39
48
  maybeRefs=[
40
49
  "columns",
41
50
  "rows",
@@ -57,7 +66,7 @@ class TableBindableUi(BindableUi[ui.table]):
57
66
 
58
67
  self._arg_selection = selection
59
68
  self._arg_row_key = row_key
60
- self._selection_ref: ReadonlyRef[List[Any]] = to_ref([])
69
+ self._selection_ref: ReadonlyRef[List[Any]] = to_ref([]) # type: ignore
61
70
 
62
71
  def on_selection(_):
63
72
  self._selection_ref.value = self.element.selected # type: ignore
@@ -68,8 +77,9 @@ class TableBindableUi(BindableUi[ui.table]):
68
77
  def selection_ref(self):
69
78
  return self._selection_ref
70
79
 
71
- @staticmethod
80
+ @classmethod
72
81
  def from_pandas(
82
+ cls,
73
83
  df: TMaybeRef,
74
84
  *,
75
85
  columns_define_fn: Optional[Callable[[str], Dict]] = None,
@@ -90,15 +100,14 @@ class TableBindableUi(BindableUi[ui.table]):
90
100
  "on_pagination_change": on_pagination_change,
91
101
  }
92
102
 
93
- if is_ref(df):
103
+ if is_ref(df) or isinstance(df, Callable):
94
104
 
95
105
  @ref_computed
96
- def cp_rows():
97
- return df.value.to_dict("records")
106
+ def cp_rows_columns():
107
+ copy_df = dataframe2col_str(to_value(df))
108
+ rows = copy_df.to_dict("records")
98
109
 
99
- @ref_computed
100
- def cp_cols():
101
- return [
110
+ columns = [
102
111
  {
103
112
  **{
104
113
  "name": col,
@@ -107,11 +116,18 @@ class TableBindableUi(BindableUi[ui.table]):
107
116
  },
108
117
  **columns_define_fn(col), # type: ignore
109
118
  }
110
- for col in df.value.columns
119
+ for col in copy_df.columns
111
120
  ]
112
121
 
113
- return TableBindableUi(cp_cols, cp_rows, **other_kws)
122
+ return rows, columns
123
+
124
+ return cls(
125
+ lambda: cp_rows_columns.value[1],
126
+ lambda: cp_rows_columns.value[0],
127
+ **other_kws,
128
+ )
114
129
 
130
+ df = dataframe2col_str(df)
115
131
  rows = df.to_dict("records") # type: ignore
116
132
 
117
133
  cols = [
@@ -125,7 +141,7 @@ class TableBindableUi(BindableUi[ui.table]):
125
141
  }
126
142
  for col in df.columns # type: ignore
127
143
  ]
128
- return TableBindableUi(cols, rows, **other_kws)
144
+ return cls(cols, rows, **other_kws)
129
145
 
130
146
  def bind_prop(self, prop: str, ref_ui: ReadonlyRef):
131
147
  if prop == "dataframe":
@@ -0,0 +1,43 @@
1
+ from typing import Any, Callable, Optional
2
+ from ex4nicegui.reactive.utils import ParameterClassifier
3
+ from ex4nicegui.utils.signals import (
4
+ to_value,
5
+ _TMaybeRef as TMaybeRef,
6
+ )
7
+ from nicegui import ui
8
+ from .base import BindableUi
9
+
10
+
11
+ class TabsBindableUi(BindableUi[ui.tabs]):
12
+ def __init__(
13
+ self,
14
+ value: Optional[TMaybeRef[str]] = None,
15
+ on_change: Optional[Callable[..., Any]] = None,
16
+ ) -> None:
17
+ pc = ParameterClassifier(
18
+ locals(),
19
+ maybeRefs=["value"],
20
+ v_model=("value", "on_change"),
21
+ events=["on_change"],
22
+ )
23
+
24
+ element = ui.tabs(**pc.get_values_kws())
25
+ super().__init__(element)
26
+
27
+ for key, value in pc.get_bindings().items():
28
+ self.bind_prop(key, value) # type: ignore
29
+
30
+ @property
31
+ def value(self):
32
+ return self.element.value
33
+
34
+ def bind_prop(self, prop: str, ref_ui: TMaybeRef):
35
+ if prop == "value":
36
+ return self.bind_value(ref_ui)
37
+
38
+ return super().bind_prop(prop, ref_ui)
39
+
40
+ def bind_value(self, ref_ui: TMaybeRef):
41
+ @self._ui_effect
42
+ def _():
43
+ self.element.set_value(to_value(ref_ui))
@@ -0,0 +1,39 @@
1
+ export default {
2
+ props: {
3
+ // childOrderKey: Array,
4
+ // transitionGroupArgs: Array,
5
+ },
6
+ data() {
7
+ return {
8
+ }
9
+ },
10
+ mounted() {
11
+ },
12
+ render() {
13
+ // debugger
14
+ const h = Vue.h
15
+ const vfgt = Vue.Fragment
16
+ const slotBox = this.$slots.default()
17
+ console.log('tg:', slotBox);
18
+ const wrap = () => [h(vfgt, slotBox)]
19
+ // const wrap = slotBox
20
+ console.log(wrap);
21
+
22
+
23
+ // slotBox[0].children = this.childOrderKey.map(idx => {
24
+ // return slotBox[0].children[idx]
25
+ // })
26
+ // console.log(slots);
27
+
28
+ return h(
29
+ Vue.TransitionGroup,
30
+ {
31
+ tag: 'span',
32
+ name: 'list',
33
+ // css: false,
34
+ // ...this.transitionGroupArgs
35
+ }, wrap
36
+ )
37
+
38
+ }
39
+ }
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+ from nicegui.element import Element
3
+ from typing import (
4
+ Any,
5
+ Dict,
6
+ List,
7
+ )
8
+
9
+
10
+ class TransitionGroup(Element, component="transitionGroup.js"):
11
+ def __init__(self) -> None:
12
+ super().__init__()
13
+
14
+ def apply_transition_group(self, args: Dict[str, Any]):
15
+ self._props["transitionGroupArgs"] = args
16
+ self.update()
17
+
18
+ def update_child_order_keys(self, keys: List[Any]):
19
+ print(f"keys:{keys=}")
20
+ self._props["childOrderKey"] = keys
21
+ self.update()
22
+
23
+
24
+ def transition_group(fn):
25
+ pass
@@ -15,6 +15,11 @@ from typing import (
15
15
  from ex4nicegui.utils.signals import is_ref, to_value, is_setter_ref
16
16
  from nicegui.events import handle_event
17
17
 
18
+ try:
19
+ import pandas as pd
20
+ except ImportError:
21
+ pass
22
+
18
23
 
19
24
  @runtime_checkable
20
25
  class GetItemProtocol(Protocol):
@@ -113,3 +118,30 @@ def set_attribute(
113
118
  obj[name] = value
114
119
  else:
115
120
  setattr(obj, name, value) # type: ignore
121
+
122
+
123
+ def dataframe2col_str(df, copy=True):
124
+ if isinstance(df.columns, pd.MultiIndex):
125
+ raise ValueError(
126
+ "MultiIndex columns are not supported. "
127
+ "You can convert them to strings using something like "
128
+ '`df.columns = ["_".join(col) for col in df.columns.values]`.'
129
+ )
130
+
131
+ date_cols = df.columns[df.dtypes == "datetime64[ns]"]
132
+ time_cols = df.columns[df.dtypes == "timedelta64[ns]"]
133
+ complex_cols = df.columns[df.dtypes == "complex128"]
134
+ period_cols = df.columns[df.dtypes == "period[M]"]
135
+ if (
136
+ len(date_cols) != 0
137
+ or len(time_cols) != 0
138
+ or len(complex_cols) != 0
139
+ or len(period_cols) != 0
140
+ ):
141
+ df = df.copy() if copy else df
142
+ df[date_cols] = df[date_cols].astype(str)
143
+ df[time_cols] = df[time_cols].astype(str)
144
+ df[complex_cols] = df[complex_cols].astype(str)
145
+ df[period_cols] = df[period_cols].astype(str)
146
+
147
+ return df
@@ -1,3 +1,13 @@
1
1
  export default {
2
- template: `<slot></slot>`,
3
- };
2
+ props: { itemIds: Array },
3
+
4
+ render() {
5
+ const slotBox = this.$slots.default()
6
+
7
+ const slots = this.itemIds.map(({ elementId }) => {
8
+ return slotBox.find(v => v.key === elementId)
9
+ });
10
+
11
+ return slots
12
+ }
13
+ }
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
  from nicegui.element import Element
3
- from nicegui import ui
3
+ from nicegui import context
4
4
  from ex4nicegui.utils.clientScope import _CLIENT_SCOPE_MANAGER
5
5
  from ex4nicegui.utils.signals import (
6
6
  TReadonlyRef,
@@ -8,6 +8,9 @@ from ex4nicegui.utils.signals import (
8
8
  to_ref,
9
9
  to_ref_wrapper,
10
10
  TGetterOrReadonlyRef,
11
+ to_value,
12
+ is_reactive,
13
+ RefWrapper,
11
14
  )
12
15
  from typing import (
13
16
  Any,
@@ -24,9 +27,25 @@ from functools import partial
24
27
  from dataclasses import dataclass
25
28
  from signe.core.scope import Scope
26
29
  from .utils import get_attribute
30
+ from ex4nicegui.reactive.empty import Empty
31
+ # from .transitionGroup import TransitionGroup
27
32
 
28
33
  _T = TypeVar("_T")
29
- _T_data = TGetterOrReadonlyRef[List[Any]]
34
+ _T_data = Union[List[Any], TGetterOrReadonlyRef[List[Any]], RefWrapper]
35
+
36
+
37
+ class VforItem(Empty):
38
+ pass
39
+
40
+
41
+ class VforContainer(Element, component="vfor.js"):
42
+ def __init__(self) -> None:
43
+ super().__init__()
44
+ self._props["itemIds"] = []
45
+
46
+ def update_items(self, item_ids: List[Dict]):
47
+ self._props["itemIds"] = item_ids
48
+ self.update()
30
49
 
31
50
 
32
51
  class VforStore(Generic[_T]):
@@ -40,10 +59,10 @@ class VforStore(Generic[_T]):
40
59
 
41
60
  def get(self) -> TReadonlyRef[_T]:
42
61
  def base_setter(value):
43
- self._source.value[self._data_index.value] = value
62
+ to_value(self._source)[self._data_index.value] = value
44
63
 
45
64
  wrapper = to_ref_wrapper(
46
- lambda: self._source.value[self._data_index.value],
65
+ lambda: to_value(self._source)[self._data_index.value],
47
66
  base_setter,
48
67
  )
49
68
  wrapper._is_readonly = True
@@ -62,10 +81,6 @@ class StoreItem:
62
81
  scope: Scope
63
82
 
64
83
 
65
- class VforContainer(Element, component="vfor.js"):
66
- pass
67
-
68
-
69
84
  def _get_key_with_index(idx: int, data: Any):
70
85
  return idx
71
86
 
@@ -112,20 +127,25 @@ class vfor(Generic[_T]):
112
127
  self,
113
128
  data: _T_data,
114
129
  *,
115
- key: Optional[str] = None,
130
+ key: Optional[Union[str, Callable[[int, Any], Any]]] = None,
116
131
  ) -> None:
117
- self._container = VforContainer()
118
- self._data = data
119
- self._get_key = (
120
- _get_key_with_index if key is None else partial(_get_key_with_getter, key)
121
- )
132
+ self._vfor_container = VforContainer()
133
+ self._data = to_ref_wrapper(lambda: data) if is_reactive(data) else data
134
+ self._get_key = _get_key_with_index
135
+
136
+ if isinstance(key, str):
137
+ self._get_key = partial(_get_key_with_getter, key)
138
+ elif isinstance(key, Callable):
139
+ self._get_key = key
140
+
122
141
  self._store_map: Dict[Union[Any, int], StoreItem] = {}
123
142
 
124
143
  def __call__(self, fn: Callable[[Any], None]):
125
144
  def build_element(index: int, value):
126
145
  key = self._get_key(index, value)
127
- with VforContainer() as element:
128
- store = VforStore(self._data, index)
146
+
147
+ with self._vfor_container, VforItem() as element:
148
+ store = VforStore(self._data, index) # type: ignore
129
149
  scope = _CLIENT_SCOPE_MANAGER.new_scope()
130
150
 
131
151
  @scope.run
@@ -134,54 +154,46 @@ class vfor(Generic[_T]):
134
154
 
135
155
  return key, element, store, scope
136
156
 
137
- with self._container:
138
- for idx, value in enumerate(self._data.value):
139
- key, element, store, scope = build_element(idx, value)
140
- self._store_map[key] = StoreItem(store, element.id, scope)
157
+ for idx, value in enumerate(to_value(self._data)): # type: ignore
158
+ key, element, store, scope = build_element(idx, value)
159
+ self._store_map[key] = StoreItem(store, element.id, scope)
160
+
161
+ ng_client = context.get_client()
141
162
 
142
163
  @on(self._data, deep=True)
143
164
  def _():
144
165
  data_map = {
145
- self._get_key(idx, d): d for idx, d in enumerate(self._data.value)
166
+ self._get_key(idx, d): d for idx, d in enumerate(to_value(self._data))
146
167
  }
147
168
 
148
- temp_box = ui.element("div")
149
-
150
- element_map: Dict[int, ui.element] = {}
151
- for element in list(self._container):
152
- element.move(temp_box)
153
- element_map[element.id] = element
154
-
155
- new_store_map: Dict[Union[Any, int], StoreItem] = {}
156
-
157
- with self._container:
158
- for idx, (key, value) in enumerate(data_map.items()):
159
- store_item = self._store_map.get(key)
160
- if store_item:
161
- # `data` may have changed the value of a dictionary item,
162
- # so should update the values in the store one by one.
163
- store_item.store.update(idx)
164
- element = element_map.get(store_item.elementId)
165
- assert element
166
- element.move(self._container)
167
-
168
- new_store_map[key] = store_item
169
- else:
170
- # new row item
171
- key, element, store, score = build_element(idx, value)
172
- store_item = StoreItem(store, element.id, score)
173
- element.move(self._container)
174
- new_store_map[key] = store_item
175
-
176
- del_store_items = tuple(
177
- value
169
+ for idx, (key, value) in enumerate(data_map.items()):
170
+ store_item = self._store_map.get(key)
171
+ if store_item:
172
+ # `data` may have changed the value of a dictionary item,
173
+ # so should update the values in the store one by one.
174
+ store_item.store.update(idx)
175
+
176
+ else:
177
+ # new row item
178
+ key, element, store, score = build_element(idx, value)
179
+ self._store_map[key] = StoreItem(store, element.id, score)
180
+
181
+ # remove item
182
+ remove_items = [
183
+ (key, value)
178
184
  for key, value in self._store_map.items()
179
- if key not in new_store_map
185
+ if key not in data_map
186
+ ]
187
+
188
+ for key, item in remove_items:
189
+ target = ng_client.elements.get(item.elementId)
190
+ self._vfor_container.remove(target) # type: ignore
191
+ item.scope.dispose()
192
+ del self._store_map[key]
193
+
194
+ self._vfor_container.update_items(
195
+ [
196
+ {"key": key, "elementId": self._store_map.get(key).elementId} # type: ignore
197
+ for key in data_map.keys()
198
+ ]
180
199
  )
181
-
182
- for store_item in del_store_items:
183
- store_item.scope.dispose()
184
-
185
- self._store_map.clear()
186
- self._store_map = new_store_map
187
- temp_box.delete()
@@ -99,7 +99,7 @@ def is_ref(obj):
99
99
  return signe.is_signal(obj) or isinstance(obj, (RefWrapper))
100
100
 
101
101
 
102
- def to_value(obj: _TMaybeRef[T]) -> T:
102
+ def to_value(obj: Union[_TMaybeRef[T], RefWrapper]) -> T:
103
103
  if is_ref(obj):
104
104
  return obj.value # type: ignore
105
105
  if isinstance(obj, Callable):
@@ -329,7 +329,7 @@ class effect_refreshable:
329
329
 
330
330
 
331
331
  def on(
332
- refs: Union[TGetterOrReadonlyRef, Sequence[TGetterOrReadonlyRef]],
332
+ refs: Union[TGetterOrReadonlyRef, RefWrapper, Sequence[TGetterOrReadonlyRef]],
333
333
  onchanges=False,
334
334
  priority_level=1,
335
335
  effect_kws: Optional[Dict[str, Any]] = None,
@@ -349,8 +349,10 @@ def on(
349
349
 
350
350
  """
351
351
  effect_kws = effect_kws or {}
352
- # if not isinstance(refs, Sequence):
353
- # refs = [refs]
352
+ if not isinstance(refs, Sequence):
353
+ refs = [refs] # type: ignore
354
+
355
+ refs = [(lambda: ref.value) if isinstance(ref, RefWrapper) else ref for ref in refs] # type: ignore
354
356
 
355
357
  effect_kws.update({"priority_level": priority_level})
356
358
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ex4nicegui
3
- Version: 0.6.1
3
+ Version: 0.6.3
4
4
  Summary: Extension library based on nicegui, providing data responsive,BI functionality modules
5
5
  Home-page:
6
6
  Author: carson_jia
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.8
14
14
  Requires-Python: >=3.8
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: signe (>=0.4.10)
17
+ Requires-Dist: signe (>=0.4.14)
18
18
  Requires-Dist: nicegui (>=1.4.0)
19
19
  Requires-Dist: typing-extensions
20
20
  Requires-Dist: executing
@@ -1,4 +1,4 @@
1
- ex4nicegui/__init__.py,sha256=jBlUbAydyR6psfeKjZkCCvG2d3SzdNnCJR4vjdZHF2I,818
1
+ ex4nicegui/__init__.py,sha256=4qmuKMR7xYKdce4byUKEYP8jNnl6o_n-rxHZwFvCYCo,818
2
2
  ex4nicegui/bi/__init__.py,sha256=eu-2CuzzrcHCyKQOfoo87v6C9nSwFDdeLhjY0cRV13M,315
3
3
  ex4nicegui/bi/dataSource.py,sha256=sQzuQvNWBf20n9cz--1OX5gxT6RUAhwrq_xTQEpSa0o,7691
4
4
  ex4nicegui/bi/dataSourceFacade.py,sha256=6NuAyrTIk_L2Tz9IRFpuHKRQY_OwJINDzxoM6JYOc14,8186
@@ -66,15 +66,19 @@ ex4nicegui/libs/gsap/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
66
66
  ex4nicegui/libs/gsap/utils/matrix.js,sha256=77scrxbQZXx4ex5HkvnT9IkhMG1rQoDNp4TSYgUeYVk,15235
67
67
  ex4nicegui/libs/gsap/utils/paths.js,sha256=2SPaRHQ7zgba9cH8hGhkTYPCZdrrEhE2qhh6ECAEvSA,49314
68
68
  ex4nicegui/libs/gsap/utils/strings.js,sha256=47G9slz5ltG9mDSwrfQDtWzzdV5QJ-AIMLRMNK0VSiM,10472
69
- ex4nicegui/reactive/__init__.py,sha256=osdHgTwRTI2jOjHtbOpMjdQlvGiJW-1vaqcPPLhzbdQ,3339
69
+ ex4nicegui/reactive/__init__.py,sha256=PxPRQonNUdgz6Kp6eP0QaYp09tM_zN-S0V9LncYrXS4,3637
70
+ ex4nicegui/reactive/empty.js,sha256=Y-caS4CN8jUq59LgGgcvgfkndze-RgWF_ZMmAcxOrbw,50
71
+ ex4nicegui/reactive/empty.py,sha256=kB851B12V1_VCNsFKW6OmjcdIiuZqGEGjLgA6k6yug8,132
70
72
  ex4nicegui/reactive/fileWatcher.py,sha256=gjeZhgar02f-qGQa47Tj5SMaCP_ftRtSU898XUmXl1U,1472
71
73
  ex4nicegui/reactive/local_file_picker.py,sha256=d3ERoTOGiPGgE3osCdDwklvERqMl_CVbh-qokTLaAuI,6225
72
74
  ex4nicegui/reactive/q_pagination.py,sha256=WcSYpB75NH3lffw3HXwZRcvnh0l6EA1Eqp5bcJKIJaw,1505
73
75
  ex4nicegui/reactive/rxui.py,sha256=gZ8ZEjGuJFKcedEZhcm4PIZguNkY-Wv5yQx80QnsBKI,31
76
+ ex4nicegui/reactive/transitionGroup.js,sha256=rbfNU3Jrz9WFDQih3BgZOgC1MBr6j9cODZ9XggMAaTs,898
77
+ ex4nicegui/reactive/transitionGroup.py,sha256=VWyYL3QUfX6YQXuxwBFF4sJ4P5VP1S-bCjLKUr28KEY,597
74
78
  ex4nicegui/reactive/usePagination.py,sha256=Gop9KV780DcQK5bH4zQZ5sRpV3jOD2sRX_Bpmb1LTFI,2598
75
- ex4nicegui/reactive/utils.py,sha256=GSk_eUH-iNUi18Tg7MsBVnMK5basgX6EQgbSD1zxsZw,3315
76
- ex4nicegui/reactive/vfor.js,sha256=OmpeLZdjV_GiG5HdglMXGBKpgrKYh3UIeugpszAVdWw,52
77
- ex4nicegui/reactive/vfor.py,sha256=hyvbmMJtC8usSYeq023bCL8kOrmnFw09YOS_r6GWBzk,5495
79
+ ex4nicegui/reactive/utils.py,sha256=DZ77y0qQAeHUb52yyCKlLE5_3MQlE_OAUH_M1s31_Qg,4360
80
+ ex4nicegui/reactive/vfor.js,sha256=xtKVUPSN0BP99H0BO6LRUYFqxqTGIRttQh5UjoUTwBc,282
81
+ ex4nicegui/reactive/vfor.py,sha256=MirlPrwDSy0YR85i4wcGGGX4QjuN5iek3fIxhIpFbGo,5786
78
82
  ex4nicegui/reactive/vmodel.py,sha256=SgmchPVfM6uQAH60VlVyNobfuTg3ScuxlXNFJLRD-r8,5091
79
83
  ex4nicegui/reactive/EChartsComponent/ECharts.js,sha256=HMmgXlDl548H4JpozDSbLWv-opvPi3OdJz_XY_MHbnY,3326
80
84
  ex4nicegui/reactive/EChartsComponent/ECharts.py,sha256=hoUZ7h-KOUKNd-1BybSX4xxv33gYuTkky70yEzv-oIA,6344
@@ -89,8 +93,8 @@ ex4nicegui/reactive/mermaid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
89
93
  ex4nicegui/reactive/mermaid/mermaid.js,sha256=Ds5VevGWZE1_N0WKf-uITd8xSCO9gQzVUsmb80EajNY,2308
90
94
  ex4nicegui/reactive/mermaid/mermaid.py,sha256=uP321QiNj_S5E5I2KF9h03WlSFdRITE8tvObGdk6m7M,2144
91
95
  ex4nicegui/reactive/officials/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
92
- ex4nicegui/reactive/officials/aggrid.py,sha256=2HL2eS23jez8zA_N7ph76XqNflwXnwxBt8CR1ycTB2M,2787
93
- ex4nicegui/reactive/officials/base.py,sha256=1d6ePyz4ZiOsZYLKtCr04Aw4_9YyYl2HpCN8PpRJis4,10325
96
+ ex4nicegui/reactive/officials/aggrid.py,sha256=aSaRgpdX3IXlqcq8E1xm3HOD8qDvuwz0H_rU5A7AAEk,2929
97
+ ex4nicegui/reactive/officials/base.py,sha256=V4Bg33sJnlTY5LUGbRphoXtQhVlVyvo_AVJqrmVCLO8,10440
94
98
  ex4nicegui/reactive/officials/button.py,sha256=12FZ2cQGFsTsivSHT9YfDD_wP_D4xrpHHfKmVNNKLJE,1660
95
99
  ex4nicegui/reactive/officials/card.py,sha256=8-tBwm3xfVybolQ87i8lAYUpBV6FdaVdeSH6xu0736U,1275
96
100
  ex4nicegui/reactive/officials/checkbox.py,sha256=BcSbWuMTA2UgwOeuBTtpvzqdOivb2xbkLazfGqwuboo,1562
@@ -116,7 +120,11 @@ ex4nicegui/reactive/officials/row.py,sha256=pwFJBdv3_D5-E-oga-OG5t57ahHcYXGSI_-U
116
120
  ex4nicegui/reactive/officials/select.py,sha256=Fj4V96ju2tpsP1ok0RFokN1K7TxzNS9YLVCz55O788Y,2980
117
121
  ex4nicegui/reactive/officials/slider.py,sha256=6E6UeyX_hXIDz_-0v6WEIfTM5oqzukonRyqDRJHT-PA,2954
118
122
  ex4nicegui/reactive/officials/switch.py,sha256=-QDXbOQllnWRE28pAH74qrgpN1TXs-1snLDqN6pdiVM,1605
119
- ex4nicegui/reactive/officials/table.py,sha256=GiEKBrJhVuyw5icxCDzZtyEE_wrzlaIt2yvWz4teY00,5397
123
+ ex4nicegui/reactive/officials/tab.py,sha256=RaS152hfUBz4aOidahzwy7GPV3jR4eWBWKnVXrITXUw,1796
124
+ ex4nicegui/reactive/officials/tab_panel.py,sha256=96aR6Y8oqVFnZo_U8lX5V8mpXR7YIUldSvQMI3oFYQ0,630
125
+ ex4nicegui/reactive/officials/tab_panels.py,sha256=kHxr5JdMvyW2pnmhT6oHsXu9XxqHC898AVrt9skJk84,1375
126
+ ex4nicegui/reactive/officials/table.py,sha256=lisS7PN-3rUo-7i5Fa7h-nssq1xh0Pjq5tyf5ew0sQo,6042
127
+ ex4nicegui/reactive/officials/tabs.py,sha256=mKIz9qsMeTxeZfpAKk4mErpK4m7XrwFQ-jpYm29P8yw,1232
120
128
  ex4nicegui/reactive/officials/textarea.py,sha256=EeimLMd1oycdhlrzdET29riv9HaSlQyNxvGkXu00nsc,3044
121
129
  ex4nicegui/reactive/officials/upload.py,sha256=WYbbOqAcgnVUuqfF4WAMawldRHB9DthP6NSTyc-4Bwo,2360
122
130
  ex4nicegui/reactive/officials/utils.py,sha256=-fnRjyb4Yx7_6jmVeQIZKXFRNV3BWbI7JR_EemMEoTg,213
@@ -132,9 +140,9 @@ ex4nicegui/utils/clientScope.py,sha256=-KJnrZGqdAQUNNbQbqMAqNzWHCVon-lC3aaQGuaTd
132
140
  ex4nicegui/utils/common.py,sha256=7P0vboDadLun6EMxNi3br9rKJgKt0QT4sy_66cHEwb4,994
133
141
  ex4nicegui/utils/effect.py,sha256=MgvWuAP3OFs2bR4ef6uXPwGCkKORUK-4hmx1oSwl04Y,2310
134
142
  ex4nicegui/utils/scheduler.py,sha256=Wa963Df3UDvWHjXXoVYGIBevIILzCFoz-yAWjvxeyfQ,1218
135
- ex4nicegui/utils/signals.py,sha256=mQgP80oQNAFbHt-OV1j96oCTJoio6pgQ59cqsUUSFl0,11195
136
- ex4nicegui-0.6.1.dist-info/LICENSE,sha256=0KDDElS2dl-HIsWvbpy8ywbLzJMBFzXLev57LnMIZXs,1094
137
- ex4nicegui-0.6.1.dist-info/METADATA,sha256=27I2vQWe3boznUiasrC0nMy4oJdUZfzsF471ASVL5hM,27618
138
- ex4nicegui-0.6.1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
139
- ex4nicegui-0.6.1.dist-info/top_level.txt,sha256=VFwMiO9AFjj5rfLMJwN1ipLRASk9fJXB8tM6DNrpvPQ,11
140
- ex4nicegui-0.6.1.dist-info/RECORD,,
143
+ ex4nicegui/utils/signals.py,sha256=-80iaAzIyUDR_7LBr8TfOY59Fj4dSq92klWXoRy538g,11346
144
+ ex4nicegui-0.6.3.dist-info/LICENSE,sha256=0KDDElS2dl-HIsWvbpy8ywbLzJMBFzXLev57LnMIZXs,1094
145
+ ex4nicegui-0.6.3.dist-info/METADATA,sha256=wmIPVn_YKEZWuMghpS4n3k86j___PqFROxrmOR9CQrc,27618
146
+ ex4nicegui-0.6.3.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
147
+ ex4nicegui-0.6.3.dist-info/top_level.txt,sha256=VFwMiO9AFjj5rfLMJwN1ipLRASk9fJXB8tM6DNrpvPQ,11
148
+ ex4nicegui-0.6.3.dist-info/RECORD,,