ex4nicegui 0.5.2__py3-none-any.whl → 0.5.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 +3 -1
- ex4nicegui/gsap/__init__.py +2 -0
- ex4nicegui/gsap/timeline.js +56 -0
- ex4nicegui/gsap/timeline.py +78 -0
- ex4nicegui/reactive/__init__.py +6 -1
- ex4nicegui/reactive/officials/circular_progress.py +64 -0
- ex4nicegui/reactive/officials/column.py +19 -4
- ex4nicegui/reactive/officials/knob.py +75 -0
- ex4nicegui/reactive/officials/number.py +1 -2
- ex4nicegui/reactive/officials/row.py +18 -4
- ex4nicegui/reactive/utils.py +40 -1
- ex4nicegui/reactive/vfor.py +19 -39
- ex4nicegui/reactive/vmodel.py +210 -0
- ex4nicegui/utils/signals.py +12 -2
- {ex4nicegui-0.5.2.dist-info → ex4nicegui-0.5.3.dist-info}/METADATA +55 -34
- {ex4nicegui-0.5.2.dist-info → ex4nicegui-0.5.3.dist-info}/RECORD +19 -14
- {ex4nicegui-0.5.2.dist-info → ex4nicegui-0.5.3.dist-info}/LICENSE +0 -0
- {ex4nicegui-0.5.2.dist-info → ex4nicegui-0.5.3.dist-info}/WHEEL +0 -0
- {ex4nicegui-0.5.2.dist-info → ex4nicegui-0.5.3.dist-info}/top_level.txt +0 -0
ex4nicegui/__init__.py
CHANGED
|
@@ -17,10 +17,12 @@ from ex4nicegui.utils.signals import (
|
|
|
17
17
|
deep_ref,
|
|
18
18
|
is_setter_ref,
|
|
19
19
|
batch,
|
|
20
|
+
is_reactive,
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
__all__ = [
|
|
25
|
+
"is_reactive",
|
|
24
26
|
"rxui",
|
|
25
27
|
"ref_computed",
|
|
26
28
|
"effect",
|
|
@@ -41,4 +43,4 @@ __all__ = [
|
|
|
41
43
|
"is_setter_ref",
|
|
42
44
|
]
|
|
43
45
|
|
|
44
|
-
__version__ = "0.5.
|
|
46
|
+
__version__ = "0.5.3"
|
ex4nicegui/gsap/__init__.py
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { gsap } from "gsap";
|
|
2
|
+
import { convertDynamicProperties } from "../../static/utils/dynamic_properties.js";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
template: `<template></template>`,
|
|
7
|
+
data() {
|
|
8
|
+
|
|
9
|
+
const tl = gsap.timeline(this.defaults)
|
|
10
|
+
this.tl = tl
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
},
|
|
16
|
+
mounted() {
|
|
17
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
18
|
+
/**
|
|
19
|
+
* @type any[]
|
|
20
|
+
*/
|
|
21
|
+
const tasks = this.tasks
|
|
22
|
+
tasks.forEach(t => {
|
|
23
|
+
this[t.method](t.targets, t.vars, t.position)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
this.scriptTasks.forEach(script => {
|
|
27
|
+
const fn = new Function('return ' + script)()
|
|
28
|
+
fn(this.tl, gsap)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
})
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
methods: {
|
|
35
|
+
from(targets, vars, position) {
|
|
36
|
+
convertDynamicProperties(vars, false)
|
|
37
|
+
this.tl.from(targets, vars, position)
|
|
38
|
+
},
|
|
39
|
+
to(targets, vars, position) {
|
|
40
|
+
convertDynamicProperties(vars, false)
|
|
41
|
+
this.tl.to(targets, vars, position)
|
|
42
|
+
},
|
|
43
|
+
runScript(script) {
|
|
44
|
+
const fn = new Function('return ' + script)()
|
|
45
|
+
fn(this.tl, gsap)
|
|
46
|
+
},
|
|
47
|
+
callTimeline(name, ...args) {
|
|
48
|
+
this.tl[name](...args)
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
props: {
|
|
52
|
+
defaults: Object,
|
|
53
|
+
tasks: Array,
|
|
54
|
+
scriptTasks: Array,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Dict, Optional, Union
|
|
4
|
+
from nicegui.element import Element
|
|
5
|
+
from nicegui import context as ng_context
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Timeline(
|
|
9
|
+
Element,
|
|
10
|
+
component="timeline.js",
|
|
11
|
+
exposed_libraries=["../libs/gsap/gsap.mjs"],
|
|
12
|
+
extra_libraries=["../libs/gsap/*.js", "../libs/gsap/utils/*.js"],
|
|
13
|
+
):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
defaults: Optional[Dict] = None,
|
|
17
|
+
) -> None:
|
|
18
|
+
super().__init__()
|
|
19
|
+
self._props["defaults"] = defaults or {}
|
|
20
|
+
self._props["tasks"] = []
|
|
21
|
+
self._props["scriptTasks"] = []
|
|
22
|
+
|
|
23
|
+
def from_(self, targets: str, vars: Dict, position: Optional[str] = None):
|
|
24
|
+
self.__try_run_task("from", targets, vars, position)
|
|
25
|
+
return self
|
|
26
|
+
|
|
27
|
+
def to(self, targets: str, vars: Dict, position: Optional[str] = None):
|
|
28
|
+
self.__try_run_task("to", targets, vars, position)
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
def run_script(self, script: Union[str, Path]):
|
|
32
|
+
if isinstance(script, Path):
|
|
33
|
+
script = script.read_text("utf8")
|
|
34
|
+
|
|
35
|
+
script = os.linesep.join([s for s in script.splitlines() if s])
|
|
36
|
+
self.__try_run_script_task(script)
|
|
37
|
+
return self
|
|
38
|
+
|
|
39
|
+
def __try_run_script_task(self, script: str):
|
|
40
|
+
def fn():
|
|
41
|
+
self.run_method("runScript", script)
|
|
42
|
+
|
|
43
|
+
if ng_context.get_client().has_socket_connection:
|
|
44
|
+
fn()
|
|
45
|
+
else:
|
|
46
|
+
tasks = self._props["scriptTasks"]
|
|
47
|
+
tasks.append(script)
|
|
48
|
+
|
|
49
|
+
def __try_run_task(
|
|
50
|
+
self, name: str, targets: str, vars: Dict, position: Optional[str] = None
|
|
51
|
+
):
|
|
52
|
+
def fn():
|
|
53
|
+
self.run_method(name, targets, vars)
|
|
54
|
+
|
|
55
|
+
if ng_context.get_client().has_socket_connection:
|
|
56
|
+
fn()
|
|
57
|
+
else:
|
|
58
|
+
tasks = self._props["tasks"]
|
|
59
|
+
tasks.append(
|
|
60
|
+
{"method": name, "targets": targets, "vars": vars, "position": position}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def pause(self):
|
|
64
|
+
self.run_method("callTimeline", "pause")
|
|
65
|
+
|
|
66
|
+
def play(self):
|
|
67
|
+
self.run_method("callTimeline", "play")
|
|
68
|
+
|
|
69
|
+
def resume(
|
|
70
|
+
self,
|
|
71
|
+
):
|
|
72
|
+
self.run_method("callTimeline", "resume")
|
|
73
|
+
|
|
74
|
+
def seek(self, position: Optional[str] = None, suppressEvents=True):
|
|
75
|
+
self.run_method("callTimeline", "seek", position, suppressEvents)
|
|
76
|
+
|
|
77
|
+
def reverse(self):
|
|
78
|
+
self.run_method("callTimeline", "reverse")
|
ex4nicegui/reactive/__init__.py
CHANGED
|
@@ -44,7 +44,8 @@ from .officials.number import NumberBindableUi as number
|
|
|
44
44
|
from .officials.grid import GridBindableUi as grid
|
|
45
45
|
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
|
+
from .officials.circular_progress import CircularProgressBindableUi as circular_progress
|
|
48
49
|
from .q_pagination import PaginationBindableUi as q_pagination
|
|
49
50
|
|
|
50
51
|
from .local_file_picker import local_file_picker
|
|
@@ -56,11 +57,14 @@ from .dropZone.dropZone import use_drag_zone
|
|
|
56
57
|
from .fileWatcher import FilesWatcher
|
|
57
58
|
from .mermaid.mermaid import Mermaid as mermaid
|
|
58
59
|
from .vfor import vfor, VforStore
|
|
60
|
+
from .vmodel import vmodel
|
|
59
61
|
|
|
60
62
|
pagination = q_pagination
|
|
61
63
|
|
|
62
64
|
|
|
63
65
|
__all__ = [
|
|
66
|
+
"circular_progress",
|
|
67
|
+
"knob",
|
|
64
68
|
"UploadResult",
|
|
65
69
|
"local_file_picker",
|
|
66
70
|
"use_draggable",
|
|
@@ -70,6 +74,7 @@ __all__ = [
|
|
|
70
74
|
"FilesWatcher",
|
|
71
75
|
"vfor",
|
|
72
76
|
"VforStore",
|
|
77
|
+
"vmodel",
|
|
73
78
|
"html",
|
|
74
79
|
"aggird",
|
|
75
80
|
"button",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
Optional,
|
|
3
|
+
)
|
|
4
|
+
from ex4nicegui.reactive.utils import ParameterClassifier
|
|
5
|
+
from ex4nicegui.utils.apiEffect import ui_effect
|
|
6
|
+
|
|
7
|
+
from ex4nicegui.utils.signals import (
|
|
8
|
+
ReadonlyRef,
|
|
9
|
+
_TMaybeRef as TMaybeRef,
|
|
10
|
+
to_value,
|
|
11
|
+
)
|
|
12
|
+
from nicegui import ui
|
|
13
|
+
from .base import BindableUi, DisableableMixin
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CircularProgressBindableUi(
|
|
17
|
+
BindableUi[ui.circular_progress],
|
|
18
|
+
DisableableMixin,
|
|
19
|
+
):
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
value: TMaybeRef[float] = 0.0,
|
|
23
|
+
*,
|
|
24
|
+
min: TMaybeRef[float] = 0.0, # pylint: disable=redefined-builtin
|
|
25
|
+
max: TMaybeRef[float] = 1.0, # pylint: disable=redefined-builtin
|
|
26
|
+
size: Optional[TMaybeRef[str]] = "xl",
|
|
27
|
+
show_value: TMaybeRef[bool] = True,
|
|
28
|
+
color: Optional[TMaybeRef[str]] = "primary",
|
|
29
|
+
) -> None:
|
|
30
|
+
pc = ParameterClassifier(
|
|
31
|
+
locals(),
|
|
32
|
+
maybeRefs=[
|
|
33
|
+
"value",
|
|
34
|
+
"min",
|
|
35
|
+
"max",
|
|
36
|
+
"color",
|
|
37
|
+
"size",
|
|
38
|
+
"show_value",
|
|
39
|
+
],
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
value_kws = pc.get_values_kws()
|
|
43
|
+
element = ui.circular_progress(**value_kws)
|
|
44
|
+
super().__init__(element) # type: ignore
|
|
45
|
+
|
|
46
|
+
for key, value in pc.get_bindings().items():
|
|
47
|
+
self.bind_prop(key, value) # type: ignore
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def value(self):
|
|
51
|
+
return self.element.value
|
|
52
|
+
|
|
53
|
+
def bind_prop(self, prop: str, ref_ui: ReadonlyRef):
|
|
54
|
+
if prop == "value":
|
|
55
|
+
return self.bind_value(ref_ui)
|
|
56
|
+
|
|
57
|
+
return super().bind_prop(prop, ref_ui)
|
|
58
|
+
|
|
59
|
+
def bind_value(self, ref_ui: ReadonlyRef[float]):
|
|
60
|
+
@ui_effect
|
|
61
|
+
def _():
|
|
62
|
+
self.element.set_value(to_value(ref_ui))
|
|
63
|
+
|
|
64
|
+
return self
|
|
@@ -1,18 +1,33 @@
|
|
|
1
1
|
from typing import (
|
|
2
2
|
Any,
|
|
3
3
|
)
|
|
4
|
+
from ex4nicegui.reactive.utils import ParameterClassifier
|
|
5
|
+
from ex4nicegui.utils.signals import (
|
|
6
|
+
_TMaybeRef as TMaybeRef,
|
|
7
|
+
)
|
|
4
8
|
from nicegui import ui
|
|
5
9
|
from .base import BindableUi
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
class ColumnBindableUi(BindableUi[ui.column]):
|
|
9
|
-
def __init__(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
element = ui.column()
|
|
13
|
+
def __init__(self, *, wrap: TMaybeRef[bool] = True) -> None:
|
|
14
|
+
pc = ParameterClassifier(locals(), maybeRefs=["wrap"], events=[])
|
|
15
|
+
element = ui.column(**pc.get_values_kws())
|
|
13
16
|
|
|
14
17
|
super().__init__(element)
|
|
15
18
|
|
|
19
|
+
for key, value in pc.get_bindings().items():
|
|
20
|
+
self.bind_prop(key, value) # type: ignore
|
|
21
|
+
|
|
22
|
+
def bind_prop(self, prop: str, ref_ui: TMaybeRef):
|
|
23
|
+
if prop == "wrap":
|
|
24
|
+
return self.bind_wrap(ref_ui)
|
|
25
|
+
|
|
26
|
+
return super().bind_prop(prop, ref_ui)
|
|
27
|
+
|
|
28
|
+
def bind_wrap(self, ref_ui: TMaybeRef):
|
|
29
|
+
self.bind_classes({"wrap": ref_ui})
|
|
30
|
+
|
|
16
31
|
def __enter__(self):
|
|
17
32
|
self.element.__enter__()
|
|
18
33
|
return self
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
Any,
|
|
3
|
+
Callable,
|
|
4
|
+
Optional,
|
|
5
|
+
)
|
|
6
|
+
from ex4nicegui.reactive.utils import ParameterClassifier
|
|
7
|
+
from ex4nicegui.utils.apiEffect import ui_effect
|
|
8
|
+
|
|
9
|
+
from ex4nicegui.utils.signals import (
|
|
10
|
+
ReadonlyRef,
|
|
11
|
+
_TMaybeRef as TMaybeRef,
|
|
12
|
+
to_value,
|
|
13
|
+
)
|
|
14
|
+
from nicegui import ui
|
|
15
|
+
from .base import BindableUi, DisableableMixin
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class KnobBindableUi(
|
|
19
|
+
BindableUi[ui.knob],
|
|
20
|
+
DisableableMixin,
|
|
21
|
+
):
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
value: TMaybeRef[float] = 0.0,
|
|
25
|
+
*,
|
|
26
|
+
min: TMaybeRef[float] = 0.0, # pylint: disable=redefined-builtin
|
|
27
|
+
max: TMaybeRef[float] = 1.0, # pylint: disable=redefined-builtin
|
|
28
|
+
step: TMaybeRef[float] = 0.01,
|
|
29
|
+
color: Optional[TMaybeRef[str]] = "primary",
|
|
30
|
+
center_color: Optional[TMaybeRef[str]] = None,
|
|
31
|
+
track_color: Optional[TMaybeRef[str]] = None,
|
|
32
|
+
size: Optional[TMaybeRef[str]] = None,
|
|
33
|
+
show_value: TMaybeRef[bool] = False,
|
|
34
|
+
on_change: Optional[Callable[..., Any]] = None,
|
|
35
|
+
) -> None:
|
|
36
|
+
pc = ParameterClassifier(
|
|
37
|
+
locals(),
|
|
38
|
+
maybeRefs=[
|
|
39
|
+
"value",
|
|
40
|
+
"min",
|
|
41
|
+
"max",
|
|
42
|
+
"step",
|
|
43
|
+
"color",
|
|
44
|
+
"center_color",
|
|
45
|
+
"track_color",
|
|
46
|
+
"size",
|
|
47
|
+
"show_value",
|
|
48
|
+
],
|
|
49
|
+
v_model=("value", "on_change"),
|
|
50
|
+
events=["on_change"],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
value_kws = pc.get_values_kws()
|
|
54
|
+
element = ui.knob(**value_kws)
|
|
55
|
+
super().__init__(element) # type: ignore
|
|
56
|
+
|
|
57
|
+
for key, value in pc.get_bindings().items():
|
|
58
|
+
self.bind_prop(key, value) # type: ignore
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def value(self):
|
|
62
|
+
return self.element.value
|
|
63
|
+
|
|
64
|
+
def bind_prop(self, prop: str, ref_ui: ReadonlyRef):
|
|
65
|
+
if prop == "value":
|
|
66
|
+
return self.bind_value(ref_ui)
|
|
67
|
+
|
|
68
|
+
return super().bind_prop(prop, ref_ui)
|
|
69
|
+
|
|
70
|
+
def bind_value(self, ref_ui: ReadonlyRef[float]):
|
|
71
|
+
@ui_effect
|
|
72
|
+
def _():
|
|
73
|
+
self.element.set_value(to_value(ref_ui))
|
|
74
|
+
|
|
75
|
+
return self
|
|
@@ -4,7 +4,6 @@ from typing import (
|
|
|
4
4
|
Optional,
|
|
5
5
|
TypeVar,
|
|
6
6
|
Dict,
|
|
7
|
-
Union,
|
|
8
7
|
)
|
|
9
8
|
from ex4nicegui.reactive.utils import ParameterClassifier
|
|
10
9
|
from ex4nicegui.utils.apiEffect import ui_effect
|
|
@@ -26,7 +25,7 @@ class NumberBindableUi(BindableUi[ui.number]):
|
|
|
26
25
|
label: Optional[TMaybeRef[str]] = None,
|
|
27
26
|
*,
|
|
28
27
|
placeholder: Optional[TMaybeRef[str]] = None,
|
|
29
|
-
value: TMaybeRef[
|
|
28
|
+
value: Optional[TMaybeRef[float]] = None,
|
|
30
29
|
min: Optional[TMaybeRef[float]] = None,
|
|
31
30
|
max: Optional[TMaybeRef[float]] = None,
|
|
32
31
|
step: Optional[TMaybeRef[float]] = None,
|
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
from typing import (
|
|
2
2
|
Any,
|
|
3
3
|
)
|
|
4
|
+
from ex4nicegui.reactive.utils import ParameterClassifier
|
|
5
|
+
from ex4nicegui.utils.signals import (
|
|
6
|
+
_TMaybeRef as TMaybeRef,
|
|
7
|
+
)
|
|
4
8
|
from nicegui import ui
|
|
5
9
|
from .base import BindableUi
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
class RowBindableUi(BindableUi[ui.row]):
|
|
9
|
-
def __init__(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
element = ui.row()
|
|
13
|
+
def __init__(self, *, wrap: TMaybeRef[bool] = True) -> None:
|
|
14
|
+
pc = ParameterClassifier(locals(), maybeRefs=["wrap"], events=[])
|
|
15
|
+
element = ui.row(**pc.get_values_kws())
|
|
13
16
|
|
|
14
17
|
super().__init__(element)
|
|
18
|
+
for key, value in pc.get_bindings().items():
|
|
19
|
+
self.bind_prop(key, value) # type: ignore
|
|
20
|
+
|
|
21
|
+
def bind_prop(self, prop: str, ref_ui: TMaybeRef):
|
|
22
|
+
if prop == "wrap":
|
|
23
|
+
return self.bind_wrap(ref_ui)
|
|
24
|
+
|
|
25
|
+
return super().bind_prop(prop, ref_ui)
|
|
26
|
+
|
|
27
|
+
def bind_wrap(self, ref_ui: TMaybeRef):
|
|
28
|
+
self.bind_classes({"wrap": ref_ui})
|
|
15
29
|
|
|
16
30
|
def __enter__(self):
|
|
17
31
|
self.element.__enter__()
|
ex4nicegui/reactive/utils.py
CHANGED
|
@@ -1,9 +1,33 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import (
|
|
2
|
+
Any,
|
|
3
|
+
Callable,
|
|
4
|
+
Dict,
|
|
5
|
+
Iterable,
|
|
6
|
+
List,
|
|
7
|
+
Optional,
|
|
8
|
+
Protocol,
|
|
9
|
+
Tuple,
|
|
10
|
+
cast,
|
|
11
|
+
runtime_checkable,
|
|
12
|
+
Union,
|
|
13
|
+
)
|
|
2
14
|
|
|
3
15
|
from ex4nicegui.utils.signals import is_ref, to_value, is_setter_ref
|
|
4
16
|
from nicegui.events import handle_event
|
|
5
17
|
|
|
6
18
|
|
|
19
|
+
@runtime_checkable
|
|
20
|
+
class GetItemProtocol(Protocol):
|
|
21
|
+
def __getitem__(self, key):
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@runtime_checkable
|
|
26
|
+
class SetItemProtocol(Protocol):
|
|
27
|
+
def __setitem__(self, key, value):
|
|
28
|
+
...
|
|
29
|
+
|
|
30
|
+
|
|
7
31
|
def _convert_kws_ref2value(kws: Dict) -> Dict:
|
|
8
32
|
return {key: to_value(value) for key, value in kws.items()}
|
|
9
33
|
|
|
@@ -74,3 +98,18 @@ class ParameterClassifier:
|
|
|
74
98
|
for k, v in self._args.items()
|
|
75
99
|
if (k in self.maybeRefs and (is_ref(v) or isinstance(v, Callable)))
|
|
76
100
|
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_attribute(obj: Union[object, GetItemProtocol], name: Union[str, int]) -> Any:
|
|
104
|
+
if isinstance(obj, (GetItemProtocol)):
|
|
105
|
+
return obj[name]
|
|
106
|
+
return getattr(obj, name) # type: ignore
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def set_attribute(
|
|
110
|
+
obj: Union[object, SetItemProtocol], name: Union[str, int], value: Any
|
|
111
|
+
) -> None:
|
|
112
|
+
if isinstance(obj, SetItemProtocol):
|
|
113
|
+
obj[name] = value
|
|
114
|
+
else:
|
|
115
|
+
setattr(obj, name, value) # type: ignore
|
ex4nicegui/reactive/vfor.py
CHANGED
|
@@ -2,13 +2,18 @@ from __future__ import annotations
|
|
|
2
2
|
from nicegui.element import Element
|
|
3
3
|
from nicegui import ui
|
|
4
4
|
from ex4nicegui.utils.clientScope import _CLIENT_SCOPE_MANAGER
|
|
5
|
-
from ex4nicegui.utils.signals import
|
|
5
|
+
from ex4nicegui.utils.signals import (
|
|
6
|
+
TReadonlyRef,
|
|
7
|
+
on,
|
|
8
|
+
to_ref,
|
|
9
|
+
to_ref_wrapper,
|
|
10
|
+
TGetterOrReadonlyRef,
|
|
11
|
+
)
|
|
6
12
|
from typing import (
|
|
7
13
|
Any,
|
|
8
14
|
Callable,
|
|
9
15
|
Dict,
|
|
10
16
|
List,
|
|
11
|
-
Mapping,
|
|
12
17
|
Optional,
|
|
13
18
|
TypeVar,
|
|
14
19
|
Generic,
|
|
@@ -17,11 +22,11 @@ from typing import (
|
|
|
17
22
|
)
|
|
18
23
|
from functools import partial
|
|
19
24
|
from dataclasses import dataclass
|
|
20
|
-
from signe.core.reactive import DictProxy as signe_DictProxy
|
|
21
25
|
from signe.core.scope import Scope
|
|
26
|
+
from .utils import get_attribute
|
|
22
27
|
|
|
23
28
|
_T = TypeVar("_T")
|
|
24
|
-
_T_data =
|
|
29
|
+
_T_data = TGetterOrReadonlyRef[List[Any]]
|
|
25
30
|
|
|
26
31
|
|
|
27
32
|
class VforStore(Generic[_T]):
|
|
@@ -33,29 +38,17 @@ class VforStore(Generic[_T]):
|
|
|
33
38
|
def row_index(self):
|
|
34
39
|
return self._data_index
|
|
35
40
|
|
|
36
|
-
def get(self
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if attr:
|
|
40
|
-
setter = None
|
|
41
|
-
if isinstance(item, signe_DictProxy):
|
|
42
|
-
|
|
43
|
-
def base_setter(value):
|
|
44
|
-
item[attr] = value
|
|
41
|
+
def get(self) -> TReadonlyRef[_T]:
|
|
42
|
+
def base_setter(value):
|
|
43
|
+
self._source.value[self._data_index.value] = value
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
_T,
|
|
52
|
-
to_ref_wrapper(
|
|
53
|
-
lambda: _get_attribute(item, attr),
|
|
54
|
-
setter,
|
|
55
|
-
),
|
|
56
|
-
)
|
|
45
|
+
wrapper = to_ref_wrapper(
|
|
46
|
+
lambda: self._source.value[self._data_index.value],
|
|
47
|
+
base_setter,
|
|
48
|
+
)
|
|
49
|
+
wrapper._is_readonly = True
|
|
57
50
|
|
|
58
|
-
return
|
|
51
|
+
return cast(TReadonlyRef, wrapper)
|
|
59
52
|
|
|
60
53
|
def update(self, index: int):
|
|
61
54
|
self._data_index.value = index
|
|
@@ -73,25 +66,12 @@ class VforContainer(Element, component="vfor.js"):
|
|
|
73
66
|
pass
|
|
74
67
|
|
|
75
68
|
|
|
76
|
-
def _get_attribute(obj: Union[object, Mapping], name: str) -> Any:
|
|
77
|
-
if isinstance(obj, Mapping):
|
|
78
|
-
return obj[name]
|
|
79
|
-
return getattr(obj, name)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def _set_attribute(obj: Union[object, Mapping], name: str, value: Any) -> None:
|
|
83
|
-
if isinstance(obj, dict):
|
|
84
|
-
obj[name] = value
|
|
85
|
-
else:
|
|
86
|
-
setattr(obj, name, value)
|
|
87
|
-
|
|
88
|
-
|
|
89
69
|
def _get_key_with_index(idx: int, data: Any):
|
|
90
70
|
return idx
|
|
91
71
|
|
|
92
72
|
|
|
93
73
|
def _get_key_with_getter(attr: str, idx: int, data: Any):
|
|
94
|
-
return
|
|
74
|
+
return get_attribute(data, attr)
|
|
95
75
|
|
|
96
76
|
|
|
97
77
|
class vfor(Generic[_T]):
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import inspect
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
from ex4nicegui.utils.signals import (
|
|
5
|
+
RefWrapper,
|
|
6
|
+
TRef,
|
|
7
|
+
to_raw,
|
|
8
|
+
is_setter_ref,
|
|
9
|
+
to_ref_wrapper,
|
|
10
|
+
to_value,
|
|
11
|
+
is_reactive,
|
|
12
|
+
is_setter_ref,
|
|
13
|
+
to_ref_wrapper,
|
|
14
|
+
to_value,
|
|
15
|
+
)
|
|
16
|
+
from typing import (
|
|
17
|
+
Any,
|
|
18
|
+
Callable,
|
|
19
|
+
TypeVar,
|
|
20
|
+
Union,
|
|
21
|
+
cast,
|
|
22
|
+
Tuple,
|
|
23
|
+
NamedTuple,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from types import FrameType
|
|
27
|
+
import tokenize
|
|
28
|
+
import executing
|
|
29
|
+
import ast
|
|
30
|
+
import warnings
|
|
31
|
+
from .utils import get_attribute, set_attribute
|
|
32
|
+
|
|
33
|
+
_T = TypeVar("_T")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_caller() -> FrameType:
|
|
37
|
+
return inspect.currentframe().f_back.f_back # type: ignore
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_args_code(caller: FrameType) -> str:
|
|
41
|
+
node = executing.Source.executing(caller).node
|
|
42
|
+
|
|
43
|
+
assert isinstance(node, ast.Call)
|
|
44
|
+
|
|
45
|
+
source = inspect.getsource(inspect.getmodule(caller)) # type: ignore
|
|
46
|
+
key_code = ast.get_source_segment(source, node.args[0])
|
|
47
|
+
return key_code # type: ignore
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CodeInfo(NamedTuple):
|
|
51
|
+
model: str
|
|
52
|
+
is_ref: bool
|
|
53
|
+
keys: Tuple[Union[str, int], ...] = tuple() # type: ignore
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def normalize(token: tokenize.TokenInfo):
|
|
57
|
+
if token.type == tokenize.NUMBER:
|
|
58
|
+
return int(token.string)
|
|
59
|
+
|
|
60
|
+
if token.type == tokenize.STRING:
|
|
61
|
+
return token.string[1:-1]
|
|
62
|
+
|
|
63
|
+
return token.string
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def parse_code(code: str) -> CodeInfo:
|
|
67
|
+
tokens = [
|
|
68
|
+
t
|
|
69
|
+
for t in tokenize.tokenize(BytesIO(code.encode("utf8")).readline)
|
|
70
|
+
if t.type in (tokenize.NAME, tokenize.NUMBER, tokenize.OP, tokenize.STRING)
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
model = tokens[0].string
|
|
74
|
+
is_ref = (
|
|
75
|
+
len(tokens) >= 3
|
|
76
|
+
and tokens[1].type == tokenize.OP
|
|
77
|
+
and tokens[1].string == "."
|
|
78
|
+
and tokens[2].string == "value"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
keys_search_start = 3 if is_ref else 1
|
|
82
|
+
|
|
83
|
+
keys = tuple(
|
|
84
|
+
normalize(token)
|
|
85
|
+
for token in tokens[keys_search_start:]
|
|
86
|
+
if token.type != tokenize.OP
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return CodeInfo(model, is_ref, keys)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def create_writeable_wrapper(expr, ref_data, attrs: Tuple[Union[str, int], ...]):
|
|
93
|
+
if not attrs:
|
|
94
|
+
|
|
95
|
+
def maybe_ref_getter():
|
|
96
|
+
return to_value(expr)
|
|
97
|
+
|
|
98
|
+
def reactive_getter():
|
|
99
|
+
return to_raw(expr)
|
|
100
|
+
|
|
101
|
+
def setter(value):
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
wrapper = to_ref_wrapper(
|
|
105
|
+
reactive_getter if is_reactive(expr) else maybe_ref_getter,
|
|
106
|
+
setter,
|
|
107
|
+
)
|
|
108
|
+
wrapper._is_readonly = False
|
|
109
|
+
return wrapper
|
|
110
|
+
|
|
111
|
+
def getter():
|
|
112
|
+
obj = get_attribute(to_value(ref_data), attrs[0])
|
|
113
|
+
for attr in attrs[1:]:
|
|
114
|
+
obj = get_attribute(obj, attr)
|
|
115
|
+
|
|
116
|
+
return obj
|
|
117
|
+
|
|
118
|
+
def setter(value):
|
|
119
|
+
obj = to_value(ref_data)
|
|
120
|
+
|
|
121
|
+
for attr in attrs[:-1]:
|
|
122
|
+
obj = get_attribute(obj, attr)
|
|
123
|
+
|
|
124
|
+
set_attribute(obj, attrs[-1], value)
|
|
125
|
+
|
|
126
|
+
wrapper = to_ref_wrapper(
|
|
127
|
+
getter,
|
|
128
|
+
setter,
|
|
129
|
+
)
|
|
130
|
+
wrapper._is_readonly = False
|
|
131
|
+
return wrapper
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def vmodel(expr: Any, *attrs: Union[str, int]) -> TRef[Any]:
|
|
135
|
+
"""Create a two-way binding on a form input element or a component.
|
|
136
|
+
|
|
137
|
+
@see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#vmodel
|
|
138
|
+
@中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#vmodel
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
expr (Any): _description_
|
|
142
|
+
|
|
143
|
+
## Examples
|
|
144
|
+
```python
|
|
145
|
+
from ex4nicegui.reactive import rxui
|
|
146
|
+
from ex4nicegui import deep_ref
|
|
147
|
+
|
|
148
|
+
data = deep_ref({"a": 1, "b": [1, 2, 3, 4]})
|
|
149
|
+
|
|
150
|
+
rxui.label(lambda: f"{data.value=!s}")
|
|
151
|
+
|
|
152
|
+
# No binding effect
|
|
153
|
+
rxui.input(value=data.value["a"])
|
|
154
|
+
|
|
155
|
+
# readonly binding
|
|
156
|
+
rxui.input(value=lambda: data.value["a"])
|
|
157
|
+
|
|
158
|
+
# two-way binding
|
|
159
|
+
rxui.input(value=rxui.vmodel(data.value["a"]))
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
assert not isinstance(expr, Callable), "argument expr cannot be a function"
|
|
165
|
+
|
|
166
|
+
if isinstance(expr, RefWrapper):
|
|
167
|
+
expr._is_readonly = False
|
|
168
|
+
|
|
169
|
+
if is_setter_ref(expr):
|
|
170
|
+
if attrs:
|
|
171
|
+
wrapper = create_writeable_wrapper(expr, expr, attrs)
|
|
172
|
+
|
|
173
|
+
return cast(
|
|
174
|
+
TRef,
|
|
175
|
+
wrapper,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return cast(
|
|
179
|
+
TRef,
|
|
180
|
+
expr,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
caller = get_caller()
|
|
184
|
+
code = get_args_code(caller)
|
|
185
|
+
|
|
186
|
+
info = parse_code(code)
|
|
187
|
+
ref_data = caller.f_locals.get(info.model) or caller.f_globals.get(info.model)
|
|
188
|
+
assert ref_data is not None, f"{info.model} not found"
|
|
189
|
+
all_attrs = (*info.keys, *attrs)
|
|
190
|
+
|
|
191
|
+
if not all_attrs:
|
|
192
|
+
warn_mes = ""
|
|
193
|
+
if is_reactive(expr) or is_setter_ref(expr):
|
|
194
|
+
warn_mes = rf"""Expression missing the key,result is read-only binding.Maybe you meant `{code}['key']`"""
|
|
195
|
+
elif not info.is_ref:
|
|
196
|
+
warn_mes = """Maybe you don't need to use vmodel"""
|
|
197
|
+
else:
|
|
198
|
+
warn_mes = rf"""No binding.Maybe you meant `{info.model}`"""
|
|
199
|
+
|
|
200
|
+
warnings.warn(
|
|
201
|
+
warn_mes,
|
|
202
|
+
stacklevel=2,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
wrapper = create_writeable_wrapper(expr, ref_data, all_attrs)
|
|
206
|
+
|
|
207
|
+
return cast(
|
|
208
|
+
TRef,
|
|
209
|
+
wrapper,
|
|
210
|
+
)
|
ex4nicegui/utils/signals.py
CHANGED
|
@@ -3,6 +3,7 @@ from functools import partial
|
|
|
3
3
|
import types
|
|
4
4
|
from weakref import WeakValueDictionary
|
|
5
5
|
import signe
|
|
6
|
+
from signe.core.protocols import ComputedResultProtocol
|
|
6
7
|
from .clientScope import _CLIENT_SCOPE_MANAGER
|
|
7
8
|
from typing import (
|
|
8
9
|
Any,
|
|
@@ -20,23 +21,27 @@ from typing import (
|
|
|
20
21
|
from nicegui import ui
|
|
21
22
|
from .effect import effect
|
|
22
23
|
from .scheduler import get_uiScheduler
|
|
24
|
+
import warnings
|
|
23
25
|
|
|
24
26
|
T = TypeVar("T")
|
|
25
27
|
|
|
26
28
|
|
|
27
|
-
TReadonlyRef =
|
|
29
|
+
TReadonlyRef = ComputedResultProtocol[T]
|
|
28
30
|
ReadonlyRef = TReadonlyRef[T]
|
|
29
31
|
DescReadonlyRef = TReadonlyRef[T]
|
|
30
32
|
|
|
31
33
|
TGetterOrReadonlyRef = signe.TGetter[T]
|
|
32
34
|
|
|
33
35
|
|
|
36
|
+
is_reactive = signe.is_reactive
|
|
37
|
+
|
|
38
|
+
|
|
34
39
|
def reactive(obj: T) -> T:
|
|
35
40
|
return signe.reactive(obj, get_uiScheduler())
|
|
36
41
|
|
|
37
42
|
|
|
38
43
|
class RefWrapper(Generic[T]):
|
|
39
|
-
__slot__ = ("_getter_fn", "_setter_fn")
|
|
44
|
+
__slot__ = ("_getter_fn", "_setter_fn", "")
|
|
40
45
|
|
|
41
46
|
def __init__(
|
|
42
47
|
self,
|
|
@@ -57,12 +62,17 @@ class RefWrapper(Generic[T]):
|
|
|
57
62
|
self._getter_fn = lambda: getter_or_ref
|
|
58
63
|
self._setter_fn = lambda x: None
|
|
59
64
|
|
|
65
|
+
self._is_readonly = False
|
|
66
|
+
|
|
60
67
|
@property
|
|
61
68
|
def value(self) -> T:
|
|
62
69
|
return cast(T, self._getter_fn())
|
|
63
70
|
|
|
64
71
|
@value.setter
|
|
65
72
|
def value(self, new_value: T):
|
|
73
|
+
if self._is_readonly:
|
|
74
|
+
warnings.warn("readonly ref cannot be assigned.")
|
|
75
|
+
return
|
|
66
76
|
return self._setter_fn(new_value)
|
|
67
77
|
|
|
68
78
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ex4nicegui
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.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,9 +14,10 @@ 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.
|
|
17
|
+
Requires-Dist: signe (>=0.4.9)
|
|
18
18
|
Requires-Dist: nicegui (>=1.4.0)
|
|
19
19
|
Requires-Dist: typing-extensions
|
|
20
|
+
Requires-Dist: executing
|
|
20
21
|
|
|
21
22
|
# ex4nicegui
|
|
22
23
|
[中文 README](./README.md)
|
|
@@ -389,16 +390,58 @@ ui.button("change b", on_click=change_b)
|
|
|
389
390
|
|
|
390
391
|
---
|
|
391
392
|
|
|
393
|
+
## functionality
|
|
394
|
+
|
|
395
|
+
### vmodel
|
|
396
|
+
Create two-way bindings on form input elements or components.
|
|
397
|
+
|
|
398
|
+
Bidirectional bindings are supported by default for `ref` simple value types
|
|
399
|
+
```python
|
|
400
|
+
from ex4nicegui import rxui, to_ref, deep_ref
|
|
401
|
+
|
|
402
|
+
data = to_ref("init")
|
|
403
|
+
|
|
404
|
+
rxui.label(lambda: f"{data.value=}")
|
|
405
|
+
# two-way binding
|
|
406
|
+
rxui.input(value=data)
|
|
407
|
+
```
|
|
408
|
+
- Simple value types are generally immutable values such as `str`, `int`, etc.
|
|
409
|
+
|
|
410
|
+
When using complex data structures, `deep_ref` is used to keep nested values responsive.
|
|
411
|
+
```python
|
|
412
|
+
data = deep_ref({"a": 1, "b": [1, 2, 3, 4]})
|
|
413
|
+
|
|
414
|
+
rxui.label(lambda: f"{data.value=!s}")
|
|
415
|
+
|
|
416
|
+
# No binding effect
|
|
417
|
+
rxui.input(value=data.value["a"])
|
|
418
|
+
|
|
419
|
+
# readonly binding
|
|
420
|
+
rxui.input(value=lambda: data.value["a"])
|
|
421
|
+
|
|
422
|
+
# two-way binding
|
|
423
|
+
rxui.input(value=rxui.vmodel(data.value["a"]))
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
- The first input box will be completely unresponsive because the code is equivalent to passing in a value `1` directly
|
|
427
|
+
- The second input box will get read responsiveness due to the use of a function.
|
|
428
|
+
- The third input box, wrapped in `rxui.vmodel`, can be bi-directionally bound.
|
|
429
|
+
|
|
430
|
+
Most use `vmodel` with `vfor`, see [todo list examples](./examples/todomvc/)
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
392
434
|
### vfor
|
|
393
435
|
Render list components based on list responsive data. Each component is updated on demand. Data items support dictionaries or objects of any type
|
|
394
436
|
|
|
395
437
|
```python
|
|
396
438
|
from nicegui import ui
|
|
397
439
|
from ex4nicegui.reactive import rxui
|
|
398
|
-
from ex4nicegui import
|
|
440
|
+
from ex4nicegui import deep_ref, ref_computed
|
|
441
|
+
from typing import Dict
|
|
399
442
|
|
|
400
443
|
# refs
|
|
401
|
-
items =
|
|
444
|
+
items = deep_ref(
|
|
402
445
|
[
|
|
403
446
|
{"id": 1, "message": "foo", "done": False},
|
|
404
447
|
{"id": 2, "message": "bar", "done": True},
|
|
@@ -414,7 +457,6 @@ def done_count_info():
|
|
|
414
457
|
def check():
|
|
415
458
|
for item in items.value:
|
|
416
459
|
item["done"] = not item["done"]
|
|
417
|
-
items.value = items.value
|
|
418
460
|
|
|
419
461
|
|
|
420
462
|
# ui
|
|
@@ -423,54 +465,33 @@ ui.button("check", on_click=check)
|
|
|
423
465
|
|
|
424
466
|
|
|
425
467
|
@rxui.vfor(items,key='id')
|
|
426
|
-
def _(store: rxui.VforStore):
|
|
468
|
+
def _(store: rxui.VforStore[Dict]):
|
|
427
469
|
# function to build the interface for each row of data
|
|
428
|
-
|
|
470
|
+
item = store.get() # Get responsive object with `store.get`
|
|
471
|
+
mes = rxui.vmodel(item.value['message'])
|
|
429
472
|
|
|
430
473
|
# Enter the content of the input box,
|
|
431
474
|
# you can see the title of the radio box changes synchronously
|
|
432
475
|
with ui.card():
|
|
433
|
-
|
|
434
|
-
|
|
476
|
+
with ui.row():
|
|
477
|
+
rxui.input(value=mes)
|
|
478
|
+
rxui.label(lambda: f"{mes.value=!s}")
|
|
479
|
+
rxui.checkbox(text=mes, value=rxui.vmodel(item.value['done']))
|
|
435
480
|
|
|
436
481
|
```
|
|
437
482
|
|
|
438
483
|
- `rxui.vfor` decorator to custom function
|
|
439
484
|
- The first argument is passed to the responsive list. Each item in the list can be a dictionary or other object (`dataclasses` etc.)
|
|
440
485
|
- Second parameter `key`: In order to be able to keep track of the identity of each node, and thus reuse and reorder existing elements, you can provide a unique key for the block corresponding to each element. The default(`None`) is to use the list element index.
|
|
441
|
-
- The custom function takes one argument. The current row's
|
|
486
|
+
- The custom function takes one argument. The current row's can be retrieved via `store.get`, which is a responsive object.
|
|
442
487
|
|
|
443
488
|
|
|
444
489
|
> vfor are created only when new data is added.
|
|
445
490
|
|
|
446
|
-
In the above example, you'll notice that when the checkbox is clicked, the text of the number of completed counts (`done_count_info`) doesn't change synchronously
|
|
447
|
-
|
|
448
|
-
This is because responsive data changes in the `vfor` function do not affect the data source list. This is a restriction to prevent writing overly complex bi-directional data flow response logic.
|
|
449
|
-
|
|
450
|
-
We should make changes to the data source list via events in the function
|
|
451
|
-
|
|
452
|
-
```python
|
|
453
|
-
...
|
|
454
|
-
|
|
455
|
-
@rxui.vfor(items, key="id")
|
|
456
|
-
def _(store: rxui.VforStore):
|
|
457
|
-
msg_ref = store.get("message")
|
|
458
|
-
|
|
459
|
-
def on_check_change(e):
|
|
460
|
-
items.value[store.row_index]["done"] = e.value
|
|
461
|
-
items.value = items.value
|
|
462
|
-
|
|
463
|
-
with ui.card():
|
|
464
|
-
rxui.input(value=msg_ref)
|
|
465
|
-
rxui.checkbox(text=msg_ref, value=store.get("done"),on_change=on_check_change)
|
|
466
|
-
|
|
467
|
-
```
|
|
468
491
|
|
|
469
492
|
---
|
|
470
493
|
|
|
471
494
|
|
|
472
|
-
## functionality
|
|
473
|
-
|
|
474
495
|
### Bind class names
|
|
475
496
|
|
|
476
497
|
All component classes provide `bind_classes` for binding `class`, supporting three different data structures.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
ex4nicegui/__init__.py,sha256=
|
|
1
|
+
ex4nicegui/__init__.py,sha256=sbp4UdZz1sLg5LIpMSt87LwclMQb13_IDD9mewNPRuE,736
|
|
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
|
|
@@ -22,8 +22,10 @@ ex4nicegui/bi/elements/ui_table.py,sha256=aKL26xbiCIQG_-KFEozXjbB4G88FdOK25k2H0k
|
|
|
22
22
|
ex4nicegui/experimental_/__init__.py,sha256=HODL0f70HUzVrfRwUzdCwxTp_9mYr4D1nnzd8jevlMw,69
|
|
23
23
|
ex4nicegui/experimental_/gridLayout/__init__.py,sha256=c9k-zykhKW3Ol6QECUoKqJW9QEuhA9xPi8s4Dm4m7SU,125
|
|
24
24
|
ex4nicegui/experimental_/gridLayout/index.py,sha256=zFXuvFroo5EC1CFjt-b4hMiEy67hGP5J1GYTKH6kpUU,4737
|
|
25
|
-
ex4nicegui/gsap/__init__.py,sha256=
|
|
25
|
+
ex4nicegui/gsap/__init__.py,sha256=xBApQlq9st9exhgcSkqCzWeS6bc1eKiPJfYP25MSInw,218
|
|
26
26
|
ex4nicegui/gsap/gsap.py,sha256=-1sncG2_37LYN6RosnHZAgYSI899HMj9FB6MQWUj8yw,4675
|
|
27
|
+
ex4nicegui/gsap/timeline.js,sha256=CB300drH7UUSfy_WDeuWHSNh3WX-vwbRtKBLL1Ak43o,1241
|
|
28
|
+
ex4nicegui/gsap/timeline.py,sha256=B3CvN83CzJMMFMVdZZC2UHR0l_I90amRQ0Sf-P623zo,2421
|
|
27
29
|
ex4nicegui/gsap/wrapGsap.js,sha256=0Iz7qh8aA-h3svV7fW4U5k_pX7zXCcIdHDaG7NNvgLU,1045
|
|
28
30
|
ex4nicegui/layout/__init__.py,sha256=YT76Ec7p4aFOGms6wc19207flBeyI6jrq7Kg_FQ2wnQ,278
|
|
29
31
|
ex4nicegui/layout/gridFlex/GridFlex.js,sha256=ljkxGFucBUIPksMAT5w_35sxGogC7OzxzXnOw21Z3_k,4468
|
|
@@ -64,15 +66,16 @@ ex4nicegui/libs/gsap/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
|
64
66
|
ex4nicegui/libs/gsap/utils/matrix.js,sha256=77scrxbQZXx4ex5HkvnT9IkhMG1rQoDNp4TSYgUeYVk,15235
|
|
65
67
|
ex4nicegui/libs/gsap/utils/paths.js,sha256=2SPaRHQ7zgba9cH8hGhkTYPCZdrrEhE2qhh6ECAEvSA,49314
|
|
66
68
|
ex4nicegui/libs/gsap/utils/strings.js,sha256=47G9slz5ltG9mDSwrfQDtWzzdV5QJ-AIMLRMNK0VSiM,10472
|
|
67
|
-
ex4nicegui/reactive/__init__.py,sha256=
|
|
69
|
+
ex4nicegui/reactive/__init__.py,sha256=osdHgTwRTI2jOjHtbOpMjdQlvGiJW-1vaqcPPLhzbdQ,3339
|
|
68
70
|
ex4nicegui/reactive/fileWatcher.py,sha256=gjeZhgar02f-qGQa47Tj5SMaCP_ftRtSU898XUmXl1U,1472
|
|
69
71
|
ex4nicegui/reactive/local_file_picker.py,sha256=6kh7pW4RZk1gqYjxkOvTfJ-2T_KwZznr8AekcZoxthw,6171
|
|
70
72
|
ex4nicegui/reactive/q_pagination.py,sha256=WcSYpB75NH3lffw3HXwZRcvnh0l6EA1Eqp5bcJKIJaw,1505
|
|
71
73
|
ex4nicegui/reactive/rxui.py,sha256=gZ8ZEjGuJFKcedEZhcm4PIZguNkY-Wv5yQx80QnsBKI,31
|
|
72
74
|
ex4nicegui/reactive/usePagination.py,sha256=Gop9KV780DcQK5bH4zQZ5sRpV3jOD2sRX_Bpmb1LTFI,2598
|
|
73
|
-
ex4nicegui/reactive/utils.py,sha256=
|
|
75
|
+
ex4nicegui/reactive/utils.py,sha256=GSk_eUH-iNUi18Tg7MsBVnMK5basgX6EQgbSD1zxsZw,3315
|
|
74
76
|
ex4nicegui/reactive/vfor.js,sha256=OmpeLZdjV_GiG5HdglMXGBKpgrKYh3UIeugpszAVdWw,52
|
|
75
|
-
ex4nicegui/reactive/vfor.py,sha256=
|
|
77
|
+
ex4nicegui/reactive/vfor.py,sha256=hyvbmMJtC8usSYeq023bCL8kOrmnFw09YOS_r6GWBzk,5495
|
|
78
|
+
ex4nicegui/reactive/vmodel.py,sha256=FH1rXjbzD6r0MeFdf-3GDvz8JJ_qgmI7rTo8itw_StY,5147
|
|
76
79
|
ex4nicegui/reactive/EChartsComponent/ECharts.js,sha256=HMmgXlDl548H4JpozDSbLWv-opvPi3OdJz_XY_MHbnY,3326
|
|
77
80
|
ex4nicegui/reactive/EChartsComponent/ECharts.py,sha256=hoUZ7h-KOUKNd-1BybSX4xxv33gYuTkky70yEzv-oIA,6344
|
|
78
81
|
ex4nicegui/reactive/EChartsComponent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -91,8 +94,9 @@ ex4nicegui/reactive/officials/base.py,sha256=1d6ePyz4ZiOsZYLKtCr04Aw4_9YyYl2HpCN
|
|
|
91
94
|
ex4nicegui/reactive/officials/button.py,sha256=12FZ2cQGFsTsivSHT9YfDD_wP_D4xrpHHfKmVNNKLJE,1660
|
|
92
95
|
ex4nicegui/reactive/officials/card.py,sha256=8-tBwm3xfVybolQ87i8lAYUpBV6FdaVdeSH6xu0736U,1275
|
|
93
96
|
ex4nicegui/reactive/officials/checkbox.py,sha256=BcSbWuMTA2UgwOeuBTtpvzqdOivb2xbkLazfGqwuboo,1562
|
|
97
|
+
ex4nicegui/reactive/officials/circular_progress.py,sha256=BVLjCJtEcwa2pqGZ4nC-w-8FlkTS5IuqlL_tY6xOXNY,1787
|
|
94
98
|
ex4nicegui/reactive/officials/color_picker.py,sha256=fSRICdzdH16w3BIGyAk3CtaauYqpPfUHCRc2kq-w-ZA,3402
|
|
95
|
-
ex4nicegui/reactive/officials/column.py,sha256=
|
|
99
|
+
ex4nicegui/reactive/officials/column.py,sha256=CfoJigEdpRoGaJTY_O7wbK8cp8ufX8mYUKNRUnPWUr0,1040
|
|
96
100
|
ex4nicegui/reactive/officials/date.py,sha256=jp75WgZaICnCHLS_wpF3uJI2kaGeNqmRLj3qpn5IIyg,2681
|
|
97
101
|
ex4nicegui/reactive/officials/drawer.py,sha256=y9_gbDDy_6Y7mRfSB8BGawM-_g6kECM1ieZXMX2YCQo,2370
|
|
98
102
|
ex4nicegui/reactive/officials/echarts.py,sha256=rBYHL6K_uAp4A4r_qacXt_bFukOMPWxdN_9xjEXG4RY,9683
|
|
@@ -103,11 +107,12 @@ ex4nicegui/reactive/officials/html.py,sha256=yasDeGMJyyGmCpJoiwmG-QszfLSOsHLYWSh
|
|
|
103
107
|
ex4nicegui/reactive/officials/icon.py,sha256=L0yHo4Ute7PNJw2R2vn8PJGhIjreqeoZnS4p6tijmg0,1559
|
|
104
108
|
ex4nicegui/reactive/officials/image.py,sha256=qtdtrjZ2OPF1mmtrL-RSgH3hNcXbRdwH9oD-RyFYVJM,1120
|
|
105
109
|
ex4nicegui/reactive/officials/input.py,sha256=d338LH7tYArhUQYXJleytIOXsDieXEm4yLxOrjRbxLM,4030
|
|
110
|
+
ex4nicegui/reactive/officials/knob.py,sha256=ZCKXUbFQUhdsRoTADk2gqdEwmi1CeW9Jmc7AGJyV1Yg,2153
|
|
106
111
|
ex4nicegui/reactive/officials/label.py,sha256=7xhvQIa-3Zm44xJIoKeMys1M6d6AMJTeE5l9THPICGo,1398
|
|
107
112
|
ex4nicegui/reactive/officials/linear_progress.py,sha256=3ew4uqUIDrDH0PdX42K7rnKQL4khmZgxmJfmWX6qMDs,2128
|
|
108
|
-
ex4nicegui/reactive/officials/number.py,sha256=
|
|
113
|
+
ex4nicegui/reactive/officials/number.py,sha256=Ro20LiSjzeLkcPM5oIe3phulLH5Gsw3YwYBCyRl9bqI,2201
|
|
109
114
|
ex4nicegui/reactive/officials/radio.py,sha256=35KCAE0JhzBczaCGrlufZystgpzljobOoIw1H7js1KA,1929
|
|
110
|
-
ex4nicegui/reactive/officials/row.py,sha256=
|
|
115
|
+
ex4nicegui/reactive/officials/row.py,sha256=pwFJBdv3_D5-E-oga-OG5t57ahHcYXGSI_-UofWg_j0,1029
|
|
111
116
|
ex4nicegui/reactive/officials/select.py,sha256=Fj4V96ju2tpsP1ok0RFokN1K7TxzNS9YLVCz55O788Y,2980
|
|
112
117
|
ex4nicegui/reactive/officials/slider.py,sha256=6E6UeyX_hXIDz_-0v6WEIfTM5oqzukonRyqDRJHT-PA,2954
|
|
113
118
|
ex4nicegui/reactive/officials/switch.py,sha256=-QDXbOQllnWRE28pAH74qrgpN1TXs-1snLDqN6pdiVM,1605
|
|
@@ -126,9 +131,9 @@ ex4nicegui/utils/clientScope.py,sha256=-KJnrZGqdAQUNNbQbqMAqNzWHCVon-lC3aaQGuaTd
|
|
|
126
131
|
ex4nicegui/utils/common.py,sha256=7P0vboDadLun6EMxNi3br9rKJgKt0QT4sy_66cHEwb4,994
|
|
127
132
|
ex4nicegui/utils/effect.py,sha256=MgvWuAP3OFs2bR4ef6uXPwGCkKORUK-4hmx1oSwl04Y,2310
|
|
128
133
|
ex4nicegui/utils/scheduler.py,sha256=Wa963Df3UDvWHjXXoVYGIBevIILzCFoz-yAWjvxeyfQ,1218
|
|
129
|
-
ex4nicegui/utils/signals.py,sha256=
|
|
130
|
-
ex4nicegui-0.5.
|
|
131
|
-
ex4nicegui-0.5.
|
|
132
|
-
ex4nicegui-0.5.
|
|
133
|
-
ex4nicegui-0.5.
|
|
134
|
-
ex4nicegui-0.5.
|
|
134
|
+
ex4nicegui/utils/signals.py,sha256=Eruq0WUH-G7dpnfHZ2y9Uf-mS6L_RBVcsaqbK8FKfvE,11193
|
|
135
|
+
ex4nicegui-0.5.3.dist-info/LICENSE,sha256=0KDDElS2dl-HIsWvbpy8ywbLzJMBFzXLev57LnMIZXs,1094
|
|
136
|
+
ex4nicegui-0.5.3.dist-info/METADATA,sha256=hGb8ZKAD6yb90SRQ6tALuCBNwsGaPwbJFK6rGsWboG0,26519
|
|
137
|
+
ex4nicegui-0.5.3.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
138
|
+
ex4nicegui-0.5.3.dist-info/top_level.txt,sha256=VFwMiO9AFjj5rfLMJwN1ipLRASk9fJXB8tM6DNrpvPQ,11
|
|
139
|
+
ex4nicegui-0.5.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|