reflex 0.6.5a3__py3-none-any.whl → 0.6.6a1__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/web/utils/state.js +15 -1
- reflex/__init__.py +3 -1
- reflex/__init__.pyi +3 -0
- reflex/app.py +4 -2
- reflex/assets.py +95 -0
- reflex/base.py +2 -2
- reflex/components/base/error_boundary.py +99 -36
- reflex/components/base/error_boundary.pyi +3 -4
- reflex/components/component.py +29 -7
- reflex/components/core/cond.py +8 -0
- reflex/components/datadisplay/code.py +1 -1
- reflex/components/datadisplay/logo.py +26 -13
- reflex/components/el/__init__.pyi +2 -0
- reflex/components/el/elements/__init__.py +1 -0
- reflex/components/el/elements/__init__.pyi +3 -0
- reflex/components/el/elements/forms.py +1 -0
- reflex/components/el/elements/forms.pyi +1 -0
- reflex/components/moment/moment.py +4 -3
- reflex/components/moment/moment.pyi +12 -2
- reflex/components/radix/primitives/drawer.py +5 -23
- reflex/components/radix/themes/base.py +3 -0
- reflex/components/radix/themes/components/segmented_control.py +3 -1
- reflex/components/radix/themes/components/segmented_control.pyi +7 -2
- reflex/components/radix/themes/components/text_field.py +3 -0
- reflex/components/radix/themes/components/text_field.pyi +4 -0
- reflex/components/sonner/toast.py +23 -12
- reflex/components/sonner/toast.pyi +6 -6
- reflex/config.py +60 -9
- reflex/constants/base.py +12 -0
- reflex/constants/installer.py +3 -3
- reflex/constants/style.py +1 -1
- reflex/event.py +22 -5
- reflex/experimental/assets.py +14 -36
- reflex/reflex.py +15 -34
- reflex/state.py +81 -23
- reflex/utils/exceptions.py +4 -0
- reflex/utils/prerequisites.py +174 -40
- reflex/utils/redir.py +13 -4
- reflex/utils/serializers.py +52 -1
- reflex/utils/telemetry.py +2 -1
- reflex/utils/types.py +52 -1
- reflex/vars/base.py +18 -4
- reflex/vars/function.py +283 -37
- {reflex-0.6.5a3.dist-info → reflex-0.6.6a1.dist-info}/METADATA +3 -2
- {reflex-0.6.5a3.dist-info → reflex-0.6.6a1.dist-info}/RECORD +48 -47
- {reflex-0.6.5a3.dist-info → reflex-0.6.6a1.dist-info}/LICENSE +0 -0
- {reflex-0.6.5a3.dist-info → reflex-0.6.6a1.dist-info}/WHEEL +0 -0
- {reflex-0.6.5a3.dist-info → reflex-0.6.6a1.dist-info}/entry_points.txt +0 -0
|
@@ -705,6 +705,11 @@ export const useEventLoop = (
|
|
|
705
705
|
_e.stopPropagation();
|
|
706
706
|
}
|
|
707
707
|
const combined_name = events.map((e) => e.name).join("+++");
|
|
708
|
+
if (event_actions?.temporal) {
|
|
709
|
+
if (!socket.current || !socket.current.connected) {
|
|
710
|
+
return; // don't queue when the backend is not connected
|
|
711
|
+
}
|
|
712
|
+
}
|
|
708
713
|
if (event_actions?.throttle) {
|
|
709
714
|
// If throttle returns false, the events are not added to the queue.
|
|
710
715
|
if (!throttle(combined_name, event_actions.throttle)) {
|
|
@@ -762,7 +767,7 @@ export const useEventLoop = (
|
|
|
762
767
|
window.onunhandledrejection = function (event) {
|
|
763
768
|
addEvents([
|
|
764
769
|
Event(`${exception_state_name}.handle_frontend_exception`, {
|
|
765
|
-
stack: event.reason
|
|
770
|
+
stack: event.reason?.stack,
|
|
766
771
|
component_stack: "",
|
|
767
772
|
}),
|
|
768
773
|
]);
|
|
@@ -837,11 +842,20 @@ export const useEventLoop = (
|
|
|
837
842
|
}
|
|
838
843
|
};
|
|
839
844
|
const change_complete = () => addEvents(onLoadInternalEvent());
|
|
845
|
+
const change_error = () => {
|
|
846
|
+
// Remove cached error state from router for this page, otherwise the
|
|
847
|
+
// page will never send on_load events again.
|
|
848
|
+
if (router.components[router.pathname].error) {
|
|
849
|
+
delete router.components[router.pathname].error;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
840
852
|
router.events.on("routeChangeStart", change_start);
|
|
841
853
|
router.events.on("routeChangeComplete", change_complete);
|
|
854
|
+
router.events.on("routeChangeError", change_error);
|
|
842
855
|
return () => {
|
|
843
856
|
router.events.off("routeChangeStart", change_start);
|
|
844
857
|
router.events.off("routeChangeComplete", change_complete);
|
|
858
|
+
router.events.off("routeChangeError", change_error);
|
|
845
859
|
};
|
|
846
860
|
}, [router]);
|
|
847
861
|
|
reflex/__init__.py
CHANGED
|
@@ -264,6 +264,7 @@ _MAPPING: dict = {
|
|
|
264
264
|
"experimental": ["_x"],
|
|
265
265
|
"admin": ["AdminDash"],
|
|
266
266
|
"app": ["App", "UploadFile"],
|
|
267
|
+
"assets": ["asset"],
|
|
267
268
|
"base": ["Base"],
|
|
268
269
|
"components.component": [
|
|
269
270
|
"Component",
|
|
@@ -298,6 +299,7 @@ _MAPPING: dict = {
|
|
|
298
299
|
"components.moment": ["MomentDelta", "moment"],
|
|
299
300
|
"config": ["Config", "DBConfig"],
|
|
300
301
|
"constants": ["Env"],
|
|
302
|
+
"constants.colors": ["Color"],
|
|
301
303
|
"event": [
|
|
302
304
|
"EventChain",
|
|
303
305
|
"EventHandler",
|
|
@@ -338,7 +340,7 @@ _MAPPING: dict = {
|
|
|
338
340
|
],
|
|
339
341
|
"istate.wrappers": ["get_state"],
|
|
340
342
|
"style": ["Style", "toggle_color_mode"],
|
|
341
|
-
"utils.imports": ["ImportVar"],
|
|
343
|
+
"utils.imports": ["ImportDict", "ImportVar"],
|
|
342
344
|
"utils.serializers": ["serializer"],
|
|
343
345
|
"vars": ["Var", "field", "Field"],
|
|
344
346
|
}
|
reflex/__init__.pyi
CHANGED
|
@@ -19,6 +19,7 @@ from . import vars as vars
|
|
|
19
19
|
from .admin import AdminDash as AdminDash
|
|
20
20
|
from .app import App as App
|
|
21
21
|
from .app import UploadFile as UploadFile
|
|
22
|
+
from .assets import asset as asset
|
|
22
23
|
from .base import Base as Base
|
|
23
24
|
from .components import el as el
|
|
24
25
|
from .components import lucide as lucide
|
|
@@ -152,6 +153,7 @@ from .components.suneditor import editor as editor
|
|
|
152
153
|
from .config import Config as Config
|
|
153
154
|
from .config import DBConfig as DBConfig
|
|
154
155
|
from .constants import Env as Env
|
|
156
|
+
from .constants.colors import Color as Color
|
|
155
157
|
from .event import EventChain as EventChain
|
|
156
158
|
from .event import EventHandler as EventHandler
|
|
157
159
|
from .event import background as background
|
|
@@ -192,6 +194,7 @@ from .state import dynamic as dynamic
|
|
|
192
194
|
from .state import var as var
|
|
193
195
|
from .style import Style as Style
|
|
194
196
|
from .style import toggle_color_mode as toggle_color_mode
|
|
197
|
+
from .utils.imports import ImportDict as ImportDict
|
|
195
198
|
from .utils.imports import ImportVar as ImportVar
|
|
196
199
|
from .utils.serializers import serializer as serializer
|
|
197
200
|
from .vars import Field as Field
|
reflex/app.py
CHANGED
|
@@ -850,10 +850,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
850
850
|
if self.theme is not None:
|
|
851
851
|
# If a theme component was provided, wrap the app with it
|
|
852
852
|
app_wrappers[(20, "Theme")] = self.theme
|
|
853
|
-
# Fix #2992 by removing the top-level appearance prop
|
|
854
|
-
self.theme.appearance = None
|
|
855
853
|
|
|
856
854
|
for route in self.unevaluated_pages:
|
|
855
|
+
console.debug(f"Evaluating page: {route}")
|
|
857
856
|
self._compile_page(route)
|
|
858
857
|
|
|
859
858
|
# Add the optional endpoints (_upload)
|
|
@@ -1006,6 +1005,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1006
1005
|
compile_results.append(
|
|
1007
1006
|
compiler.compile_contexts(self.state, self.theme),
|
|
1008
1007
|
)
|
|
1008
|
+
if self.theme is not None:
|
|
1009
|
+
# Fix #2992 by removing the top-level appearance prop
|
|
1010
|
+
self.theme.appearance = None
|
|
1009
1011
|
progress.advance(task)
|
|
1010
1012
|
|
|
1011
1013
|
# Compile the app root.
|
reflex/assets.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Helper functions for adding assets to the app."""
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from reflex import constants
|
|
8
|
+
from reflex.utils.exec import is_backend_only
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def asset(
|
|
12
|
+
path: str,
|
|
13
|
+
shared: bool = False,
|
|
14
|
+
subfolder: Optional[str] = None,
|
|
15
|
+
_stack_level: int = 1,
|
|
16
|
+
) -> str:
|
|
17
|
+
"""Add an asset to the app, either shared as a symlink or local.
|
|
18
|
+
|
|
19
|
+
Shared/External/Library assets:
|
|
20
|
+
Place the file next to your including python file.
|
|
21
|
+
Links the file to the app's external assets directory.
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
```python
|
|
25
|
+
# my_custom_javascript.js is a shared asset located next to the including python file.
|
|
26
|
+
rx.script(src=rx.asset(path="my_custom_javascript.js", shared=True))
|
|
27
|
+
rx.image(src=rx.asset(path="test_image.png", shared=True, subfolder="subfolder"))
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Local/Internal assets:
|
|
31
|
+
Place the file in the app's assets/ directory.
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
```python
|
|
35
|
+
# local_image.png is an asset located in the app's assets/ directory. It cannot be shared when developing a library.
|
|
36
|
+
rx.image(src=rx.asset(path="local_image.png"))
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
path: The relative path of the asset.
|
|
41
|
+
subfolder: The directory to place the shared asset in.
|
|
42
|
+
shared: Whether to expose the asset to other apps.
|
|
43
|
+
_stack_level: The stack level to determine the calling file, defaults to
|
|
44
|
+
the immediate caller 1. When using rx.asset via a helper function,
|
|
45
|
+
increase this number for each helper function in the stack.
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
FileNotFoundError: If the file does not exist.
|
|
49
|
+
ValueError: If subfolder is provided for local assets.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
The relative URL to the asset.
|
|
53
|
+
"""
|
|
54
|
+
assets = constants.Dirs.APP_ASSETS
|
|
55
|
+
backend_only = is_backend_only()
|
|
56
|
+
|
|
57
|
+
# Local asset handling
|
|
58
|
+
if not shared:
|
|
59
|
+
cwd = Path.cwd()
|
|
60
|
+
src_file_local = cwd / assets / path
|
|
61
|
+
if subfolder is not None:
|
|
62
|
+
raise ValueError("Subfolder is not supported for local assets.")
|
|
63
|
+
if not backend_only and not src_file_local.exists():
|
|
64
|
+
raise FileNotFoundError(f"File not found: {src_file_local}")
|
|
65
|
+
return f"/{path}"
|
|
66
|
+
|
|
67
|
+
# Shared asset handling
|
|
68
|
+
# Determine the file by which the asset is exposed.
|
|
69
|
+
frame = inspect.stack()[_stack_level]
|
|
70
|
+
calling_file = frame.filename
|
|
71
|
+
module = inspect.getmodule(frame[0])
|
|
72
|
+
assert module is not None
|
|
73
|
+
|
|
74
|
+
external = constants.Dirs.EXTERNAL_APP_ASSETS
|
|
75
|
+
src_file_shared = Path(calling_file).parent / path
|
|
76
|
+
if not src_file_shared.exists():
|
|
77
|
+
raise FileNotFoundError(f"File not found: {src_file_shared}")
|
|
78
|
+
|
|
79
|
+
caller_module_path = module.__name__.replace(".", "/")
|
|
80
|
+
subfolder = f"{caller_module_path}/{subfolder}" if subfolder else caller_module_path
|
|
81
|
+
|
|
82
|
+
# Symlink the asset to the app's external assets directory if running frontend.
|
|
83
|
+
if not backend_only:
|
|
84
|
+
# Create the asset folder in the currently compiling app.
|
|
85
|
+
asset_folder = Path.cwd() / assets / external / subfolder
|
|
86
|
+
asset_folder.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
|
|
88
|
+
dst_file = asset_folder / path
|
|
89
|
+
|
|
90
|
+
if not dst_file.exists() and (
|
|
91
|
+
not dst_file.is_symlink() or dst_file.resolve() != src_file_shared.resolve()
|
|
92
|
+
):
|
|
93
|
+
dst_file.symlink_to(src_file_shared)
|
|
94
|
+
|
|
95
|
+
return f"/{external}/{subfolder}/{path}"
|
reflex/base.py
CHANGED
|
@@ -130,8 +130,8 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
|
|
|
130
130
|
Returns:
|
|
131
131
|
The value of the field.
|
|
132
132
|
"""
|
|
133
|
-
if isinstance(key, str)
|
|
133
|
+
if isinstance(key, str):
|
|
134
134
|
# Seems like this function signature was wrong all along?
|
|
135
135
|
# If the user wants a field that we know of, get it and pass it off to _get_value
|
|
136
|
-
|
|
136
|
+
return getattr(self, key, key)
|
|
137
137
|
return key
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import Dict,
|
|
5
|
+
from typing import Dict, Tuple
|
|
6
6
|
|
|
7
|
-
from reflex.compiler.compiler import _compile_component
|
|
8
7
|
from reflex.components.component import Component
|
|
9
|
-
from reflex.components.
|
|
10
|
-
from reflex.
|
|
8
|
+
from reflex.components.datadisplay.logo import svg_logo
|
|
9
|
+
from reflex.components.el import a, button, details, div, h2, hr, p, pre, summary
|
|
10
|
+
from reflex.event import EventHandler, set_clipboard
|
|
11
11
|
from reflex.state import FrontendEventExceptionState
|
|
12
12
|
from reflex.vars.base import Var
|
|
13
|
+
from reflex.vars.function import ArgsFunctionOperation
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def on_error_spec(
|
|
@@ -40,38 +41,7 @@ class ErrorBoundary(Component):
|
|
|
40
41
|
on_error: EventHandler[on_error_spec]
|
|
41
42
|
|
|
42
43
|
# Rendered instead of the children when an error is caught.
|
|
43
|
-
|
|
44
|
-
_var_type=Component
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
def add_custom_code(self) -> List[str]:
|
|
48
|
-
"""Add custom Javascript code into the page that contains this component.
|
|
49
|
-
|
|
50
|
-
Custom code is inserted at module level, after any imports.
|
|
51
|
-
|
|
52
|
-
Returns:
|
|
53
|
-
The custom code to add.
|
|
54
|
-
"""
|
|
55
|
-
fallback_container = div(
|
|
56
|
-
p("Ooops...Unknown Reflex error has occured:"),
|
|
57
|
-
p(
|
|
58
|
-
Var(_js_expr="error.message"),
|
|
59
|
-
color="red",
|
|
60
|
-
),
|
|
61
|
-
p("Please contact the support."),
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
compiled_fallback = _compile_component(fallback_container)
|
|
65
|
-
|
|
66
|
-
return [
|
|
67
|
-
f"""
|
|
68
|
-
function Fallback({{ error, resetErrorBoundary }}) {{
|
|
69
|
-
return (
|
|
70
|
-
{compiled_fallback}
|
|
71
|
-
);
|
|
72
|
-
}}
|
|
73
|
-
"""
|
|
74
|
-
]
|
|
44
|
+
fallback_render: Var[Component]
|
|
75
45
|
|
|
76
46
|
@classmethod
|
|
77
47
|
def create(cls, *children, **props):
|
|
@@ -86,6 +56,99 @@ class ErrorBoundary(Component):
|
|
|
86
56
|
"""
|
|
87
57
|
if "on_error" not in props:
|
|
88
58
|
props["on_error"] = FrontendEventExceptionState.handle_frontend_exception
|
|
59
|
+
if "fallback_render" not in props:
|
|
60
|
+
props["fallback_render"] = ArgsFunctionOperation.create(
|
|
61
|
+
("event_args",),
|
|
62
|
+
Var.create(
|
|
63
|
+
div(
|
|
64
|
+
div(
|
|
65
|
+
div(
|
|
66
|
+
h2(
|
|
67
|
+
"An error occurred while rendering this page.",
|
|
68
|
+
font_size="1.25rem",
|
|
69
|
+
font_weight="bold",
|
|
70
|
+
),
|
|
71
|
+
p(
|
|
72
|
+
"This is an error with the application itself.",
|
|
73
|
+
opacity="0.75",
|
|
74
|
+
),
|
|
75
|
+
details(
|
|
76
|
+
summary("Error message", padding="0.5rem"),
|
|
77
|
+
div(
|
|
78
|
+
div(
|
|
79
|
+
pre(
|
|
80
|
+
Var(
|
|
81
|
+
_js_expr="event_args.error.stack",
|
|
82
|
+
),
|
|
83
|
+
),
|
|
84
|
+
padding="0.5rem",
|
|
85
|
+
width="fit-content",
|
|
86
|
+
),
|
|
87
|
+
width="100%",
|
|
88
|
+
max_height="50vh",
|
|
89
|
+
overflow="auto",
|
|
90
|
+
background="#000",
|
|
91
|
+
color="#fff",
|
|
92
|
+
border_radius="0.25rem",
|
|
93
|
+
),
|
|
94
|
+
button(
|
|
95
|
+
"Copy",
|
|
96
|
+
on_click=set_clipboard(
|
|
97
|
+
Var(_js_expr="event_args.error.stack"),
|
|
98
|
+
),
|
|
99
|
+
padding="0.35rem 0.75rem",
|
|
100
|
+
margin="0.5rem",
|
|
101
|
+
background="#fff",
|
|
102
|
+
color="#000",
|
|
103
|
+
border="1px solid #000",
|
|
104
|
+
border_radius="0.25rem",
|
|
105
|
+
font_weight="bold",
|
|
106
|
+
),
|
|
107
|
+
),
|
|
108
|
+
display="flex",
|
|
109
|
+
flex_direction="column",
|
|
110
|
+
gap="1rem",
|
|
111
|
+
max_width="50ch",
|
|
112
|
+
border="1px solid #888888",
|
|
113
|
+
border_radius="0.25rem",
|
|
114
|
+
padding="1rem",
|
|
115
|
+
),
|
|
116
|
+
hr(
|
|
117
|
+
border_color="currentColor",
|
|
118
|
+
opacity="0.25",
|
|
119
|
+
),
|
|
120
|
+
a(
|
|
121
|
+
div(
|
|
122
|
+
"Built with ",
|
|
123
|
+
svg_logo("currentColor"),
|
|
124
|
+
display="flex",
|
|
125
|
+
align_items="baseline",
|
|
126
|
+
justify_content="center",
|
|
127
|
+
font_family="monospace",
|
|
128
|
+
gap="0.5rem",
|
|
129
|
+
),
|
|
130
|
+
href="https://reflex.dev",
|
|
131
|
+
),
|
|
132
|
+
display="flex",
|
|
133
|
+
flex_direction="column",
|
|
134
|
+
gap="1rem",
|
|
135
|
+
),
|
|
136
|
+
height="100%",
|
|
137
|
+
width="100%",
|
|
138
|
+
position="absolute",
|
|
139
|
+
display="flex",
|
|
140
|
+
align_items="center",
|
|
141
|
+
justify_content="center",
|
|
142
|
+
)
|
|
143
|
+
),
|
|
144
|
+
_var_type=Component,
|
|
145
|
+
)
|
|
146
|
+
else:
|
|
147
|
+
props["fallback_render"] = ArgsFunctionOperation.create(
|
|
148
|
+
("event_args",),
|
|
149
|
+
props["fallback_render"],
|
|
150
|
+
_var_type=Component,
|
|
151
|
+
)
|
|
89
152
|
return super().create(*children, **props)
|
|
90
153
|
|
|
91
154
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# ------------------- DO NOT EDIT ----------------------
|
|
4
4
|
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
5
5
|
# ------------------------------------------------------
|
|
6
|
-
from typing import Any, Dict,
|
|
6
|
+
from typing import Any, Dict, Optional, Tuple, Union, overload
|
|
7
7
|
|
|
8
8
|
from reflex.components.component import Component
|
|
9
9
|
from reflex.event import BASE_STATE, EventType
|
|
@@ -15,13 +15,12 @@ def on_error_spec(
|
|
|
15
15
|
) -> Tuple[Var[str], Var[str]]: ...
|
|
16
16
|
|
|
17
17
|
class ErrorBoundary(Component):
|
|
18
|
-
def add_custom_code(self) -> List[str]: ...
|
|
19
18
|
@overload
|
|
20
19
|
@classmethod
|
|
21
20
|
def create( # type: ignore
|
|
22
21
|
cls,
|
|
23
22
|
*children,
|
|
24
|
-
|
|
23
|
+
fallback_render: Optional[Union[Component, Var[Component]]] = None,
|
|
25
24
|
style: Optional[Style] = None,
|
|
26
25
|
key: Optional[Any] = None,
|
|
27
26
|
id: Optional[Any] = None,
|
|
@@ -57,7 +56,7 @@ class ErrorBoundary(Component):
|
|
|
57
56
|
Args:
|
|
58
57
|
*children: The children of the component.
|
|
59
58
|
on_error: Fired when the boundary catches an error.
|
|
60
|
-
|
|
59
|
+
fallback_render: Rendered instead of the children when an error is caught.
|
|
61
60
|
style: The style of the component.
|
|
62
61
|
key: A unique key for the component.
|
|
63
62
|
id: The id for the component.
|
reflex/components/component.py
CHANGED
|
@@ -186,6 +186,23 @@ ComponentStyle = Dict[
|
|
|
186
186
|
ComponentChild = Union[types.PrimitiveType, Var, BaseComponent]
|
|
187
187
|
|
|
188
188
|
|
|
189
|
+
def satisfies_type_hint(obj: Any, type_hint: Any) -> bool:
|
|
190
|
+
"""Check if an object satisfies a type hint.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
obj: The object to check.
|
|
194
|
+
type_hint: The type hint to check against.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Whether the object satisfies the type hint.
|
|
198
|
+
"""
|
|
199
|
+
if isinstance(obj, LiteralVar):
|
|
200
|
+
return types._isinstance(obj._var_value, type_hint)
|
|
201
|
+
if isinstance(obj, Var):
|
|
202
|
+
return types._issubclass(obj._var_type, type_hint)
|
|
203
|
+
return types._isinstance(obj, type_hint)
|
|
204
|
+
|
|
205
|
+
|
|
189
206
|
class Component(BaseComponent, ABC):
|
|
190
207
|
"""A component with style, event trigger and other props."""
|
|
191
208
|
|
|
@@ -460,8 +477,7 @@ class Component(BaseComponent, ABC):
|
|
|
460
477
|
)
|
|
461
478
|
) or (
|
|
462
479
|
# Else just check if the passed var type is valid.
|
|
463
|
-
not passed_types
|
|
464
|
-
and not types._issubclass(passed_type, expected_type, value)
|
|
480
|
+
not passed_types and not satisfies_type_hint(value, expected_type)
|
|
465
481
|
):
|
|
466
482
|
value_name = value._js_expr if isinstance(value, Var) else value
|
|
467
483
|
|
|
@@ -1904,6 +1920,11 @@ memo = custom_component
|
|
|
1904
1920
|
class NoSSRComponent(Component):
|
|
1905
1921
|
"""A dynamic component that is not rendered on the server."""
|
|
1906
1922
|
|
|
1923
|
+
def _get_import_name(self) -> None | str:
|
|
1924
|
+
if not self.library:
|
|
1925
|
+
return None
|
|
1926
|
+
return f"${self.library}" if self.library.startswith("/") else self.library
|
|
1927
|
+
|
|
1907
1928
|
def _get_imports(self) -> ParsedImportDict:
|
|
1908
1929
|
"""Get the imports for the component.
|
|
1909
1930
|
|
|
@@ -1917,8 +1938,9 @@ class NoSSRComponent(Component):
|
|
|
1917
1938
|
_imports = super()._get_imports()
|
|
1918
1939
|
|
|
1919
1940
|
# Do NOT import the main library/tag statically.
|
|
1920
|
-
|
|
1921
|
-
|
|
1941
|
+
import_name = self._get_import_name()
|
|
1942
|
+
if import_name is not None:
|
|
1943
|
+
_imports[import_name] = [
|
|
1922
1944
|
imports.ImportVar(
|
|
1923
1945
|
tag=None,
|
|
1924
1946
|
render=False,
|
|
@@ -1936,10 +1958,10 @@ class NoSSRComponent(Component):
|
|
|
1936
1958
|
opts_fragment = ", { ssr: false });"
|
|
1937
1959
|
|
|
1938
1960
|
# extract the correct import name from library name
|
|
1939
|
-
|
|
1961
|
+
base_import_name = self._get_import_name()
|
|
1962
|
+
if base_import_name is None:
|
|
1940
1963
|
raise ValueError("Undefined library for NoSSRComponent")
|
|
1941
|
-
|
|
1942
|
-
import_name = format.format_library_name(self.library)
|
|
1964
|
+
import_name = format.format_library_name(base_import_name)
|
|
1943
1965
|
|
|
1944
1966
|
library_import = f"const {self.alias if self.alias else self.tag} = dynamic(() => import('{import_name}')"
|
|
1945
1967
|
mod_import = (
|
reflex/components/core/cond.py
CHANGED
|
@@ -171,6 +171,14 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var:
|
|
|
171
171
|
)
|
|
172
172
|
|
|
173
173
|
|
|
174
|
+
@overload
|
|
175
|
+
def color_mode_cond(light: Component, dark: Component | None = None) -> Component: ... # type: ignore
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@overload
|
|
179
|
+
def color_mode_cond(light: Any, dark: Any = None) -> Var: ...
|
|
180
|
+
|
|
181
|
+
|
|
174
182
|
def color_mode_cond(light: Any, dark: Any = None) -> Var | Component:
|
|
175
183
|
"""Create a component or Prop based on color_mode.
|
|
176
184
|
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
"""A Reflex logo component."""
|
|
2
2
|
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
3
5
|
import reflex as rx
|
|
4
6
|
|
|
5
7
|
|
|
6
|
-
def
|
|
7
|
-
"""A Reflex logo.
|
|
8
|
+
def svg_logo(color: Union[str, rx.Var[str]] = rx.color_mode_cond("#110F1F", "white")):
|
|
9
|
+
"""A Reflex logo SVG.
|
|
8
10
|
|
|
9
11
|
Args:
|
|
10
|
-
|
|
12
|
+
color: The color of the logo.
|
|
11
13
|
|
|
12
14
|
Returns:
|
|
13
|
-
The logo
|
|
15
|
+
The Reflex logo SVG.
|
|
14
16
|
"""
|
|
15
17
|
|
|
16
18
|
def logo_path(d):
|
|
17
19
|
return rx.el.svg.path(
|
|
18
20
|
d=d,
|
|
19
|
-
fill=rx.color_mode_cond("#110F1F", "white"),
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
paths = [
|
|
@@ -28,18 +29,30 @@ def logo(**props):
|
|
|
28
29
|
"M47.04 4.8799V0.399902H49.28V4.8799H47.04ZM53.76 4.8799V0.399902H56V4.8799H53.76ZM49.28 7.1199V4.8799H53.76V7.1199H49.28ZM47.04 11.5999V7.1199H49.28V11.5999H47.04ZM53.76 11.5999V7.1199H56V11.5999H53.76Z",
|
|
29
30
|
]
|
|
30
31
|
|
|
32
|
+
return rx.el.svg(
|
|
33
|
+
*[logo_path(d) for d in paths],
|
|
34
|
+
width="56",
|
|
35
|
+
height="12",
|
|
36
|
+
viewBox="0 0 56 12",
|
|
37
|
+
fill=color,
|
|
38
|
+
xmlns="http://www.w3.org/2000/svg",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def logo(**props):
|
|
43
|
+
"""A Reflex logo.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
**props: The props to pass to the component.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
The logo component.
|
|
50
|
+
"""
|
|
31
51
|
return rx.center(
|
|
32
52
|
rx.link(
|
|
33
53
|
rx.hstack(
|
|
34
54
|
"Built with ",
|
|
35
|
-
|
|
36
|
-
*[logo_path(d) for d in paths],
|
|
37
|
-
width="56",
|
|
38
|
-
height="12",
|
|
39
|
-
viewBox="0 0 56 12",
|
|
40
|
-
fill="none",
|
|
41
|
-
xmlns="http://www.w3.org/2000/svg",
|
|
42
|
-
),
|
|
55
|
+
svg_logo(),
|
|
43
56
|
text_align="center",
|
|
44
57
|
align="center",
|
|
45
58
|
padding="1em",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
from . import elements as elements
|
|
7
7
|
from .elements.forms import Button as Button
|
|
8
|
+
from .elements.forms import Datalist as Datalist
|
|
8
9
|
from .elements.forms import Fieldset as Fieldset
|
|
9
10
|
from .elements.forms import Form as Form
|
|
10
11
|
from .elements.forms import Input as Input
|
|
@@ -18,6 +19,7 @@ from .elements.forms import Progress as Progress
|
|
|
18
19
|
from .elements.forms import Select as Select
|
|
19
20
|
from .elements.forms import Textarea as Textarea
|
|
20
21
|
from .elements.forms import button as button
|
|
22
|
+
from .elements.forms import datalist as datalist
|
|
21
23
|
from .elements.forms import fieldset as fieldset
|
|
22
24
|
from .elements.forms import form as form
|
|
23
25
|
from .elements.forms import input as input
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from .forms import Button as Button
|
|
7
|
+
from .forms import Datalist as Datalist
|
|
7
8
|
from .forms import Fieldset as Fieldset
|
|
8
9
|
from .forms import Form as Form
|
|
9
10
|
from .forms import Input as Input
|
|
@@ -17,6 +18,7 @@ from .forms import Progress as Progress
|
|
|
17
18
|
from .forms import Select as Select
|
|
18
19
|
from .forms import Textarea as Textarea
|
|
19
20
|
from .forms import button as button
|
|
21
|
+
from .forms import datalist as datalist
|
|
20
22
|
from .forms import fieldset as fieldset
|
|
21
23
|
from .forms import form as form
|
|
22
24
|
from .forms import input as input
|
|
@@ -226,6 +228,7 @@ from .typography import ul as ul
|
|
|
226
228
|
_MAPPING = {
|
|
227
229
|
"forms": [
|
|
228
230
|
"button",
|
|
231
|
+
"datalist",
|
|
229
232
|
"fieldset",
|
|
230
233
|
"form",
|
|
231
234
|
"input",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Moment component for humanized date rendering."""
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
|
-
from
|
|
4
|
+
from datetime import date, datetime, time, timedelta
|
|
5
|
+
from typing import List, Optional, Union
|
|
5
6
|
|
|
6
7
|
from reflex.components.component import NoSSRComponent
|
|
7
8
|
from reflex.event import EventHandler, passthrough_event_spec
|
|
@@ -19,7 +20,7 @@ class MomentDelta:
|
|
|
19
20
|
weeks: Optional[int] = dataclasses.field(default=None)
|
|
20
21
|
days: Optional[int] = dataclasses.field(default=None)
|
|
21
22
|
hours: Optional[int] = dataclasses.field(default=None)
|
|
22
|
-
|
|
23
|
+
minutes: Optional[int] = dataclasses.field(default=None)
|
|
23
24
|
seconds: Optional[int] = dataclasses.field(default=None)
|
|
24
25
|
milliseconds: Optional[int] = dataclasses.field(default=None)
|
|
25
26
|
|
|
@@ -78,7 +79,7 @@ class Moment(NoSSRComponent):
|
|
|
78
79
|
duration: Var[str]
|
|
79
80
|
|
|
80
81
|
# The date to display (also work if passed as children).
|
|
81
|
-
date: Var[str]
|
|
82
|
+
date: Var[Union[str, datetime, date, time, timedelta]]
|
|
82
83
|
|
|
83
84
|
# Shows the duration (elapsed time) between now and the provided datetime.
|
|
84
85
|
duration_from_now: Var[bool]
|