|
| 1 | +import urllib.parse |
| 2 | +from typing import ClassVar |
| 3 | +from uuid import uuid4 |
| 4 | + |
| 5 | +from jinja2_simple_tags import StandaloneTag |
| 6 | + |
| 7 | + |
| 8 | +class ComponentTag(StandaloneTag): |
| 9 | + """This allows enables a `component` tag to be used in any Jinja2 rendering context.""" |
| 10 | + |
| 11 | + safe_output = True |
| 12 | + tags: ClassVar[set[str]] = {"reactpy_component"} |
| 13 | + |
| 14 | + def render(self, dotted_path: str, *args, **kwargs): |
| 15 | + uuid = uuid4().hex |
| 16 | + class_ = kwargs.pop("class", "") |
| 17 | + kwargs.pop("key", "") # `key` is effectively useless for the root node |
| 18 | + |
| 19 | + # TODO: Fetch these from ReactPy settings |
| 20 | + client_js_path = "/reactpy/static/index.js" |
| 21 | + path_prefix = "/reactpy/" |
| 22 | + reconnect_interval = 750 |
| 23 | + reconnect_max_interval = 60000 |
| 24 | + reconnect_max_retries = 150 |
| 25 | + reconnect_backoff_multiplier = 1.25 |
| 26 | + |
| 27 | + # Generate the websocket URL |
| 28 | + # TODO: This will require rewriting the websocket URL to `reactpy-ws/<uuid>?<kwargs>` |
| 29 | + component_path = f"{dotted_path}/" |
| 30 | + if kwargs.get("args") is not None: |
| 31 | + raise ValueError("Cannot specify `args` as a keyword argument") |
| 32 | + if args: |
| 33 | + kwargs["args"] = args |
| 34 | + if kwargs: |
| 35 | + component_path += f"?{urllib.parse.urlencode(kwargs)}" |
| 36 | + |
| 37 | + # TODO: Turn this into a util function and maybe use it for the standalone version too? |
| 38 | + return ( |
| 39 | + f'<div id="{uuid}" class="{class_}"></div>' |
| 40 | + '<script type="module" crossorigin="anonymous">' |
| 41 | + f'import {{ mountReactPy }} from "{client_js_path}";' |
| 42 | + f'const mountElement = document.getElementById("{uuid}");' |
| 43 | + "mountReactPy({" |
| 44 | + f' mountElement: document.getElementById("{uuid}"),' |
| 45 | + f' pathPrefix: "{path_prefix}",' |
| 46 | + f' appendComponentPath: "{component_path}",' |
| 47 | + f" reconnectInterval: {reconnect_interval}," |
| 48 | + f" reconnectMaxInterval: {reconnect_max_interval}," |
| 49 | + f" reconnectMaxRetries: {reconnect_max_retries}," |
| 50 | + f" reconnectBackoffMultiplier: {reconnect_backoff_multiplier}," |
| 51 | + "});" |
| 52 | + "</script>" |
| 53 | + ) |
0 commit comments