reflex 0.6.0a1__py3-none-any.whl → 0.6.0a3__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.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +2 -2
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
- reflex/__init__.py +8 -2
- reflex/__init__.pyi +2 -1
- reflex/app.py +10 -4
- reflex/base.py +1 -1
- reflex/compiler/compiler.py +2 -2
- reflex/compiler/utils.py +3 -3
- reflex/components/base/app_wrap.py +2 -2
- reflex/components/base/app_wrap.pyi +17 -27
- reflex/components/base/bare.py +4 -5
- reflex/components/base/body.pyi +17 -27
- reflex/components/base/document.pyi +81 -131
- reflex/components/base/error_boundary.py +6 -7
- reflex/components/base/error_boundary.pyi +20 -33
- reflex/components/base/fragment.pyi +17 -27
- reflex/components/base/head.pyi +33 -53
- reflex/components/base/link.py +1 -1
- reflex/components/base/link.pyi +33 -54
- reflex/components/base/meta.pyi +65 -105
- reflex/components/base/script.py +1 -2
- reflex/components/base/script.pyi +21 -38
- reflex/components/component.py +45 -47
- reflex/components/core/banner.py +23 -27
- reflex/components/core/banner.pyi +134 -171
- reflex/components/core/breakpoints.py +3 -1
- reflex/components/core/client_side_routing.py +2 -3
- reflex/components/core/client_side_routing.pyi +33 -54
- reflex/components/core/clipboard.py +2 -1
- reflex/components/core/clipboard.pyi +20 -33
- reflex/components/core/cond.py +5 -5
- reflex/components/core/debounce.py +5 -5
- reflex/components/core/debounce.pyi +20 -33
- reflex/components/core/foreach.py +3 -4
- reflex/components/core/html.py +1 -1
- reflex/components/core/html.pyi +35 -46
- reflex/components/core/match.py +17 -17
- reflex/components/core/upload.py +17 -23
- reflex/components/core/upload.pyi +78 -124
- reflex/components/datadisplay/code.py +9 -10
- reflex/components/datadisplay/code.pyi +302 -412
- reflex/components/datadisplay/dataeditor.py +8 -10
- reflex/components/datadisplay/dataeditor.pyi +40 -53
- reflex/components/el/element.pyi +17 -27
- reflex/components/el/elements/base.py +1 -1
- reflex/components/el/elements/base.pyi +34 -45
- reflex/components/el/elements/forms.py +16 -16
- reflex/components/el/elements/forms.pyi +554 -707
- reflex/components/el/elements/inline.py +1 -1
- reflex/components/el/elements/inline.pyi +937 -1218
- reflex/components/el/elements/media.py +1 -1
- reflex/components/el/elements/media.pyi +786 -997
- reflex/components/el/elements/metadata.py +3 -6
- reflex/components/el/elements/metadata.pyi +181 -242
- reflex/components/el/elements/other.py +1 -1
- reflex/components/el/elements/other.pyi +235 -306
- reflex/components/el/elements/scripts.py +1 -1
- reflex/components/el/elements/scripts.pyi +109 -140
- reflex/components/el/elements/sectioning.py +0 -2
- reflex/components/el/elements/sectioning.pyi +496 -647
- reflex/components/el/elements/tables.py +1 -1
- reflex/components/el/elements/tables.pyi +351 -452
- reflex/components/el/elements/typography.py +1 -1
- reflex/components/el/elements/typography.pyi +506 -657
- reflex/components/gridjs/datatable.py +6 -9
- reflex/components/gridjs/datatable.pyi +35 -56
- reflex/components/lucide/icon.py +1 -1
- reflex/components/lucide/icon.pyi +33 -54
- reflex/components/markdown/markdown.py +26 -31
- reflex/components/markdown/markdown.pyi +27 -37
- reflex/components/moment/moment.py +13 -12
- reflex/components/moment/moment.pyi +23 -35
- reflex/components/next/base.pyi +17 -27
- reflex/components/next/image.py +1 -1
- reflex/components/next/image.pyi +22 -37
- reflex/components/next/link.py +1 -1
- reflex/components/next/link.pyi +17 -28
- reflex/components/next/video.py +1 -1
- reflex/components/next/video.pyi +17 -28
- reflex/components/plotly/plotly.py +12 -13
- reflex/components/plotly/plotly.pyi +39 -54
- reflex/components/props.py +1 -1
- reflex/components/radix/__init__.pyi +1 -0
- reflex/components/radix/primitives/__init__.pyi +1 -0
- reflex/components/radix/primitives/accordion.py +4 -4
- reflex/components/radix/primitives/accordion.pyi +424 -495
- reflex/components/radix/primitives/base.py +1 -1
- reflex/components/radix/primitives/base.pyi +33 -54
- reflex/components/radix/primitives/drawer.py +1 -1
- reflex/components/radix/primitives/drawer.pyi +172 -273
- reflex/components/radix/primitives/form.py +1 -1
- reflex/components/radix/primitives/form.pyi +257 -364
- reflex/components/radix/primitives/progress.py +1 -1
- reflex/components/radix/primitives/progress.pyi +231 -282
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/radix/primitives/slider.pyi +87 -138
- reflex/components/radix/themes/base.py +3 -24
- reflex/components/radix/themes/base.pyi +178 -250
- reflex/components/radix/themes/color_mode.py +5 -5
- reflex/components/radix/themes/color_mode.pyi +187 -220
- reflex/components/radix/themes/components/alert_dialog.py +1 -1
- reflex/components/radix/themes/components/alert_dialog.pyi +136 -207
- reflex/components/radix/themes/components/aspect_ratio.py +1 -1
- reflex/components/radix/themes/components/aspect_ratio.pyi +17 -28
- reflex/components/radix/themes/components/avatar.py +1 -1
- reflex/components/radix/themes/components/avatar.pyi +70 -81
- reflex/components/radix/themes/components/badge.py +1 -1
- reflex/components/radix/themes/components/badge.pyi +88 -99
- reflex/components/radix/themes/components/button.py +1 -1
- reflex/components/radix/themes/components/button.pyi +98 -109
- reflex/components/radix/themes/components/callout.py +1 -1
- reflex/components/radix/themes/components/callout.pyi +322 -373
- reflex/components/radix/themes/components/card.py +1 -1
- reflex/components/radix/themes/components/card.pyi +38 -49
- reflex/components/radix/themes/components/checkbox.py +1 -2
- reflex/components/radix/themes/components/checkbox.pyi +208 -245
- reflex/components/radix/themes/components/checkbox_cards.py +1 -1
- reflex/components/radix/themes/components/checkbox_cards.pyi +94 -115
- reflex/components/radix/themes/components/checkbox_group.py +1 -1
- reflex/components/radix/themes/components/checkbox_group.pyi +86 -107
- reflex/components/radix/themes/components/context_menu.py +1 -1
- reflex/components/radix/themes/components/context_menu.pyi +238 -319
- reflex/components/radix/themes/components/data_list.py +1 -1
- reflex/components/radix/themes/components/data_list.pyi +130 -171
- reflex/components/radix/themes/components/dialog.py +1 -1
- reflex/components/radix/themes/components/dialog.pyi +139 -210
- reflex/components/radix/themes/components/dropdown_menu.py +1 -1
- reflex/components/radix/themes/components/dropdown_menu.pyi +249 -332
- reflex/components/radix/themes/components/hover_card.py +1 -1
- reflex/components/radix/themes/components/hover_card.pyi +90 -131
- reflex/components/radix/themes/components/icon_button.py +2 -3
- reflex/components/radix/themes/components/icon_button.pyi +98 -109
- reflex/components/radix/themes/components/inset.py +1 -1
- reflex/components/radix/themes/components/inset.pyi +47 -58
- reflex/components/radix/themes/components/popover.py +1 -1
- reflex/components/radix/themes/components/popover.pyi +95 -136
- reflex/components/radix/themes/components/progress.py +1 -1
- reflex/components/radix/themes/components/progress.pyi +71 -82
- reflex/components/radix/themes/components/radio.py +1 -1
- reflex/components/radix/themes/components/radio.pyi +69 -80
- reflex/components/radix/themes/components/radio_cards.py +1 -1
- reflex/components/radix/themes/components/radio_cards.pyi +98 -119
- reflex/components/radix/themes/components/radio_group.py +8 -11
- reflex/components/radix/themes/components/radio_group.pyi +228 -271
- reflex/components/radix/themes/components/scroll_area.py +1 -1
- reflex/components/radix/themes/components/scroll_area.pyi +21 -32
- reflex/components/radix/themes/components/segmented_control.py +1 -1
- reflex/components/radix/themes/components/segmented_control.pyi +90 -113
- reflex/components/radix/themes/components/select.py +2 -3
- reflex/components/radix/themes/components/select.pyi +374 -471
- reflex/components/radix/themes/components/separator.py +1 -2
- reflex/components/radix/themes/components/separator.pyi +69 -80
- reflex/components/radix/themes/components/skeleton.py +1 -1
- reflex/components/radix/themes/components/skeleton.pyi +23 -34
- reflex/components/radix/themes/components/slider.py +2 -3
- reflex/components/radix/themes/components/slider.pyi +75 -88
- reflex/components/radix/themes/components/spinner.py +1 -1
- reflex/components/radix/themes/components/spinner.pyi +19 -30
- reflex/components/radix/themes/components/switch.py +1 -1
- reflex/components/radix/themes/components/switch.pyi +71 -84
- reflex/components/radix/themes/components/table.py +1 -1
- reflex/components/radix/themes/components/table.pyi +261 -332
- reflex/components/radix/themes/components/tabs.py +1 -1
- reflex/components/radix/themes/components/tabs.pyi +139 -194
- reflex/components/radix/themes/components/text_area.py +1 -1
- reflex/components/radix/themes/components/text_area.pyi +96 -111
- reflex/components/radix/themes/components/text_field.py +1 -1
- reflex/components/radix/themes/components/text_field.pyi +247 -286
- reflex/components/radix/themes/components/tooltip.py +1 -1
- reflex/components/radix/themes/components/tooltip.pyi +26 -37
- reflex/components/radix/themes/layout/__init__.pyi +1 -0
- reflex/components/radix/themes/layout/base.py +1 -1
- reflex/components/radix/themes/layout/base.pyi +56 -67
- reflex/components/radix/themes/layout/box.pyi +34 -45
- reflex/components/radix/themes/layout/center.pyi +56 -67
- reflex/components/radix/themes/layout/container.py +1 -2
- reflex/components/radix/themes/layout/container.pyi +36 -47
- reflex/components/radix/themes/layout/flex.py +1 -1
- reflex/components/radix/themes/layout/flex.pyi +56 -67
- reflex/components/radix/themes/layout/grid.py +1 -1
- reflex/components/radix/themes/layout/grid.pyi +64 -75
- reflex/components/radix/themes/layout/list.py +5 -6
- reflex/components/radix/themes/layout/list.pyi +193 -244
- reflex/components/radix/themes/layout/section.py +1 -2
- reflex/components/radix/themes/layout/section.pyi +36 -47
- reflex/components/radix/themes/layout/spacer.pyi +56 -67
- reflex/components/radix/themes/layout/stack.py +1 -1
- reflex/components/radix/themes/layout/stack.pyi +128 -159
- reflex/components/radix/themes/typography/blockquote.py +1 -1
- reflex/components/radix/themes/typography/blockquote.pyi +89 -100
- reflex/components/radix/themes/typography/code.py +1 -1
- reflex/components/radix/themes/typography/code.pyi +90 -101
- reflex/components/radix/themes/typography/heading.py +1 -1
- reflex/components/radix/themes/typography/heading.pyi +96 -107
- reflex/components/radix/themes/typography/link.py +1 -1
- reflex/components/radix/themes/typography/link.pyi +102 -113
- reflex/components/radix/themes/typography/text.py +1 -1
- reflex/components/radix/themes/typography/text.pyi +501 -572
- reflex/components/react_player/audio.pyi +33 -60
- reflex/components/react_player/react_player.py +1 -1
- reflex/components/react_player/react_player.pyi +33 -60
- reflex/components/react_player/video.pyi +33 -60
- reflex/components/recharts/cartesian.py +2 -3
- reflex/components/recharts/cartesian.pyi +678 -861
- reflex/components/recharts/charts.py +4 -5
- reflex/components/recharts/charts.pyi +252 -357
- reflex/components/recharts/general.py +1 -2
- reflex/components/recharts/general.pyi +180 -231
- reflex/components/recharts/polar.py +4 -5
- reflex/components/recharts/polar.pyi +144 -181
- reflex/components/recharts/recharts.pyi +33 -53
- reflex/components/sonner/toast.py +16 -17
- reflex/components/sonner/toast.pyi +36 -47
- reflex/components/suneditor/editor.py +2 -3
- reflex/components/suneditor/editor.pyi +55 -78
- reflex/components/tags/cond_tag.py +6 -4
- reflex/components/tags/iter_tag.py +28 -16
- reflex/components/tags/match_tag.py +6 -4
- reflex/components/tags/tag.py +40 -23
- reflex/custom_components/custom_components.py +3 -1
- reflex/event.py +115 -67
- reflex/experimental/client_state.py +18 -18
- reflex/experimental/hooks.py +16 -16
- reflex/experimental/layout.py +5 -5
- reflex/experimental/layout.pyi +136 -187
- reflex/middleware/hydrate_middleware.py +2 -0
- reflex/middleware/middleware.py +3 -3
- reflex/state.py +149 -82
- reflex/style.py +21 -22
- reflex/utils/exceptions.py +20 -0
- reflex/utils/format.py +54 -34
- reflex/utils/imports.py +16 -73
- reflex/utils/prerequisites.py +15 -8
- reflex/utils/pyi_generator.py +13 -8
- reflex/utils/serializers.py +12 -22
- reflex/utils/telemetry.py +3 -2
- reflex/utils/types.py +11 -6
- reflex/{ivars → vars}/__init__.py +6 -2
- reflex/{ivars → vars}/base.py +599 -216
- reflex/{ivars → vars}/function.py +15 -19
- reflex/{ivars → vars}/number.py +41 -20
- reflex/{ivars → vars}/object.py +28 -30
- reflex/{ivars → vars}/sequence.py +53 -42
- {reflex-0.6.0a1.dist-info → reflex-0.6.0a3.dist-info}/METADATA +4 -6
- reflex-0.6.0a3.dist-info/RECORD +382 -0
- reflex/.templates/web/components/reflex/chakra_color_mode_provider.js +0 -36
- reflex/vars.py +0 -501
- reflex-0.6.0a1.dist-info/RECORD +0 -384
- {reflex-0.6.0a1.dist-info → reflex-0.6.0a3.dist-info}/LICENSE +0 -0
- {reflex-0.6.0a1.dist-info → reflex-0.6.0a3.dist-info}/WHEEL +0 -0
- {reflex-0.6.0a1.dist-info → reflex-0.6.0a3.dist-info}/entry_points.txt +0 -0
|
@@ -2,31 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import dataclasses
|
|
5
6
|
import inspect
|
|
6
|
-
from typing import
|
|
7
|
+
from typing import (
|
|
8
|
+
TYPE_CHECKING,
|
|
9
|
+
Any,
|
|
10
|
+
Callable,
|
|
11
|
+
Iterable,
|
|
12
|
+
Tuple,
|
|
13
|
+
Type,
|
|
14
|
+
Union,
|
|
15
|
+
get_args,
|
|
16
|
+
)
|
|
7
17
|
|
|
8
18
|
from reflex.components.tags.tag import Tag
|
|
9
|
-
from reflex.
|
|
10
|
-
from reflex.vars import Var
|
|
19
|
+
from reflex.vars import LiteralArrayVar, Var, get_unique_variable_name
|
|
11
20
|
|
|
12
21
|
if TYPE_CHECKING:
|
|
13
22
|
from reflex.components.component import Component
|
|
14
23
|
|
|
15
24
|
|
|
25
|
+
@dataclasses.dataclass()
|
|
16
26
|
class IterTag(Tag):
|
|
17
27
|
"""An iterator tag."""
|
|
18
28
|
|
|
19
29
|
# The var to iterate over.
|
|
20
|
-
iterable: Var[
|
|
30
|
+
iterable: Var[Iterable] = dataclasses.field(
|
|
31
|
+
default_factory=lambda: LiteralArrayVar.create([])
|
|
32
|
+
)
|
|
21
33
|
|
|
22
34
|
# The component render function for each item in the iterable.
|
|
23
|
-
render_fn: Callable
|
|
35
|
+
render_fn: Callable = dataclasses.field(default_factory=lambda: lambda x: x)
|
|
24
36
|
|
|
25
37
|
# The name of the arg var.
|
|
26
|
-
arg_var_name: str
|
|
38
|
+
arg_var_name: str = dataclasses.field(default_factory=get_unique_variable_name)
|
|
27
39
|
|
|
28
40
|
# The name of the index var.
|
|
29
|
-
index_var_name: str
|
|
41
|
+
index_var_name: str = dataclasses.field(default_factory=get_unique_variable_name)
|
|
30
42
|
|
|
31
43
|
def get_iterable_var_type(self) -> Type:
|
|
32
44
|
"""Get the type of the iterable var.
|
|
@@ -34,7 +46,7 @@ class IterTag(Tag):
|
|
|
34
46
|
Returns:
|
|
35
47
|
The type of the iterable var.
|
|
36
48
|
"""
|
|
37
|
-
iterable = self.iterable
|
|
49
|
+
iterable = self.iterable
|
|
38
50
|
try:
|
|
39
51
|
if iterable._var_type.mro()[0] == dict:
|
|
40
52
|
# Arg is a tuple of (key, value).
|
|
@@ -55,8 +67,8 @@ class IterTag(Tag):
|
|
|
55
67
|
Returns:
|
|
56
68
|
The index var.
|
|
57
69
|
"""
|
|
58
|
-
return
|
|
59
|
-
|
|
70
|
+
return Var(
|
|
71
|
+
_js_expr=self.index_var_name,
|
|
60
72
|
_var_type=int,
|
|
61
73
|
).guess_type()
|
|
62
74
|
|
|
@@ -68,8 +80,8 @@ class IterTag(Tag):
|
|
|
68
80
|
Returns:
|
|
69
81
|
The arg var.
|
|
70
82
|
"""
|
|
71
|
-
return
|
|
72
|
-
|
|
83
|
+
return Var(
|
|
84
|
+
_js_expr=self.arg_var_name,
|
|
73
85
|
_var_type=self.get_iterable_var_type(),
|
|
74
86
|
).guess_type()
|
|
75
87
|
|
|
@@ -81,8 +93,8 @@ class IterTag(Tag):
|
|
|
81
93
|
Returns:
|
|
82
94
|
The index var.
|
|
83
95
|
"""
|
|
84
|
-
return
|
|
85
|
-
|
|
96
|
+
return Var(
|
|
97
|
+
_js_expr=self.index_var_name,
|
|
86
98
|
_var_type=int,
|
|
87
99
|
).guess_type()
|
|
88
100
|
|
|
@@ -94,8 +106,8 @@ class IterTag(Tag):
|
|
|
94
106
|
Returns:
|
|
95
107
|
The arg var.
|
|
96
108
|
"""
|
|
97
|
-
return
|
|
98
|
-
|
|
109
|
+
return Var(
|
|
110
|
+
_js_expr=self.arg_var_name,
|
|
99
111
|
_var_type=self.get_iterable_var_type(),
|
|
100
112
|
).guess_type()
|
|
101
113
|
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
"""Tag to conditionally match cases."""
|
|
2
2
|
|
|
3
|
+
import dataclasses
|
|
3
4
|
from typing import Any, List
|
|
4
5
|
|
|
5
6
|
from reflex.components.tags.tag import Tag
|
|
6
|
-
from reflex.vars import Var
|
|
7
|
+
from reflex.vars.base import Var
|
|
7
8
|
|
|
8
9
|
|
|
10
|
+
@dataclasses.dataclass()
|
|
9
11
|
class MatchTag(Tag):
|
|
10
12
|
"""A match tag."""
|
|
11
13
|
|
|
12
14
|
# The condition to determine which case to match.
|
|
13
|
-
cond: Var[Any]
|
|
15
|
+
cond: Var[Any] = dataclasses.field(default_factory=lambda: Var.create(True))
|
|
14
16
|
|
|
15
17
|
# The list of match cases to be matched.
|
|
16
|
-
match_cases: List[Any]
|
|
18
|
+
match_cases: List[Any] = dataclasses.field(default_factory=list)
|
|
17
19
|
|
|
18
20
|
# The catchall case to match.
|
|
19
|
-
default: Any
|
|
21
|
+
default: Any = dataclasses.field(default=Var.create(None))
|
reflex/components/tags/tag.py
CHANGED
|
@@ -2,22 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import dataclasses
|
|
6
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
6
7
|
|
|
7
|
-
from reflex.base import Base
|
|
8
8
|
from reflex.event import EventChain
|
|
9
|
-
from reflex.ivars.base import ImmutableVar, LiteralVar
|
|
10
9
|
from reflex.utils import format, types
|
|
10
|
+
from reflex.vars.base import LiteralVar, Var
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
@dataclasses.dataclass()
|
|
14
|
+
class Tag:
|
|
14
15
|
"""A React tag."""
|
|
15
16
|
|
|
16
17
|
# The name of the tag.
|
|
17
18
|
name: str = ""
|
|
18
19
|
|
|
19
20
|
# The props of the tag.
|
|
20
|
-
props: Dict[str, Any] =
|
|
21
|
+
props: Dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
21
22
|
|
|
22
23
|
# The inner contents of the tag.
|
|
23
24
|
contents: str = ""
|
|
@@ -26,25 +27,18 @@ class Tag(Base):
|
|
|
26
27
|
args: Optional[Tuple[str, ...]] = None
|
|
27
28
|
|
|
28
29
|
# Special props that aren't key value pairs.
|
|
29
|
-
special_props:
|
|
30
|
+
special_props: List[Var] = dataclasses.field(default_factory=list)
|
|
30
31
|
|
|
31
32
|
# The children components.
|
|
32
|
-
children: List[Any] =
|
|
33
|
-
|
|
34
|
-
def
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# Convert any props to vars.
|
|
42
|
-
if "props" in kwargs:
|
|
43
|
-
kwargs["props"] = {
|
|
44
|
-
name: LiteralVar.create(value)
|
|
45
|
-
for name, value in kwargs["props"].items()
|
|
46
|
-
}
|
|
47
|
-
super().__init__(*args, **kwargs)
|
|
33
|
+
children: List[Any] = dataclasses.field(default_factory=list)
|
|
34
|
+
|
|
35
|
+
def __post_init__(self):
|
|
36
|
+
"""Post initialize the tag."""
|
|
37
|
+
object.__setattr__(
|
|
38
|
+
self,
|
|
39
|
+
"props",
|
|
40
|
+
{name: LiteralVar.create(value) for name, value in self.props.items()},
|
|
41
|
+
)
|
|
48
42
|
|
|
49
43
|
def format_props(self) -> List:
|
|
50
44
|
"""Format the tag's props.
|
|
@@ -54,6 +48,29 @@ class Tag(Base):
|
|
|
54
48
|
"""
|
|
55
49
|
return format.format_props(*self.special_props, **self.props)
|
|
56
50
|
|
|
51
|
+
def set(self, **kwargs: Any):
|
|
52
|
+
"""Set the tag's fields.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
kwargs: The fields to set.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
The tag with the fields
|
|
59
|
+
"""
|
|
60
|
+
for name, value in kwargs.items():
|
|
61
|
+
setattr(self, name, value)
|
|
62
|
+
|
|
63
|
+
return self
|
|
64
|
+
|
|
65
|
+
def __iter__(self):
|
|
66
|
+
"""Iterate over the tag's fields.
|
|
67
|
+
|
|
68
|
+
Yields:
|
|
69
|
+
Tuple[str, Any]: The field name and value.
|
|
70
|
+
"""
|
|
71
|
+
for field in dataclasses.fields(self):
|
|
72
|
+
yield field.name, getattr(self, field.name)
|
|
73
|
+
|
|
57
74
|
def add_props(self, **kwargs: Optional[Any]) -> Tag:
|
|
58
75
|
"""Add props to the tag.
|
|
59
76
|
|
|
@@ -92,7 +109,7 @@ class Tag(Base):
|
|
|
92
109
|
return self
|
|
93
110
|
|
|
94
111
|
@staticmethod
|
|
95
|
-
def is_valid_prop(prop: Optional[
|
|
112
|
+
def is_valid_prop(prop: Optional[Var]) -> bool:
|
|
96
113
|
"""Check if the prop is valid.
|
|
97
114
|
|
|
98
115
|
Args:
|
|
@@ -65,7 +65,9 @@ def _create_package_config(module_name: str, package_name: str):
|
|
|
65
65
|
with open(CustomComponents.PYPROJECT_TOML, "w") as f:
|
|
66
66
|
f.write(
|
|
67
67
|
templates.CUSTOM_COMPONENTS_PYPROJECT_TOML.render(
|
|
68
|
-
module_name=module_name,
|
|
68
|
+
module_name=module_name,
|
|
69
|
+
package_name=package_name,
|
|
70
|
+
reflex_version=constants.Reflex.VERSION,
|
|
69
71
|
)
|
|
70
72
|
)
|
|
71
73
|
|
reflex/event.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import dataclasses
|
|
5
6
|
import inspect
|
|
6
7
|
import types
|
|
7
8
|
import urllib.parse
|
|
@@ -18,14 +19,13 @@ from typing import (
|
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
from reflex import constants
|
|
21
|
-
from reflex.base import Base
|
|
22
|
-
from reflex.ivars.base import ImmutableVar, LiteralVar
|
|
23
|
-
from reflex.ivars.function import FunctionStringVar, FunctionVar
|
|
24
|
-
from reflex.ivars.object import ObjectVar
|
|
25
22
|
from reflex.utils import format
|
|
26
23
|
from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch
|
|
27
24
|
from reflex.utils.types import ArgsSpec
|
|
28
|
-
from reflex.vars import
|
|
25
|
+
from reflex.vars import VarData
|
|
26
|
+
from reflex.vars.base import LiteralVar, Var
|
|
27
|
+
from reflex.vars.function import FunctionStringVar, FunctionVar
|
|
28
|
+
from reflex.vars.object import ObjectVar
|
|
29
29
|
|
|
30
30
|
try:
|
|
31
31
|
from typing import Annotated
|
|
@@ -33,7 +33,11 @@ except ImportError:
|
|
|
33
33
|
from typing_extensions import Annotated
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
@dataclasses.dataclass(
|
|
37
|
+
init=True,
|
|
38
|
+
frozen=True,
|
|
39
|
+
)
|
|
40
|
+
class Event:
|
|
37
41
|
"""An event that describes any state change in the app."""
|
|
38
42
|
|
|
39
43
|
# The token to specify the client that the event is for.
|
|
@@ -43,10 +47,10 @@ class Event(Base):
|
|
|
43
47
|
name: str
|
|
44
48
|
|
|
45
49
|
# The routing data where event occurred
|
|
46
|
-
router_data: Dict[str, Any] =
|
|
50
|
+
router_data: Dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
47
51
|
|
|
48
52
|
# The event payload.
|
|
49
|
-
payload: Dict[str, Any] =
|
|
53
|
+
payload: Dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
50
54
|
|
|
51
55
|
@property
|
|
52
56
|
def substate_token(self) -> str:
|
|
@@ -81,11 +85,15 @@ def background(fn):
|
|
|
81
85
|
return fn
|
|
82
86
|
|
|
83
87
|
|
|
84
|
-
|
|
88
|
+
@dataclasses.dataclass(
|
|
89
|
+
init=True,
|
|
90
|
+
frozen=True,
|
|
91
|
+
)
|
|
92
|
+
class EventActionsMixin:
|
|
85
93
|
"""Mixin for DOM event actions."""
|
|
86
94
|
|
|
87
95
|
# Whether to `preventDefault` or `stopPropagation` on the event.
|
|
88
|
-
event_actions: Dict[str, Union[bool, int]] =
|
|
96
|
+
event_actions: Dict[str, Union[bool, int]] = dataclasses.field(default_factory=dict)
|
|
89
97
|
|
|
90
98
|
@property
|
|
91
99
|
def stop_propagation(self):
|
|
@@ -94,8 +102,9 @@ class EventActionsMixin(Base):
|
|
|
94
102
|
Returns:
|
|
95
103
|
New EventHandler-like with stopPropagation set to True.
|
|
96
104
|
"""
|
|
97
|
-
return
|
|
98
|
-
|
|
105
|
+
return dataclasses.replace(
|
|
106
|
+
self,
|
|
107
|
+
event_actions={"stopPropagation": True, **self.event_actions},
|
|
99
108
|
)
|
|
100
109
|
|
|
101
110
|
@property
|
|
@@ -105,8 +114,9 @@ class EventActionsMixin(Base):
|
|
|
105
114
|
Returns:
|
|
106
115
|
New EventHandler-like with preventDefault set to True.
|
|
107
116
|
"""
|
|
108
|
-
return
|
|
109
|
-
|
|
117
|
+
return dataclasses.replace(
|
|
118
|
+
self,
|
|
119
|
+
event_actions={"preventDefault": True, **self.event_actions},
|
|
110
120
|
)
|
|
111
121
|
|
|
112
122
|
def throttle(self, limit_ms: int):
|
|
@@ -118,8 +128,9 @@ class EventActionsMixin(Base):
|
|
|
118
128
|
Returns:
|
|
119
129
|
New EventHandler-like with throttle set to limit_ms.
|
|
120
130
|
"""
|
|
121
|
-
return
|
|
122
|
-
|
|
131
|
+
return dataclasses.replace(
|
|
132
|
+
self,
|
|
133
|
+
event_actions={"throttle": limit_ms, **self.event_actions},
|
|
123
134
|
)
|
|
124
135
|
|
|
125
136
|
def debounce(self, delay_ms: int):
|
|
@@ -131,26 +142,25 @@ class EventActionsMixin(Base):
|
|
|
131
142
|
Returns:
|
|
132
143
|
New EventHandler-like with debounce set to delay_ms.
|
|
133
144
|
"""
|
|
134
|
-
return
|
|
135
|
-
|
|
145
|
+
return dataclasses.replace(
|
|
146
|
+
self,
|
|
147
|
+
event_actions={"debounce": delay_ms, **self.event_actions},
|
|
136
148
|
)
|
|
137
149
|
|
|
138
150
|
|
|
151
|
+
@dataclasses.dataclass(
|
|
152
|
+
init=True,
|
|
153
|
+
frozen=True,
|
|
154
|
+
)
|
|
139
155
|
class EventHandler(EventActionsMixin):
|
|
140
156
|
"""An event handler responds to an event to update the state."""
|
|
141
157
|
|
|
142
158
|
# The function to call in response to the event.
|
|
143
|
-
fn: Any
|
|
159
|
+
fn: Any = dataclasses.field(default=None)
|
|
144
160
|
|
|
145
161
|
# The full name of the state class this event handler is attached to.
|
|
146
162
|
# Empty string means this event handler is a server side event.
|
|
147
|
-
state_full_name: str = ""
|
|
148
|
-
|
|
149
|
-
class Config:
|
|
150
|
-
"""The Pydantic config."""
|
|
151
|
-
|
|
152
|
-
# Needed to allow serialization of Callable.
|
|
153
|
-
frozen = True
|
|
163
|
+
state_full_name: str = dataclasses.field(default="")
|
|
154
164
|
|
|
155
165
|
@classmethod
|
|
156
166
|
def __class_getitem__(cls, args_spec: str) -> Annotated:
|
|
@@ -191,7 +201,7 @@ class EventHandler(EventActionsMixin):
|
|
|
191
201
|
|
|
192
202
|
# Get the function args.
|
|
193
203
|
fn_args = inspect.getfullargspec(self.fn).args[1:]
|
|
194
|
-
fn_args = (
|
|
204
|
+
fn_args = (Var(_js_expr=arg) for arg in fn_args)
|
|
195
205
|
|
|
196
206
|
# Construct the payload.
|
|
197
207
|
values = []
|
|
@@ -207,7 +217,7 @@ class EventHandler(EventActionsMixin):
|
|
|
207
217
|
raise EventHandlerTypeError(
|
|
208
218
|
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
|
209
219
|
) from e
|
|
210
|
-
payload = tuple(zip(fn_args, values))
|
|
220
|
+
payload = tuple(zip(fn_args, values, strict=False))
|
|
211
221
|
|
|
212
222
|
# Return the event spec.
|
|
213
223
|
return EventSpec(
|
|
@@ -215,6 +225,10 @@ class EventHandler(EventActionsMixin):
|
|
|
215
225
|
)
|
|
216
226
|
|
|
217
227
|
|
|
228
|
+
@dataclasses.dataclass(
|
|
229
|
+
init=True,
|
|
230
|
+
frozen=True,
|
|
231
|
+
)
|
|
218
232
|
class EventSpec(EventActionsMixin):
|
|
219
233
|
"""An event specification.
|
|
220
234
|
|
|
@@ -223,23 +237,37 @@ class EventSpec(EventActionsMixin):
|
|
|
223
237
|
"""
|
|
224
238
|
|
|
225
239
|
# The event handler.
|
|
226
|
-
handler: EventHandler
|
|
240
|
+
handler: EventHandler = dataclasses.field(default=None) # type: ignore
|
|
227
241
|
|
|
228
242
|
# The handler on the client to process event.
|
|
229
|
-
client_handler_name: str = ""
|
|
243
|
+
client_handler_name: str = dataclasses.field(default="")
|
|
230
244
|
|
|
231
245
|
# The arguments to pass to the function.
|
|
232
|
-
args: Tuple[Tuple[
|
|
246
|
+
args: Tuple[Tuple[Var, Var], ...] = dataclasses.field(default_factory=tuple)
|
|
233
247
|
|
|
234
|
-
|
|
235
|
-
|
|
248
|
+
def __init__(
|
|
249
|
+
self,
|
|
250
|
+
handler: EventHandler,
|
|
251
|
+
event_actions: Dict[str, Union[bool, int]] | None = None,
|
|
252
|
+
client_handler_name: str = "",
|
|
253
|
+
args: Tuple[Tuple[Var, Var], ...] = tuple(),
|
|
254
|
+
):
|
|
255
|
+
"""Initialize an EventSpec.
|
|
236
256
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
257
|
+
Args:
|
|
258
|
+
event_actions: The event actions.
|
|
259
|
+
handler: The event handler.
|
|
260
|
+
client_handler_name: The client handler name.
|
|
261
|
+
args: The arguments to pass to the function.
|
|
262
|
+
"""
|
|
263
|
+
if event_actions is None:
|
|
264
|
+
event_actions = {}
|
|
265
|
+
object.__setattr__(self, "event_actions", event_actions)
|
|
266
|
+
object.__setattr__(self, "handler", handler)
|
|
267
|
+
object.__setattr__(self, "client_handler_name", client_handler_name)
|
|
268
|
+
object.__setattr__(self, "args", args or tuple())
|
|
269
|
+
|
|
270
|
+
def with_args(self, args: Tuple[Tuple[Var, Var], ...]) -> EventSpec:
|
|
243
271
|
"""Copy the event spec, with updated args.
|
|
244
272
|
|
|
245
273
|
Args:
|
|
@@ -255,7 +283,7 @@ class EventSpec(EventActionsMixin):
|
|
|
255
283
|
event_actions=self.event_actions.copy(),
|
|
256
284
|
)
|
|
257
285
|
|
|
258
|
-
def add_args(self, *args:
|
|
286
|
+
def add_args(self, *args: Var) -> EventSpec:
|
|
259
287
|
"""Add arguments to the event spec.
|
|
260
288
|
|
|
261
289
|
Args:
|
|
@@ -271,7 +299,7 @@ class EventSpec(EventActionsMixin):
|
|
|
271
299
|
|
|
272
300
|
# Get the remaining unfilled function args.
|
|
273
301
|
fn_args = inspect.getfullargspec(self.handler.fn).args[1 + len(self.args) :]
|
|
274
|
-
fn_args = (
|
|
302
|
+
fn_args = (Var(_js_expr=arg) for arg in fn_args)
|
|
275
303
|
|
|
276
304
|
# Construct the payload.
|
|
277
305
|
values = []
|
|
@@ -282,10 +310,13 @@ class EventSpec(EventActionsMixin):
|
|
|
282
310
|
raise EventHandlerTypeError(
|
|
283
311
|
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
|
284
312
|
) from e
|
|
285
|
-
new_payload = tuple(zip(fn_args, values))
|
|
313
|
+
new_payload = tuple(zip(fn_args, values, strict=False))
|
|
286
314
|
return self.with_args(self.args + new_payload)
|
|
287
315
|
|
|
288
316
|
|
|
317
|
+
@dataclasses.dataclass(
|
|
318
|
+
frozen=True,
|
|
319
|
+
)
|
|
289
320
|
class CallableEventSpec(EventSpec):
|
|
290
321
|
"""Decorate an EventSpec-returning function to act as both a EventSpec and a function.
|
|
291
322
|
|
|
@@ -305,10 +336,13 @@ class CallableEventSpec(EventSpec):
|
|
|
305
336
|
if fn is not None:
|
|
306
337
|
default_event_spec = fn()
|
|
307
338
|
super().__init__(
|
|
308
|
-
|
|
309
|
-
|
|
339
|
+
event_actions=default_event_spec.event_actions,
|
|
340
|
+
client_handler_name=default_event_spec.client_handler_name,
|
|
341
|
+
args=default_event_spec.args,
|
|
342
|
+
handler=default_event_spec.handler,
|
|
310
343
|
**kwargs,
|
|
311
344
|
)
|
|
345
|
+
object.__setattr__(self, "fn", fn)
|
|
312
346
|
else:
|
|
313
347
|
super().__init__(**kwargs)
|
|
314
348
|
|
|
@@ -332,12 +366,16 @@ class CallableEventSpec(EventSpec):
|
|
|
332
366
|
return self.fn(*args, **kwargs)
|
|
333
367
|
|
|
334
368
|
|
|
369
|
+
@dataclasses.dataclass(
|
|
370
|
+
init=True,
|
|
371
|
+
frozen=True,
|
|
372
|
+
)
|
|
335
373
|
class EventChain(EventActionsMixin):
|
|
336
374
|
"""Container for a chain of events that will be executed in order."""
|
|
337
375
|
|
|
338
|
-
events: List[EventSpec]
|
|
376
|
+
events: List[EventSpec] = dataclasses.field(default_factory=list)
|
|
339
377
|
|
|
340
|
-
args_spec: Optional[Callable]
|
|
378
|
+
args_spec: Optional[Callable] = dataclasses.field(default=None)
|
|
341
379
|
|
|
342
380
|
|
|
343
381
|
# These chains can be used for their side effects when no other events are desired.
|
|
@@ -345,14 +383,22 @@ stop_propagation = EventChain(events=[], args_spec=lambda: []).stop_propagation
|
|
|
345
383
|
prevent_default = EventChain(events=[], args_spec=lambda: []).prevent_default
|
|
346
384
|
|
|
347
385
|
|
|
348
|
-
|
|
386
|
+
@dataclasses.dataclass(
|
|
387
|
+
init=True,
|
|
388
|
+
frozen=True,
|
|
389
|
+
)
|
|
390
|
+
class Target:
|
|
349
391
|
"""A Javascript event target."""
|
|
350
392
|
|
|
351
393
|
checked: bool = False
|
|
352
394
|
value: Any = None
|
|
353
395
|
|
|
354
396
|
|
|
355
|
-
|
|
397
|
+
@dataclasses.dataclass(
|
|
398
|
+
init=True,
|
|
399
|
+
frozen=True,
|
|
400
|
+
)
|
|
401
|
+
class FrontendEvent:
|
|
356
402
|
"""A Javascript event."""
|
|
357
403
|
|
|
358
404
|
target: Target = Target()
|
|
@@ -360,7 +406,11 @@ class FrontendEvent(Base):
|
|
|
360
406
|
value: Any = None
|
|
361
407
|
|
|
362
408
|
|
|
363
|
-
|
|
409
|
+
@dataclasses.dataclass(
|
|
410
|
+
init=True,
|
|
411
|
+
frozen=True,
|
|
412
|
+
)
|
|
413
|
+
class FileUpload:
|
|
364
414
|
"""Class to represent a file upload."""
|
|
365
415
|
|
|
366
416
|
upload_id: Optional[str] = None
|
|
@@ -395,15 +445,15 @@ class FileUpload(Base):
|
|
|
395
445
|
upload_id = self.upload_id or DEFAULT_UPLOAD_ID
|
|
396
446
|
spec_args = [
|
|
397
447
|
(
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
448
|
+
Var(_js_expr="files"),
|
|
449
|
+
Var(
|
|
450
|
+
_js_expr="filesById",
|
|
401
451
|
_var_type=Dict[str, Any],
|
|
402
452
|
_var_data=VarData.merge(upload_files_context_var_data),
|
|
403
453
|
).to(ObjectVar)[LiteralVar.create(upload_id)],
|
|
404
454
|
),
|
|
405
455
|
(
|
|
406
|
-
|
|
456
|
+
Var(_js_expr="upload_id"),
|
|
407
457
|
LiteralVar.create(upload_id),
|
|
408
458
|
),
|
|
409
459
|
]
|
|
@@ -423,7 +473,7 @@ class FileUpload(Base):
|
|
|
423
473
|
) # type: ignore
|
|
424
474
|
else:
|
|
425
475
|
raise ValueError(f"{on_upload_progress} is not a valid event handler.")
|
|
426
|
-
if isinstance(events,
|
|
476
|
+
if isinstance(events, Var):
|
|
427
477
|
raise ValueError(f"{on_upload_progress} cannot return a var {events}.")
|
|
428
478
|
on_upload_progress_chain = EventChain(
|
|
429
479
|
events=events,
|
|
@@ -432,7 +482,7 @@ class FileUpload(Base):
|
|
|
432
482
|
formatted_chain = str(format.format_prop(on_upload_progress_chain))
|
|
433
483
|
spec_args.append(
|
|
434
484
|
(
|
|
435
|
-
|
|
485
|
+
Var(_js_expr="on_upload_progress"),
|
|
436
486
|
FunctionStringVar(
|
|
437
487
|
formatted_chain.strip("{}"),
|
|
438
488
|
).to(FunctionVar, EventChain),
|
|
@@ -472,7 +522,7 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
|
|
|
472
522
|
handler=EventHandler(fn=fn),
|
|
473
523
|
args=tuple(
|
|
474
524
|
(
|
|
475
|
-
|
|
525
|
+
Var(_js_expr=k),
|
|
476
526
|
LiteralVar.create(v),
|
|
477
527
|
)
|
|
478
528
|
for k, v in kwargs.items()
|
|
@@ -678,9 +728,9 @@ def set_clipboard(content: str) -> EventSpec:
|
|
|
678
728
|
|
|
679
729
|
|
|
680
730
|
def download(
|
|
681
|
-
url: str |
|
|
682
|
-
filename: Optional[str |
|
|
683
|
-
data: str | bytes |
|
|
731
|
+
url: str | Var | None = None,
|
|
732
|
+
filename: Optional[str | Var] = None,
|
|
733
|
+
data: str | bytes | Var | None = None,
|
|
684
734
|
) -> EventSpec:
|
|
685
735
|
"""Download the file at a given path or with the specified data.
|
|
686
736
|
|
|
@@ -716,7 +766,7 @@ def download(
|
|
|
716
766
|
if isinstance(data, str):
|
|
717
767
|
# Caller provided a plain text string to download.
|
|
718
768
|
url = "data:text/plain," + urllib.parse.quote(data)
|
|
719
|
-
elif isinstance(data,
|
|
769
|
+
elif isinstance(data, Var):
|
|
720
770
|
# Need to check on the frontend if the Var already looks like a data: URI.
|
|
721
771
|
|
|
722
772
|
is_data_url = (data.js_type() == "string") & (
|
|
@@ -873,15 +923,13 @@ def parse_args_spec(arg_spec: ArgsSpec):
|
|
|
873
923
|
annotations = get_type_hints(arg_spec)
|
|
874
924
|
return arg_spec(
|
|
875
925
|
*[
|
|
876
|
-
|
|
877
|
-
ObjectVar, annotations.get(l_arg, FrontendEvent)
|
|
878
|
-
)
|
|
926
|
+
Var(f"_{l_arg}").to(annotations.get(l_arg, FrontendEvent))
|
|
879
927
|
for l_arg in spec.args
|
|
880
928
|
]
|
|
881
929
|
)
|
|
882
930
|
|
|
883
931
|
|
|
884
|
-
def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] |
|
|
932
|
+
def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var:
|
|
885
933
|
"""Call a function to a list of event specs.
|
|
886
934
|
|
|
887
935
|
The function should return a single EventSpec, a list of EventSpecs, or a
|
|
@@ -922,7 +970,7 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Immutab
|
|
|
922
970
|
out = fn(*parsed_args)
|
|
923
971
|
|
|
924
972
|
# If the function returns a Var, assume it's an EventChain and render it directly.
|
|
925
|
-
if isinstance(out,
|
|
973
|
+
if isinstance(out, Var):
|
|
926
974
|
return out
|
|
927
975
|
|
|
928
976
|
# Convert the output to a list.
|
|
@@ -951,7 +999,7 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Immutab
|
|
|
951
999
|
|
|
952
1000
|
def get_handler_args(
|
|
953
1001
|
event_spec: EventSpec,
|
|
954
|
-
) -> tuple[tuple[
|
|
1002
|
+
) -> tuple[tuple[Var, Var], ...]:
|
|
955
1003
|
"""Get the handler args for the given event spec.
|
|
956
1004
|
|
|
957
1005
|
Args:
|
|
@@ -1002,7 +1050,7 @@ def fix_events(
|
|
|
1002
1050
|
e = e()
|
|
1003
1051
|
assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
|
|
1004
1052
|
name = format.format_event_handler(e.handler)
|
|
1005
|
-
payload = {k.
|
|
1053
|
+
payload = {k._js_expr: v._decode() for k, v in e.args} # type: ignore
|
|
1006
1054
|
|
|
1007
1055
|
# Filter router_data to reduce payload size
|
|
1008
1056
|
event_router_data = {
|