Skip to content

Commit fb6c258

Browse files
authored
Client-side Python components using PyScript (#243)
- Client-side Python components can now be rendered via the new `{% pyscript_component %}` template tag - You must first call the `{% pyscript_setup %}` template tag to load PyScript dependencies - Client-side components can be embedded into existing server-side components via `reactpy_django.components.pyscript_component`. - Tired of writing JavaScript? You can now write PyScript code that runs directly within client browser via the `reactpy_django.html.pyscript` element. - This is a viable substitution for most JavaScript code.
1 parent 2e343aa commit fb6c258

File tree

80 files changed

+7395
-183
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+7395
-183
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# ReactPy-Django Build Artifacts
2-
src/reactpy_django/static/*
2+
src/reactpy_django/static/reactpy_django/client.js
3+
src/reactpy_django/static/reactpy_django/pyscript
4+
src/reactpy_django/static/reactpy_django/morphdom
35

46
# Django #
57
logs

CHANGELOG.md

+15-3
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,33 @@ Using the following categories, list your changes in this order:
3434

3535
## [Unreleased]
3636

37+
### Added
38+
39+
- Client-side Python components can now be rendered via the new `{% pyscript_component %}` template tag
40+
- You must first call the `{% pyscript_setup %}` template tag to load PyScript dependencies
41+
- Client-side components can be embedded into existing server-side components via `reactpy_django.components.pyscript_component`.
42+
- Tired of writing JavaScript? You can now write PyScript code that runs directly within client browser via the `reactpy_django.html.pyscript` element.
43+
- This is a viable substitution for most JavaScript code.
44+
3745
### Changed
3846

3947
- New syntax for `use_query` and `use_mutation` hooks. Here's a quick comparison of the changes:
4048

4149
```python
42-
query = use_query(QueryOptions(thread_sensitive=True), get_items, value=123456, foo="bar") # Old
43-
query = use_query(get_items, {"value":12356, "foo":"bar"}, thread_sensitive=True) # New
50+
query = use_query(QueryOptions(thread_sensitive=True), get_items, foo="bar") # Old
51+
query = use_query(get_items, {"foo":"bar"}, thread_sensitive=True) # New
4452

4553
mutation = use_mutation(MutationOptions(thread_sensitive=True), remove_item) # Old
4654
mutation = use_mutation(remove_item, thread_sensitive=True) # New
4755
```
4856

4957
### Removed
5058

51-
- `QueryOptions` and `MutationOptions` have been removed. Their values are now passed direct into the hook.
59+
- `QueryOptions` and `MutationOptions` have been removed. The value contained within these objects are now passed directly into the hook.
60+
61+
### Fixed
62+
63+
- Resolved a bug where Django-ReactPy would not properly detect `settings.py:DEBUG`.
5264

5365
## [3.8.1] - 2024-05-07
5466

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# <img src="https://raw.githubusercontent.com/reactive-python/reactpy/main/branding/svg/reactpy-logo-square.svg" align="left" height="45"/> ReactPy Django
1+
# <img src="https://raw.githubusercontent.com/reactive-python/reactpy/main/branding/svg/reactpy-logo-square.svg" align="left" height="45"/> ReactPy-Django
22

33
<p>
44
<a href="https://github.com/reactive-python/reactpy-django/actions?query=workflow%3ATest">
@@ -21,6 +21,7 @@
2121
[ReactPy-Django](https://github.com/reactive-python/reactpy-django) is used to add [ReactPy](https://reactpy.dev/) support to an existing **Django project**. This package also turbocharges ReactPy with features such as...
2222

2323
- [SEO compatible rendering](https://reactive-python.github.io/reactpy-django/latest/reference/settings/#reactpy_prerender)
24+
- [Client-Side Python components](https://reactive-python.github.io/reactpy-django/latest/reference/template-tag/#pyscript-component)
2425
- [Single page application (SPA) capabilities](https://reactive-python.github.io/reactpy-django/latest/reference/router/#django-router)
2526
- [Distributed computing](https://reactive-python.github.io/reactpy-django/latest/reference/settings/#reactpy_default_hosts)
2627
- [Performance enhancements](https://reactive-python.github.io/reactpy-django/latest/reference/settings/#performance-settings)
@@ -82,7 +83,7 @@ def hello_world(recipient: str):
8283

8384
<!--py-code-end-->
8485

85-
## [`my_app/templates/my-template.html`](https://docs.djangoproject.com/en/dev/topics/templates/)
86+
## [`my_app/templates/my_template.html`](https://docs.djangoproject.com/en/dev/topics/templates/)
8687

8788
<!--html-header-start-->
8889

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{% load reactpy %}
2+
<!DOCTYPE html>
3+
<html>
4+
5+
<head>
6+
<title>ReactPy</title>
7+
{% pyscript_setup %}
8+
</head>
9+
10+
<body>
11+
{% pyscript_component "./example_project/my_app/components/hello_world.py" %}
12+
</body>
13+
14+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<body>
2+
{% pyscript_component "./example_project/my_app/components/root.py" initial=my_initial_object %}
3+
</body>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<body>
2+
{% pyscript_component "./example_project/my_app/components/root.py" initial="<div> Loading ... </div>" %}
3+
</body>
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{% load reactpy %}
2+
<!DOCTYPE html>
3+
<html>
4+
5+
<head>
6+
<title>ReactPy</title>
7+
{% pyscript_setup extra_js='{"/static/moment.js":"moment"}' %}
8+
</head>
9+
10+
<body>
11+
{% component "example_project.my_app.components.root.py" %}
12+
</body>
13+
14+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{% load reactpy %}
2+
<!DOCTYPE html>
3+
<html>
4+
5+
<head>
6+
<title>ReactPy</title>
7+
{% pyscript_setup %}
8+
</head>
9+
10+
<body>
11+
{% pyscript_component "./example_project/my_app/components/root.py"
12+
"./example_project/my_app/components/child.py" %}
13+
</body>
14+
15+
</html>

docs/examples/html/pyscript-root.html

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<body>
2+
{% pyscript_component "./example_project/my_app/components/main.py" root="main" %}
3+
</body>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<head>
2+
<title>ReactPy</title>
3+
{% pyscript_setup config=my_config_object %}
4+
</head>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<head>
2+
<title>ReactPy</title>
3+
{% pyscript_setup config='{"experimental_create_proxy":"auto"}' %}
4+
</head>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<head>
2+
<title>ReactPy</title>
3+
{% pyscript_setup "dill==0.3.5" "markdown<=3.6.0" "nest_asyncio" "titlecase" %}
4+
</head>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{% load reactpy %}
2+
<!DOCTYPE html>
3+
<html>
4+
5+
<head>
6+
<title>ReactPy</title>
7+
{% pyscript_setup extra_js=my_extra_js_object %}
8+
</head>
9+
10+
<body>
11+
{% component "example_project.my_app.components.root.py" %}
12+
</body>
13+
14+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{% load reactpy %}
2+
<!DOCTYPE html>
3+
<html>
4+
5+
<head>
6+
<title>ReactPy</title>
7+
{% pyscript_setup extra_js='{"/static/moment.js":"moment"}' %}
8+
</head>
9+
10+
<body>
11+
{% component "example_project.my_app.components.root.py" %}
12+
</body>
13+
14+
</html>
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{% load reactpy %}
2+
3+
<head>
4+
<title>ReactPy</title>
5+
{% pyscript_setup %}
6+
</head>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{% load reactpy %}
2+
<!DOCTYPE html>
3+
<html>
4+
5+
<head>
6+
<title>ReactPy</title>
7+
{% pyscript_setup %}
8+
</head>
9+
10+
<body>
11+
{% component "example_project.my_app.components.server_side_component" %}
12+
</body>
13+
14+
</html>

docs/examples/html/pyscript-tag.html

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{% load reactpy %}
2+
<!DOCTYPE html>
3+
<html>
4+
5+
<head>
6+
<title>ReactPy</title>
7+
{% pyscript_setup %}
8+
</head>
9+
10+
<body>
11+
{% component "example_project.my_app.components.server_side_component.py" %}
12+
</body>
13+
14+
</html>

docs/examples/python/example/views.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33

44
def index(request):
5-
return render(request, "my-template.html")
5+
return render(request, "my_template.html")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from reactpy import component, html
2+
from reactpy_django.components import pyscript_component
3+
4+
5+
@component
6+
def server_side_component():
7+
return html.div(
8+
pyscript_component(
9+
"./example_project/my_app/components/root.py",
10+
initial=html.div("Loading ..."),
11+
),
12+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from reactpy import component, html
2+
from reactpy_django.components import pyscript_component
3+
4+
5+
@component
6+
def server_side_component():
7+
return html.div(
8+
pyscript_component(
9+
"./example_project/my_app/components/root.py",
10+
initial="<div> Loading ... </div>",
11+
),
12+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from reactpy import component, html
2+
from reactpy_django.components import pyscript_component
3+
4+
5+
@component
6+
def server_side_component():
7+
return html.div(
8+
pyscript_component(
9+
"./example_project/my_app/components/root.py",
10+
"./example_project/my_app/components/child.py",
11+
),
12+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from reactpy import component, html
2+
from reactpy_django.components import pyscript_component
3+
4+
5+
@component
6+
def server_side_component():
7+
return html.div(
8+
pyscript_component(
9+
"./example_project/my_app/components/main.py",
10+
root="main",
11+
),
12+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from reactpy import component, html
2+
3+
4+
@component
5+
def root():
6+
return html.div("Hello, World!")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from django.shortcuts import render
2+
from reactpy import html
3+
4+
5+
def index(request):
6+
return render(
7+
request,
8+
"my_template.html",
9+
context={"my_initial_object": html.div("Loading ...")},
10+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import js
2+
from reactpy import component, html
3+
4+
5+
@component
6+
def root():
7+
8+
def onClick(event):
9+
js.document.title = "New window title"
10+
11+
return html.button({"onClick": onClick}, "Click Me!")
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from reactpy import component, html
2+
3+
4+
@component
5+
def root():
6+
from pyscript.js_modules import moment
7+
8+
return html.div(
9+
{"id": "moment"},
10+
"Using the JavaScript package 'moment' to calculate time: ",
11+
moment.default().format("YYYY-MM-DD HH:mm:ss"),
12+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from reactpy import component, html
2+
3+
4+
@component
5+
def child_component():
6+
return html.div("This is a child component from a different file.")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from typing import TYPE_CHECKING
2+
3+
from reactpy import component, html
4+
5+
if TYPE_CHECKING:
6+
from .child import child_component
7+
8+
9+
@component
10+
def root():
11+
return html.div("This text is from the root component.", child_component())

docs/examples/python/pyscript-root.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from reactpy import component, html
2+
3+
4+
@component
5+
def main():
6+
return html.div("Hello, World!")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from django.shortcuts import render
2+
3+
4+
def index(request):
5+
return render(
6+
request,
7+
"my_template.html",
8+
context={"my_config_object": {"experimental_create_proxy": "auto"}},
9+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from django.shortcuts import render
2+
from django.templatetags.static import static
3+
4+
5+
def index(request):
6+
return render(
7+
request,
8+
"my_template.html",
9+
context={"my_extra_js_object": {static("moment.js"): "moment"}},
10+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from reactpy import component, html
2+
3+
4+
@component
5+
def root():
6+
return html.div("This text is from my client-side component")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from reactpy import component, html
2+
from reactpy_django.components import pyscript_component
3+
4+
5+
@component
6+
def server_side_component():
7+
return html.div(
8+
"This text is from my server-side component",
9+
pyscript_component("./example_project/my_app/components/root.py"),
10+
)

docs/examples/python/pyscript-tag.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from reactpy import component, html
2+
from reactpy_django.html import pyscript
3+
4+
example_source_code = """
5+
import js
6+
7+
js.console.log("Hello, World!")
8+
"""
9+
10+
11+
@component
12+
def server_side_component():
13+
return html.div(
14+
pyscript(example_source_code.strip()),
15+
)

0 commit comments

Comments
 (0)