reflex 0.6.5a3__py3-none-any.whl → 0.6.6__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 +39 -14
- reflex/__init__.py +3 -1
- reflex/__init__.pyi +3 -0
- reflex/app.py +24 -4
- 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/core/upload.py +8 -5
- 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/plotly/plotly.py +1 -1
- 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/recharts/recharts.py +2 -14
- reflex/components/recharts/recharts.pyi +0 -1
- 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 +16 -35
- reflex/state.py +90 -25
- 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.6.dist-info}/METADATA +3 -2
- {reflex-0.6.5a3.dist-info → reflex-0.6.6.dist-info}/RECORD +52 -51
- {reflex-0.6.5a3.dist-info → reflex-0.6.6.dist-info}/LICENSE +0 -0
- {reflex-0.6.5a3.dist-info → reflex-0.6.6.dist-info}/WHEEL +0 -0
- {reflex-0.6.5a3.dist-info → reflex-0.6.6.dist-info}/entry_points.txt +0 -0
|
@@ -454,6 +454,10 @@ export const connect = async (
|
|
|
454
454
|
queueEvents(update.events, socket);
|
|
455
455
|
}
|
|
456
456
|
});
|
|
457
|
+
socket.current.on("reload", async (event) => {
|
|
458
|
+
event_processing = false;
|
|
459
|
+
queueEvents([...initialEvents(), JSON5.parse(event)], socket);
|
|
460
|
+
});
|
|
457
461
|
|
|
458
462
|
document.addEventListener("visibilitychange", checkVisibility);
|
|
459
463
|
};
|
|
@@ -486,23 +490,30 @@ export const uploadFiles = async (
|
|
|
486
490
|
return false;
|
|
487
491
|
}
|
|
488
492
|
|
|
493
|
+
// Track how many partial updates have been processed for this upload.
|
|
489
494
|
let resp_idx = 0;
|
|
490
495
|
const eventHandler = (progressEvent) => {
|
|
491
|
-
|
|
496
|
+
const event_callbacks = socket._callbacks.$event;
|
|
497
|
+
// Whenever called, responseText will contain the entire response so far.
|
|
492
498
|
const chunks = progressEvent.event.target.responseText.trim().split("\n");
|
|
499
|
+
// So only process _new_ chunks beyond resp_idx.
|
|
493
500
|
chunks.slice(resp_idx).map((chunk) => {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
501
|
+
event_callbacks.map((f, ix) => {
|
|
502
|
+
f(chunk)
|
|
503
|
+
.then(() => {
|
|
504
|
+
if (ix === event_callbacks.length - 1) {
|
|
505
|
+
// Mark this chunk as processed.
|
|
506
|
+
resp_idx += 1;
|
|
507
|
+
}
|
|
508
|
+
})
|
|
509
|
+
.catch((e) => {
|
|
510
|
+
if (progressEvent.progress === 1) {
|
|
511
|
+
// Chunk may be incomplete, so only report errors when full response is available.
|
|
512
|
+
console.log("Error parsing chunk", chunk, e);
|
|
513
|
+
}
|
|
514
|
+
return;
|
|
515
|
+
});
|
|
516
|
+
});
|
|
506
517
|
});
|
|
507
518
|
};
|
|
508
519
|
|
|
@@ -705,6 +716,11 @@ export const useEventLoop = (
|
|
|
705
716
|
_e.stopPropagation();
|
|
706
717
|
}
|
|
707
718
|
const combined_name = events.map((e) => e.name).join("+++");
|
|
719
|
+
if (event_actions?.temporal) {
|
|
720
|
+
if (!socket.current || !socket.current.connected) {
|
|
721
|
+
return; // don't queue when the backend is not connected
|
|
722
|
+
}
|
|
723
|
+
}
|
|
708
724
|
if (event_actions?.throttle) {
|
|
709
725
|
// If throttle returns false, the events are not added to the queue.
|
|
710
726
|
if (!throttle(combined_name, event_actions.throttle)) {
|
|
@@ -762,7 +778,7 @@ export const useEventLoop = (
|
|
|
762
778
|
window.onunhandledrejection = function (event) {
|
|
763
779
|
addEvents([
|
|
764
780
|
Event(`${exception_state_name}.handle_frontend_exception`, {
|
|
765
|
-
stack: event.reason
|
|
781
|
+
stack: event.reason?.stack,
|
|
766
782
|
component_stack: "",
|
|
767
783
|
}),
|
|
768
784
|
]);
|
|
@@ -837,11 +853,20 @@ export const useEventLoop = (
|
|
|
837
853
|
}
|
|
838
854
|
};
|
|
839
855
|
const change_complete = () => addEvents(onLoadInternalEvent());
|
|
856
|
+
const change_error = () => {
|
|
857
|
+
// Remove cached error state from router for this page, otherwise the
|
|
858
|
+
// page will never send on_load events again.
|
|
859
|
+
if (router.components[router.pathname].error) {
|
|
860
|
+
delete router.components[router.pathname].error;
|
|
861
|
+
}
|
|
862
|
+
};
|
|
840
863
|
router.events.on("routeChangeStart", change_start);
|
|
841
864
|
router.events.on("routeChangeComplete", change_complete);
|
|
865
|
+
router.events.on("routeChangeError", change_error);
|
|
842
866
|
return () => {
|
|
843
867
|
router.events.off("routeChangeStart", change_start);
|
|
844
868
|
router.events.off("routeChangeComplete", change_complete);
|
|
869
|
+
router.events.off("routeChangeError", change_error);
|
|
845
870
|
};
|
|
846
871
|
}, [router]);
|
|
847
872
|
|
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
|
@@ -73,6 +73,7 @@ from reflex.event import (
|
|
|
73
73
|
EventSpec,
|
|
74
74
|
EventType,
|
|
75
75
|
IndividualEventType,
|
|
76
|
+
get_hydrate_event,
|
|
76
77
|
window_alert,
|
|
77
78
|
)
|
|
78
79
|
from reflex.model import Model, get_db_status
|
|
@@ -850,10 +851,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
850
851
|
if self.theme is not None:
|
|
851
852
|
# If a theme component was provided, wrap the app with it
|
|
852
853
|
app_wrappers[(20, "Theme")] = self.theme
|
|
853
|
-
# Fix #2992 by removing the top-level appearance prop
|
|
854
|
-
self.theme.appearance = None
|
|
855
854
|
|
|
856
855
|
for route in self.unevaluated_pages:
|
|
856
|
+
console.debug(f"Evaluating page: {route}")
|
|
857
857
|
self._compile_page(route)
|
|
858
858
|
|
|
859
859
|
# Add the optional endpoints (_upload)
|
|
@@ -1006,6 +1006,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1006
1006
|
compile_results.append(
|
|
1007
1007
|
compiler.compile_contexts(self.state, self.theme),
|
|
1008
1008
|
)
|
|
1009
|
+
if self.theme is not None:
|
|
1010
|
+
# Fix #2992 by removing the top-level appearance prop
|
|
1011
|
+
self.theme.appearance = None
|
|
1009
1012
|
progress.advance(task)
|
|
1010
1013
|
|
|
1011
1014
|
# Compile the app root.
|
|
@@ -1257,6 +1260,21 @@ async def process(
|
|
|
1257
1260
|
)
|
|
1258
1261
|
# Get the state for the session exclusively.
|
|
1259
1262
|
async with app.state_manager.modify_state(event.substate_token) as state:
|
|
1263
|
+
# When this is a brand new instance of the state, signal the
|
|
1264
|
+
# frontend to reload before processing it.
|
|
1265
|
+
if (
|
|
1266
|
+
not state.router_data
|
|
1267
|
+
and event.name != get_hydrate_event(state)
|
|
1268
|
+
and app.event_namespace is not None
|
|
1269
|
+
):
|
|
1270
|
+
await asyncio.create_task(
|
|
1271
|
+
app.event_namespace.emit(
|
|
1272
|
+
"reload",
|
|
1273
|
+
data=format.json_dumps(event),
|
|
1274
|
+
to=sid,
|
|
1275
|
+
)
|
|
1276
|
+
)
|
|
1277
|
+
return
|
|
1260
1278
|
# re-assign only when the value is different
|
|
1261
1279
|
if state.router_data != router_data:
|
|
1262
1280
|
# assignment will recurse into substates and force recalculation of
|
|
@@ -1460,10 +1478,10 @@ class EventNamespace(AsyncNamespace):
|
|
|
1460
1478
|
app: App
|
|
1461
1479
|
|
|
1462
1480
|
# Keep a mapping between socket ID and client token.
|
|
1463
|
-
token_to_sid: dict[str, str]
|
|
1481
|
+
token_to_sid: dict[str, str]
|
|
1464
1482
|
|
|
1465
1483
|
# Keep a mapping between client token and socket ID.
|
|
1466
|
-
sid_to_token: dict[str, str]
|
|
1484
|
+
sid_to_token: dict[str, str]
|
|
1467
1485
|
|
|
1468
1486
|
def __init__(self, namespace: str, app: App):
|
|
1469
1487
|
"""Initialize the event namespace.
|
|
@@ -1473,6 +1491,8 @@ class EventNamespace(AsyncNamespace):
|
|
|
1473
1491
|
app: The application object.
|
|
1474
1492
|
"""
|
|
1475
1493
|
super().__init__(namespace)
|
|
1494
|
+
self.token_to_sid = {}
|
|
1495
|
+
self.sid_to_token = {}
|
|
1476
1496
|
self.app = app
|
|
1477
1497
|
|
|
1478
1498
|
def on_connect(self, sid, environ):
|
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
|
|
reflex/components/core/upload.py
CHANGED
|
@@ -293,13 +293,15 @@ class Upload(MemoizationLeaf):
|
|
|
293
293
|
format.to_camel_case(key): value for key, value in upload_props.items()
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
use_dropzone_arguments =
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
296
|
+
use_dropzone_arguments = Var.create(
|
|
297
|
+
{
|
|
298
|
+
"onDrop": event_var,
|
|
299
|
+
**upload_props,
|
|
300
|
+
}
|
|
301
|
+
)
|
|
300
302
|
|
|
301
303
|
left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} "
|
|
302
|
-
right_side = f"useDropzone({str(
|
|
304
|
+
right_side = f"useDropzone({str(use_dropzone_arguments)})"
|
|
303
305
|
|
|
304
306
|
var_data = VarData.merge(
|
|
305
307
|
VarData(
|
|
@@ -307,6 +309,7 @@ class Upload(MemoizationLeaf):
|
|
|
307
309
|
hooks={Hooks.EVENTS: None},
|
|
308
310
|
),
|
|
309
311
|
event_var._get_all_var_data(),
|
|
312
|
+
use_dropzone_arguments._get_all_var_data(),
|
|
310
313
|
VarData(
|
|
311
314
|
hooks={
|
|
312
315
|
callback_str: None,
|