Skip to content

Client-side Python components using PyScript #243

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d529fed
functional pyscript prototype
Archmonger Jun 20, 2024
bdf1960
first attempt at pyscript components with server-side parent components
Archmonger Jun 20, 2024
260ca3d
fully functional server-side parent component
Archmonger Jun 21, 2024
027d2a1
add pyscript tag to the user API
Archmonger Jun 21, 2024
9bd02bf
add more fixme details
Archmonger Jun 21, 2024
300d141
reusable layout manager
Archmonger Jun 21, 2024
0210f79
fix client memory leak
Archmonger Jun 21, 2024
62579bd
layout manager -> layout handler
Archmonger Jun 21, 2024
d4e6abe
fix type hints
Archmonger Jun 21, 2024
be6a8d5
workflow for pyscript dist
Archmonger Jun 21, 2024
c90fbff
quick PR self review
Archmonger Jun 21, 2024
59c351a
add tests
Archmonger Jun 21, 2024
258194b
more tests
Archmonger Jun 21, 2024
4ac4ded
remove extra pros
Archmonger Jun 21, 2024
bc58040
Cache file reads for pyscript code
Archmonger Jun 21, 2024
b5b8862
support multi-file pyscript components
Archmonger Jun 21, 2024
fc71864
Add docstrings to the layout handler
Archmonger Jun 21, 2024
4066adb
import encapsulation
Archmonger Jun 21, 2024
eb72c4f
add warning to router
Archmonger Jun 21, 2024
53c0499
component and template tag docs
Archmonger Jun 22, 2024
577b831
html primitive docs
Archmonger Jun 22, 2024
f493fa2
fix docs warning
Archmonger Jun 22, 2024
95aa90c
simplify desc
Archmonger Jun 22, 2024
b22ce44
self-review
Archmonger Jun 22, 2024
825fde6
Use morphdom to modify the DOM
Archmonger Jun 22, 2024
dd7179c
Allow defining custom JS modules
Archmonger Jun 22, 2024
cb7e279
self review
Archmonger Jun 22, 2024
416927a
fix typo
Archmonger Jun 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ReactPy-Django Build Artifacts
src/reactpy_django/static/*
src/reactpy_django/static/reactpy_django/client.js
src/reactpy_django/static/reactpy_django/pyscript
src/reactpy_django/static/reactpy_django/morphdom

# Django #
logs
Expand Down
18 changes: 15 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,33 @@ Using the following categories, list your changes in this order:

## [Unreleased]

### Added

- 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.

### Changed

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

```python
query = use_query(QueryOptions(thread_sensitive=True), get_items, value=123456, foo="bar") # Old
query = use_query(get_items, {"value":12356, "foo":"bar"}, thread_sensitive=True) # New
query = use_query(QueryOptions(thread_sensitive=True), get_items, foo="bar") # Old
query = use_query(get_items, {"foo":"bar"}, thread_sensitive=True) # New

mutation = use_mutation(MutationOptions(thread_sensitive=True), remove_item) # Old
mutation = use_mutation(remove_item, thread_sensitive=True) # New
```

### Removed

- `QueryOptions` and `MutationOptions` have been removed. Their values are now passed direct into the hook.
- `QueryOptions` and `MutationOptions` have been removed. The value contained within these objects are now passed directly into the hook.

### Fixed

- Resolved a bug where Django-ReactPy would not properly detect `settings.py:DEBUG`.

## [3.8.1] - 2024-05-07

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# <img src="https://raw.githubusercontent.com/reactive-python/reactpy/main/branding/svg/reactpy-logo-square.svg" align="left" height="45"/> ReactPy Django
# <img src="https://raw.githubusercontent.com/reactive-python/reactpy/main/branding/svg/reactpy-logo-square.svg" align="left" height="45"/> ReactPy-Django

<p>
<a href="https://github.com/reactive-python/reactpy-django/actions?query=workflow%3ATest">
Expand All @@ -21,6 +21,7 @@
[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...

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

<!--py-code-end-->

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

<!--html-header-start-->

Expand Down
14 changes: 14 additions & 0 deletions docs/examples/html/pyscript-component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% load reactpy %}
<!DOCTYPE html>
<html>

<head>
<title>ReactPy</title>
{% pyscript_setup %}
</head>

<body>
{% pyscript_component "./example_project/my_app/components/hello_world.py" %}
</body>

</html>
3 changes: 3 additions & 0 deletions docs/examples/html/pyscript-initial-object.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<body>
{% pyscript_component "./example_project/my_app/components/root.py" initial=my_initial_object %}
</body>
3 changes: 3 additions & 0 deletions docs/examples/html/pyscript-initial-string.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<body>
{% pyscript_component "./example_project/my_app/components/root.py" initial="<div> Loading ... </div>" %}
</body>
14 changes: 14 additions & 0 deletions docs/examples/html/pyscript-js-module.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% load reactpy %}
<!DOCTYPE html>
<html>

<head>
<title>ReactPy</title>
{% pyscript_setup extra_js='{"/static/moment.js":"moment"}' %}
</head>

<body>
{% component "example_project.my_app.components.root.py" %}
</body>

</html>
15 changes: 15 additions & 0 deletions docs/examples/html/pyscript-multiple-files.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% load reactpy %}
<!DOCTYPE html>
<html>

<head>
<title>ReactPy</title>
{% pyscript_setup %}
</head>

<body>
{% pyscript_component "./example_project/my_app/components/root.py"
"./example_project/my_app/components/child.py" %}
</body>

</html>
3 changes: 3 additions & 0 deletions docs/examples/html/pyscript-root.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<body>
{% pyscript_component "./example_project/my_app/components/main.py" root="main" %}
</body>
4 changes: 4 additions & 0 deletions docs/examples/html/pyscript-setup-config-object.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<head>
<title>ReactPy</title>
{% pyscript_setup config=my_config_object %}
</head>
4 changes: 4 additions & 0 deletions docs/examples/html/pyscript-setup-config-string.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<head>
<title>ReactPy</title>
{% pyscript_setup config='{"experimental_create_proxy":"auto"}' %}
</head>
4 changes: 4 additions & 0 deletions docs/examples/html/pyscript-setup-dependencies.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<head>
<title>ReactPy</title>
{% pyscript_setup "dill==0.3.5" "markdown<=3.6.0" "nest_asyncio" "titlecase" %}
</head>
14 changes: 14 additions & 0 deletions docs/examples/html/pyscript-setup-extra-js-object.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% load reactpy %}
<!DOCTYPE html>
<html>

<head>
<title>ReactPy</title>
{% pyscript_setup extra_js=my_extra_js_object %}
</head>

<body>
{% component "example_project.my_app.components.root.py" %}
</body>

</html>
14 changes: 14 additions & 0 deletions docs/examples/html/pyscript-setup-extra-js-string.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% load reactpy %}
<!DOCTYPE html>
<html>

<head>
<title>ReactPy</title>
{% pyscript_setup extra_js='{"/static/moment.js":"moment"}' %}
</head>

<body>
{% component "example_project.my_app.components.root.py" %}
</body>

</html>
6 changes: 6 additions & 0 deletions docs/examples/html/pyscript-setup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% load reactpy %}

<head>
<title>ReactPy</title>
{% pyscript_setup %}
</head>
14 changes: 14 additions & 0 deletions docs/examples/html/pyscript-ssr-parent.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% load reactpy %}
<!DOCTYPE html>
<html>

<head>
<title>ReactPy</title>
{% pyscript_setup %}
</head>

<body>
{% component "example_project.my_app.components.server_side_component" %}
</body>

</html>
14 changes: 14 additions & 0 deletions docs/examples/html/pyscript-tag.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% load reactpy %}
<!DOCTYPE html>
<html>

<head>
<title>ReactPy</title>
{% pyscript_setup %}
</head>

<body>
{% component "example_project.my_app.components.server_side_component.py" %}
</body>

</html>
2 changes: 1 addition & 1 deletion docs/examples/python/example/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@


def index(request):
return render(request, "my-template.html")
return render(request, "my_template.html")
12 changes: 12 additions & 0 deletions docs/examples/python/pyscript-component-initial-object.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from reactpy import component, html
from reactpy_django.components import pyscript_component


@component
def server_side_component():
return html.div(
pyscript_component(
"./example_project/my_app/components/root.py",
initial=html.div("Loading ..."),
),
)
12 changes: 12 additions & 0 deletions docs/examples/python/pyscript-component-initial-string.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from reactpy import component, html
from reactpy_django.components import pyscript_component


@component
def server_side_component():
return html.div(
pyscript_component(
"./example_project/my_app/components/root.py",
initial="<div> Loading ... </div>",
),
)
12 changes: 12 additions & 0 deletions docs/examples/python/pyscript-component-multiple-files-root.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from reactpy import component, html
from reactpy_django.components import pyscript_component


@component
def server_side_component():
return html.div(
pyscript_component(
"./example_project/my_app/components/root.py",
"./example_project/my_app/components/child.py",
),
)
12 changes: 12 additions & 0 deletions docs/examples/python/pyscript-component-root.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from reactpy import component, html
from reactpy_django.components import pyscript_component


@component
def server_side_component():
return html.div(
pyscript_component(
"./example_project/my_app/components/main.py",
root="main",
),
)
6 changes: 6 additions & 0 deletions docs/examples/python/pyscript-hello-world.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from reactpy import component, html


@component
def root():
return html.div("Hello, World!")
10 changes: 10 additions & 0 deletions docs/examples/python/pyscript-initial-object.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.shortcuts import render
from reactpy import html


def index(request):
return render(
request,
"my_template.html",
context={"my_initial_object": html.div("Loading ...")},
)
11 changes: 11 additions & 0 deletions docs/examples/python/pyscript-js-execution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import js
from reactpy import component, html


@component
def root():

def onClick(event):
js.document.title = "New window title"

return html.button({"onClick": onClick}, "Click Me!")
12 changes: 12 additions & 0 deletions docs/examples/python/pyscript-js-module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from reactpy import component, html


@component
def root():
from pyscript.js_modules import moment

return html.div(
{"id": "moment"},
"Using the JavaScript package 'moment' to calculate time: ",
moment.default().format("YYYY-MM-DD HH:mm:ss"),
)
6 changes: 6 additions & 0 deletions docs/examples/python/pyscript-multiple-files-child.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from reactpy import component, html


@component
def child_component():
return html.div("This is a child component from a different file.")
11 changes: 11 additions & 0 deletions docs/examples/python/pyscript-multiple-files-root.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import TYPE_CHECKING

from reactpy import component, html

if TYPE_CHECKING:
from .child import child_component


@component
def root():
return html.div("This text is from the root component.", child_component())
6 changes: 6 additions & 0 deletions docs/examples/python/pyscript-root.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from reactpy import component, html


@component
def main():
return html.div("Hello, World!")
9 changes: 9 additions & 0 deletions docs/examples/python/pyscript-setup-config-object.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.shortcuts import render


def index(request):
return render(
request,
"my_template.html",
context={"my_config_object": {"experimental_create_proxy": "auto"}},
)
10 changes: 10 additions & 0 deletions docs/examples/python/pyscript-setup-extra-js-object.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.shortcuts import render
from django.templatetags.static import static


def index(request):
return render(
request,
"my_template.html",
context={"my_extra_js_object": {static("moment.js"): "moment"}},
)
6 changes: 6 additions & 0 deletions docs/examples/python/pyscript-ssr-child.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from reactpy import component, html


@component
def root():
return html.div("This text is from my client-side component")
10 changes: 10 additions & 0 deletions docs/examples/python/pyscript-ssr-parent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from reactpy import component, html
from reactpy_django.components import pyscript_component


@component
def server_side_component():
return html.div(
"This text is from my server-side component",
pyscript_component("./example_project/my_app/components/root.py"),
)
15 changes: 15 additions & 0 deletions docs/examples/python/pyscript-tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from reactpy import component, html
from reactpy_django.html import pyscript

example_source_code = """
import js

js.console.log("Hello, World!")
"""


@component
def server_side_component():
return html.div(
pyscript(example_source_code.strip()),
)
Loading
Loading