reflex 0.8.0a3__py3-none-any.whl → 0.8.0a5__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/web/styles/styles.css.jinja2 +1 -0
- reflex/.templates/web/app/routes.js +3 -3
- reflex/.templates/web/styles/__reflex_style_reset.css +399 -0
- reflex/.templates/web/utils/client_side_routing.js +1 -1
- reflex/.templates/web/utils/state.js +32 -21
- reflex/.templates/web/vite.config.js +6 -0
- reflex/app.py +50 -46
- reflex/compiler/compiler.py +26 -10
- reflex/compiler/utils.py +4 -2
- reflex/components/base/meta.py +4 -15
- reflex/components/core/foreach.py +2 -2
- reflex/components/core/match.py +3 -3
- reflex/components/core/upload.py +2 -1
- reflex/components/datadisplay/code.py +12 -7
- reflex/components/datadisplay/shiki_code_block.py +5 -3
- reflex/components/markdown/markdown.py +5 -3
- reflex/components/plotly/plotly.py +12 -6
- reflex/components/radix/themes/color_mode.py +5 -6
- reflex/components/recharts/cartesian.py +9 -2
- reflex/constants/compiler.py +7 -0
- reflex/constants/route.py +13 -6
- reflex/environment.py +6 -4
- reflex/route.py +159 -71
- reflex/state.py +21 -4
- reflex/utils/exec.py +10 -10
- reflex/utils/format.py +1 -5
- reflex/utils/misc.py +40 -0
- reflex/utils/prerequisites.py +6 -11
- reflex/utils/pyi_generator.py +23 -40
- reflex/utils/types.py +1 -1
- {reflex-0.8.0a3.dist-info → reflex-0.8.0a5.dist-info}/METADATA +2 -2
- {reflex-0.8.0a3.dist-info → reflex-0.8.0a5.dist-info}/RECORD +35 -34
- {reflex-0.8.0a3.dist-info → reflex-0.8.0a5.dist-info}/WHEEL +0 -0
- {reflex-0.8.0a3.dist-info → reflex-0.8.0a5.dist-info}/entry_points.txt +0 -0
- {reflex-0.8.0a3.dist-info → reflex-0.8.0a5.dist-info}/licenses/LICENSE +0 -0
reflex/app.py
CHANGED
|
@@ -77,6 +77,7 @@ from reflex.event import (
|
|
|
77
77
|
EventType,
|
|
78
78
|
IndividualEventType,
|
|
79
79
|
get_hydrate_event,
|
|
80
|
+
noop,
|
|
80
81
|
)
|
|
81
82
|
from reflex.model import Model, get_db_status
|
|
82
83
|
from reflex.page import DECORATED_PAGES
|
|
@@ -213,17 +214,21 @@ def default_overlay_component() -> Component:
|
|
|
213
214
|
return Fragment.create(memo(default_overlay_components)())
|
|
214
215
|
|
|
215
216
|
|
|
216
|
-
def default_error_boundary(*children: Component) -> Component:
|
|
217
|
+
def default_error_boundary(*children: Component, **props) -> Component:
|
|
217
218
|
"""Default error_boundary attribute for App.
|
|
218
219
|
|
|
219
220
|
Args:
|
|
220
221
|
*children: The children to render in the error boundary.
|
|
222
|
+
**props: The props to pass to the error boundary.
|
|
221
223
|
|
|
222
224
|
Returns:
|
|
223
225
|
The default error_boundary, which is an ErrorBoundary.
|
|
224
226
|
|
|
225
227
|
"""
|
|
226
|
-
return ErrorBoundary.create(
|
|
228
|
+
return ErrorBoundary.create(
|
|
229
|
+
*children,
|
|
230
|
+
**props,
|
|
231
|
+
)
|
|
227
232
|
|
|
228
233
|
|
|
229
234
|
class OverlayFragment(Fragment):
|
|
@@ -332,6 +337,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
332
337
|
# A list of URLs to [stylesheets](https://reflex.dev/docs/styling/custom-stylesheets/) to include in the app.
|
|
333
338
|
stylesheets: list[str] = dataclasses.field(default_factory=list)
|
|
334
339
|
|
|
340
|
+
# Whether to include CSS reset for margin and padding (defaults to True).
|
|
341
|
+
reset_style: bool = dataclasses.field(default=True)
|
|
342
|
+
|
|
335
343
|
# A component that is present on every page (defaults to the Connection Error banner).
|
|
336
344
|
overlay_component: Component | ComponentCallable | None = dataclasses.field(
|
|
337
345
|
default=None
|
|
@@ -342,7 +350,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
342
350
|
dataclasses.field(
|
|
343
351
|
default_factory=lambda: {
|
|
344
352
|
(55, "ErrorBoundary"): (
|
|
345
|
-
lambda stateful: default_error_boundary(
|
|
353
|
+
lambda stateful: default_error_boundary(
|
|
354
|
+
**({"on_error": noop()} if not stateful else {})
|
|
355
|
+
)
|
|
346
356
|
),
|
|
347
357
|
(5, "Overlay"): (
|
|
348
358
|
lambda stateful: default_overlay_component() if stateful else None
|
|
@@ -755,9 +765,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
755
765
|
msg = "Route must be set if component is not a callable."
|
|
756
766
|
raise exceptions.RouteValueError(msg)
|
|
757
767
|
# Format the route.
|
|
758
|
-
route = format.format_route(component.__name__)
|
|
768
|
+
route = format.format_route(format.to_kebab_case(component.__name__))
|
|
759
769
|
else:
|
|
760
|
-
route = format.format_route(route
|
|
770
|
+
route = format.format_route(route)
|
|
761
771
|
|
|
762
772
|
if route == constants.Page404.SLUG:
|
|
763
773
|
if component is None:
|
|
@@ -812,10 +822,11 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
812
822
|
state = self._state if self._state else State
|
|
813
823
|
state.setup_dynamic_args(get_route_args(route))
|
|
814
824
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
825
|
+
self._load_events[route] = (
|
|
826
|
+
(on_load if isinstance(on_load, list) else [on_load])
|
|
827
|
+
if on_load is not None
|
|
828
|
+
else []
|
|
829
|
+
)
|
|
819
830
|
|
|
820
831
|
self._unevaluated_pages[route] = unevaluated_page
|
|
821
832
|
|
|
@@ -844,48 +855,35 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
844
855
|
if save_page:
|
|
845
856
|
self._pages[route] = component
|
|
846
857
|
|
|
847
|
-
|
|
858
|
+
@functools.cached_property
|
|
859
|
+
def router(self) -> Callable[[str], str | None]:
|
|
860
|
+
"""Get the route computer function.
|
|
861
|
+
|
|
862
|
+
Returns:
|
|
863
|
+
The route computer function.
|
|
864
|
+
"""
|
|
865
|
+
from reflex.route import get_router
|
|
866
|
+
|
|
867
|
+
return get_router(list(self._pages))
|
|
868
|
+
|
|
869
|
+
def get_load_events(self, path: str) -> list[IndividualEventType[()]]:
|
|
848
870
|
"""Get the load events for a route.
|
|
849
871
|
|
|
850
872
|
Args:
|
|
851
|
-
|
|
873
|
+
path: The route to get the load events for.
|
|
852
874
|
|
|
853
875
|
Returns:
|
|
854
876
|
The load events for the route.
|
|
855
877
|
"""
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
if "[" in page_path and "]" in page_path:
|
|
866
|
-
dynamic_page_paths_to_page_route[page_path] = page_route
|
|
867
|
-
else:
|
|
868
|
-
static_page_paths_to_page_route[page_path] = page_route
|
|
869
|
-
|
|
870
|
-
# Check for static routes.
|
|
871
|
-
if (page_route := static_page_paths_to_page_route.get(route)) is not None:
|
|
872
|
-
return self._load_events.get(page_route, [])
|
|
873
|
-
|
|
874
|
-
# Check for dynamic routes.
|
|
875
|
-
parts = route.split("/")
|
|
876
|
-
for page_path, page_route in dynamic_page_paths_to_page_route.items():
|
|
877
|
-
page_parts = page_path.split("/")
|
|
878
|
-
if len(page_parts) != len(parts):
|
|
879
|
-
continue
|
|
880
|
-
if all(
|
|
881
|
-
part == page_part
|
|
882
|
-
or (page_part.startswith("[") and page_part.endswith("]"))
|
|
883
|
-
for part, page_part in zip(parts, page_parts, strict=False)
|
|
884
|
-
):
|
|
885
|
-
return self._load_events.get(page_route, [])
|
|
886
|
-
|
|
887
|
-
# Default to 404 page load events if no match found.
|
|
888
|
-
return self._load_events.get("404", [])
|
|
878
|
+
four_oh_four_load_events = self._load_events.get("404", [])
|
|
879
|
+
route = self.router(path)
|
|
880
|
+
if not route:
|
|
881
|
+
# If the path is not a valid route, return the 404 page load events.
|
|
882
|
+
return four_oh_four_load_events
|
|
883
|
+
return self._load_events.get(
|
|
884
|
+
route,
|
|
885
|
+
four_oh_four_load_events,
|
|
886
|
+
)
|
|
889
887
|
|
|
890
888
|
def _check_routes_conflict(self, new_route: str):
|
|
891
889
|
"""Verify if there is any conflict between the new route and any existing route.
|
|
@@ -906,7 +904,6 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
906
904
|
segments = (
|
|
907
905
|
constants.RouteRegex.SINGLE_SEGMENT,
|
|
908
906
|
constants.RouteRegex.DOUBLE_SEGMENT,
|
|
909
|
-
constants.RouteRegex.SINGLE_CATCHALL_SEGMENT,
|
|
910
907
|
constants.RouteRegex.DOUBLE_CATCHALL_SEGMENT,
|
|
911
908
|
)
|
|
912
909
|
for route in self._pages:
|
|
@@ -1358,7 +1355,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1358
1355
|
)
|
|
1359
1356
|
|
|
1360
1357
|
# Compile the root stylesheet with base styles.
|
|
1361
|
-
_submit_work(
|
|
1358
|
+
_submit_work(
|
|
1359
|
+
compiler.compile_root_stylesheet, self.stylesheets, self.reset_style
|
|
1360
|
+
)
|
|
1362
1361
|
|
|
1363
1362
|
# Compile the theme.
|
|
1364
1363
|
_submit_work(compile_theme, self.style)
|
|
@@ -1730,6 +1729,11 @@ async def process(
|
|
|
1730
1729
|
# assignment will recurse into substates and force recalculation of
|
|
1731
1730
|
# dependent ComputedVar (dynamic route variables)
|
|
1732
1731
|
state.router_data = router_data
|
|
1732
|
+
router_data[constants.RouteVar.PATH] = "/" + (
|
|
1733
|
+
app.router(path) or "404"
|
|
1734
|
+
if (path := router_data.get(constants.RouteVar.PATH))
|
|
1735
|
+
else "404"
|
|
1736
|
+
).removeprefix("/")
|
|
1733
1737
|
state.router = RouterData(router_data)
|
|
1734
1738
|
|
|
1735
1739
|
# Preprocess the event.
|
reflex/compiler/compiler.py
CHANGED
|
@@ -19,7 +19,7 @@ from reflex.components.component import (
|
|
|
19
19
|
StatefulComponent,
|
|
20
20
|
)
|
|
21
21
|
from reflex.config import get_config
|
|
22
|
-
from reflex.constants.compiler import PageNames
|
|
22
|
+
from reflex.constants.compiler import PageNames, ResetStylesheet
|
|
23
23
|
from reflex.constants.state import FIELD_MARKER
|
|
24
24
|
from reflex.environment import environment
|
|
25
25
|
from reflex.state import BaseState
|
|
@@ -86,6 +86,8 @@ def _compile_app(app_root: Component) -> str:
|
|
|
86
86
|
(_normalize_library_name(name), name) for name in bundled_libraries
|
|
87
87
|
]
|
|
88
88
|
|
|
89
|
+
window_libraries_deduped = list(dict.fromkeys(window_libraries))
|
|
90
|
+
|
|
89
91
|
app_root_imports = app_root._get_all_imports()
|
|
90
92
|
_apply_common_imports(app_root_imports)
|
|
91
93
|
|
|
@@ -93,7 +95,7 @@ def _compile_app(app_root: Component) -> str:
|
|
|
93
95
|
imports=utils.compile_imports(app_root_imports),
|
|
94
96
|
custom_codes=app_root._get_all_custom_code(),
|
|
95
97
|
hooks=app_root._get_all_hooks(),
|
|
96
|
-
window_libraries=
|
|
98
|
+
window_libraries=window_libraries_deduped,
|
|
97
99
|
render=app_root.render(),
|
|
98
100
|
dynamic_imports=app_root._get_all_dynamic_imports(),
|
|
99
101
|
)
|
|
@@ -171,18 +173,21 @@ def _compile_page(
|
|
|
171
173
|
)
|
|
172
174
|
|
|
173
175
|
|
|
174
|
-
def compile_root_stylesheet(
|
|
176
|
+
def compile_root_stylesheet(
|
|
177
|
+
stylesheets: list[str], reset_style: bool = True
|
|
178
|
+
) -> tuple[str, str]:
|
|
175
179
|
"""Compile the root stylesheet.
|
|
176
180
|
|
|
177
181
|
Args:
|
|
178
182
|
stylesheets: The stylesheets to include in the root stylesheet.
|
|
183
|
+
reset_style: Whether to include CSS reset for margin and padding.
|
|
179
184
|
|
|
180
185
|
Returns:
|
|
181
186
|
The path and code of the compiled root stylesheet.
|
|
182
187
|
"""
|
|
183
188
|
output_path = utils.get_root_stylesheet_path()
|
|
184
189
|
|
|
185
|
-
code = _compile_root_stylesheet(stylesheets)
|
|
190
|
+
code = _compile_root_stylesheet(stylesheets, reset_style)
|
|
186
191
|
|
|
187
192
|
return output_path, code
|
|
188
193
|
|
|
@@ -224,11 +229,12 @@ def _validate_stylesheet(stylesheet_full_path: Path, assets_app_path: Path) -> N
|
|
|
224
229
|
RADIX_THEMES_STYLESHEET = "@radix-ui/themes/styles.css"
|
|
225
230
|
|
|
226
231
|
|
|
227
|
-
def _compile_root_stylesheet(stylesheets: list[str]) -> str:
|
|
232
|
+
def _compile_root_stylesheet(stylesheets: list[str], reset_style: bool = True) -> str:
|
|
228
233
|
"""Compile the root stylesheet.
|
|
229
234
|
|
|
230
235
|
Args:
|
|
231
236
|
stylesheets: The stylesheets to include in the root stylesheet.
|
|
237
|
+
reset_style: Whether to include CSS reset for margin and padding.
|
|
232
238
|
|
|
233
239
|
Returns:
|
|
234
240
|
The compiled root stylesheet.
|
|
@@ -237,11 +243,21 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
|
|
|
237
243
|
FileNotFoundError: If a specified stylesheet in assets directory does not exist.
|
|
238
244
|
"""
|
|
239
245
|
# Add stylesheets from plugins.
|
|
240
|
-
sheets = [
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
246
|
+
sheets = []
|
|
247
|
+
|
|
248
|
+
# Add CSS reset if enabled
|
|
249
|
+
if reset_style:
|
|
250
|
+
# Reference the vendored style reset file (automatically copied from .templates/web)
|
|
251
|
+
sheets.append(f"./{ResetStylesheet.FILENAME}")
|
|
252
|
+
|
|
253
|
+
sheets.extend(
|
|
254
|
+
[RADIX_THEMES_STYLESHEET]
|
|
255
|
+
+ [
|
|
256
|
+
sheet
|
|
257
|
+
for plugin in get_config().plugins
|
|
258
|
+
for sheet in plugin.get_stylesheet_paths()
|
|
259
|
+
]
|
|
260
|
+
)
|
|
245
261
|
|
|
246
262
|
failed_to_import_sass = False
|
|
247
263
|
assets_app_path = Path.cwd() / constants.Dirs.APP_ASSETS
|
reflex/compiler/utils.py
CHANGED
|
@@ -417,9 +417,11 @@ def _format_route_part(part: str) -> str:
|
|
|
417
417
|
if part.startswith("[") and part.endswith("]"):
|
|
418
418
|
if part.startswith(("[...", "[[...")):
|
|
419
419
|
return "$"
|
|
420
|
+
if part.startswith("[["):
|
|
421
|
+
return "($" + part.removeprefix("[[").removesuffix("]]") + ")"
|
|
420
422
|
# We don't add [] here since we are reusing them from the input
|
|
421
|
-
return "$" + part
|
|
422
|
-
return "[" + part + "]
|
|
423
|
+
return "$" + part
|
|
424
|
+
return "[" + part + "]"
|
|
423
425
|
|
|
424
426
|
|
|
425
427
|
def _path_to_file_stem(path: str) -> str:
|
reflex/components/base/meta.py
CHANGED
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from reflex.components.base.bare import Bare
|
|
6
6
|
from reflex.components.el import elements
|
|
7
|
+
from reflex.vars.base import Var
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class Title(elements.Title):
|
|
@@ -28,31 +29,19 @@ class Title(elements.Title):
|
|
|
28
29
|
class Meta(elements.Meta):
|
|
29
30
|
"""A component that displays metadata for the current page."""
|
|
30
31
|
|
|
31
|
-
# The description of character encoding.
|
|
32
|
-
char_set: str | None = None
|
|
33
|
-
|
|
34
|
-
# The value of meta.
|
|
35
|
-
content: str | None = None
|
|
36
|
-
|
|
37
|
-
# The name of metadata.
|
|
38
|
-
name: str | None = None
|
|
39
|
-
|
|
40
|
-
# The type of metadata value.
|
|
41
|
-
property: str | None = None
|
|
42
|
-
|
|
43
32
|
# The type of metadata value.
|
|
44
|
-
|
|
33
|
+
property: Var[str]
|
|
45
34
|
|
|
46
35
|
|
|
47
36
|
class Description(elements.Meta):
|
|
48
37
|
"""A component that displays the title of the current page."""
|
|
49
38
|
|
|
50
39
|
# The type of the description.
|
|
51
|
-
name: str
|
|
40
|
+
name: Var[str] = Var.create("description")
|
|
52
41
|
|
|
53
42
|
|
|
54
43
|
class Image(elements.Meta):
|
|
55
44
|
"""A component that displays the title of the current page."""
|
|
56
45
|
|
|
57
46
|
# The type of the image.
|
|
58
|
-
property: str
|
|
47
|
+
property: Var[str] = Var.create("og:image")
|
|
@@ -8,7 +8,7 @@ from collections.abc import Callable, Iterable
|
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
10
|
from reflex.components.base.fragment import Fragment
|
|
11
|
-
from reflex.components.component import Component
|
|
11
|
+
from reflex.components.component import Component, field
|
|
12
12
|
from reflex.components.core.cond import cond
|
|
13
13
|
from reflex.components.tags import IterTag
|
|
14
14
|
from reflex.constants import MemoizationMode
|
|
@@ -36,7 +36,7 @@ class Foreach(Component):
|
|
|
36
36
|
iterable: Var[Iterable]
|
|
37
37
|
|
|
38
38
|
# A function from the render args to the component.
|
|
39
|
-
render_fn: Callable = Fragment.create
|
|
39
|
+
render_fn: Callable = field(default=Fragment.create, is_javascript_property=False)
|
|
40
40
|
|
|
41
41
|
@classmethod
|
|
42
42
|
def create(
|
reflex/components/core/match.py
CHANGED
|
@@ -4,7 +4,7 @@ import textwrap
|
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
6
|
from reflex.components.base import Fragment
|
|
7
|
-
from reflex.components.component import BaseComponent, Component, MemoizationLeaf
|
|
7
|
+
from reflex.components.component import BaseComponent, Component, MemoizationLeaf, field
|
|
8
8
|
from reflex.components.tags import MatchTag, Tag
|
|
9
9
|
from reflex.style import Style
|
|
10
10
|
from reflex.utils import format
|
|
@@ -21,10 +21,10 @@ class Match(MemoizationLeaf):
|
|
|
21
21
|
cond: Var[Any]
|
|
22
22
|
|
|
23
23
|
# The list of match cases to be matched.
|
|
24
|
-
match_cases: list[Any] =
|
|
24
|
+
match_cases: list[Any] = field(default_factory=list, is_javascript_property=False)
|
|
25
25
|
|
|
26
26
|
# The catchall case to match.
|
|
27
|
-
default: Any
|
|
27
|
+
default: Any = field(default=None, is_javascript_property=False)
|
|
28
28
|
|
|
29
29
|
@classmethod
|
|
30
30
|
def create(cls, cond: Any, *cases) -> Component | Var:
|
reflex/components/core/upload.py
CHANGED
|
@@ -12,6 +12,7 @@ from reflex.components.component import (
|
|
|
12
12
|
ComponentNamespace,
|
|
13
13
|
MemoizationLeaf,
|
|
14
14
|
StatefulComponent,
|
|
15
|
+
field,
|
|
15
16
|
)
|
|
16
17
|
from reflex.components.core.cond import cond
|
|
17
18
|
from reflex.components.el.elements.forms import Input
|
|
@@ -234,7 +235,7 @@ class Upload(MemoizationLeaf):
|
|
|
234
235
|
on_drop: EventHandler[_on_drop_spec]
|
|
235
236
|
|
|
236
237
|
# Style rules to apply when actively dragging.
|
|
237
|
-
drag_active_style: Style | None = None
|
|
238
|
+
drag_active_style: Style | None = field(default=None, is_javascript_property=False)
|
|
238
239
|
|
|
239
240
|
@classmethod
|
|
240
241
|
def create(cls, *children, **props) -> Component:
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import dataclasses
|
|
6
6
|
from typing import ClassVar, Literal
|
|
7
7
|
|
|
8
|
-
from reflex.components.component import Component, ComponentNamespace
|
|
8
|
+
from reflex.components.component import Component, ComponentNamespace, field
|
|
9
9
|
from reflex.components.core.cond import color_mode_cond
|
|
10
10
|
from reflex.components.lucide.icon import Icon
|
|
11
11
|
from reflex.components.markdown.markdown import MarkdownComponentMap
|
|
@@ -407,16 +407,24 @@ class CodeBlock(Component, MarkdownComponentMap):
|
|
|
407
407
|
wrap_long_lines: Var[bool]
|
|
408
408
|
|
|
409
409
|
# A custom style for the code block.
|
|
410
|
-
custom_style: dict[str, str | Var | Color] =
|
|
410
|
+
custom_style: dict[str, str | Var | Color] = field(
|
|
411
|
+
default_factory=dict, is_javascript_property=False
|
|
412
|
+
)
|
|
411
413
|
|
|
412
414
|
# Props passed down to the code tag.
|
|
413
415
|
code_tag_props: Var[dict[str, str]]
|
|
414
416
|
|
|
415
417
|
# Whether a copy button should appear.
|
|
416
|
-
can_copy: bool | None =
|
|
418
|
+
can_copy: bool | None = field(
|
|
419
|
+
default=False,
|
|
420
|
+
is_javascript_property=False,
|
|
421
|
+
)
|
|
417
422
|
|
|
418
423
|
# A custom copy button to override the default one.
|
|
419
|
-
copy_button: bool | Component | None =
|
|
424
|
+
copy_button: bool | Component | None = field(
|
|
425
|
+
default=None,
|
|
426
|
+
is_javascript_property=False,
|
|
427
|
+
)
|
|
420
428
|
|
|
421
429
|
@classmethod
|
|
422
430
|
def create(
|
|
@@ -498,9 +506,6 @@ class CodeBlock(Component, MarkdownComponentMap):
|
|
|
498
506
|
|
|
499
507
|
return out
|
|
500
508
|
|
|
501
|
-
def _exclude_props(self) -> list[str]:
|
|
502
|
-
return ["can_copy", "copy_button"]
|
|
503
|
-
|
|
504
509
|
|
|
505
510
|
class CodeblockNamespace(ComponentNamespace):
|
|
506
511
|
"""Namespace for the CodeBlock component."""
|
|
@@ -8,7 +8,7 @@ from collections import defaultdict
|
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
from typing import Any, Literal
|
|
10
10
|
|
|
11
|
-
from reflex.components.component import Component, ComponentNamespace
|
|
11
|
+
from reflex.components.component import Component, ComponentNamespace, field
|
|
12
12
|
from reflex.components.core.colors import color
|
|
13
13
|
from reflex.components.core.cond import color_mode_cond
|
|
14
14
|
from reflex.components.el.elements.forms import Button
|
|
@@ -721,10 +721,12 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
|
|
|
721
721
|
show_line_numbers: Var[bool]
|
|
722
722
|
|
|
723
723
|
# Whether a copy button should appear.
|
|
724
|
-
can_copy: bool = False
|
|
724
|
+
can_copy: bool = field(default=False, is_javascript_property=False)
|
|
725
725
|
|
|
726
726
|
# copy_button: A custom copy button to override the default one.
|
|
727
|
-
copy_button: Component | bool | None =
|
|
727
|
+
copy_button: Component | bool | None = field(
|
|
728
|
+
default=None, is_javascript_property=False
|
|
729
|
+
)
|
|
728
730
|
|
|
729
731
|
@classmethod
|
|
730
732
|
def create(
|
|
@@ -9,7 +9,7 @@ from functools import lru_cache
|
|
|
9
9
|
from hashlib import md5
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
|
-
from reflex.components.component import BaseComponent, Component, CustomComponent
|
|
12
|
+
from reflex.components.component import BaseComponent, Component, CustomComponent, field
|
|
13
13
|
from reflex.components.tags.tag import Tag
|
|
14
14
|
from reflex.utils.imports import ImportDict, ImportVar
|
|
15
15
|
from reflex.vars.base import LiteralVar, Var, VarData
|
|
@@ -150,10 +150,12 @@ class Markdown(Component):
|
|
|
150
150
|
is_default = True
|
|
151
151
|
|
|
152
152
|
# The component map from a tag to a lambda that creates a component.
|
|
153
|
-
component_map: dict[str, Any] =
|
|
153
|
+
component_map: dict[str, Any] = field(
|
|
154
|
+
default_factory=dict, is_javascript_property=False
|
|
155
|
+
)
|
|
154
156
|
|
|
155
157
|
# The hash of the component map, generated at create() time.
|
|
156
|
-
component_map_hash: str = ""
|
|
158
|
+
component_map_hash: str = field(default="", is_javascript_property=False)
|
|
157
159
|
|
|
158
160
|
@classmethod
|
|
159
161
|
def create(cls, *children, **props) -> Component:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import Any, TypedDict, TypeVar
|
|
5
|
+
from typing import TYPE_CHECKING, Any, TypedDict, TypeVar
|
|
6
6
|
|
|
7
7
|
from reflex.components.component import Component, NoSSRComponent
|
|
8
8
|
from reflex.components.core.cond import color_mode_cond
|
|
@@ -17,8 +17,9 @@ try:
|
|
|
17
17
|
|
|
18
18
|
except ImportError:
|
|
19
19
|
console.warn("Plotly is not installed. Please run `pip install plotly`.")
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
if not TYPE_CHECKING:
|
|
21
|
+
Figure = Any
|
|
22
|
+
Template = Any
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
def _event_points_data_signature(e0: Var) -> tuple[Var[list[Point]]]:
|
|
@@ -78,13 +79,13 @@ class Plotly(NoSSRComponent):
|
|
|
78
79
|
is_default = True
|
|
79
80
|
|
|
80
81
|
# The figure to display. This can be a plotly figure or a plotly data json.
|
|
81
|
-
data: Var[Figure]
|
|
82
|
+
data: Var[Figure]
|
|
82
83
|
|
|
83
84
|
# The layout of the graph.
|
|
84
85
|
layout: Var[dict]
|
|
85
86
|
|
|
86
87
|
# The template for visual appearance of the graph.
|
|
87
|
-
template: Var[Template]
|
|
88
|
+
template: Var[Template]
|
|
88
89
|
|
|
89
90
|
# The config of the graph.
|
|
90
91
|
config: Var[dict]
|
|
@@ -213,6 +214,7 @@ const extractPoints = (points) => {
|
|
|
213
214
|
Returns:
|
|
214
215
|
The Plotly component.
|
|
215
216
|
"""
|
|
217
|
+
from plotly.graph_objs.layout import Template
|
|
216
218
|
from plotly.io import templates
|
|
217
219
|
|
|
218
220
|
responsive_template = color_mode_cond(
|
|
@@ -277,8 +279,12 @@ def dynamic_plotly_import(name: str, package: str) -> str:
|
|
|
277
279
|
Returns:
|
|
278
280
|
The dynamic import for the plotly component.
|
|
279
281
|
"""
|
|
282
|
+
library_import = f"import('{package}')"
|
|
283
|
+
mod_import = ".then((mod) => ({ default: createPlotlyComponent(mod) }))"
|
|
280
284
|
return f"""
|
|
281
|
-
const {name} =
|
|
285
|
+
const {name} = ClientSide(lazy(() =>
|
|
286
|
+
{library_import}{mod_import}
|
|
287
|
+
))
|
|
282
288
|
"""
|
|
283
289
|
|
|
284
290
|
|
|
@@ -19,7 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
from typing import Any, Literal, get_args
|
|
21
21
|
|
|
22
|
-
from reflex.components.component import BaseComponent
|
|
22
|
+
from reflex.components.component import BaseComponent, field
|
|
23
23
|
from reflex.components.core.cond import Cond, color_mode_cond, cond
|
|
24
24
|
from reflex.components.lucide.icon import Icon
|
|
25
25
|
from reflex.components.radix.themes.components.dropdown_menu import dropdown_menu
|
|
@@ -99,10 +99,12 @@ class ColorModeIconButton(IconButton):
|
|
|
99
99
|
"""Icon Button for toggling light / dark mode via toggle_color_mode."""
|
|
100
100
|
|
|
101
101
|
# The position of the icon button. Follow document flow if None.
|
|
102
|
-
position: LiteralPosition | Var[LiteralPosition] | None =
|
|
102
|
+
position: LiteralPosition | Var[LiteralPosition] | None = field(
|
|
103
|
+
default=None, is_javascript_property=False
|
|
104
|
+
)
|
|
103
105
|
|
|
104
106
|
# Allow picking the "system" value for the color mode.
|
|
105
|
-
allow_system: bool = False
|
|
107
|
+
allow_system: bool = field(default=False, is_javascript_property=False)
|
|
106
108
|
|
|
107
109
|
@classmethod
|
|
108
110
|
def create(
|
|
@@ -168,9 +170,6 @@ class ColorModeIconButton(IconButton):
|
|
|
168
170
|
**props,
|
|
169
171
|
)
|
|
170
172
|
|
|
171
|
-
def _exclude_props(self) -> list[str]:
|
|
172
|
-
return ["position", "allow_system"]
|
|
173
|
-
|
|
174
173
|
|
|
175
174
|
class ColorModeSwitch(Switch):
|
|
176
175
|
"""Switch for toggling light / dark mode via toggle_color_mode."""
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from collections.abc import Sequence
|
|
6
|
-
from typing import Any, ClassVar
|
|
6
|
+
from typing import Any, ClassVar, TypedDict
|
|
7
7
|
|
|
8
8
|
from reflex.constants import EventTriggers
|
|
9
9
|
from reflex.constants.colors import Color
|
|
@@ -663,6 +663,13 @@ class Reference(Recharts):
|
|
|
663
663
|
is_front: Var[bool]
|
|
664
664
|
|
|
665
665
|
|
|
666
|
+
class Segment(TypedDict):
|
|
667
|
+
"""A segment in a ReferenceLine or ReferenceArea."""
|
|
668
|
+
|
|
669
|
+
x: str | int
|
|
670
|
+
y: str | int
|
|
671
|
+
|
|
672
|
+
|
|
666
673
|
class ReferenceLine(Reference):
|
|
667
674
|
"""A ReferenceLine component in Recharts."""
|
|
668
675
|
|
|
@@ -686,7 +693,7 @@ class ReferenceLine(Reference):
|
|
|
686
693
|
_valid_children: ClassVar[list[str]] = ["Label"]
|
|
687
694
|
|
|
688
695
|
# Array of endpoints in { x, y } format. These endpoints would be used to draw the ReferenceLine.
|
|
689
|
-
segment: Sequence[
|
|
696
|
+
segment: Var[Sequence[Segment]]
|
|
690
697
|
|
|
691
698
|
|
|
692
699
|
class ReferenceDot(Reference):
|
reflex/constants/compiler.py
CHANGED
|
@@ -204,3 +204,10 @@ class SpecialAttributes(enum.Enum):
|
|
|
204
204
|
True if the attribute is special.
|
|
205
205
|
"""
|
|
206
206
|
return attr.startswith(SPECIAL_ATTRS)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class ResetStylesheet(SimpleNamespace):
|
|
210
|
+
"""Constants for CSS reset stylesheet."""
|
|
211
|
+
|
|
212
|
+
# The filename of the CSS reset file.
|
|
213
|
+
FILENAME = "__reflex_style_reset.css"
|
reflex/constants/route.py
CHANGED
|
@@ -41,23 +41,30 @@ class RouteRegex(SimpleNamespace):
|
|
|
41
41
|
_CLOSING_BRACKET = r"\]"
|
|
42
42
|
_ARG_NAME = r"[a-zA-Z_]\w*"
|
|
43
43
|
|
|
44
|
+
# The regex for a valid arg name, e.g. "slug" in "[slug]"
|
|
45
|
+
_ARG_NAME_PATTERN = re.compile(_ARG_NAME)
|
|
46
|
+
|
|
47
|
+
SLUG = re.compile(r"[a-zA-Z0-9_-]+")
|
|
44
48
|
# match a single arg (i.e. "[slug]"), returns the name of the arg
|
|
45
49
|
ARG = re.compile(rf"{_OPENING_BRACKET}({_ARG_NAME}){_CLOSING_BRACKET}")
|
|
46
|
-
# match a single
|
|
47
|
-
|
|
48
|
-
rf"
|
|
50
|
+
# match a single optional arg (i.e. "[[slug]]"), returns the name of the arg
|
|
51
|
+
OPTIONAL_ARG = re.compile(
|
|
52
|
+
rf"{_OPENING_BRACKET * 2}({_ARG_NAME}){_CLOSING_BRACKET * 2}"
|
|
49
53
|
)
|
|
54
|
+
|
|
50
55
|
# match a single non-optional catch-all arg (i.e. "[...slug]"), returns the name of the arg
|
|
51
56
|
STRICT_CATCHALL = re.compile(
|
|
52
57
|
rf"{_OPENING_BRACKET}{_DOT_DOT_DOT}({_ARG_NAME}){_CLOSING_BRACKET}"
|
|
53
58
|
)
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
|
|
60
|
+
# match a single optional catch-all arg (i.e. "[[...slug]]"), returns the name of the arg
|
|
61
|
+
OPTIONAL_CATCHALL = re.compile(
|
|
56
62
|
rf"{_OPENING_BRACKET * 2}{_DOT_DOT_DOT}({_ARG_NAME}){_CLOSING_BRACKET * 2}"
|
|
57
63
|
)
|
|
64
|
+
|
|
65
|
+
SPLAT_CATCHALL = "[[...splat]]"
|
|
58
66
|
SINGLE_SEGMENT = "__SINGLE_SEGMENT__"
|
|
59
67
|
DOUBLE_SEGMENT = "__DOUBLE_SEGMENT__"
|
|
60
|
-
SINGLE_CATCHALL_SEGMENT = "__SINGLE_CATCHALL_SEGMENT__"
|
|
61
68
|
DOUBLE_CATCHALL_SEGMENT = "__DOUBLE_CATCHALL_SEGMENT__"
|
|
62
69
|
|
|
63
70
|
|