|
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