ex4nicegui 0.6.6__py3-none-any.whl → 0.6.8__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 +10 -6
- ex4nicegui/gsap/__init__.py +11 -0
- ex4nicegui/helper/__init__.py +4 -0
- ex4nicegui/helper/client_instance_locker.py +31 -0
- ex4nicegui/libs/gsap/.DS_Store +0 -0
- ex4nicegui/libs/gsap/gsap.mjs +6 -0
- ex4nicegui/reactive/EChartsComponent/ECharts.js +19 -9
- ex4nicegui/reactive/local_file_picker.py +1 -2
- ex4nicegui/reactive/officials/aggrid.py +7 -7
- ex4nicegui/reactive/officials/base.py +198 -86
- ex4nicegui/reactive/officials/button.py +5 -5
- ex4nicegui/reactive/officials/checkbox.py +5 -6
- ex4nicegui/reactive/officials/circular_progress.py +5 -6
- ex4nicegui/reactive/officials/color_picker.py +7 -8
- ex4nicegui/reactive/officials/column.py +4 -3
- ex4nicegui/reactive/officials/date.py +6 -12
- ex4nicegui/reactive/officials/drawer.py +2 -3
- ex4nicegui/reactive/officials/echarts.py +32 -15
- ex4nicegui/reactive/officials/expansion.py +5 -5
- ex4nicegui/reactive/officials/grid.py +1 -1
- ex4nicegui/reactive/officials/html.py +1 -3
- ex4nicegui/reactive/officials/icon.py +9 -10
- ex4nicegui/reactive/officials/image.py +5 -7
- ex4nicegui/reactive/officials/input.py +8 -10
- ex4nicegui/reactive/officials/knob.py +5 -7
- ex4nicegui/reactive/officials/label.py +5 -4
- ex4nicegui/reactive/officials/linear_progress.py +9 -10
- ex4nicegui/reactive/officials/number.py +23 -8
- ex4nicegui/reactive/officials/radio.py +7 -9
- ex4nicegui/reactive/officials/row.py +1 -1
- ex4nicegui/reactive/officials/select.py +7 -9
- ex4nicegui/reactive/officials/slider.py +6 -8
- ex4nicegui/reactive/officials/switch.py +5 -7
- ex4nicegui/reactive/officials/tab.py +1 -1
- ex4nicegui/reactive/officials/tab_panel.py +1 -1
- ex4nicegui/reactive/officials/tab_panels.py +15 -3
- ex4nicegui/reactive/officials/table.py +12 -10
- ex4nicegui/reactive/officials/tabs.py +4 -3
- ex4nicegui/reactive/officials/textarea.py +6 -8
- ex4nicegui/reactive/officials/upload.py +2 -2
- ex4nicegui/reactive/q_pagination.py +5 -4
- ex4nicegui/reactive/scopedStyle.js +55 -0
- ex4nicegui/reactive/scopedStyle.py +22 -0
- ex4nicegui/reactive/services/color_service.py +56 -0
- ex4nicegui/reactive/services/pandas_service.py +31 -0
- ex4nicegui/reactive/{utils.py → services/reactive_service.py} +9 -67
- ex4nicegui/reactive/systems/color_system.py +25 -0
- ex4nicegui/reactive/systems/object_system.py +33 -0
- ex4nicegui/reactive/systems/reactive_system.py +21 -0
- ex4nicegui/reactive/useMouse/UseMouse.py +4 -4
- ex4nicegui/reactive/usePagination.py +1 -1
- ex4nicegui/reactive/vfor.py +1 -2
- ex4nicegui/reactive/vmodel.py +1 -1
- ex4nicegui/utils/refComputed.py +147 -0
- ex4nicegui/utils/refWrapper.py +57 -0
- ex4nicegui/utils/signals.py +51 -192
- ex4nicegui/utils/types.py +16 -0
- ex4nicegui/version.py +3 -0
- {ex4nicegui-0.6.6.dist-info → ex4nicegui-0.6.8.dist-info}/METADATA +1274 -1124
- {ex4nicegui-0.6.6.dist-info → ex4nicegui-0.6.8.dist-info}/RECORD +81 -74
- {ex4nicegui-0.6.6.dist-info → ex4nicegui-0.6.8.dist-info}/WHEEL +1 -2
- ex4nicegui/reactive/EChartsComponent/__init__.py +0 -0
- ex4nicegui/reactive/UseDraggable/__init__.py +0 -0
- ex4nicegui/reactive/dropZone/__init__.py +0 -0
- ex4nicegui/reactive/mermaid/__init__.py +0 -0
- ex4nicegui/reactive/officials/__init__.py +0 -1
- ex4nicegui/reactive/officials/utils.py +0 -11
- ex4nicegui/reactive/useMouse/__init__.py +0 -0
- ex4nicegui-0.6.6.dist-info/top_level.txt +0 -1
- {ex4nicegui-0.6.6.dist-info → ex4nicegui-0.6.8.dist-info}/LICENSE +0 -0
ex4nicegui/__init__.py
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
from ex4nicegui import reactive as rxui
|
|
2
|
+
from ex4nicegui.utils.refComputed import ref_computed
|
|
3
|
+
from ex4nicegui.utils.types import (
|
|
4
|
+
_TMaybeRef as TMaybeRef,
|
|
5
|
+
Ref,
|
|
6
|
+
ReadonlyRef,
|
|
7
|
+
TGetterOrReadonlyRef,
|
|
8
|
+
)
|
|
2
9
|
from ex4nicegui.utils.signals import (
|
|
3
|
-
ref_computed,
|
|
4
10
|
effect,
|
|
5
11
|
effect_refreshable,
|
|
6
12
|
to_raw,
|
|
@@ -10,9 +16,6 @@ from ex4nicegui.utils.signals import (
|
|
|
10
16
|
ref,
|
|
11
17
|
on,
|
|
12
18
|
event_batch,
|
|
13
|
-
_TMaybeRef as TMaybeRef,
|
|
14
|
-
Ref,
|
|
15
|
-
ReadonlyRef,
|
|
16
19
|
reactive,
|
|
17
20
|
deep_ref,
|
|
18
21
|
is_setter_ref,
|
|
@@ -21,6 +24,7 @@ from ex4nicegui.utils.signals import (
|
|
|
21
24
|
)
|
|
22
25
|
from ex4nicegui.utils.asyncComputed import async_computed
|
|
23
26
|
from ex4nicegui.utils.clientScope import new_scope
|
|
27
|
+
from .version import __version__
|
|
24
28
|
|
|
25
29
|
__all__ = [
|
|
26
30
|
"async_computed",
|
|
@@ -36,6 +40,7 @@ __all__ = [
|
|
|
36
40
|
"on",
|
|
37
41
|
"event_batch",
|
|
38
42
|
"TMaybeRef",
|
|
43
|
+
"TGetterOrReadonlyRef",
|
|
39
44
|
"Ref",
|
|
40
45
|
"ReadonlyRef",
|
|
41
46
|
"reactive",
|
|
@@ -44,6 +49,5 @@ __all__ = [
|
|
|
44
49
|
"to_raw",
|
|
45
50
|
"is_setter_ref",
|
|
46
51
|
"new_scope",
|
|
52
|
+
"__version__",
|
|
47
53
|
]
|
|
48
|
-
|
|
49
|
-
__version__ = "0.6.6"
|
ex4nicegui/gsap/__init__.py
CHANGED
|
@@ -2,6 +2,17 @@ from .gsap import set_defaults, from_, to, new, run_script
|
|
|
2
2
|
from .timeline import Timeline as timeline
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
RED = "\033[91m"
|
|
8
|
+
RESET = "\033[0m"
|
|
9
|
+
|
|
10
|
+
warnings.warn(
|
|
11
|
+
f"{RED}The gsap module is deprecated and will be removed in the next major version.{RESET}",
|
|
12
|
+
DeprecationWarning,
|
|
13
|
+
stacklevel=2,
|
|
14
|
+
)
|
|
15
|
+
|
|
5
16
|
__all__ = [
|
|
6
17
|
"set_defaults",
|
|
7
18
|
"from_",
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Callable, TypeVar, Generic
|
|
2
|
+
from nicegui import ui, Client
|
|
3
|
+
from weakref import WeakKeyDictionary
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
_T = TypeVar("_T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ClientInstanceLocker(Generic[_T]):
|
|
10
|
+
def __init__(self, factory: Callable[[], _T]):
|
|
11
|
+
"""Creates a new instance locker that creates a new instance for each client.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
factory (Callable[[], _T]): A factory function that creates a new instance.
|
|
15
|
+
"""
|
|
16
|
+
self._client_instances: WeakKeyDictionary[Client, _T] = WeakKeyDictionary()
|
|
17
|
+
self._factory = factory
|
|
18
|
+
|
|
19
|
+
def get_object(self):
|
|
20
|
+
if not ui.context.slot_stack:
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
client = ui.context.client
|
|
24
|
+
if client not in self._client_instances:
|
|
25
|
+
self._client_instances[client] = self._factory()
|
|
26
|
+
|
|
27
|
+
@client.on_disconnect
|
|
28
|
+
def _():
|
|
29
|
+
del self._client_instances[client]
|
|
30
|
+
|
|
31
|
+
return self._client_instances[client]
|
|
Binary file
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { gsap, Power0, Power1, Power2, Power3, Power4, Linear, Quad, Cubic, Quart, Quint, Strong, Elastic, Back, SteppedEase, Bounce, Sine, Expo, Circ, TweenLite, TimelineLite, TimelineMax } from "./gsap-core.js";
|
|
2
|
+
import { CSSPlugin } from "./CSSPlugin.js";
|
|
3
|
+
var gsapWithCSS = gsap.registerPlugin(CSSPlugin) || gsap,
|
|
4
|
+
// to protect from tree shaking
|
|
5
|
+
TweenMaxWithCSS = gsapWithCSS.core.Tween;
|
|
6
|
+
export { gsapWithCSS as gsap, gsapWithCSS as default, CSSPlugin, TweenMaxWithCSS as TweenMax, TweenLite, TimelineMax, TimelineLite, Power0, Power1, Power2, Power3, Power4, Linear, Quad, Cubic, Quart, Quint, Strong, Elastic, Back, SteppedEase, Bounce, Sine, Expo, Circ };
|
|
@@ -5,13 +5,23 @@ function collectMapRegisterTask() {
|
|
|
5
5
|
|
|
6
6
|
if (typeof window.ex4ngEchartsMapTasks !== "undefined") {
|
|
7
7
|
|
|
8
|
-
for (const [mapName,
|
|
9
|
-
|
|
8
|
+
for (const [mapName, opt] of window.ex4ngEchartsMapTasks.entries()) {
|
|
9
|
+
const { src, type: mapDataType, specialAreas } = opt;
|
|
10
10
|
const registerPromise = new Promise((resolve, reject) => {
|
|
11
11
|
fetch(src)
|
|
12
|
-
.then((response) =>
|
|
12
|
+
.then((response) => {
|
|
13
|
+
if (mapDataType === "genJSON") {
|
|
14
|
+
return response.json();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return response.text();
|
|
18
|
+
})
|
|
13
19
|
.then((data) => {
|
|
14
|
-
|
|
20
|
+
if (mapDataType === "svg") {
|
|
21
|
+
data = { svg: data }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
echarts.registerMap(mapName, data, specialAreas);
|
|
15
25
|
resolve();
|
|
16
26
|
});
|
|
17
27
|
|
|
@@ -46,10 +56,6 @@ export default {
|
|
|
46
56
|
async mounted() {
|
|
47
57
|
await this.$nextTick(); // wait for Tailwind classes to be applied
|
|
48
58
|
|
|
49
|
-
if (hasMapOrGeo(this.options)) {
|
|
50
|
-
await Promise.all(Array.from(mapRegisterTasks.values()));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
59
|
this.chart = echarts.init(this.$el, this.theme);
|
|
54
60
|
|
|
55
61
|
this.resizeObs = new ResizeObserver(this.chart.resize)
|
|
@@ -63,10 +69,14 @@ export default {
|
|
|
63
69
|
this.chart.on("finished", createResizeObserver);
|
|
64
70
|
|
|
65
71
|
if (this.options) {
|
|
72
|
+
if (hasMapOrGeo(this.options)) {
|
|
73
|
+
await Promise.all(Array.from(mapRegisterTasks.values()));
|
|
74
|
+
}
|
|
66
75
|
this.update_chart();
|
|
67
76
|
} else {
|
|
68
77
|
const fn = new Function('return ' + this.code)()
|
|
69
|
-
|
|
78
|
+
await Promise.all(Array.from(mapRegisterTasks.values()));
|
|
79
|
+
fn(this.chart, echarts)
|
|
70
80
|
this.$emit("__update_options_from_client", this.chart.getOption())
|
|
71
81
|
}
|
|
72
82
|
this.chart.getZr().on("click", (e) => {
|
|
@@ -6,7 +6,6 @@ from pathlib import Path
|
|
|
6
6
|
from ex4nicegui.utils.signals import (
|
|
7
7
|
Ref,
|
|
8
8
|
effect_refreshable,
|
|
9
|
-
ReadonlyRef,
|
|
10
9
|
effect,
|
|
11
10
|
ref_computed as computed,
|
|
12
11
|
to_ref,
|
|
@@ -17,7 +16,7 @@ SelectMode = Literal["dir", "file"]
|
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
class LocalFilePickerResult:
|
|
20
|
-
def __init__(self, ref:
|
|
19
|
+
def __init__(self, ref: Ref[str], open_fn: Callable[..., None]) -> None:
|
|
21
20
|
self.__open_fn = open_fn
|
|
22
21
|
self._ref = ref
|
|
23
22
|
|
|
@@ -6,16 +6,16 @@ from typing import (
|
|
|
6
6
|
Optional,
|
|
7
7
|
)
|
|
8
8
|
from ex4nicegui.utils.signals import (
|
|
9
|
-
ReadonlyRef,
|
|
10
9
|
is_ref,
|
|
11
10
|
ref_computed,
|
|
12
11
|
to_value,
|
|
13
|
-
|
|
12
|
+
TMaybeRef,
|
|
13
|
+
TGetterOrReadonlyRef,
|
|
14
14
|
)
|
|
15
|
-
from ex4nicegui.utils.apiEffect import ui_effect
|
|
16
15
|
from nicegui import ui
|
|
17
16
|
from .base import BindableUi
|
|
18
|
-
from ex4nicegui.reactive.
|
|
17
|
+
from ex4nicegui.reactive.services.reactive_service import ParameterClassifier
|
|
18
|
+
from ex4nicegui.reactive.services.pandas_service import dataframe2col_str
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class AggridBindableUi(BindableUi[ui.aggrid]):
|
|
@@ -78,14 +78,14 @@ class AggridBindableUi(BindableUi[ui.aggrid]):
|
|
|
78
78
|
options = {"columnDefs": columnDefs, "rowData": rowData}
|
|
79
79
|
return cls(options, **org_kws)
|
|
80
80
|
|
|
81
|
-
def bind_prop(self, prop: str, ref_ui:
|
|
81
|
+
def bind_prop(self, prop: str, ref_ui: TGetterOrReadonlyRef[Any]):
|
|
82
82
|
if prop == "options":
|
|
83
83
|
return self.bind_options(ref_ui)
|
|
84
84
|
|
|
85
85
|
return super().bind_prop(prop, ref_ui)
|
|
86
86
|
|
|
87
|
-
def bind_options(self, ref_ui:
|
|
88
|
-
@
|
|
87
|
+
def bind_options(self, ref_ui: TGetterOrReadonlyRef[List[Dict]]):
|
|
88
|
+
@self._ui_effect
|
|
89
89
|
def _():
|
|
90
90
|
ele = self.element
|
|
91
91
|
data = to_value(ref_ui)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
from typing import (
|
|
4
5
|
Any,
|
|
5
6
|
Callable,
|
|
@@ -11,7 +12,7 @@ from typing import (
|
|
|
11
12
|
Generic,
|
|
12
13
|
Union,
|
|
13
14
|
cast,
|
|
14
|
-
|
|
15
|
+
overload,
|
|
15
16
|
)
|
|
16
17
|
|
|
17
18
|
from typing_extensions import Self
|
|
@@ -19,21 +20,18 @@ from ex4nicegui.utils.apiEffect import ui_effect
|
|
|
19
20
|
import signe
|
|
20
21
|
from ex4nicegui.utils.signals import (
|
|
21
22
|
TGetterOrReadonlyRef,
|
|
22
|
-
effect,
|
|
23
23
|
to_value,
|
|
24
24
|
is_ref,
|
|
25
25
|
WatchedState,
|
|
26
26
|
on,
|
|
27
27
|
)
|
|
28
|
+
from ex4nicegui.utils.clientScope import new_scope
|
|
28
29
|
from nicegui import Tailwind, ui
|
|
29
|
-
from nicegui.elements.mixins.color_elements import (
|
|
30
|
-
TextColorElement,
|
|
31
|
-
QUASAR_COLORS,
|
|
32
|
-
TAILWIND_COLORS,
|
|
33
|
-
)
|
|
34
30
|
from nicegui.elements.mixins.text_element import TextElement
|
|
35
31
|
from nicegui.elements.mixins.disableable_element import DisableableElement
|
|
36
|
-
|
|
32
|
+
from ex4nicegui.reactive.services.reactive_service import inject_handle_delete
|
|
33
|
+
from ex4nicegui.reactive.scopedStyle import ScopedStyle
|
|
34
|
+
from functools import partial
|
|
37
35
|
|
|
38
36
|
T = TypeVar("T")
|
|
39
37
|
|
|
@@ -42,21 +40,38 @@ TWidget = TypeVar("TWidget", bound=ui.element)
|
|
|
42
40
|
|
|
43
41
|
_T_bind_classes_type_dict = Dict[str, TGetterOrReadonlyRef[bool]]
|
|
44
42
|
_T_bind_classes_type_ref_dict = TGetterOrReadonlyRef[Dict[str, bool]]
|
|
45
|
-
|
|
43
|
+
_T_bind_classes_type_single = TGetterOrReadonlyRef[str]
|
|
44
|
+
_T_bind_classes_type_array = List[_T_bind_classes_type_single]
|
|
46
45
|
|
|
47
46
|
|
|
48
47
|
_T_bind_classes_type = Union[
|
|
49
|
-
_T_bind_classes_type_dict,
|
|
48
|
+
_T_bind_classes_type_dict,
|
|
49
|
+
_T_bind_classes_type_ref_dict,
|
|
50
|
+
_T_bind_classes_type_single,
|
|
51
|
+
_T_bind_classes_type_array,
|
|
50
52
|
]
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
class BindableUi(Generic[TWidget]):
|
|
54
56
|
def __init__(self, element: TWidget) -> None:
|
|
55
57
|
self._element = element
|
|
58
|
+
inject_handle_delete(self.element, self._on_element_delete)
|
|
56
59
|
self.tailwind = Tailwind(cast(ui.element, self._element))
|
|
60
|
+
self._effect_scope = new_scope()
|
|
57
61
|
|
|
58
|
-
def
|
|
59
|
-
|
|
62
|
+
def _on_element_delete(self):
|
|
63
|
+
self._effect_scope.dispose()
|
|
64
|
+
scope_style = ScopedStyle.get()
|
|
65
|
+
if scope_style:
|
|
66
|
+
scope_style.remove_style(self.element)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def _ui_effect(self):
|
|
70
|
+
return partial(ui_effect, scope=self._effect_scope)
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def _ui_signal_on(self):
|
|
74
|
+
return partial(on, scope=self._effect_scope)
|
|
60
75
|
|
|
61
76
|
def props(self, add: Optional[str] = None, *, remove: Optional[str] = None):
|
|
62
77
|
cast(ui.element, self.element).props(add, remove=remove)
|
|
@@ -108,7 +123,7 @@ class BindableUi(Generic[TWidget]):
|
|
|
108
123
|
|
|
109
124
|
def delete(self) -> None:
|
|
110
125
|
"""Delete the element."""
|
|
111
|
-
self.delete()
|
|
126
|
+
self.element.delete()
|
|
112
127
|
|
|
113
128
|
def move(
|
|
114
129
|
self, target_container: Optional[ui.element] = None, target_index: int = -1
|
|
@@ -118,7 +133,7 @@ class BindableUi(Generic[TWidget]):
|
|
|
118
133
|
:param target_container: container to move the element to (default: the parent container)
|
|
119
134
|
:param target_index: index within the target slot (default: append to the end)
|
|
120
135
|
"""
|
|
121
|
-
return self.move(target_container, target_index)
|
|
136
|
+
return self.element.move(target_container, target_index)
|
|
122
137
|
|
|
123
138
|
def remove(self, element: Union[ui.element, int]) -> None:
|
|
124
139
|
"""Remove a child element.
|
|
@@ -128,6 +143,16 @@ class BindableUi(Generic[TWidget]):
|
|
|
128
143
|
return self.element.remove(element)
|
|
129
144
|
|
|
130
145
|
def bind_prop(self, prop: str, ref_ui: TGetterOrReadonlyRef[Any]):
|
|
146
|
+
"""data binding is manipulating an element's property
|
|
147
|
+
|
|
148
|
+
@see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#bind_prop
|
|
149
|
+
@中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#bind_prop
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
prop (str): property name
|
|
153
|
+
ref_ui (TGetterOrReadonlyRef[Any]): a reference to the value to bind to
|
|
154
|
+
|
|
155
|
+
"""
|
|
131
156
|
if prop == "visible":
|
|
132
157
|
return self.bind_visible(ref_ui)
|
|
133
158
|
|
|
@@ -182,6 +207,22 @@ class BindableUi(Generic[TWidget]):
|
|
|
182
207
|
def clear(self) -> None:
|
|
183
208
|
cast(ui.element, self.element).clear()
|
|
184
209
|
|
|
210
|
+
@overload
|
|
211
|
+
def bind_classes(self, classes: Dict[str, TGetterOrReadonlyRef[bool]]):
|
|
212
|
+
...
|
|
213
|
+
|
|
214
|
+
@overload
|
|
215
|
+
def bind_classes(self, classes: TGetterOrReadonlyRef[Dict[str, bool]]):
|
|
216
|
+
...
|
|
217
|
+
|
|
218
|
+
@overload
|
|
219
|
+
def bind_classes(self, classes: List[TGetterOrReadonlyRef[str]]):
|
|
220
|
+
...
|
|
221
|
+
|
|
222
|
+
@overload
|
|
223
|
+
def bind_classes(self, classes: TGetterOrReadonlyRef[str]):
|
|
224
|
+
...
|
|
225
|
+
|
|
185
226
|
def bind_classes(self, classes: _T_bind_classes_type):
|
|
186
227
|
"""data binding is manipulating an element's class list
|
|
187
228
|
|
|
@@ -189,7 +230,37 @@ class BindableUi(Generic[TWidget]):
|
|
|
189
230
|
@中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#%E7%BB%91%E5%AE%9A%E7%B1%BB%E5%90%8D
|
|
190
231
|
|
|
191
232
|
Args:
|
|
192
|
-
classes (_T_bind_classes_type):
|
|
233
|
+
classes (_T_bind_classes_type): dict of refs | ref to dict | str ref | list of refs
|
|
234
|
+
|
|
235
|
+
## usage
|
|
236
|
+
|
|
237
|
+
bind class names with dict,value is bool ref, for example:
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
bg_color = to_ref(True)
|
|
241
|
+
has_error = to_ref(False)
|
|
242
|
+
|
|
243
|
+
rxui.label('Hello').bind_classes({'bg-blue':bg_color, 'text-red':has_error})
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
bind list of class names with ref
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
color = to_ref('red')
|
|
250
|
+
bg_color = lambda: f"bg-{color.value}"
|
|
251
|
+
|
|
252
|
+
rxui.label('Hello').bind_classes([bg_color])
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
bind single class name with ref
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
color = to_ref('red')
|
|
259
|
+
bg_color = lambda: f"bg-{color.value}"
|
|
260
|
+
|
|
261
|
+
rxui.label('Hello').bind_classes(bg_color)
|
|
262
|
+
```
|
|
263
|
+
|
|
193
264
|
"""
|
|
194
265
|
if isinstance(classes, dict):
|
|
195
266
|
for name, ref_obj in classes.items():
|
|
@@ -203,24 +274,33 @@ class BindableUi(Generic[TWidget]):
|
|
|
203
274
|
|
|
204
275
|
elif is_ref(classes) or isinstance(classes, Callable):
|
|
205
276
|
ref_obj = to_value(classes) # type: ignore
|
|
206
|
-
assert isinstance(ref_obj, dict)
|
|
207
277
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
278
|
+
if isinstance(ref_obj, dict):
|
|
279
|
+
|
|
280
|
+
@self._ui_effect
|
|
281
|
+
def _():
|
|
282
|
+
for name, value in cast(Dict, to_value(classes)).items(): # type: ignore
|
|
283
|
+
if value:
|
|
284
|
+
self.classes(add=name)
|
|
285
|
+
else:
|
|
286
|
+
self.classes(remove=name)
|
|
287
|
+
else:
|
|
288
|
+
self._bind_single_class(cast(_T_bind_classes_type_single, classes))
|
|
289
|
+
|
|
215
290
|
elif isinstance(classes, list):
|
|
216
291
|
for ref_name in classes:
|
|
217
|
-
|
|
292
|
+
self._bind_single_class(ref_name)
|
|
293
|
+
|
|
294
|
+
return self
|
|
218
295
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
296
|
+
def _bind_single_class(self, class_name: _T_bind_classes_type_single):
|
|
297
|
+
if is_ref(class_name) or isinstance(class_name, Callable):
|
|
298
|
+
|
|
299
|
+
@on(class_name)
|
|
300
|
+
def _(state: WatchedState):
|
|
301
|
+
self.classes(add=state.current, remove=state.previous)
|
|
302
|
+
else:
|
|
303
|
+
self.classes(class_name) # type: ignore
|
|
224
304
|
|
|
225
305
|
return self
|
|
226
306
|
|
|
@@ -231,7 +311,21 @@ class BindableUi(Generic[TWidget]):
|
|
|
231
311
|
@中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#bind-style
|
|
232
312
|
|
|
233
313
|
Args:
|
|
234
|
-
style (Dict[str, Union[ReadonlyRef[str], Ref[str]]]):
|
|
314
|
+
style (Dict[str, Union[ReadonlyRef[str], Ref[str]]]): dict of style name and ref value
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
## usage
|
|
318
|
+
```python
|
|
319
|
+
bg_color = to_ref("blue")
|
|
320
|
+
text_color = to_ref("red")
|
|
321
|
+
|
|
322
|
+
rxui.label("test").bind_style(
|
|
323
|
+
{
|
|
324
|
+
"background-color": bg_color,
|
|
325
|
+
"color": text_color,
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
```
|
|
235
329
|
"""
|
|
236
330
|
if isinstance(style, dict):
|
|
237
331
|
for name, ref_obj in style.items():
|
|
@@ -244,26 +338,88 @@ class BindableUi(Generic[TWidget]):
|
|
|
244
338
|
|
|
245
339
|
return self
|
|
246
340
|
|
|
341
|
+
def scoped_style(self, selector: str, style: Union[str, Path]):
|
|
342
|
+
"""add scoped style to the element
|
|
343
|
+
|
|
344
|
+
@see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#scoped_style
|
|
345
|
+
@中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#scoped_style
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
selector (str): css selector
|
|
349
|
+
style (Union[str, Path]): path to css file or inline style string
|
|
350
|
+
|
|
351
|
+
## usage
|
|
352
|
+
```python
|
|
353
|
+
# all children of the element will have red outline, excluding itself
|
|
354
|
+
with rxui.row().scoped_style("*", "outline: 1px solid red;") as row:
|
|
355
|
+
ui.label("Hello")
|
|
356
|
+
ui.label("World")
|
|
357
|
+
|
|
358
|
+
# all children of the element will have red outline, including the element itself
|
|
359
|
+
with rxui.row().scoped_style(":self *", "outline: 1px solid red;") as row:
|
|
360
|
+
ui.label("Hello")
|
|
361
|
+
ui.label("World")
|
|
362
|
+
|
|
363
|
+
# all children of the element will have red outline when element is hovered
|
|
364
|
+
with rxui.row().scoped_style(":hover *", "outline: 1px solid red;") as row:
|
|
365
|
+
ui.label("Hello")
|
|
366
|
+
ui.label("World")
|
|
367
|
+
|
|
368
|
+
# all children of the element and itself will have red outline when element is hovered
|
|
369
|
+
with rxui.row().scoped_style(":self:hover *", "outline: 1px solid red;") as row:
|
|
370
|
+
ui.label("Hello")
|
|
371
|
+
ui.label("World")
|
|
372
|
+
```
|
|
373
|
+
"""
|
|
374
|
+
|
|
375
|
+
is_css_file = isinstance(style, Path)
|
|
376
|
+
|
|
377
|
+
if is_css_file:
|
|
378
|
+
style = style.read_text(encoding="utf-8")
|
|
379
|
+
|
|
380
|
+
id = f"c{self.element.id}"
|
|
381
|
+
selector_with_self = _utils._parent_id_with_selector(id, selector, is_css_file)
|
|
382
|
+
css = ""
|
|
383
|
+
if is_css_file:
|
|
384
|
+
css = f"{selector_with_self} {style}"
|
|
385
|
+
else:
|
|
386
|
+
css = f"{selector_with_self}{{{style}}}"
|
|
387
|
+
|
|
388
|
+
scope_style = ScopedStyle.get()
|
|
389
|
+
assert scope_style, "can not find scope style"
|
|
390
|
+
scope_style.create_style(self.element, css)
|
|
391
|
+
|
|
392
|
+
return self
|
|
393
|
+
|
|
247
394
|
def update(self):
|
|
248
395
|
"""Update the element on the client side."""
|
|
249
396
|
self.element.update()
|
|
250
397
|
|
|
251
398
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
399
|
+
class _utils:
|
|
400
|
+
@staticmethod
|
|
401
|
+
def _parent_id_with_selector(
|
|
402
|
+
parent_id: str, selector: str, is_css_file=False
|
|
403
|
+
) -> str:
|
|
404
|
+
selector_with_self = f"#{parent_id}"
|
|
405
|
+
|
|
406
|
+
selector = selector.strip()
|
|
407
|
+
if (not selector) and (not is_css_file):
|
|
408
|
+
selector = "* "
|
|
409
|
+
|
|
410
|
+
if selector.startswith(":self"):
|
|
411
|
+
selector = selector[5:].lstrip()
|
|
412
|
+
parent_selector = f"#{parent_id}"
|
|
413
|
+
if selector.startswith(":"):
|
|
414
|
+
parent_selector = f"{parent_selector}{selector.split()[0]}"
|
|
256
415
|
|
|
257
|
-
|
|
258
|
-
# def value(self) -> T:
|
|
259
|
-
# return self._ref.value # type: ignore
|
|
416
|
+
selector_with_self = f"{parent_selector},{selector_with_self}"
|
|
260
417
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
# def _():
|
|
264
|
-
# ref.value = self._ref.value # type: ignore
|
|
418
|
+
if not selector.startswith(":"):
|
|
419
|
+
selector_with_self = selector_with_self + " "
|
|
265
420
|
|
|
266
|
-
|
|
421
|
+
selector_with_self = selector_with_self + selector
|
|
422
|
+
return selector_with_self
|
|
267
423
|
|
|
268
424
|
|
|
269
425
|
_T_DisableableBinder = TypeVar("_T_DisableableBinder", bound=DisableableElement)
|
|
@@ -296,47 +452,3 @@ class DisableableMixin(Protocol):
|
|
|
296
452
|
|
|
297
453
|
|
|
298
454
|
DisableableBindableUi = DisableableMixin
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
_color_sys_type = Literal["QUASAR", "TAILWIND", "STYLE"]
|
|
302
|
-
_color_attr_name = "data-ex4ng-color"
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
def _bind_color(bindable_ui: BindableUi, ref_ui: TGetterOrReadonlyRef):
|
|
306
|
-
@effect
|
|
307
|
-
def _():
|
|
308
|
-
ele = cast(TextColorElement, bindable_ui.element)
|
|
309
|
-
color = to_value(ref_ui)
|
|
310
|
-
|
|
311
|
-
# get exists color
|
|
312
|
-
# e.g 'QUASAR:red'
|
|
313
|
-
pre_color = ele._props.get(_color_attr_name) # type: str | None
|
|
314
|
-
if pre_color:
|
|
315
|
-
color_sys, value = pre_color.split(":") # type: ignore
|
|
316
|
-
color_sys: _color_sys_type
|
|
317
|
-
|
|
318
|
-
if color_sys == "QUASAR":
|
|
319
|
-
del ele._props[ele.TEXT_COLOR_PROP]
|
|
320
|
-
elif color_sys == "TAILWIND":
|
|
321
|
-
ele.classes(remove=value)
|
|
322
|
-
else:
|
|
323
|
-
del ele._style["color"]
|
|
324
|
-
|
|
325
|
-
cur_sys: _color_sys_type = "STYLE"
|
|
326
|
-
cur_color = color
|
|
327
|
-
|
|
328
|
-
if color in QUASAR_COLORS:
|
|
329
|
-
ele._props[ele.TEXT_COLOR_PROP] = color
|
|
330
|
-
cur_sys = "QUASAR"
|
|
331
|
-
elif color in TAILWIND_COLORS:
|
|
332
|
-
cur_color = f"text-{color}"
|
|
333
|
-
ele.classes(replace=cur_color)
|
|
334
|
-
cur_sys = "TAILWIND"
|
|
335
|
-
elif color is not None:
|
|
336
|
-
ele._style["color"] = color
|
|
337
|
-
|
|
338
|
-
ele._props[_color_attr_name] = f"{cur_sys}:{color}"
|
|
339
|
-
|
|
340
|
-
ele.update()
|
|
341
|
-
|
|
342
|
-
return bindable_ui
|
|
@@ -3,9 +3,9 @@ from typing import (
|
|
|
3
3
|
Callable,
|
|
4
4
|
Optional,
|
|
5
5
|
)
|
|
6
|
-
from ex4nicegui.reactive.
|
|
6
|
+
from ex4nicegui.reactive.services.reactive_service import ParameterClassifier
|
|
7
7
|
from ex4nicegui.utils.signals import (
|
|
8
|
-
|
|
8
|
+
TGetterOrReadonlyRef,
|
|
9
9
|
_TMaybeRef as TMaybeRef,
|
|
10
10
|
to_value,
|
|
11
11
|
)
|
|
@@ -33,7 +33,7 @@ class ButtonBindableUi(BindableUi[ui.button], DisableableMixin):
|
|
|
33
33
|
for key, value in pc.get_bindings().items():
|
|
34
34
|
self.bind_prop(key, value) # type: ignore
|
|
35
35
|
|
|
36
|
-
def bind_prop(self, prop: str, ref_ui:
|
|
36
|
+
def bind_prop(self, prop: str, ref_ui: TGetterOrReadonlyRef):
|
|
37
37
|
if prop == "text":
|
|
38
38
|
return self.bind_text(ref_ui)
|
|
39
39
|
if prop == "icon":
|
|
@@ -41,7 +41,7 @@ class ButtonBindableUi(BindableUi[ui.button], DisableableMixin):
|
|
|
41
41
|
|
|
42
42
|
return super().bind_prop(prop, ref_ui)
|
|
43
43
|
|
|
44
|
-
def bind_text(self, ref_ui:
|
|
44
|
+
def bind_text(self, ref_ui: TGetterOrReadonlyRef):
|
|
45
45
|
@self._ui_effect
|
|
46
46
|
def _():
|
|
47
47
|
ele = self.element
|
|
@@ -50,7 +50,7 @@ class ButtonBindableUi(BindableUi[ui.button], DisableableMixin):
|
|
|
50
50
|
|
|
51
51
|
return self
|
|
52
52
|
|
|
53
|
-
def bind_icon(self, ref_ui:
|
|
53
|
+
def bind_icon(self, ref_ui: TGetterOrReadonlyRef[str]):
|
|
54
54
|
@self._ui_effect
|
|
55
55
|
def _():
|
|
56
56
|
ele = self.element
|