|  | 
| 2 | 2 | 
 | 
| 3 | 3 | from pathlib import Path | 
| 4 | 4 | from typing import Any | 
|  | 5 | +from urllib.parse import urljoin | 
| 5 | 6 | from uuid import uuid4 | 
| 6 | 7 | 
 | 
| 7 |  | -from reactpy import component, html | 
|  | 8 | +from reactpy import component, event, html, use_connection | 
| 8 | 9 | from reactpy.backend.types import Location | 
| 9 | 10 | from reactpy.core.component import Component | 
| 10 | 11 | from reactpy.core.types import VdomDict | 
|  | 
| 25 | 26 | ) | 
| 26 | 27 | """Client-side portion of link handling""" | 
| 27 | 28 | 
 | 
|  | 29 | +link_js_content = (Path(__file__).parent / "static" / "link.js").read_text(encoding="utf-8") | 
|  | 30 | + | 
| 28 | 31 | 
 | 
| 29 | 32 | def link(attributes: dict[str, Any], *children: Any) -> Component: | 
| 30 | 33 |     """Create a link with the given attributes and children.""" | 
| @@ -55,10 +58,42 @@ def _link(attributes: dict[str, Any], *children: Any) -> VdomDict: | 
| 55 | 58 |         "className": class_name, | 
| 56 | 59 |     } | 
| 57 | 60 | 
 | 
|  | 61 | +    # FIXME: This component currently works in a "dumb" way by trusting that ReactPy's script tag \ | 
|  | 62 | +    # properly sets the location due to bugs in ReactPy rendering. | 
|  | 63 | +    # https://github.com/reactive-python/reactpy/pull/1224 | 
|  | 64 | +    current_path = use_connection().location.pathname | 
|  | 65 | + | 
|  | 66 | +    @event(prevent_default=True) | 
| 58 | 67 |     def on_click(_event: dict[str, Any]) -> None: | 
| 59 |  | -        set_location(Location(**_event)) | 
|  | 68 | +        pathname, search = to.split("?", 1) if "?" in to else (to, "") | 
|  | 69 | +        if search: | 
|  | 70 | +            search = f"?{search}" | 
|  | 71 | + | 
|  | 72 | +        # Resolve relative paths that match `../foo` | 
|  | 73 | +        if pathname.startswith("../"): | 
|  | 74 | +            pathname = urljoin(current_path, pathname) | 
|  | 75 | + | 
|  | 76 | +        # Resolve relative paths that match `foo` | 
|  | 77 | +        if not pathname.startswith("/"): | 
|  | 78 | +            pathname = urljoin(current_path, pathname) | 
|  | 79 | + | 
|  | 80 | +        # Resolve relative paths that match `/foo/../bar` | 
|  | 81 | +        while "/../" in pathname: | 
|  | 82 | +            part_1, part_2 = pathname.split("/../", 1) | 
|  | 83 | +            pathname = urljoin(f"{part_1}/", f"../{part_2}") | 
|  | 84 | + | 
|  | 85 | +        # Resolve relative paths that match `foo/./bar` | 
|  | 86 | +        pathname = pathname.replace("/./", "/") | 
|  | 87 | + | 
|  | 88 | +        set_location(Location(pathname, search)) | 
|  | 89 | + | 
|  | 90 | +    attrs["onClick"] = on_click | 
|  | 91 | + | 
|  | 92 | +    return html._(html.a(attrs, *children), html.script(link_js_content.replace("UUID", uuid_string))) | 
| 60 | 93 | 
 | 
| 61 |  | -    return html._(html.a(attrs, *children), Link({"onClick": on_click, "linkClass": uuid_string})) | 
|  | 94 | +    # def on_click(_event: dict[str, Any]) -> None: | 
|  | 95 | +    #     set_location(Location(**_event)) | 
|  | 96 | +    # return html._(html.a(attrs, *children), Link({"onClick": on_click, "linkClass": uuid_string})) | 
| 62 | 97 | 
 | 
| 63 | 98 | 
 | 
| 64 | 99 | def route(path: str, element: Any | None, *routes: Route) -> Route: | 
|  | 
0 commit comments