Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions pyi_hashes.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"reflex/__init__.pyi": "b7b74375ddf026bb6005b6b8c31c11e5",
"reflex/components/__init__.pyi": "ab525d2518be848826c7cea553963e29",
"reflex/components/base/__init__.pyi": "c51852a7a25def994081676540665d74",
"reflex/__init__.pyi": "c69e4120c505941af8087b9c18df6121",
"reflex/components/__init__.pyi": "ac05995852baa81062ba3d18fbc489fb",
"reflex/components/base/__init__.pyi": "16e47bf19e0d62835a605baa3d039c5a",
"reflex/components/base/app_wrap.pyi": "ae600e2cc9d70f2ce613bdd6c1da3b16",
"reflex/components/base/body.pyi": "4a6aafea0bcd6304d778bf9f4e6f9380",
"reflex/components/base/document.pyi": "12cd9945bbd54623f7184dbe1adc2e50",
Expand All @@ -11,7 +11,7 @@
"reflex/components/base/meta.pyi": "907920173adfabfe1bd6b066032f4ef7",
"reflex/components/base/script.pyi": "43a0e21f257b10d2c76ed359284a9d80",
"reflex/components/base/strict_mode.pyi": "d169c575d676c73edc6a3f593badfd1f",
"reflex/components/core/__init__.pyi": "7ab6923c0e08c8990a1d2e8f7869ca38",
"reflex/components/core/__init__.pyi": "6419485660830fe0af9c9c5715a4ed03",
"reflex/components/core/auto_scroll.pyi": "c628ed503c7bfcee0dd05cf48d5b763d",
"reflex/components/core/banner.pyi": "407352aa1833b80b21d30647ec7717d8",
"reflex/components/core/client_side_routing.pyi": "c3d38a1de89cfcd76735a1559e99ed05",
Expand All @@ -21,13 +21,13 @@
"reflex/components/core/html.pyi": "faf9bb353ef4784e7f17ac97c93ef697",
"reflex/components/core/sticky.pyi": "cdf17e6cd287e7300acd25669701d117",
"reflex/components/core/upload.pyi": "f9be9b74d97d841b53b963d8704d5809",
"reflex/components/datadisplay/__init__.pyi": "87eae833026739e8d48a9891e185b3c2",
"reflex/components/datadisplay/__init__.pyi": "52755871369acbfd3a96b46b9a11d32e",
"reflex/components/datadisplay/code.pyi": "3787ca724cae7b29d57ea03f981b8c22",
"reflex/components/datadisplay/dataeditor.pyi": "23f777b8a46eff2afd95035dd5fc51a7",
"reflex/components/datadisplay/shiki_code_block.pyi": "0bf1ed97bcc4159df150d56d92d78945",
"reflex/components/el/__init__.pyi": "93bde7e3672a442bb2562a9b04d8b9d9",
"reflex/components/el/__init__.pyi": "8943a76f52fb5ed6a1c04b7cd31976f9",
"reflex/components/el/element.pyi": "7faa2cda13a04870d6c1cbfb4b3a2a0a",
"reflex/components/el/elements/__init__.pyi": "6f09637fb00205071f19eb73491d256f",
"reflex/components/el/elements/__init__.pyi": "5042206599bd3483a6b7a8f922f7c1f2",
"reflex/components/el/elements/base.pyi": "3fde62b5c749a40c43d1a4f0d0dccda4",
"reflex/components/el/elements/forms.pyi": "b7db5e64a2d0ee1a7ecf72cac927587a",
"reflex/components/el/elements/inline.pyi": "027f051369a253446365e77a4d621013",
Expand All @@ -43,18 +43,18 @@
"reflex/components/markdown/markdown.pyi": "a569dd6a60d67baebfc4d04cddf85020",
"reflex/components/moment/moment.pyi": "4ca29ae9cae720eb5c4955682e4cb7df",
"reflex/components/plotly/plotly.pyi": "1de86aa6881e59d4053206e62e3039c3",
"reflex/components/radix/__init__.pyi": "7dcf3869b498d6cbf3ec9986c9bf3c86",
"reflex/components/radix/primitives/__init__.pyi": "c7ee572acc52aef4441045d38449180f",
"reflex/components/radix/__init__.pyi": "5d8e3579912473e563676bfc71f29191",
"reflex/components/radix/primitives/__init__.pyi": "68fcf93acb9a40d94561d375a3a5fdb1",
"reflex/components/radix/primitives/accordion.pyi": "2df705ef76706119731b410165a0bdcd",
"reflex/components/radix/primitives/base.pyi": "bde318c8eb87aa3f63bd827493675da3",
"reflex/components/radix/primitives/drawer.pyi": "5ef31326d1f76956fd8eaafbc661f1a2",
"reflex/components/radix/primitives/form.pyi": "2461e70c7a114381bb7d1f609747cbeb",
"reflex/components/radix/primitives/progress.pyi": "da8ac90b79c3d4473a5dbf42347da437",
"reflex/components/radix/primitives/slider.pyi": "6dd3197c7d8e4af0a7de3e34ff940597",
"reflex/components/radix/themes/__init__.pyi": "55d491b7e1fe8aaa9ddc9834a819b54d",
"reflex/components/radix/themes/__init__.pyi": "582b4a7ead62b2ae8605e17fa084c063",
"reflex/components/radix/themes/base.pyi": "82885483a3aa7cc88d19b33d596d140a",
"reflex/components/radix/themes/color_mode.pyi": "889d9f492136a4600da3a79b9a3c5cc4",
"reflex/components/radix/themes/components/__init__.pyi": "4e70d106bc0aba70f2f23fc32c452aee",
"reflex/components/radix/themes/components/__init__.pyi": "efa279ee05479d7bb8a64d49da808d03",
"reflex/components/radix/themes/components/alert_dialog.pyi": "ccff6da776d85e93199982c59c412a6d",
"reflex/components/radix/themes/components/aspect_ratio.pyi": "b11efdce4c5954f6cbd2ba6f3b1b544c",
"reflex/components/radix/themes/components/avatar.pyi": "c6401c5525b3b7aa2ab5dafb7d306bb7",
Expand Down Expand Up @@ -90,7 +90,7 @@
"reflex/components/radix/themes/components/text_area.pyi": "cb1597d4f2a4f1812ba0c1372e808b87",
"reflex/components/radix/themes/components/text_field.pyi": "ea090c1e929d767b862c9147749c9e2f",
"reflex/components/radix/themes/components/tooltip.pyi": "2a10da7739f3cf60c60b326a81838492",
"reflex/components/radix/themes/layout/__init__.pyi": "9aee49e604d9febebaa49335e064df32",
"reflex/components/radix/themes/layout/__init__.pyi": "73eefc509a49215b1797b5b5d28d035e",
"reflex/components/radix/themes/layout/base.pyi": "fec7f00ba5448f7cada8fa4ae28403da",
"reflex/components/radix/themes/layout/box.pyi": "95326bca509849128ecde69d653968fe",
"reflex/components/radix/themes/layout/center.pyi": "b21a1394b46e7142abc933902d925e2b",
Expand All @@ -101,16 +101,17 @@
"reflex/components/radix/themes/layout/section.pyi": "f88c212a5f3ab7880b727a6b4ad6418c",
"reflex/components/radix/themes/layout/spacer.pyi": "da6f92eb2c7dd2d32aa409fa8f0527cd",
"reflex/components/radix/themes/layout/stack.pyi": "68e1f6403542fbbaa746ea3e2304acbf",
"reflex/components/radix/themes/typography/__init__.pyi": "0fe45264500d8ac5ff0bff761f1c52ae",
"reflex/components/radix/themes/typography/__init__.pyi": "b8ef970530397e9984004961f3aaee62",
"reflex/components/radix/themes/typography/blockquote.pyi": "a45375020023f67f0085f02d81186101",
"reflex/components/radix/themes/typography/code.pyi": "91ca62acae8bbfeb9ae812b26ac7f1be",
"reflex/components/radix/themes/typography/heading.pyi": "7f348dfa49a3b4b51bbb8e7461d0dd50",
"reflex/components/radix/themes/typography/link.pyi": "d9d1e3d5c168d830df25741ac3ec7f38",
"reflex/components/radix/themes/typography/link.pyi": "f9e94bef5a15d8ee359c5ac32b828619",
"reflex/components/radix/themes/typography/text.pyi": "2c648e168c7c32fbfe2388a93767b168",
"reflex/components/react_player/audio.pyi": "1ba0b1c8756676375abd2248f2bf3807",
"reflex/components/react_player/react_player.pyi": "a9917f411d51cf3f291758c9e01844f2",
"reflex/components/react_player/video.pyi": "ef4730235b7902fa30859083db8b36a3",
"reflex/components/recharts/__init__.pyi": "ea5b61055f486497a1901429a9d7c9df",
"reflex/components/react_router/dom.pyi": "b7341095353c7b494252377368419351",
"reflex/components/recharts/__init__.pyi": "6ee7f1ca2c0912f389ba6f3251a74d99",
"reflex/components/recharts/cartesian.pyi": "38f884467330e1c6e2e9e435ac4ac754",
"reflex/components/recharts/charts.pyi": "58415e7aef3d4642494e1b86aea6589c",
"reflex/components/recharts/general.pyi": "a12735989ef36152b2dab954604953c0",
Expand Down
1 change: 1 addition & 0 deletions reflex/components/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"plotly",
"radix",
"react_player",
"react_router",
"sonner",
"el",
"base",
Expand Down
8 changes: 7 additions & 1 deletion reflex/components/el/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@

_SUBMODULES: set[str] = {"elements"}
_SUBMOD_ATTRS: dict[str, list[str]] = {
f"elements.{k}": v for k, v in elements._MAPPING.items()
# rx.el.a is replaced by React Router's Link.
f"elements.{k}": [_v for _v in v if _v != "a"]
for k, v in elements._MAPPING.items()
}
_EXTRA_MAPPINGS: dict[str, str] = {
"a": "reflex.components.react_router.link",
}

__getattr__, __dir__, __all__ = lazy_loader.attach(
__name__,
submodules=_SUBMODULES,
submod_attrs=_SUBMOD_ATTRS,
**_EXTRA_MAPPINGS,
)
31 changes: 1 addition & 30 deletions reflex/components/radix/themes/typography/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,43 +14,14 @@
from reflex.components.el.elements.inline import A
from reflex.components.markdown.markdown import MarkdownComponentMap
from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent
from reflex.components.react_router.dom import ReactRouterLink
from reflex.utils.imports import ImportDict, ImportVar
from reflex.vars.base import Var

from .base import LiteralTextSize, LiteralTextTrim, LiteralTextWeight

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

LiteralLinkDiscover = Literal["none", "render"]


class ReactRouterLink(A):
"""Links are accessible elements used primarily for navigation. This component is styled to resemble a hyperlink and semantically renders an <a>."""

library = "react-router"

tag = "Link"

alias = "ReactRouterLink"

# The page to link to.
to: Var[str]

# Replaces the current entry in the history stack instead of pushing a new one onto it.
replace: Var[bool]

# 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>).
reload_document: Var[bool]

# 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.
prevent_scroll_reset: Var[bool]

# Defines the link discovery behavior
discover: Var[LiteralLinkDiscover]

# Enables a View Transition for this navigation.
view_transition: Var[bool]


_KNOWN_REACT_ROUTER_LINK_PROPS = frozenset(ReactRouterLink.get_props())

Expand Down
5 changes: 5 additions & 0 deletions reflex/components/react_router/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""React-router internal components."""

from .dom import ReactRouterLink

link = ReactRouterLink.create
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding type hints here for better IDE support and type safety: link: Callable[..., Component] = ReactRouterLink.create

69 changes: 69 additions & 0 deletions reflex/components/react_router/dom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""Components for client side navigation within React Router applications."""

from __future__ import annotations

from typing import ClassVar, Literal, TypedDict

from reflex.components.el.elements.inline import A
from reflex.vars.base import Var

LiteralLinkDiscover = Literal["none", "render"]


class To(TypedDict):
"""Structured object for navigating via the `to` prop."""

# A URL pathname, beginning with a /
pathname: str

# A URL search string, beginning with a ?.
search: str

# A URL fragment identifier, beginning with a #.
hash: str


class ReactRouterLink(A):
"""Links are accessible elements used primarily for navigation. This component is styled to resemble a hyperlink and semantically renders an <a>."""

library = "react-router"

tag = "Link"

alias = "ReactRouterLink"

# The page to link to.
to: Var[str | To]

# Replaces the current entry in the history stack instead of pushing a new one onto it.
replace: Var[bool]

# 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>).
reload_document: Var[bool]

# 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.
prevent_scroll_reset: Var[bool]

# Defines the link discovery behavior
discover: Var[LiteralLinkDiscover]

# Enables a View Transition for this navigation.
view_transition: Var[bool]

@classmethod
def create(cls, *children, **props):
"""Create a ReactRouterLink component for client-side navigation.

Args:
*children: The children of the component.
**props: The props of the component.

Returns:
The ReactRouterLink component.
"""
# React Router special behavior is triggered on the `to` prop, not href.
if "to" not in props and "href" in props:
props["to"] = props.pop("href")
return super().create(*children, **props)

_invalid_children: ClassVar[list[str]] = ["A", "ReactRouterLink"]
8 changes: 7 additions & 1 deletion reflex/utils/lazy_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def attach(
package_name: str,
submodules: set[str] | None = None,
submod_attrs: dict[str, list[str]] | None = None,
**extra_mappings,
):
"""Replaces a package's __getattr__, __dir__, and __all__ attributes using lazy.attach.
The lazy loader __getattr__ doesn't support tuples as list values. We needed to add
Expand All @@ -39,6 +40,7 @@ def attach(
submodules : List of submodules to attach.
submod_attrs : Dictionary of submodule -> list of attributes / functions.
These attributes are imported as they are used.
extra_mappings: Additional mappings to resolve lazily.

Returns:
__getattr__, __dir__, __all__
Expand All @@ -60,9 +62,13 @@ def attach(
attr: mod for mod, attrs in submod_attrs.items() for attr in attrs
}

__all__ = sorted(submodules | attr_to_modules.keys())
__all__ = sorted([*(submodules | attr_to_modules.keys()), *(extra_mappings or [])])

def __getattr__(name: str): # noqa: N807
if name in extra_mappings:
submod_path, attr = extra_mappings[name].rsplit(".", 1)
submod = importlib.import_module(submod_path)
return getattr(submod, attr)
if name in submodules:
return importlib.import_module(f"{package_name}.{name}")
if name in attr_to_modules:
Expand Down
19 changes: 17 additions & 2 deletions reflex/utils/pyi_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1108,11 +1108,13 @@ def _get_init_lazy_imports(self, mod: tuple | ModuleType, new_tree: ast.AST):
sub_mod_attrs: dict[str, list[str | tuple[str, str]]] | None = getattr(
mod, "_SUBMOD_ATTRS", None
)
extra_mappings: dict[str, str] | None = getattr(mod, "_EXTRA_MAPPINGS", None)

if not sub_mods and not sub_mod_attrs:
if not sub_mods and not sub_mod_attrs and not extra_mappings:
return None
sub_mods_imports = []
sub_mod_attrs_imports = []
extra_mappings_imports = []

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

text = "\n" + "\n".join([*sub_mods_imports, *sub_mod_attrs_imports])
if extra_mappings:
for alias, import_path in extra_mappings.items():
module_name, import_name = import_path.rsplit(".", 1)
extra_mappings_imports.append(
f"from {module_name} import {import_name} as {alias}"
)

text = (
"\n"
+ "\n".join(
[*sub_mods_imports, *sub_mod_attrs_imports, *extra_mappings_imports]
)
+ "\n"
)
text += ast.unparse(new_tree) + "\n\n"
text += f"__all__ = {getattr(mod, '__all__', [])!r}\n"
return text
Expand Down
Loading
Loading