Created
July 9, 2023 22:15
-
-
Save rmorshea/c888e6e34519edababf49351c63c335e to your computer and use it in GitHub Desktop.
ReactPy + PyScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>ReactPy</title> | |
<meta charset="utf-8"> | |
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" /> | |
<script defer src="https://pyscript.net/latest/pyscript.js"></script> | |
</head> | |
<body> | |
<div id="root"></div> | |
<py-config> | |
packages = ["reactpy", "ssl", "jsonpointer"] | |
</py-config> | |
<py-script> | |
import js | |
import asyncio | |
import reactpy as rp | |
from jsonpointer import set_pointer | |
from pyodide.ffi.wrappers import add_event_listener | |
# --- Application Code --------------------------------------------------------- | |
@rp.component | |
def app(): | |
value, set_value = rp.use_state(0) | |
return rp.html._( | |
rp.html.button({"on_click": lambda event: set_value(value + 1)}, "+"), | |
rp.html.button({"on_click": lambda event: set_value(value - 1)}, "-"), | |
rp.html.div({"style": {"color": "red"}}, str(value)), | |
) | |
# --- Framework Code --------------------------------------------------------- | |
model = {} | |
def apply_update(update): | |
if update["path"]: | |
set_pointer(model, update["path"], update["model"]) | |
else: | |
model.update(update["model"]) | |
def render_model(layout, model): | |
root = js.document.getElementById("root") | |
root.innerHTML = "" | |
_render_model(layout, root, model) | |
def _render_model(layout, parent, model): | |
if isinstance(model, str): | |
parent.appendChild(js.document.createTextNode(model)) | |
elif isinstance(model, dict): | |
if not model["tagName"]: | |
for child in model.get("children", []): | |
_render_model(layout, parent, child) | |
return | |
tag = model["tagName"] | |
attributes = model.get("attributes", {}) | |
children = model.get("children", []) | |
element = js.document.createElement(tag) | |
for key, value in attributes.items(): | |
if key == "style": | |
for style_key, style_value in value.items(): | |
setattr(element.style, style_key, style_value) | |
else: | |
element.setAttribute(key, value) | |
for event_name, event_handler_model in model.get("eventHandlers", {}).items(): | |
_create_event_handler(layout, element, event_name, event_handler_model) | |
for child in children: | |
_render_model(layout, element, child) | |
parent.appendChild(element) | |
else: | |
raise ValueError(f"Unknown model type: {type(model)}") | |
def _create_event_handler(layout, element, event_name, event_handler_model): | |
target = event_handler_model["target"] | |
def event_handler(*args): | |
asyncio.create_task(layout.deliver({ | |
"type": "layout-event", | |
"target": target, | |
"data": args, | |
})) | |
event_name = event_name.lstrip("on_").lower().replace("_", "") | |
add_event_listener(element, event_name, event_handler) | |
async def main(): | |
async with rp.Layout(app()) as layout: | |
while True: | |
update = await layout.render() | |
apply_update(update) | |
render_model(layout, model) | |
asyncio.create_task(main()) | |
</py-script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment