ex4nicegui 0.7.1__py3-none-any.whl → 0.8.1__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/reactive/EChartsComponent/ECharts.js +2 -3
- ex4nicegui/reactive/EChartsComponent/ECharts.py +2 -4
- ex4nicegui/reactive/__init__.py +2 -1
- ex4nicegui/reactive/base.py +108 -50
- ex4nicegui/reactive/mermaid/mermaid.py +2 -5
- ex4nicegui/reactive/mixins/backgroundColor.py +4 -8
- ex4nicegui/reactive/mixins/disableable.py +4 -8
- ex4nicegui/reactive/mixins/flexLayout.py +51 -0
- ex4nicegui/reactive/mixins/textColor.py +8 -10
- ex4nicegui/reactive/mixins/value_element.py +27 -0
- ex4nicegui/reactive/officials/card.py +32 -2
- ex4nicegui/reactive/officials/checkbox.py +7 -12
- ex4nicegui/reactive/officials/chip.py +11 -1
- ex4nicegui/reactive/officials/circular_progress.py +7 -11
- ex4nicegui/reactive/officials/color_picker.py +4 -10
- ex4nicegui/reactive/officials/column.py +19 -11
- ex4nicegui/reactive/officials/date.py +4 -11
- ex4nicegui/reactive/officials/dialog.py +4 -11
- ex4nicegui/reactive/officials/echarts.py +9 -7
- ex4nicegui/reactive/officials/expansion.py +4 -11
- ex4nicegui/reactive/officials/input.py +13 -13
- ex4nicegui/reactive/officials/knob.py +4 -13
- ex4nicegui/reactive/officials/linear_progress.py +6 -11
- ex4nicegui/reactive/officials/number.py +7 -11
- ex4nicegui/reactive/officials/radio.py +4 -12
- ex4nicegui/reactive/officials/row.py +18 -11
- ex4nicegui/reactive/officials/slider.py +4 -12
- ex4nicegui/reactive/officials/switch.py +5 -12
- ex4nicegui/reactive/officials/tab_panels.py +4 -8
- ex4nicegui/reactive/officials/tabs.py +4 -9
- ex4nicegui/reactive/officials/textarea.py +6 -12
- ex4nicegui/reactive/services/reactive_service.py +2 -1
- ex4nicegui/reactive/vfor.py +4 -0
- ex4nicegui/reactive/view_model.py +147 -6
- ex4nicegui/utils/proxy/__init__.py +19 -0
- ex4nicegui/utils/proxy/base.py +9 -0
- ex4nicegui/utils/proxy/bool.py +58 -0
- ex4nicegui/utils/proxy/date.py +88 -0
- ex4nicegui/utils/proxy/descriptor.py +126 -0
- ex4nicegui/utils/proxy/dict.py +110 -0
- ex4nicegui/utils/proxy/float.py +153 -0
- ex4nicegui/utils/proxy/int.py +223 -0
- ex4nicegui/utils/proxy/list.py +147 -0
- ex4nicegui/utils/proxy/string.py +430 -0
- ex4nicegui/utils/proxy/utils.py +6 -0
- ex4nicegui/utils/signals.py +9 -1
- {ex4nicegui-0.7.1.dist-info → ex4nicegui-0.8.1.dist-info}/METADATA +497 -244
- {ex4nicegui-0.7.1.dist-info → ex4nicegui-0.8.1.dist-info}/RECORD +50 -37
- {ex4nicegui-0.7.1.dist-info → ex4nicegui-0.8.1.dist-info}/LICENSE +0 -0
- {ex4nicegui-0.7.1.dist-info → ex4nicegui-0.8.1.dist-info}/WHEEL +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import 'echarts'
|
|
1
2
|
import { convertDynamicProperties } from "../../static/utils/dynamic_properties.js";
|
|
2
3
|
|
|
3
4
|
function collectMapRegisterTask() {
|
|
@@ -54,10 +55,8 @@ const mapRegisterTasks = collectMapRegisterTask();
|
|
|
54
55
|
export default {
|
|
55
56
|
template: "<div></div>",
|
|
56
57
|
async mounted() {
|
|
57
|
-
await
|
|
58
|
-
|
|
58
|
+
await new Promise((resolve) => setTimeout(resolve, 0)); // wait for Tailwind classes to be applied
|
|
59
59
|
this.chart = echarts.init(this.$el, this.theme);
|
|
60
|
-
|
|
61
60
|
this.resizeObs = new ResizeObserver(this.chart.resize)
|
|
62
61
|
|
|
63
62
|
// Prevent interruption of chart animations due to resize operations.
|
|
@@ -23,7 +23,7 @@ NG_ROOT = Path(nicegui.__file__).parent / "elements"
|
|
|
23
23
|
libraries = [NG_ROOT / "lib/echarts/echarts.min.js"]
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
class echarts(Element, component="ECharts.js",
|
|
26
|
+
class echarts(Element, component="ECharts.js", dependencies=libraries): # type: ignore
|
|
27
27
|
def __init__(
|
|
28
28
|
self,
|
|
29
29
|
options: Optional[dict] = None,
|
|
@@ -112,7 +112,7 @@ class echarts(Element, component="ECharts.js", libraries=libraries): # type: ig
|
|
|
112
112
|
self.run_method("echarts_on", ui_event_name, query)
|
|
113
113
|
|
|
114
114
|
def run_chart_method(
|
|
115
|
-
self, name: str, *args, timeout: float = 1
|
|
115
|
+
self, name: str, *args, timeout: float = 1
|
|
116
116
|
) -> AwaitableResponse:
|
|
117
117
|
"""Run a method of the JSONEditor instance.
|
|
118
118
|
|
|
@@ -124,7 +124,6 @@ class echarts(Element, component="ECharts.js", libraries=libraries): # type: ig
|
|
|
124
124
|
:param name: name of the method (a prefix ":" indicates that the arguments are JavaScript expressions)
|
|
125
125
|
:param args: arguments to pass to the method (Python objects or JavaScript expressions)
|
|
126
126
|
:param timeout: timeout in seconds (default: 1 second)
|
|
127
|
-
:param check_interval: interval in seconds to check for a response (default: 0.01 seconds)
|
|
128
127
|
|
|
129
128
|
:return: AwaitableResponse that can be awaited to get the result of the method call
|
|
130
129
|
"""
|
|
@@ -133,5 +132,4 @@ class echarts(Element, component="ECharts.js", libraries=libraries): # type: ig
|
|
|
133
132
|
name,
|
|
134
133
|
*args,
|
|
135
134
|
timeout=timeout,
|
|
136
|
-
check_interval=check_interval,
|
|
137
135
|
)
|
ex4nicegui/reactive/__init__.py
CHANGED
|
@@ -70,7 +70,7 @@ from .mermaid.mermaid import Mermaid as mermaid
|
|
|
70
70
|
from .officials.dialog import DialogBindableUi as dialog
|
|
71
71
|
from .vfor import vfor, VforStore
|
|
72
72
|
from .vmodel import vmodel
|
|
73
|
-
from .view_model import ViewModel, var, cached_var
|
|
73
|
+
from .view_model import ViewModel, var, cached_var, list_var
|
|
74
74
|
|
|
75
75
|
pagination = q_pagination
|
|
76
76
|
|
|
@@ -97,6 +97,7 @@ __all__ = [
|
|
|
97
97
|
"ViewModel",
|
|
98
98
|
"var",
|
|
99
99
|
"cached_var",
|
|
100
|
+
"list_var",
|
|
100
101
|
"html",
|
|
101
102
|
"aggrid",
|
|
102
103
|
"button",
|
ex4nicegui/reactive/base.py
CHANGED
|
@@ -48,6 +48,8 @@ _T_bind_classes_type = Union[
|
|
|
48
48
|
_T_bind_classes_type_ref_dict,
|
|
49
49
|
_T_bind_classes_type_single,
|
|
50
50
|
_T_bind_classes_type_array,
|
|
51
|
+
Dict[str, bool],
|
|
52
|
+
List[str],
|
|
51
53
|
]
|
|
52
54
|
|
|
53
55
|
|
|
@@ -150,6 +152,56 @@ class BindableUi(Generic[TWidget]):
|
|
|
150
152
|
"""
|
|
151
153
|
return self.element.remove(element)
|
|
152
154
|
|
|
155
|
+
@overload
|
|
156
|
+
def bind_props(self, props: Dict[str, TMaybeRef[Any]]) -> Self: ...
|
|
157
|
+
|
|
158
|
+
@overload
|
|
159
|
+
def bind_props(self, props: TMaybeRef[str]) -> Self: ...
|
|
160
|
+
|
|
161
|
+
def bind_props(self, props: Union[Dict[str, TMaybeRef[Any]], TMaybeRef[str]]):
|
|
162
|
+
"""data binding is manipulating an element's props
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
props (Union[Dict[str, TMaybeRef[Any]], TMaybeRef[str]]): dict of prop name and ref value
|
|
166
|
+
|
|
167
|
+
## usage
|
|
168
|
+
|
|
169
|
+
.. code-block:: python
|
|
170
|
+
outlined = to_ref(True)
|
|
171
|
+
size = to_ref("xs")
|
|
172
|
+
|
|
173
|
+
def props_str():
|
|
174
|
+
return f'{"flat" if flat.value else ""} {"size=" + size.value}'
|
|
175
|
+
|
|
176
|
+
rxui.button("click me").bind_props(props_str)
|
|
177
|
+
rxui.button("click me").bind_props({
|
|
178
|
+
"outlined": outlined,
|
|
179
|
+
"size": size,
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
"""
|
|
183
|
+
if isinstance(props, dict):
|
|
184
|
+
|
|
185
|
+
def props_str():
|
|
186
|
+
props_dict = (
|
|
187
|
+
f"""{name if isinstance(raw_value,bool) else f"{name}='{raw_value}'"}"""
|
|
188
|
+
for name, value in props.items()
|
|
189
|
+
if (raw_value := to_value(value))
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
return " ".join(props_dict)
|
|
193
|
+
|
|
194
|
+
self._bind_props_for_str_fn(props_str)
|
|
195
|
+
else:
|
|
196
|
+
self._bind_props_for_str_fn(props)
|
|
197
|
+
|
|
198
|
+
return self
|
|
199
|
+
|
|
200
|
+
def _bind_props_for_str_fn(self, props_str: TMaybeRef[str]):
|
|
201
|
+
@self._ui_signal_on(props_str, onchanges=False, deep=False)
|
|
202
|
+
def _(state: WatchedState):
|
|
203
|
+
self.props(add=state.current, remove=state.previous)
|
|
204
|
+
|
|
153
205
|
def bind_prop(self, prop: str, value: TGetterOrReadonlyRef[Any]):
|
|
154
206
|
"""data binding is manipulating an element's property
|
|
155
207
|
|
|
@@ -179,7 +231,7 @@ class BindableUi(Generic[TWidget]):
|
|
|
179
231
|
|
|
180
232
|
return self
|
|
181
233
|
|
|
182
|
-
def bind_visible(self, value:
|
|
234
|
+
def bind_visible(self, value: TMaybeRef[bool]):
|
|
183
235
|
@self._ui_effect
|
|
184
236
|
def _():
|
|
185
237
|
element = cast(ui.element, self.element)
|
|
@@ -187,7 +239,7 @@ class BindableUi(Generic[TWidget]):
|
|
|
187
239
|
|
|
188
240
|
return self
|
|
189
241
|
|
|
190
|
-
def bind_not_visible(self, value:
|
|
242
|
+
def bind_not_visible(self, value: TMaybeRef[bool]):
|
|
191
243
|
return self.bind_visible(lambda: not to_value(value))
|
|
192
244
|
|
|
193
245
|
def on(
|
|
@@ -216,26 +268,28 @@ class BindableUi(Generic[TWidget]):
|
|
|
216
268
|
cast(ui.element, self.element).clear()
|
|
217
269
|
|
|
218
270
|
@overload
|
|
219
|
-
def bind_classes(self, classes: Dict[str, TGetterOrReadonlyRef[bool]]) -> Self:
|
|
220
|
-
|
|
271
|
+
def bind_classes(self, classes: Dict[str, TGetterOrReadonlyRef[bool]]) -> Self: ...
|
|
272
|
+
|
|
273
|
+
@overload
|
|
274
|
+
def bind_classes(self, classes: Dict[str, bool]) -> Self: ...
|
|
221
275
|
|
|
222
276
|
@overload
|
|
223
|
-
def bind_classes(self, classes: TGetterOrReadonlyRef[Dict[str, bool]]) -> Self:
|
|
224
|
-
...
|
|
277
|
+
def bind_classes(self, classes: TGetterOrReadonlyRef[Dict[str, bool]]) -> Self: ...
|
|
225
278
|
|
|
226
279
|
@overload
|
|
227
|
-
def bind_classes(self, classes: List[TGetterOrReadonlyRef[str]]) -> Self:
|
|
228
|
-
...
|
|
280
|
+
def bind_classes(self, classes: List[TGetterOrReadonlyRef[str]]) -> Self: ...
|
|
229
281
|
|
|
230
282
|
@overload
|
|
231
|
-
def bind_classes(self, classes:
|
|
232
|
-
|
|
283
|
+
def bind_classes(self, classes: List[str]) -> Self: ...
|
|
284
|
+
|
|
285
|
+
@overload
|
|
286
|
+
def bind_classes(self, classes: TGetterOrReadonlyRef[str]) -> Self: ...
|
|
233
287
|
|
|
234
288
|
def bind_classes(self, classes: _T_bind_classes_type) -> Self:
|
|
235
289
|
"""data binding is manipulating an element's class list
|
|
236
290
|
|
|
237
|
-
@see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#
|
|
238
|
-
@中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main
|
|
291
|
+
@see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#bind_classes
|
|
292
|
+
@中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#bind_classes
|
|
239
293
|
|
|
240
294
|
Args:
|
|
241
295
|
classes (_T_bind_classes_type): dict of refs | ref to dict | str ref | list of refs
|
|
@@ -271,52 +325,51 @@ class BindableUi(Generic[TWidget]):
|
|
|
271
325
|
|
|
272
326
|
"""
|
|
273
327
|
if isinstance(classes, dict):
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
self.classes(add=name)
|
|
280
|
-
else:
|
|
281
|
-
self.classes(remove=name)
|
|
328
|
+
self._bind_classes_for_str_fn(
|
|
329
|
+
lambda: " ".join(
|
|
330
|
+
name for name, value in classes.items() if to_value(value)
|
|
331
|
+
)
|
|
332
|
+
)
|
|
282
333
|
|
|
334
|
+
elif isinstance(classes, list):
|
|
335
|
+
self._bind_classes_for_str_fn(
|
|
336
|
+
lambda: " ".join(to_value(c) for c in classes)
|
|
337
|
+
)
|
|
283
338
|
elif is_ref(classes) or isinstance(classes, Callable):
|
|
284
339
|
ref_obj = to_value(classes) # type: ignore
|
|
285
340
|
|
|
286
341
|
if isinstance(ref_obj, dict):
|
|
287
342
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
self.classes(remove=name)
|
|
295
|
-
else:
|
|
296
|
-
self._bind_single_class(cast(_T_bind_classes_type_single, classes))
|
|
343
|
+
def classes_str():
|
|
344
|
+
return " ".join(
|
|
345
|
+
name
|
|
346
|
+
for name, value in to_value(classes).items() # type: ignore
|
|
347
|
+
if to_value(value)
|
|
348
|
+
)
|
|
297
349
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
350
|
+
self._bind_classes_for_str_fn(classes_str)
|
|
351
|
+
|
|
352
|
+
else:
|
|
353
|
+
self._bind_classes_for_str_fn(classes) # type: ignore
|
|
301
354
|
|
|
302
355
|
return self
|
|
303
356
|
|
|
304
|
-
def
|
|
305
|
-
|
|
357
|
+
def _bind_classes_for_str_fn(self, classes_str: TGetterOrReadonlyRef[str]):
|
|
358
|
+
@self._ui_signal_on(classes_str, onchanges=False, deep=False)
|
|
359
|
+
def _(state: WatchedState):
|
|
360
|
+
self.classes(add=state.current, remove=state.previous)
|
|
306
361
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
self.classes(add=state.current, remove=state.previous)
|
|
310
|
-
else:
|
|
311
|
-
self.classes(class_name) # type: ignore
|
|
362
|
+
@overload
|
|
363
|
+
def bind_style(self, style: TMaybeRef[str]) -> Self: ...
|
|
312
364
|
|
|
313
|
-
|
|
365
|
+
@overload
|
|
366
|
+
def bind_style(self, style: Dict[str, TMaybeRef[Any]]) -> Self: ...
|
|
314
367
|
|
|
315
|
-
def bind_style(self, style: Dict[str,
|
|
368
|
+
def bind_style(self, style: Union[TMaybeRef[str], Dict[str, TMaybeRef[Any]]]):
|
|
316
369
|
"""data binding is manipulating an element's style
|
|
317
370
|
|
|
318
|
-
@see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#
|
|
319
|
-
@中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#
|
|
371
|
+
@see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#bind_style
|
|
372
|
+
@中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#bind_style
|
|
320
373
|
|
|
321
374
|
Args:
|
|
322
375
|
style (Dict[str, Union[ReadonlyRef[str], Ref[str]]]): dict of style name and ref value
|
|
@@ -337,16 +390,21 @@ class BindableUi(Generic[TWidget]):
|
|
|
337
390
|
|
|
338
391
|
"""
|
|
339
392
|
if isinstance(style, dict):
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
393
|
+
self._bind_style_for_str_fn(
|
|
394
|
+
lambda: ";".join(
|
|
395
|
+
f"{name}:{to_value(value)}" for name, value in style.items()
|
|
396
|
+
)
|
|
397
|
+
)
|
|
398
|
+
else:
|
|
399
|
+
self._bind_style_for_str_fn(style)
|
|
347
400
|
|
|
348
401
|
return self
|
|
349
402
|
|
|
403
|
+
def _bind_style_for_str_fn(self, style_str: TMaybeRef[str]):
|
|
404
|
+
@self._ui_signal_on(style_str, onchanges=False, deep=False)
|
|
405
|
+
def _(state: WatchedState):
|
|
406
|
+
self.style(add=state.current, remove=state.previous)
|
|
407
|
+
|
|
350
408
|
def scoped_style(self, selector: str, style: Union[str, Path]):
|
|
351
409
|
"""add scoped style to the element
|
|
352
410
|
|
|
@@ -10,11 +10,9 @@ NG_ROOT = Path(nicegui.__file__).parent / "elements"
|
|
|
10
10
|
|
|
11
11
|
EX4_LIBS_ROOT = Path(__file__).parent.parent.parent / "libs"
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
dependencies = [
|
|
14
14
|
NG_ROOT / "lib/mermaid/mermaid.esm.min.mjs",
|
|
15
15
|
EX4_LIBS_ROOT / "d3/*.js",
|
|
16
|
-
]
|
|
17
|
-
extra_libraries = [
|
|
18
16
|
NG_ROOT / "lib/mermaid/*.js",
|
|
19
17
|
]
|
|
20
18
|
|
|
@@ -27,8 +25,7 @@ class NodeClickEventArguments(UiEventArguments):
|
|
|
27
25
|
class Mermaid( # type: ignore
|
|
28
26
|
ContentElement,
|
|
29
27
|
component="mermaid.js",
|
|
30
|
-
|
|
31
|
-
extra_libraries=extra_libraries, # type: ignore
|
|
28
|
+
dependencies=dependencies, # type: ignore
|
|
32
29
|
):
|
|
33
30
|
CONTENT_PROP = "content"
|
|
34
31
|
|
|
@@ -5,10 +5,7 @@ from typing import (
|
|
|
5
5
|
)
|
|
6
6
|
|
|
7
7
|
import signe
|
|
8
|
-
from ex4nicegui.utils.signals import
|
|
9
|
-
TGetterOrReadonlyRef,
|
|
10
|
-
WatchedState,
|
|
11
|
-
)
|
|
8
|
+
from ex4nicegui.utils.signals import WatchedState, TMaybeRef
|
|
12
9
|
from nicegui import ui
|
|
13
10
|
from ex4nicegui.reactive.systems import color_system
|
|
14
11
|
|
|
@@ -17,10 +14,9 @@ class BackgroundColorableMixin(Protocol):
|
|
|
17
14
|
_ui_signal_on: Callable[[Callable[..., Any]], signe.Effect[None]]
|
|
18
15
|
|
|
19
16
|
@property
|
|
20
|
-
def element(self) -> ui.element:
|
|
21
|
-
...
|
|
17
|
+
def element(self) -> ui.element: ...
|
|
22
18
|
|
|
23
|
-
def _bind_background_color(self, value:
|
|
19
|
+
def _bind_background_color(self, value: TMaybeRef[str]):
|
|
24
20
|
@self._ui_signal_on(value, onchanges=False) # type: ignore
|
|
25
21
|
def _(state: WatchedState):
|
|
26
22
|
if state.previous is not None:
|
|
@@ -30,7 +26,7 @@ class BackgroundColorableMixin(Protocol):
|
|
|
30
26
|
|
|
31
27
|
self.element.update()
|
|
32
28
|
|
|
33
|
-
def bind_color(self, color:
|
|
29
|
+
def bind_color(self, color: TMaybeRef[str]):
|
|
34
30
|
"""bind color to the element
|
|
35
31
|
|
|
36
32
|
Args:
|
|
@@ -8,10 +8,7 @@ from typing import (
|
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
import signe
|
|
11
|
-
from ex4nicegui.utils.signals import
|
|
12
|
-
TGetterOrReadonlyRef,
|
|
13
|
-
to_value,
|
|
14
|
-
)
|
|
11
|
+
from ex4nicegui.utils.signals import to_value, TMaybeRef
|
|
15
12
|
from nicegui.elements.mixins.disableable_element import DisableableElement
|
|
16
13
|
|
|
17
14
|
|
|
@@ -22,10 +19,9 @@ class DisableableMixin(Protocol):
|
|
|
22
19
|
_ui_effect: Callable[[Callable[..., Any]], signe.Effect[None]]
|
|
23
20
|
|
|
24
21
|
@property
|
|
25
|
-
def element(self) -> DisableableElement:
|
|
26
|
-
...
|
|
22
|
+
def element(self) -> DisableableElement: ...
|
|
27
23
|
|
|
28
|
-
def bind_enabled(self, value:
|
|
24
|
+
def bind_enabled(self, value: TMaybeRef[bool]):
|
|
29
25
|
@self._ui_effect
|
|
30
26
|
def _():
|
|
31
27
|
raw_value = to_value(value)
|
|
@@ -34,7 +30,7 @@ class DisableableMixin(Protocol):
|
|
|
34
30
|
|
|
35
31
|
return self
|
|
36
32
|
|
|
37
|
-
def bind_disable(self, value:
|
|
33
|
+
def bind_disable(self, value: TMaybeRef[bool]):
|
|
38
34
|
@self._ui_effect
|
|
39
35
|
def _():
|
|
40
36
|
raw_value = not to_value(value)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
Any,
|
|
3
|
+
Callable,
|
|
4
|
+
Protocol,
|
|
5
|
+
)
|
|
6
|
+
from typing_extensions import Self
|
|
7
|
+
import signe
|
|
8
|
+
from ex4nicegui.utils.signals import to_value, TMaybeRef
|
|
9
|
+
from nicegui import ui
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FlexWrapMixin(Protocol):
|
|
13
|
+
_ui_signal_on: Callable[[Callable[..., Any]], signe.Effect[None]]
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def element(self) -> ui.element:
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
def bind_style(self, classes) -> Self:
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
def bind_wrap(self, value: TMaybeRef[bool]) -> Self:
|
|
23
|
+
return self.bind_style(
|
|
24
|
+
{"flex-wrap": lambda: "wrap" if to_value(value) else "nowrap"}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def _bind_specified_props(self, prop: str, value: TMaybeRef[Any]):
|
|
28
|
+
if prop == "wrap":
|
|
29
|
+
return self.bind_wrap(value)
|
|
30
|
+
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FlexAlignItemsMixin(Protocol):
|
|
35
|
+
_ui_signal_on: Callable[[Callable[..., Any]], signe.Effect[None]]
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def element(self) -> ui.element:
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
def bind_classes(self, classes) -> Self:
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
def bind_align_items(self, value: TMaybeRef[str]):
|
|
45
|
+
return self.bind_classes(lambda: f"items-{to_value(value)}")
|
|
46
|
+
|
|
47
|
+
def _bind_specified_props(self, prop: str, value: TMaybeRef[Any]):
|
|
48
|
+
if prop == "align-items":
|
|
49
|
+
return self.bind_align_items(value)
|
|
50
|
+
|
|
51
|
+
return None
|
|
@@ -9,6 +9,7 @@ import signe
|
|
|
9
9
|
from ex4nicegui.utils.signals import (
|
|
10
10
|
TGetterOrReadonlyRef,
|
|
11
11
|
WatchedState,
|
|
12
|
+
TMaybeRef,
|
|
12
13
|
)
|
|
13
14
|
from nicegui import ui
|
|
14
15
|
from ex4nicegui.reactive.systems import color_system
|
|
@@ -18,10 +19,9 @@ class TextColorableMixin(Protocol):
|
|
|
18
19
|
_ui_signal_on: Callable[[Callable[[TGetterOrReadonlyRef[str]], Any]], signe.Effect]
|
|
19
20
|
|
|
20
21
|
@property
|
|
21
|
-
def element(self) -> ui.element:
|
|
22
|
-
...
|
|
22
|
+
def element(self) -> ui.element: ...
|
|
23
23
|
|
|
24
|
-
def _bind_text_color(self, color:
|
|
24
|
+
def _bind_text_color(self, color: TMaybeRef[str]):
|
|
25
25
|
@self._ui_signal_on(color, onchanges=False) # type: ignore
|
|
26
26
|
def _(state: WatchedState):
|
|
27
27
|
if state.previous is not None:
|
|
@@ -31,7 +31,7 @@ class TextColorableMixin(Protocol):
|
|
|
31
31
|
|
|
32
32
|
self.element.update()
|
|
33
33
|
|
|
34
|
-
def bind_color(self, color:
|
|
34
|
+
def bind_color(self, color: TMaybeRef[str]):
|
|
35
35
|
"""bind text color to the element
|
|
36
36
|
|
|
37
37
|
Args:
|
|
@@ -46,16 +46,14 @@ class HtmlTextColorableMixin(Protocol):
|
|
|
46
46
|
_ui_signal_on: Callable[[Callable[[TGetterOrReadonlyRef[str]], Any]], signe.Effect]
|
|
47
47
|
|
|
48
48
|
@property
|
|
49
|
-
def element(self) -> ui.element:
|
|
50
|
-
...
|
|
49
|
+
def element(self) -> ui.element: ...
|
|
51
50
|
|
|
52
|
-
def bind_style(self, style: Dict[str,
|
|
53
|
-
...
|
|
51
|
+
def bind_style(self, style: Dict[str, TMaybeRef[Any]]): ...
|
|
54
52
|
|
|
55
|
-
def _bind_text_color(self, value:
|
|
53
|
+
def _bind_text_color(self, value: TMaybeRef[str]):
|
|
56
54
|
return self.bind_style({"color": value})
|
|
57
55
|
|
|
58
|
-
def bind_color(self, color:
|
|
56
|
+
def bind_color(self, color: TMaybeRef[str]):
|
|
59
57
|
"""bind text color to the element
|
|
60
58
|
|
|
61
59
|
Args:
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import Any, Callable, Protocol, Generic, TypeVar
|
|
2
|
+
import signe
|
|
3
|
+
from ex4nicegui.utils.signals import to_value, TMaybeRef
|
|
4
|
+
from nicegui.elements.mixins.value_element import ValueElement
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T", contravariant=True)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ValueElementMixin(Protocol, Generic[T]):
|
|
10
|
+
_ui_signal_on: Callable[[Callable[..., Any]], signe.Effect[None]]
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def element(self) -> ValueElement:
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
def bind_value(self, value: TMaybeRef[T]):
|
|
17
|
+
@self._ui_signal_on(value) # type: ignore
|
|
18
|
+
def _():
|
|
19
|
+
self.element.set_value(to_value(value))
|
|
20
|
+
|
|
21
|
+
return self
|
|
22
|
+
|
|
23
|
+
def _bind_specified_props(self, prop: str, value: TMaybeRef[T]):
|
|
24
|
+
if prop == "value":
|
|
25
|
+
return self.bind_value(value)
|
|
26
|
+
|
|
27
|
+
return None
|
|
@@ -1,18 +1,48 @@
|
|
|
1
1
|
from typing import (
|
|
2
2
|
Any,
|
|
3
|
+
Literal,
|
|
4
|
+
Optional,
|
|
3
5
|
)
|
|
4
6
|
from nicegui import ui
|
|
5
7
|
from .base import BindableUi
|
|
8
|
+
from ex4nicegui.reactive.mixins.flexLayout import FlexAlignItemsMixin
|
|
9
|
+
from ex4nicegui.utils.signals import TMaybeRef
|
|
10
|
+
from ex4nicegui.reactive.services.reactive_service import ParameterClassifier
|
|
6
11
|
|
|
7
12
|
|
|
8
|
-
class CardBindableUi(BindableUi[ui.card]):
|
|
13
|
+
class CardBindableUi(BindableUi[ui.card], FlexAlignItemsMixin):
|
|
9
14
|
def __init__(
|
|
10
15
|
self,
|
|
16
|
+
align_items: Optional[
|
|
17
|
+
TMaybeRef[Literal["start", "end", "center", "baseline", "stretch"]]
|
|
18
|
+
] = None,
|
|
11
19
|
) -> None:
|
|
12
|
-
|
|
20
|
+
"""Card
|
|
21
|
+
|
|
22
|
+
This element is based on Quasar's `QCard <https://quasar.dev/vue-components/card>`_ component.
|
|
23
|
+
It provides a container with a dropped shadow.
|
|
24
|
+
|
|
25
|
+
Note:
|
|
26
|
+
In contrast to this element,
|
|
27
|
+
the original QCard has no padding by default and hides outer borders and shadows of nested elements.
|
|
28
|
+
If you want the original behavior, use the `tight` method.
|
|
29
|
+
|
|
30
|
+
:param align_items: alignment of the items in the card ("start", "end", "center", "baseline", or "stretch"; default: `None`)
|
|
31
|
+
"""
|
|
32
|
+
pc = ParameterClassifier(locals(), maybeRefs=["wrap", "align_items"], events=[])
|
|
33
|
+
element = ui.card(**pc.get_values_kws())
|
|
13
34
|
|
|
14
35
|
super().__init__(element)
|
|
15
36
|
|
|
37
|
+
for key, value in pc.get_bindings().items():
|
|
38
|
+
self.bind_prop(key, value) # type: ignore
|
|
39
|
+
|
|
40
|
+
def bind_prop(self, prop: str, value: TMaybeRef):
|
|
41
|
+
if FlexAlignItemsMixin._bind_specified_props(self, prop, value):
|
|
42
|
+
return self
|
|
43
|
+
|
|
44
|
+
return super().bind_prop(prop, value)
|
|
45
|
+
|
|
16
46
|
def __enter__(self):
|
|
17
47
|
self.element.__enter__()
|
|
18
48
|
return self
|
|
@@ -8,15 +8,18 @@ from ex4nicegui.reactive.services.reactive_service import ParameterClassifier
|
|
|
8
8
|
from ex4nicegui.utils.signals import (
|
|
9
9
|
TGetterOrReadonlyRef,
|
|
10
10
|
_TMaybeRef as TMaybeRef,
|
|
11
|
-
to_value,
|
|
12
11
|
)
|
|
13
12
|
from nicegui import ui
|
|
14
13
|
from .base import BindableUi, DisableableMixin
|
|
14
|
+
from ex4nicegui.reactive.mixins.value_element import ValueElementMixin
|
|
15
|
+
|
|
15
16
|
|
|
16
17
|
T = TypeVar("T")
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
class CheckboxBindableUi(
|
|
20
|
+
class CheckboxBindableUi(
|
|
21
|
+
BindableUi[ui.checkbox], DisableableMixin, ValueElementMixin[bool]
|
|
22
|
+
):
|
|
20
23
|
def __init__(
|
|
21
24
|
self,
|
|
22
25
|
text: TMaybeRef[str] = "",
|
|
@@ -32,7 +35,6 @@ class CheckboxBindableUi(BindableUi[ui.checkbox], DisableableMixin):
|
|
|
32
35
|
)
|
|
33
36
|
|
|
34
37
|
value_kws = pc.get_values_kws()
|
|
35
|
-
|
|
36
38
|
element = ui.checkbox(**value_kws)
|
|
37
39
|
super().__init__(element) # type: ignore
|
|
38
40
|
|
|
@@ -44,14 +46,7 @@ class CheckboxBindableUi(BindableUi[ui.checkbox], DisableableMixin):
|
|
|
44
46
|
return self.element.value
|
|
45
47
|
|
|
46
48
|
def bind_prop(self, prop: str, value: TGetterOrReadonlyRef):
|
|
47
|
-
if prop
|
|
48
|
-
return self
|
|
49
|
+
if ValueElementMixin._bind_specified_props(self, prop, value):
|
|
50
|
+
return self
|
|
49
51
|
|
|
50
52
|
return super().bind_prop(prop, value)
|
|
51
|
-
|
|
52
|
-
def bind_value(self, value: TGetterOrReadonlyRef[bool]):
|
|
53
|
-
@self._ui_signal_on(value)
|
|
54
|
-
def _():
|
|
55
|
-
self.element.set_value(to_value(value))
|
|
56
|
-
|
|
57
|
-
return self
|
|
@@ -9,12 +9,14 @@ from nicegui import ui
|
|
|
9
9
|
from .base import BindableUi
|
|
10
10
|
from ex4nicegui.reactive.mixins.backgroundColor import BackgroundColorableMixin
|
|
11
11
|
from ex4nicegui.reactive.mixins.textColor import TextColorableMixin
|
|
12
|
+
from ex4nicegui.reactive.mixins.value_element import ValueElementMixin
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class ChipBindableUi(
|
|
15
16
|
BindableUi[ui.chip],
|
|
16
17
|
BackgroundColorableMixin,
|
|
17
18
|
TextColorableMixin,
|
|
19
|
+
ValueElementMixin[bool],
|
|
18
20
|
):
|
|
19
21
|
def __init__(
|
|
20
22
|
self,
|
|
@@ -29,6 +31,7 @@ class ChipBindableUi(
|
|
|
29
31
|
on_selection_change: Optional[Callable[..., Any]] = None,
|
|
30
32
|
removable: TMaybeRef[bool] = False,
|
|
31
33
|
on_value_change: Optional[Callable[..., Any]] = None,
|
|
34
|
+
value: TMaybeRef[bool] = True,
|
|
32
35
|
) -> None:
|
|
33
36
|
pc = ParameterClassifier(
|
|
34
37
|
locals(),
|
|
@@ -40,11 +43,15 @@ class ChipBindableUi(
|
|
|
40
43
|
"selectable",
|
|
41
44
|
"selected",
|
|
42
45
|
"removable",
|
|
46
|
+
"value",
|
|
43
47
|
],
|
|
48
|
+
v_model=("value", "on_value_change"),
|
|
44
49
|
events=["on_click", "on_selection_change", "on_value_change"],
|
|
45
50
|
)
|
|
46
51
|
|
|
47
|
-
|
|
52
|
+
kws = pc.get_values_kws()
|
|
53
|
+
kws.pop("value", None)
|
|
54
|
+
element = ui.chip(**kws)
|
|
48
55
|
super().__init__(element)
|
|
49
56
|
|
|
50
57
|
for key, value in pc.get_bindings().items():
|
|
@@ -55,6 +62,9 @@ class ChipBindableUi(
|
|
|
55
62
|
return self.element.text
|
|
56
63
|
|
|
57
64
|
def bind_prop(self, prop: str, value: TGetterOrReadonlyRef):
|
|
65
|
+
if ValueElementMixin._bind_specified_props(self, prop, value):
|
|
66
|
+
return self
|
|
67
|
+
|
|
58
68
|
if prop == "text":
|
|
59
69
|
return self.bind_text(value)
|
|
60
70
|
|