|
2 | 2 |
|
3 | 3 | from pathlib import Path
|
4 | 4 | from typing import Any
|
5 |
| -from urllib.parse import urljoin |
6 | 5 | from uuid import uuid4
|
7 | 6 |
|
8 |
| -from reactpy import component, event, html, use_connection |
| 7 | +from reactpy import component, html |
9 | 8 | from reactpy.backend.types import Location
|
10 |
| -from reactpy.core.types import VdomChild, VdomDict |
| 9 | +from reactpy.core.types import VdomDict |
11 | 10 | from reactpy.web.module import export, module_from_file
|
12 | 11 |
|
13 | 12 | from reactpy_router.hooks import _use_route_state
|
|
17 | 16 | module_from_file("reactpy-router", file=Path(__file__).parent / "static" / "bundle.js"),
|
18 | 17 | ("History"),
|
19 | 18 | )
|
20 |
| -link_js_content = (Path(__file__).parent / "static" / "link.js").read_text(encoding="utf-8") |
| 19 | +Link = export( |
| 20 | + module_from_file("reactpy-router", file=Path(__file__).parent / "static" / "bundle.js"), |
| 21 | + ("Link"), |
| 22 | +) |
21 | 23 |
|
22 | 24 |
|
23 | 25 | @component
|
24 |
| -def link(*children: VdomChild, to: str, **attributes: Any) -> VdomDict: |
| 26 | +def link(*attributes_and_children: Any, to: str | None = None, **kwargs: Any) -> VdomDict: |
25 | 27 | """A component that renders a link to the given path."""
|
26 |
| - # FIXME: This currently works in a "dumb" way by trusting that ReactPy's script tag \ |
27 |
| - # properly sets the location. When a client-server communication layer is added to a \ |
28 |
| - # future ReactPy release, this component will need to be rewritten to use that instead. \ |
29 |
| - set_location = _use_route_state().set_location |
30 |
| - current_path = use_connection().location.pathname |
31 |
| - |
32 |
| - @event(prevent_default=True) |
33 |
| - def on_click(_event: dict[str, Any]) -> None: |
34 |
| - pathname, search = to.split("?", 1) if "?" in to else (to, "") |
35 |
| - if search: |
36 |
| - search = f"?{search}" |
37 |
| - |
38 |
| - # Resolve relative paths that match `../foo` |
39 |
| - if pathname.startswith("../"): |
40 |
| - pathname = urljoin(current_path, pathname) |
41 |
| - |
42 |
| - # Resolve relative paths that match `foo` |
43 |
| - if not pathname.startswith("/"): |
44 |
| - pathname = urljoin(current_path, pathname) |
45 |
| - |
46 |
| - # Resolve relative paths that match `/foo/../bar` |
47 |
| - while "/../" in pathname: |
48 |
| - part_1, part_2 = pathname.split("/../", 1) |
49 |
| - pathname = urljoin(f"{part_1}/", f"../{part_2}") |
50 |
| - |
51 |
| - # Resolve relative paths that match `foo/./bar` |
52 |
| - pathname = pathname.replace("/./", "/") |
53 |
| - |
54 |
| - set_location(Location(pathname, search)) |
| 28 | + if to is None: |
| 29 | + raise ValueError("The `to` attribute is required for the `Link` component.") |
55 | 30 |
|
56 | 31 | uuid_string = f"link-{uuid4().hex}"
|
57 | 32 | class_name = f"{uuid_string}"
|
| 33 | + set_location = _use_route_state().set_location |
| 34 | + attributes = {} |
| 35 | + children: tuple[Any] = attributes_and_children |
| 36 | + |
| 37 | + if attributes_and_children and isinstance(attributes_and_children[0], dict): |
| 38 | + attributes = attributes_and_children[0] |
| 39 | + children = attributes_and_children[1:] |
58 | 40 | if "className" in attributes:
|
59 | 41 | class_name = " ".join([attributes.pop("className"), class_name])
|
60 |
| - # TODO: This can be removed when ReactPy stops supporting underscores in attribute names |
61 | 42 | if "class_name" in attributes: # pragma: no cover
|
| 43 | + # TODO: This can be removed when ReactPy stops supporting underscores in attribute names |
62 | 44 | class_name = " ".join([attributes.pop("class_name"), class_name])
|
63 | 45 |
|
64 | 46 | attrs = {
|
65 | 47 | **attributes,
|
66 | 48 | "href": to,
|
67 |
| - "onClick": on_click, |
68 | 49 | "className": class_name,
|
69 | 50 | }
|
70 |
| - return html._(html.a(attrs, *children), html.script(link_js_content.replace("UUID", uuid_string))) |
| 51 | + |
| 52 | + def on_click(_event: dict[str, Any]) -> None: |
| 53 | + set_location(Location(**_event)) |
| 54 | + |
| 55 | + return html._(html.a(attrs, *children, **kwargs), Link({"onClick": on_click, "linkClass": uuid_string})) |
71 | 56 |
|
72 | 57 |
|
73 | 58 | def route(path: str, element: Any | None, *routes: Route) -> Route:
|
|
0 commit comments