Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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: 1 addition & 3 deletions docs/examples/python/pyscript_component_initial_object.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from reactpy import component, html

from reactpy_django.components import pyscript_component
from reactpy import component, html, pyscript_component


@component
Expand Down
4 changes: 1 addition & 3 deletions docs/examples/python/pyscript_component_initial_string.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from reactpy import component, html

from reactpy_django.components import pyscript_component
from reactpy import component, html, pyscript_component


@component
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from reactpy import component, html

from reactpy_django.components import pyscript_component
from reactpy import component, html, pyscript_component


@component
Expand Down
4 changes: 1 addition & 3 deletions docs/examples/python/pyscript_component_root.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from reactpy import component, html

from reactpy_django.components import pyscript_component
from reactpy import component, html, pyscript_component


@component
Expand Down
8 changes: 4 additions & 4 deletions docs/examples/python/pyscript_ssr_parent.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from reactpy import component, html

from reactpy_django.components import pyscript_component
from reactpy import component, html, 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"),
pyscript_component(
"./example_project/my_app/components/root.py",
),
)
2 changes: 1 addition & 1 deletion docs/examples/python/use_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
def my_component():
location = use_location()

return html.div(location.pathname + location.search)
return html.div(location.path + location.query_string)
2 changes: 1 addition & 1 deletion docs/overrides/homepage_examples/add_interactivity.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def searchable_video_list(videos):
search_text, set_search_text = use_state("")
found_videos = filter_videos(videos, search_text)

return html._(
return html(
search_input(
{"onChange": lambda event: set_search_text(event["target"]["value"])},
value=search_text,
Expand Down
2 changes: 1 addition & 1 deletion docs/src/reference/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ This allows you to embedded any number of client-side PyScript components within
| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `#!python *file_paths` | `#!python str` | File path to your client-side component. If multiple paths are provided, the contents are automatically merged. | N/A |
| `#!python initial` | `#!python str | VdomDict | ComponentType` | The initial HTML that is displayed prior to the PyScript component loads. This can either be a string containing raw HTML, a `#!python reactpy.html` snippet, or a non-interactive component. | `#!python ""` |
| `#!python initial` | `#!python str | VdomDict | Component` | The initial HTML that is displayed prior to the PyScript component loads. This can either be a string containing raw HTML, a `#!python reactpy.html` snippet, or a non-interactive component. | `#!python ""` |
| `#!python root` | `#!python str` | The name of the root component function. | `#!python "root"` |

<!--pyscript-setup-required-start-->
Expand Down
2 changes: 1 addition & 1 deletion docs/src/reference/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ Shortcut that returns the browser's current `#!python Location`.

| Type | Description |
| --- | --- |
| `#!python Location` | An object containing the current URL's `#!python pathname` and `#!python search` query. |
| `#!python Location` | An object containing the current URL's `#!python path` and `#!python query_string` query. |

---

Expand Down
2 changes: 1 addition & 1 deletion docs/src/reference/template-tag.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ Your Python component file will be directly loaded into the browser. It must hav
| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `#!python *file_paths` | `#!python str` | File path to your client-side component. If multiple paths are provided, the contents are automatically merged. | N/A |
| `#!python initial` | `#!python str | VdomDict | ComponentType` | The initial HTML that is displayed prior to the PyScript component loads. This can either be a string containing raw HTML, a `#!python reactpy.html` snippet, or a non-interactive component. | `#!python ""` |
| `#!python initial` | `#!python str | VdomDict | Component` | The initial HTML that is displayed prior to the PyScript component loads. This can either be a string containing raw HTML, a `#!python reactpy.html` snippet, or a non-interactive component. | `#!python ""` |
| `#!python root` | `#!python str` | The name of the root component function. | `#!python "root"` |

<!--pyscript-webtypy-start-->
Expand Down
9 changes: 4 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ classifiers = [
dependencies = [
"channels>=4.0.0",
"django>=4.2.0",
"reactpy>=1.1.0, <2.0.0",
"reactpy-router>=1.0.3, <2.0.0",
"reactpy>=2.0.0b10, <3.0.0",
"reactpy-router>2.0.0",
"dill>=0.3.5",
"orjson>=3.6.0",
"nest_asyncio>=1.5.0",
Expand Down Expand Up @@ -96,18 +96,17 @@ extra-dependencies = [
"servestatic",
"django-bootstrap5",
"decorator",

]
matrix-name-format = "{variable}-{value}"

# Django 4.2
[[tool.hatch.envs.hatch-test.matrix]]
python = ["3.9", "3.10", "3.11", "3.12"]
python = ["3.11", "3.12"]
django = ["4.2"]

# Django 5.0
[[tool.hatch.envs.hatch-test.matrix]]
python = ["3.10", "3.11", "3.12"]
python = ["3.11", "3.12"]
django = ["5.0"]

# Django 5.1
Expand Down
6 changes: 3 additions & 3 deletions src/js/package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"dependencies": {
"@pyscript/core": "^0.6",
"@reactpy/client": "^0.3.2",
"event-to-object": "^0.1.2",
"@reactpy/client": "^1.0.3",
"event-to-object": "^2.0.0",
"morphdom": "^2.7.4",
"preact": "^10.26.9",
"preact": "^10.27.2",
"react": "npm:@preact/compat@17.1.2",
"react-dom": "npm:@preact/compat@17.1.2"
},
Expand Down
2 changes: 1 addition & 1 deletion src/reactpy_django/auth/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def setup_asgi_scope():
any relevant actions."""
scope["reactpy"]["synchronize_auth"] = synchronize_auth

@hooks.use_effect(dependencies=[sync_needed])
@hooks.use_async_effect(dependencies=[sync_needed])
async def synchronize_auth_watchdog():
"""Detect if the client has taken too long to request a auth session synchronization.

Expand Down
40 changes: 8 additions & 32 deletions src/reactpy_django/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
from django.http import HttpRequest
from django.urls import reverse
from reactpy import component, hooks, html, utils
from reactpy.types import ComponentType, Key, VdomDict
from reactpy.types import Component, Key, VdomDict

from reactpy_django.exceptions import ViewNotRegisteredError
from reactpy_django.forms.components import _django_form
from reactpy_django.pyscript.components import _pyscript_component
from reactpy_django.utils import (
cached_static_file,
del_html_head_body_transform,
Expand Down Expand Up @@ -54,7 +53,7 @@ def constructor(
*args,
key: Key | None = None,
**kwargs,
) -> ComponentType:
) -> Component:
return _view_to_component(
view=view,
transforms=transforms,
Expand Down Expand Up @@ -84,13 +83,13 @@ def constructor(
*args,
key: Key | None = None,
**kwargs,
) -> ComponentType:
) -> Component:
return _view_to_iframe(view=view, extra_props=extra_props, args=args, kwargs=kwargs, key=key)

return constructor


def django_css(static_path: str, key: Key | None = None) -> ComponentType:
def django_css(static_path: str, key: Key | None = None) -> Component:
"""Fetches a CSS static file for use within ReactPy. This allows for deferred CSS loading.

Args:
Expand All @@ -103,7 +102,7 @@ def django_css(static_path: str, key: Key | None = None) -> ComponentType:
return _django_css(static_path=static_path, key=key)


def django_js(static_path: str, key: Key | None = None) -> ComponentType:
def django_js(static_path: str, key: Key | None = None) -> Component:
"""Fetches a JS static file for use within ReactPy. This allows for deferred JS loading.

Args:
Expand Down Expand Up @@ -131,7 +130,7 @@ def django_form(
top_children: Sequence[Any] = (),
bottom_children: Sequence[Any] = (),
key: Key | None = None,
) -> ComponentType:
) -> Component:
"""Converts a Django form to a ReactPy component.

Args:
Expand Down Expand Up @@ -174,29 +173,6 @@ def django_form(
)


def pyscript_component(
*file_paths: str,
initial: str | VdomDict | ComponentType = "",
root: str = "root",
) -> ComponentType:
"""
Args:
file_paths: File path to your client-side component. If multiple paths are \
provided, the contents are automatically merged.

Kwargs:
initial: The initial HTML that is displayed prior to the PyScript component \
loads. This can either be a string containing raw HTML, a \
`#!python reactpy.html` snippet, or a non-interactive component.
root: The name of the root component function.
"""
return _pyscript_component(
*file_paths,
initial=initial,
root=root,
)


@component
def _view_to_component(
view: Callable | View | str,
Expand All @@ -217,7 +193,7 @@ def _view_to_component(
resolved_view: Callable = import_module(view) if isinstance(view, str) else view # type: ignore

# Render the view render within a hook
@hooks.use_effect(
@hooks.use_async_effect(
dependencies=[
json.dumps(vars(_request), default=generate_obj_name),
json.dumps([_args, _kwargs], default=generate_obj_name),
Expand All @@ -228,7 +204,7 @@ async def _render_view():
# Render the view
response = await render_view(resolved_view, _request, _args, _kwargs)
set_converted_view(
utils.html_to_vdom(
utils.string_to_reactpy(
response.content.decode("utf-8").strip(),
del_html_head_body_transform,
*transforms,
Expand Down
6 changes: 3 additions & 3 deletions src/reactpy_django/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
from django.core.cache import DEFAULT_CACHE_ALIAS
from django.db import DEFAULT_DB_ALIAS
from reactpy.config import REACTPY_ASYNC_RENDERING as _REACTPY_ASYNC_RENDERING
from reactpy.config import REACTPY_DEBUG_MODE as _REACTPY_DEBUG_MODE
from reactpy.config import REACTPY_DEBUG as _REACTPY_DEBUG

from reactpy_django.utils import import_dotted_path

if TYPE_CHECKING:
from django.views import View
from reactpy.core.types import ComponentConstructor
from reactpy.types import ComponentConstructor

from reactpy_django.types import (
AsyncPostprocessor,
Expand All @@ -27,7 +27,7 @@

# Configurable through Django settings.py
DJANGO_DEBUG = settings.DEBUG # Snapshot of Django's DEBUG setting
_REACTPY_DEBUG_MODE.set_current(settings.DEBUG)
_REACTPY_DEBUG.set_current(settings.DEBUG)
_REACTPY_ASYNC_RENDERING.set_current(getattr(settings, "REACTPY_ASYNC_RENDERING", _REACTPY_ASYNC_RENDERING.current))
REACTPY_URL_PREFIX: str = getattr(
settings,
Expand Down
2 changes: 1 addition & 1 deletion src/reactpy_django/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

if TYPE_CHECKING:
from django.contrib.auth.models import AbstractUser
from reactpy.core.types import ComponentConstructor
from reactpy.types import ComponentConstructor


def user_passes_test(
Expand Down
15 changes: 8 additions & 7 deletions src/reactpy_django/forms/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.forms import Form, ModelForm
from reactpy import component, hooks, html, utils
from reactpy.core.events import event
from reactpy.web import export, module_from_file
from reactpy.reactjs import component_from_file

from reactpy_django.forms.transforms import (
convert_html_props_to_reactjs,
Expand All @@ -24,11 +24,12 @@
if TYPE_CHECKING:
from collections.abc import Sequence

from reactpy.core.types import VdomDict
from reactpy.types import VdomDict

DjangoForm = export(
module_from_file("reactpy-django", file=Path(__file__).parent.parent / "static" / "reactpy_django" / "index.js"),
("DjangoForm"),
DjangoForm = component_from_file(
Path(__file__).parent.parent / "static" / "reactpy_django" / "index.js",
import_names=("DjangoForm"),
name="reactpy-django",
)


Expand Down Expand Up @@ -63,7 +64,7 @@ def _django_form(
)

# Validate and render the form
@hooks.use_effect(dependencies=[str(submitted_data)])
@hooks.use_async_effect(dependencies=[str(submitted_data)])
async def render_form():
"""Forms must be rendered in an async loop to allow database fields to execute."""
if submitted_data:
Expand Down Expand Up @@ -113,7 +114,7 @@ async def _on_change(_event):
},
DjangoForm({"onSubmitCallback": on_submit_callback, "formId": f"reactpy-{uuid}"}),
*top_children,
utils.html_to_vdom(
utils.string_to_reactpy(
rendered_form,
convert_html_props_to_reactjs,
convert_textarea_children_to_prop,
Expand Down
5 changes: 3 additions & 2 deletions src/reactpy_django/forms/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from reactpy.core.events import EventHandler, to_event_handler_function

if TYPE_CHECKING:
from reactpy.core.types import VdomDict
from reactpy.types import VdomDict


def convert_html_props_to_reactjs(vdom_tree: VdomDict) -> VdomDict:
Expand Down Expand Up @@ -81,7 +81,8 @@ def infer_key_from_attributes(vdom_tree: VdomDict) -> VdomDict:
key = attributes.get("name")

if key:
vdom_tree["key"] = key
attributes["key"] = key
vdom_tree["attributes"] = attributes

return vdom_tree

Expand Down
8 changes: 4 additions & 4 deletions src/reactpy_django/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from channels import DEFAULT_CHANNEL_LAYER
from channels import auth as channels_auth
from channels.layers import InMemoryChannelLayer, get_channel_layer
from reactpy import use_callback, use_effect, use_memo, use_ref, use_state
from reactpy import use_async_effect, use_callback, use_effect, use_memo, use_ref, use_state
from reactpy import use_connection as _use_connection
from reactpy import use_location as _use_location
from reactpy import use_scope as _use_scope
Expand All @@ -43,7 +43,7 @@

from channels_redis.core import RedisChannelLayer
from django.contrib.auth.models import AbstractUser
from reactpy.backend.types import Location
from reactpy.types import Location


_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -378,15 +378,15 @@ def use_channel_layer(
raise ValueError(msg)

# Add/remove a group's channel during component mount/dismount respectively.
@use_effect(dependencies=[])
@use_async_effect(dependencies=[])
async def group_manager():
if group:
await channel_layer.group_add(group, channel_name)
return lambda: asyncio.run(channel_layer.group_discard(group, channel_name))
return None

# Listen for messages on the channel using the provided `receiver` function.
@use_effect
@use_async_effect
async def message_receiver():
if not receiver:
return
Expand Down
4 changes: 2 additions & 2 deletions src/reactpy_django/html.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from reactpy.core.vdom import make_vdom_constructor
from reactpy import Vdom

pyscript = make_vdom_constructor("py-script")
pyscript = Vdom("py-script")
Comment thread
Archmonger marked this conversation as resolved.
Outdated
Loading