Skip to content

Instantly share code, notes, and snippets.

@rmorshea
Created July 9, 2023 22:15
Show Gist options
  • Save rmorshea/c888e6e34519edababf49351c63c335e to your computer and use it in GitHub Desktop.
Save rmorshea/c888e6e34519edababf49351c63c335e to your computer and use it in GitHub Desktop.
ReactPy + PyScript
<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