Skip to content

Commit 260ca3d

Browse files
committed
fully functional server-side parent component
1 parent bdf1960 commit 260ca3d

File tree

9 files changed

+52
-28
lines changed

9 files changed

+52
-28
lines changed

src/reactpy_django/components.py

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -322,33 +322,23 @@ def _python_to_pyscript(
322322
config: str | dict = "",
323323
root: str = "root",
324324
):
325+
rendered, set_rendered = hooks.use_state(False)
325326
uuid = uuid4().hex.replace("-", "")
326327
initial = vdom_or_component_to_string(initial, uuid=uuid)
327328
executor = render_pyscript_template(file_path, uuid, root)
328329
new_config = extend_pyscript_config(config, extra_packages)
329330

331+
if not rendered:
332+
# FIXME: This is needed to properly kill off any previous PyScript instances
333+
# such as when a component is re-rendered due to WebSocket disconnection.
334+
# There may be a better way to do this, but it's not clear at the moment
335+
set_rendered(True)
336+
return None
337+
330338
return html.div(
331-
html.link(
332-
{"rel": "stylesheet", "href": static("reactpy_django/pyscript/core.css")}
333-
),
334-
html.script(
335-
{
336-
"type": "module",
337-
"src": static(
338-
"reactpy_django/pyscript/core.js",
339-
),
340-
}
341-
),
342339
html.div((extra_props or {}) | {"id": f"pyscript-{uuid}"}, initial),
343340
PYSCRIPT_TAG(
344-
{
345-
"async": "",
346-
"config": orjson.dumps(new_config).decode(),
347-
"id": f"script-{uuid}",
348-
},
341+
{"async": "", "config": orjson.dumps(new_config).decode()},
349342
executor,
350343
),
351-
html.script(
352-
f"if (document.querySelector('#pyscript-{uuid}') && document.querySelector('#pyscript-{uuid}').childElementCount != 0 && document.querySelector('#script-{uuid}')) document.querySelector('#script-{uuid}').remove();"
353-
),
354344
)

src/reactpy_django/pyscript/__init__.py

Whitespace-only changes.

src/reactpy_django/pyscript_template.py renamed to src/reactpy_django/pyscript/component_template.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,6 @@ async def run(self):
8989
self.render(layout, root_model)
9090

9191

92-
asyncio.create_task(LayoutManagerUUID().run())
92+
# PyScript allows top-level await, which allows us to not throw errors on components
93+
# that terminate early (such as hook-less components)
94+
await LayoutManagerUUID().run() # noqa: F704

src/reactpy_django/templates/reactpy/pyscript_component.html

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
{% load static %}
2-
<link rel="stylesheet" href="{% static 'reactpy_django/pyscript/core.css' %}" />
3-
<script type="module" src="{% static 'reactpy_django/pyscript/core.js' %}"></script>
41
{% if reactpy_class %}<div id="pyscript-{{reactpy_uuid}}" class="{{reactpy_class}}">{{reactpy_initial_html}}</div>
52
{% endif %}
63
{% if not reactpy_class %}<div id="pyscript-{{reactpy_uuid}}">{{reactpy_initial_html}}</div>{% endif %}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{% load static %}
2+
<link rel="stylesheet" href="{% static 'reactpy_django/pyscript/core.css' %}" />
3+
<link rel="stylesheet" href="{% static 'reactpy_django/pyscript-custom.css' %}" />
4+
<script type="module" async src="{% static 'reactpy_django/pyscript/core.js' %}"></script>

src/reactpy_django/templatetags/reactpy.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,8 @@ def pyscript_component(
228228
"reactpy_initial_html": initial,
229229
"reactpy_config": orjson.dumps(new_config).decode(),
230230
}
231+
232+
233+
@register.inclusion_tag("reactpy/pyscript_static_files.html")
234+
def pyscript_static_files():
235+
return {}

src/reactpy_django/utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@
5555
+ rf"({_OFFLINE_KWARG_PATTERN}|{_GENERIC_KWARG_PATTERN})*?"
5656
+ r"\s*%}"
5757
)
58-
PYSCRIPT_TEMPLATE = (Path(__file__).parent / "pyscript_template.py").read_text(
59-
encoding="utf-8"
60-
)
58+
PYSCRIPT_COMPONENT_TEMPLATE = (
59+
Path(__file__).parent / "pyscript" / "component_template.py"
60+
).read_text(encoding="utf-8")
6161
PYSCRIPT_TAG = make_vdom_constructor("py-script")
6262
PYSCRIPT_DEFAULT_CONFIG = {
6363
"packages": [
@@ -458,7 +458,7 @@ def vdom_or_component_to_string(
458458
def render_pyscript_template(file_path: str, uuid: str, root: str):
459459
"""Inserts the user's code into our PyScript template using pattern matching."""
460460
# Create a valid PyScript executor by replacing the template values
461-
executor = PYSCRIPT_TEMPLATE.replace("UUID", uuid)
461+
executor = PYSCRIPT_COMPONENT_TEMPLATE.replace("UUID", uuid)
462462
executor = executor.replace("return root()", f"return {root}()")
463463

464464
# Insert the user code into the template

tests/test_app/pyscript/components/child.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from reactpy import component, html
1+
from reactpy import component, html, use_state
22
from reactpy_django.components import python_to_pyscript
33

44

@@ -8,3 +8,26 @@ def embed():
88
{"className": "embeddable"},
99
python_to_pyscript("./test_app/pyscript/components/child_embed.py"),
1010
)
11+
12+
13+
@component
14+
def toggeable_embed():
15+
state, set_state = use_state(False)
16+
17+
if not state:
18+
return html.div(
19+
{"className": "embeddable"},
20+
html.button(
21+
{"onClick": lambda x: set_state(not state)},
22+
"Click to show/hide",
23+
),
24+
)
25+
26+
return html.div(
27+
{"className": "embeddable"},
28+
html.button(
29+
{"onClick": lambda x: set_state(not state)},
30+
"Click to show/hide",
31+
),
32+
python_to_pyscript("./test_app/pyscript/components/child_embed.py"),
33+
)

tests/test_app/templates/pyscript.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
99
<link rel="shortcut icon" type="image/png" href="{% static 'favicon.ico' %}" />
1010
<title>ReactPy</title>
11+
{% pyscript_static_files %}
1112
</head>
1213

1314
<body>
@@ -19,6 +20,8 @@ <h1>ReactPy PyScript Test Page</h1>
1920
<hr>
2021
{% component "test_app.pyscript.components.child.embed" %}
2122
<hr>
23+
{% component "test_app.pyscript.components.child.toggeable_embed" %}
24+
<hr>
2225
</body>
2326

2427
</html>

0 commit comments

Comments
 (0)