Skip to content

Commit 74a938e

Browse files
committed
Merge remote-tracking branch 'origin/main' into release/reflex-0.8.1
2 parents f27aeac + 19e54dd commit 74a938e

File tree

10 files changed

+131
-57
lines changed

10 files changed

+131
-57
lines changed

pyi_hashes.json

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"reflex/__init__.pyi": "b7b74375ddf026bb6005b6b8c31c11e5",
3-
"reflex/components/__init__.pyi": "ab525d2518be848826c7cea553963e29",
4-
"reflex/components/base/__init__.pyi": "c51852a7a25def994081676540665d74",
2+
"reflex/__init__.pyi": "c69e4120c505941af8087b9c18df6121",
3+
"reflex/components/__init__.pyi": "ac05995852baa81062ba3d18fbc489fb",
4+
"reflex/components/base/__init__.pyi": "16e47bf19e0d62835a605baa3d039c5a",
55
"reflex/components/base/app_wrap.pyi": "ae600e2cc9d70f2ce613bdd6c1da3b16",
66
"reflex/components/base/body.pyi": "4a6aafea0bcd6304d778bf9f4e6f9380",
77
"reflex/components/base/document.pyi": "12cd9945bbd54623f7184dbe1adc2e50",
@@ -11,7 +11,7 @@
1111
"reflex/components/base/meta.pyi": "907920173adfabfe1bd6b066032f4ef7",
1212
"reflex/components/base/script.pyi": "43a0e21f257b10d2c76ed359284a9d80",
1313
"reflex/components/base/strict_mode.pyi": "d169c575d676c73edc6a3f593badfd1f",
14-
"reflex/components/core/__init__.pyi": "7ab6923c0e08c8990a1d2e8f7869ca38",
14+
"reflex/components/core/__init__.pyi": "6419485660830fe0af9c9c5715a4ed03",
1515
"reflex/components/core/auto_scroll.pyi": "c628ed503c7bfcee0dd05cf48d5b763d",
1616
"reflex/components/core/banner.pyi": "407352aa1833b80b21d30647ec7717d8",
1717
"reflex/components/core/client_side_routing.pyi": "c3d38a1de89cfcd76735a1559e99ed05",
@@ -20,14 +20,14 @@
2020
"reflex/components/core/helmet.pyi": "20afb0edbadb506d5cf374f153b3c6cf",
2121
"reflex/components/core/html.pyi": "faf9bb353ef4784e7f17ac97c93ef697",
2222
"reflex/components/core/sticky.pyi": "cdf17e6cd287e7300acd25669701d117",
23-
"reflex/components/core/upload.pyi": "4a44f34dc8e9450a383ee60a87a69e2c",
24-
"reflex/components/datadisplay/__init__.pyi": "87eae833026739e8d48a9891e185b3c2",
23+
"reflex/components/core/upload.pyi": "f9be9b74d97d841b53b963d8704d5809",
24+
"reflex/components/datadisplay/__init__.pyi": "52755871369acbfd3a96b46b9a11d32e",
2525
"reflex/components/datadisplay/code.pyi": "3787ca724cae7b29d57ea03f981b8c22",
2626
"reflex/components/datadisplay/dataeditor.pyi": "23f777b8a46eff2afd95035dd5fc51a7",
2727
"reflex/components/datadisplay/shiki_code_block.pyi": "0bf1ed97bcc4159df150d56d92d78945",
28-
"reflex/components/el/__init__.pyi": "93bde7e3672a442bb2562a9b04d8b9d9",
28+
"reflex/components/el/__init__.pyi": "8943a76f52fb5ed6a1c04b7cd31976f9",
2929
"reflex/components/el/element.pyi": "7faa2cda13a04870d6c1cbfb4b3a2a0a",
30-
"reflex/components/el/elements/__init__.pyi": "6f09637fb00205071f19eb73491d256f",
30+
"reflex/components/el/elements/__init__.pyi": "5042206599bd3483a6b7a8f922f7c1f2",
3131
"reflex/components/el/elements/base.pyi": "3fde62b5c749a40c43d1a4f0d0dccda4",
3232
"reflex/components/el/elements/forms.pyi": "b7db5e64a2d0ee1a7ecf72cac927587a",
3333
"reflex/components/el/elements/inline.pyi": "027f051369a253446365e77a4d621013",
@@ -43,18 +43,18 @@
4343
"reflex/components/markdown/markdown.pyi": "a569dd6a60d67baebfc4d04cddf85020",
4444
"reflex/components/moment/moment.pyi": "4ca29ae9cae720eb5c4955682e4cb7df",
4545
"reflex/components/plotly/plotly.pyi": "1de86aa6881e59d4053206e62e3039c3",
46-
"reflex/components/radix/__init__.pyi": "7dcf3869b498d6cbf3ec9986c9bf3c86",
47-
"reflex/components/radix/primitives/__init__.pyi": "c7ee572acc52aef4441045d38449180f",
46+
"reflex/components/radix/__init__.pyi": "5d8e3579912473e563676bfc71f29191",
47+
"reflex/components/radix/primitives/__init__.pyi": "68fcf93acb9a40d94561d375a3a5fdb1",
4848
"reflex/components/radix/primitives/accordion.pyi": "2df705ef76706119731b410165a0bdcd",
4949
"reflex/components/radix/primitives/base.pyi": "bde318c8eb87aa3f63bd827493675da3",
5050
"reflex/components/radix/primitives/drawer.pyi": "5ef31326d1f76956fd8eaafbc661f1a2",
5151
"reflex/components/radix/primitives/form.pyi": "2461e70c7a114381bb7d1f609747cbeb",
5252
"reflex/components/radix/primitives/progress.pyi": "da8ac90b79c3d4473a5dbf42347da437",
5353
"reflex/components/radix/primitives/slider.pyi": "6dd3197c7d8e4af0a7de3e34ff940597",
54-
"reflex/components/radix/themes/__init__.pyi": "55d491b7e1fe8aaa9ddc9834a819b54d",
54+
"reflex/components/radix/themes/__init__.pyi": "582b4a7ead62b2ae8605e17fa084c063",
5555
"reflex/components/radix/themes/base.pyi": "82885483a3aa7cc88d19b33d596d140a",
5656
"reflex/components/radix/themes/color_mode.pyi": "889d9f492136a4600da3a79b9a3c5cc4",
57-
"reflex/components/radix/themes/components/__init__.pyi": "4e70d106bc0aba70f2f23fc32c452aee",
57+
"reflex/components/radix/themes/components/__init__.pyi": "efa279ee05479d7bb8a64d49da808d03",
5858
"reflex/components/radix/themes/components/alert_dialog.pyi": "ccff6da776d85e93199982c59c412a6d",
5959
"reflex/components/radix/themes/components/aspect_ratio.pyi": "b11efdce4c5954f6cbd2ba6f3b1b544c",
6060
"reflex/components/radix/themes/components/avatar.pyi": "c6401c5525b3b7aa2ab5dafb7d306bb7",
@@ -90,7 +90,7 @@
9090
"reflex/components/radix/themes/components/text_area.pyi": "cb1597d4f2a4f1812ba0c1372e808b87",
9191
"reflex/components/radix/themes/components/text_field.pyi": "ea090c1e929d767b862c9147749c9e2f",
9292
"reflex/components/radix/themes/components/tooltip.pyi": "2a10da7739f3cf60c60b326a81838492",
93-
"reflex/components/radix/themes/layout/__init__.pyi": "9aee49e604d9febebaa49335e064df32",
93+
"reflex/components/radix/themes/layout/__init__.pyi": "73eefc509a49215b1797b5b5d28d035e",
9494
"reflex/components/radix/themes/layout/base.pyi": "fec7f00ba5448f7cada8fa4ae28403da",
9595
"reflex/components/radix/themes/layout/box.pyi": "95326bca509849128ecde69d653968fe",
9696
"reflex/components/radix/themes/layout/center.pyi": "b21a1394b46e7142abc933902d925e2b",
@@ -101,16 +101,17 @@
101101
"reflex/components/radix/themes/layout/section.pyi": "f88c212a5f3ab7880b727a6b4ad6418c",
102102
"reflex/components/radix/themes/layout/spacer.pyi": "da6f92eb2c7dd2d32aa409fa8f0527cd",
103103
"reflex/components/radix/themes/layout/stack.pyi": "68e1f6403542fbbaa746ea3e2304acbf",
104-
"reflex/components/radix/themes/typography/__init__.pyi": "0fe45264500d8ac5ff0bff761f1c52ae",
104+
"reflex/components/radix/themes/typography/__init__.pyi": "b8ef970530397e9984004961f3aaee62",
105105
"reflex/components/radix/themes/typography/blockquote.pyi": "a45375020023f67f0085f02d81186101",
106106
"reflex/components/radix/themes/typography/code.pyi": "91ca62acae8bbfeb9ae812b26ac7f1be",
107107
"reflex/components/radix/themes/typography/heading.pyi": "7f348dfa49a3b4b51bbb8e7461d0dd50",
108-
"reflex/components/radix/themes/typography/link.pyi": "d9d1e3d5c168d830df25741ac3ec7f38",
108+
"reflex/components/radix/themes/typography/link.pyi": "f9e94bef5a15d8ee359c5ac32b828619",
109109
"reflex/components/radix/themes/typography/text.pyi": "2c648e168c7c32fbfe2388a93767b168",
110110
"reflex/components/react_player/audio.pyi": "1ba0b1c8756676375abd2248f2bf3807",
111111
"reflex/components/react_player/react_player.pyi": "a9917f411d51cf3f291758c9e01844f2",
112112
"reflex/components/react_player/video.pyi": "ef4730235b7902fa30859083db8b36a3",
113-
"reflex/components/recharts/__init__.pyi": "ea5b61055f486497a1901429a9d7c9df",
113+
"reflex/components/react_router/dom.pyi": "b7341095353c7b494252377368419351",
114+
"reflex/components/recharts/__init__.pyi": "6ee7f1ca2c0912f389ba6f3251a74d99",
114115
"reflex/components/recharts/cartesian.pyi": "38f884467330e1c6e2e9e435ac4ac754",
115116
"reflex/components/recharts/charts.pyi": "58415e7aef3d4642494e1b86aea6589c",
116117
"reflex/components/recharts/general.pyi": "a12735989ef36152b2dab954604953c0",

reflex/components/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"plotly",
1515
"radix",
1616
"react_player",
17+
"react_router",
1718
"sonner",
1819
"el",
1920
"base",

reflex/components/core/upload.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
)
5151

5252

53-
def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var:
53+
def upload_file(id_: str | Var[str] = DEFAULT_UPLOAD_ID) -> Var:
5454
"""Get the file upload drop trigger.
5555
5656
This var is passed to the dropzone component to update the file list when a
@@ -62,7 +62,7 @@ def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var:
6262
Returns:
6363
A var referencing the file upload drop trigger.
6464
"""
65-
id_var = LiteralStringVar.create(id_)
65+
id_var = LiteralStringVar.create(id_) if not isinstance(id_, Var) else id_
6666
var_name = f"""e => setFilesById(filesById => {{
6767
const updatedFilesById = Object.assign({{}}, filesById);
6868
updatedFilesById[{id_var!s}] = e;
@@ -79,7 +79,7 @@ def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var:
7979
)
8080

8181

82-
def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> Var:
82+
def selected_files(id_: str | Var[str] = DEFAULT_UPLOAD_ID) -> Var:
8383
"""Get the list of selected files.
8484
8585
Args:
@@ -88,9 +88,9 @@ def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> Var:
8888
Returns:
8989
A var referencing the list of selected file paths.
9090
"""
91-
id_var = LiteralStringVar.create(id_)
91+
id_var = LiteralStringVar.create(id_) if not isinstance(id_, Var) else id_
9292
return Var(
93-
_js_expr=f"(filesById[{id_var!s}] ? filesById[{id_var!s}].map((f) => (f.path || f.name)) : [])",
93+
_js_expr=f"(filesById[{id_var!s}] ? filesById[{id_var!s}].map((f) => f.name) : [])",
9494
_var_type=list[str],
9595
_var_data=VarData.merge(
9696
upload_files_context_var_data, id_var._get_all_var_data()

reflex/components/el/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@
88

99
_SUBMODULES: set[str] = {"elements"}
1010
_SUBMOD_ATTRS: dict[str, list[str]] = {
11-
f"elements.{k}": v for k, v in elements._MAPPING.items()
11+
# rx.el.a is replaced by React Router's Link.
12+
f"elements.{k}": [_v for _v in v if _v != "a"]
13+
for k, v in elements._MAPPING.items()
14+
}
15+
_EXTRA_MAPPINGS: dict[str, str] = {
16+
"a": "reflex.components.react_router.link",
1217
}
1318

1419
__getattr__, __dir__, __all__ = lazy_loader.attach(
1520
__name__,
1621
submodules=_SUBMODULES,
1722
submod_attrs=_SUBMOD_ATTRS,
23+
**_EXTRA_MAPPINGS,
1824
)

reflex/components/radix/themes/typography/link.py

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,43 +14,14 @@
1414
from reflex.components.el.elements.inline import A
1515
from reflex.components.markdown.markdown import MarkdownComponentMap
1616
from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent
17+
from reflex.components.react_router.dom import ReactRouterLink
1718
from reflex.utils.imports import ImportDict, ImportVar
1819
from reflex.vars.base import Var
1920

2021
from .base import LiteralTextSize, LiteralTextTrim, LiteralTextWeight
2122

2223
LiteralLinkUnderline = Literal["auto", "hover", "always", "none"]
2324

24-
LiteralLinkDiscover = Literal["none", "render"]
25-
26-
27-
class ReactRouterLink(A):
28-
"""Links are accessible elements used primarily for navigation. This component is styled to resemble a hyperlink and semantically renders an <a>."""
29-
30-
library = "react-router"
31-
32-
tag = "Link"
33-
34-
alias = "ReactRouterLink"
35-
36-
# The page to link to.
37-
to: Var[str]
38-
39-
# Replaces the current entry in the history stack instead of pushing a new one onto it.
40-
replace: Var[bool]
41-
42-
# Will use document navigation instead of client side routing when the link is clicked: the browser will handle the transition normally (as if it were an <a href>).
43-
reload_document: Var[bool]
44-
45-
# Prevents the scroll position from being reset to the top of the window when the link is clicked and the app is using ScrollRestoration. This only prevents new locations resetting scroll to the top, scroll position will be restored for back/forward button navigation.
46-
prevent_scroll_reset: Var[bool]
47-
48-
# Defines the link discovery behavior
49-
discover: Var[LiteralLinkDiscover]
50-
51-
# Enables a View Transition for this navigation.
52-
view_transition: Var[bool]
53-
5425

5526
_KNOWN_REACT_ROUTER_LINK_PROPS = frozenset(ReactRouterLink.get_props())
5627

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""React-router internal components."""
2+
3+
from .dom import ReactRouterLink
4+
5+
link = ReactRouterLink.create
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""Components for client side navigation within React Router applications."""
2+
3+
from __future__ import annotations
4+
5+
from typing import ClassVar, Literal, TypedDict
6+
7+
from reflex.components.el.elements.inline import A
8+
from reflex.vars.base import Var
9+
10+
LiteralLinkDiscover = Literal["none", "render"]
11+
12+
13+
class To(TypedDict):
14+
"""Structured object for navigating via the `to` prop."""
15+
16+
# A URL pathname, beginning with a /
17+
pathname: str
18+
19+
# A URL search string, beginning with a ?.
20+
search: str
21+
22+
# A URL fragment identifier, beginning with a #.
23+
hash: str
24+
25+
26+
class ReactRouterLink(A):
27+
"""Links are accessible elements used primarily for navigation. This component is styled to resemble a hyperlink and semantically renders an <a>."""
28+
29+
library = "react-router"
30+
31+
tag = "Link"
32+
33+
alias = "ReactRouterLink"
34+
35+
# The page to link to.
36+
to: Var[str | To]
37+
38+
# Replaces the current entry in the history stack instead of pushing a new one onto it.
39+
replace: Var[bool]
40+
41+
# Will use document navigation instead of client side routing when the link is clicked: the browser will handle the transition normally (as if it were an <a href>).
42+
reload_document: Var[bool]
43+
44+
# Prevents the scroll position from being reset to the top of the window when the link is clicked and the app is using ScrollRestoration. This only prevents new locations resetting scroll to the top, scroll position will be restored for back/forward button navigation.
45+
prevent_scroll_reset: Var[bool]
46+
47+
# Defines the link discovery behavior
48+
discover: Var[LiteralLinkDiscover]
49+
50+
# Enables a View Transition for this navigation.
51+
view_transition: Var[bool]
52+
53+
@classmethod
54+
def create(cls, *children, **props):
55+
"""Create a ReactRouterLink component for client-side navigation.
56+
57+
Args:
58+
*children: The children of the component.
59+
**props: The props of the component.
60+
61+
Returns:
62+
The ReactRouterLink component.
63+
"""
64+
# React Router special behavior is triggered on the `to` prop, not href.
65+
if "to" not in props and "href" in props:
66+
props["to"] = props.pop("href")
67+
return super().create(*children, **props)
68+
69+
_invalid_children: ClassVar[list[str]] = ["A", "ReactRouterLink"]

reflex/utils/lazy_loader.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def attach(
2727
package_name: str,
2828
submodules: set[str] | None = None,
2929
submod_attrs: dict[str, list[str]] | None = None,
30+
**extra_mappings,
3031
):
3132
"""Replaces a package's __getattr__, __dir__, and __all__ attributes using lazy.attach.
3233
The lazy loader __getattr__ doesn't support tuples as list values. We needed to add
@@ -39,6 +40,7 @@ def attach(
3940
submodules : List of submodules to attach.
4041
submod_attrs : Dictionary of submodule -> list of attributes / functions.
4142
These attributes are imported as they are used.
43+
extra_mappings: Additional mappings to resolve lazily.
4244
4345
Returns:
4446
__getattr__, __dir__, __all__
@@ -60,9 +62,13 @@ def attach(
6062
attr: mod for mod, attrs in submod_attrs.items() for attr in attrs
6163
}
6264

63-
__all__ = sorted(submodules | attr_to_modules.keys())
65+
__all__ = sorted([*(submodules | attr_to_modules.keys()), *(extra_mappings or [])])
6466

6567
def __getattr__(name: str): # noqa: N807
68+
if name in extra_mappings:
69+
submod_path, attr = extra_mappings[name].rsplit(".", 1)
70+
submod = importlib.import_module(submod_path)
71+
return getattr(submod, attr)
6672
if name in submodules:
6773
return importlib.import_module(f"{package_name}.{name}")
6874
if name in attr_to_modules:

reflex/utils/pyi_generator.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,11 +1108,13 @@ def _get_init_lazy_imports(self, mod: tuple | ModuleType, new_tree: ast.AST):
11081108
sub_mod_attrs: dict[str, list[str | tuple[str, str]]] | None = getattr(
11091109
mod, "_SUBMOD_ATTRS", None
11101110
)
1111+
extra_mappings: dict[str, str] | None = getattr(mod, "_EXTRA_MAPPINGS", None)
11111112

1112-
if not sub_mods and not sub_mod_attrs:
1113+
if not sub_mods and not sub_mod_attrs and not extra_mappings:
11131114
return None
11141115
sub_mods_imports = []
11151116
sub_mod_attrs_imports = []
1117+
extra_mappings_imports = []
11161118

11171119
if sub_mods:
11181120
sub_mods_imports = [f"from . import {mod}" for mod in sorted(sub_mods)]
@@ -1140,7 +1142,20 @@ def _get_init_lazy_imports(self, mod: tuple | ModuleType, new_tree: ast.AST):
11401142
]
11411143
sub_mod_attrs_imports.append("")
11421144

1143-
text = "\n" + "\n".join([*sub_mods_imports, *sub_mod_attrs_imports])
1145+
if extra_mappings:
1146+
for alias, import_path in extra_mappings.items():
1147+
module_name, import_name = import_path.rsplit(".", 1)
1148+
extra_mappings_imports.append(
1149+
f"from {module_name} import {import_name} as {alias}"
1150+
)
1151+
1152+
text = (
1153+
"\n"
1154+
+ "\n".join(
1155+
[*sub_mods_imports, *sub_mod_attrs_imports, *extra_mappings_imports]
1156+
)
1157+
+ "\n"
1158+
)
11441159
text += ast.unparse(new_tree) + "\n\n"
11451160
text += f"__all__ = {getattr(mod, '__all__', [])!r}\n"
11461161
return text

0 commit comments

Comments
 (0)