diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..170d7ddb
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,36 @@
+# http://editorconfig.org
+
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+charset = utf-8
+end_of_line = lf
+
+[*.py]
+indent_size = 4
+max_line_length = 120
+
+[*.yml]
+indent_size = 4
+
+[*.md]
+indent_size = 4
+
+[*.html]
+indent_size = 4
+max_line_length = off
+
+[*.js]
+max_line_length = off
+
+[*.css]
+indent_size = 4
+max_line_length = off
+
+# Tests can violate line width restrictions in the interest of clarity.
+[**/test_*.py]
+max_line_length = off
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 17080b52..63a412e0 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -46,7 +46,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -59,7 +59,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@v3
# âšī¸ Command-line programs to run using the OS shell.
# đ See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -72,6 +72,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/publish-develop-docs.yml b/.github/workflows/publish-develop-docs.yml
index b79d3cd2..43d114af 100644
--- a/.github/workflows/publish-develop-docs.yml
+++ b/.github/workflows/publish-develop-docs.yml
@@ -5,19 +5,25 @@ on:
branches:
- main
jobs:
- deploy:
+ publish-develop-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
- uses: actions/setup-python@v5
with:
python-version: 3.x
- - run: pip install -r requirements/build-docs.txt
- - name: Publish Develop Docs
+ - name: Install dependencies
+ run: pip install hatch
+ - name: Configure Git
run: |
git config user.name github-actions
git config user.email github-actions@github.com
- cd docs
- mike deploy --push develop
+ - name: Publish Develop Docs
+ run: hatch run docs:deploy_develop
+ concurrency:
+ group: publish-docs
diff --git a/.github/workflows/publish-release-docs.yml b/.github/workflows/publish-latest-docs.yml
similarity index 56%
rename from .github/workflows/publish-release-docs.yml
rename to .github/workflows/publish-latest-docs.yml
index a98e9869..a4945b6f 100644
--- a/.github/workflows/publish-release-docs.yml
+++ b/.github/workflows/publish-latest-docs.yml
@@ -1,23 +1,29 @@
-name: Publish Release Docs
+name: Publish Latest Docs
on:
release:
types: [published]
jobs:
- deploy:
+ publish-latest-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
- uses: actions/setup-python@v5
with:
python-version: 3.x
- - run: pip install -r requirements/build-docs.txt
- - name: Publish ${{ github.event.release.name }} Docs
+ - name: Install dependencies
+ run: pip install hatch
+ - name: Configure Git
run: |
git config user.name github-actions
git config user.email github-actions@github.com
- cd docs
- mike deploy --push --update-aliases ${{ github.event.release.name }} latest
+ - name: Publish ${{ github.event.release.name }} Docs
+ run: hatch run docs:deploy_latest ${{ github.ref_name }}
+ concurrency:
+ group: publish-docs
diff --git a/.github/workflows/publish-py.yml b/.github/workflows/publish-py.yml
deleted file mode 100644
index 72a04dae..00000000
--- a/.github/workflows/publish-py.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-# This workflows will upload a Python Package using Twine when a release is created
-# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
-
-name: Publish Python
-
-on:
- release:
- types: [published]
-
-jobs:
- release-package:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: "3.x"
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- pip install -r requirements/build-pkg.txt
- - name: Build and publish
- env:
- TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
- TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
- run: |
- python -m build --sdist --wheel --outdir dist .
- twine upload dist/*
diff --git a/.github/workflows/publish-python.yml b/.github/workflows/publish-python.yml
new file mode 100644
index 00000000..e20affbb
--- /dev/null
+++ b/.github/workflows/publish-python.yml
@@ -0,0 +1,27 @@
+name: Publish Python
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ publish-python:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.x"
+ - name: Install dependencies
+ run: pip install hatch
+ - name: Build Package
+ run: hatch build --clean
+ - name: Publish to PyPI
+ env:
+ HATCH_INDEX_USER: ${{ secrets.PYPI_USERNAME }}
+ HATCH_INDEX_AUTH: ${{ secrets.PYPI_PASSWORD }}
+ run: hatch publish --yes
diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml
index 907e1a2c..5a2d4fd4 100644
--- a/.github/workflows/test-docs.yml
+++ b/.github/workflows/test-docs.yml
@@ -7,8 +7,6 @@ on:
pull_request:
branches:
- main
- schedule:
- - cron: "0 0 * * *"
jobs:
docs:
@@ -17,20 +15,18 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
- uses: actions/setup-python@v5
with:
python-version: 3.x
+ - name: Install Python Dependencies
+ run: pip install hatch
+ # DISABLED DUE TO DJANGO DOCS CONSTANTLY THROWING 429 ERRORS
+ # - name: Check documentation links
+ # run: hatch run docs:linkcheck
- name: Check docs build
- run: |
- pip install -r requirements/build-docs.txt
- linkcheckMarkdown docs/ -v -r
- linkcheckMarkdown README.md -v -r
- linkcheckMarkdown CHANGELOG.md -v -r
- cd docs
- mkdocs build --strict
+ run: hatch run docs:build
- name: Check docs examples
- run: |
- pip install -r requirements/check-types.txt
- pip install -r requirements/check-style.txt
- mypy --show-error-codes docs/examples/python/
- ruff check docs/examples/python/
+ run: hatch fmt docs --check
diff --git a/.github/workflows/test-javascript.yml b/.github/workflows/test-javascript.yml
new file mode 100644
index 00000000..8e204dcb
--- /dev/null
+++ b/.github/workflows/test-javascript.yml
@@ -0,0 +1,25 @@
+name: Test
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ javascript:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+ - uses: actions/setup-python@v5
+ with:
+ python-version: 3.x
+ - name: Install Python Dependencies
+ run: pip install hatch
+ - name: Run Tests
+ run: hatch run javascript:check
diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml
new file mode 100644
index 00000000..ac8d77b6
--- /dev/null
+++ b/.github/workflows/test-python.yml
@@ -0,0 +1,63 @@
+name: Test
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+ schedule:
+ - cron: "0 0 * * *"
+
+jobs:
+ python-source:
+ runs-on: ${{ matrix.operating-system }}
+ strategy:
+ matrix:
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
+ settings-module: ["single_db", "multi_db"]
+ operating-system: ["ubuntu-latest", "windows-latest"]
+ steps:
+ - uses: actions/checkout@v4
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+ - name: Use Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Install Python Dependencies
+ run: pip install hatch
+ - name: Run Single DB Tests
+ run: hatch test --python ${{ matrix.python-version }} --ds=test_app.settings_${{matrix.settings-module}} -v
+
+ python-formatting:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+ - uses: actions/setup-python@v5
+ with:
+ python-version: 3.x
+ - name: Install Python Dependencies
+ run: pip install hatch
+ - name: Check Python formatting
+ run: hatch fmt src tests --check
+
+ python-types:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+ - uses: actions/setup-python@v5
+ with:
+ python-version: 3.x
+ - name: Install Python Dependencies
+ run: pip install hatch
+ - name: Run Python type checker
+ run: hatch run python:type_check
diff --git a/.github/workflows/test-src.yml b/.github/workflows/test-src.yml
deleted file mode 100644
index 328bd1c3..00000000
--- a/.github/workflows/test-src.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-name: Test
-
-on:
- push:
- branches:
- - main
- pull_request:
- branches:
- - main
- schedule:
- - cron: "0 0 * * *"
-
-jobs:
- source:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- python-version: ["3.9", "3.10", "3.11", "3.12"]
- steps:
- - uses: actions/checkout@v4
- - name: Use Python ${{ matrix.python-version }}
- uses: actions/setup-python@v5
- with:
- python-version: ${{ matrix.python-version }}
- - name: Install Python Dependencies
- run: pip install -r requirements/test-run.txt
- - name: Run Tests
- run: nox -t test
diff --git a/.gitignore b/.gitignore
index ffabb7fc..e675e09a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# ReactPy-Django Build Artifacts
-src/reactpy_django/static/reactpy_django/client.js
+src/reactpy_django/static/reactpy_django/index.js
+src/reactpy_django/static/reactpy_django/index.js.map
src/reactpy_django/static/reactpy_django/pyscript
src/reactpy_django/static/reactpy_django/morphdom
@@ -101,6 +102,7 @@ venv.bak/
# mkdocs documentation
/site
+docs/site
# mypy
.mypy_cache/
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..32ad81f3
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,4 @@
+{
+ "proseWrap": "never",
+ "trailingComma": "all"
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 620c1f75..f3a0ff04 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,33 +10,88 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+
+
+
+## [Unreleased]
+
+### Changed
+
+- Updated the interface for `reactpy.hooks.use_channel_layer` to be more intuitive.
+ - Arguments now must be provided as keyworded arguments.
+ - The `name` argument has been renamed to `channel`.
+ - The `group_name` argument has been renamed to `group`.
+ - The `group_add` and `group_discard` arguments have been removed for simplicity.
+
+### [5.2.1] - 2025-01-10
+
+### Changed
+
+- Use the latest version of `@reactpy/client` which includes a fix for needless client-side component re-creation.
+
+### [5.2.0] - 2024-12-29
### Added
-- for new features.
+
+- User login/logout features!
+ - `reactpy_django.hooks.use_auth` to provide **persistent** `login` and `logout` functionality to your components.
+ - `settings.py:REACTPY_AUTH_TOKEN_MAX_AGE` to control the maximum seconds before ReactPy's login token expires.
+ - `settings.py:REACTPY_CLEAN_AUTH_TOKENS` to control whether ReactPy should clean up expired authentication tokens during automatic cleanups.
+- Automatically convert Django forms to ReactPy forms via the new `reactpy_django.components.django_form` component!
+- The ReactPy component tree can now be forcibly re-rendered via the new `reactpy_django.hooks.use_rerender` hook.
### Changed
-- for changes in existing functionality.
-### Deprecated
-- for soon-to-be removed features.
+- Refactoring of internal code to improve maintainability. No changes to publicly documented API.
-### Removed
-- for removed features.
+### Fixed
+
+- Fixed bug where pre-rendered components could generate a `SynchronousOnlyOperation` exception if they access a freshly logged out Django user object.
+
+## [5.1.1] - 2024-12-02
### Fixed
-- for bug fixes.
-### Security
-- for vulnerability fixes.
- -->
+- Fixed regression from the previous release where components would sometimes not output debug messages when `settings.py:DEBUG` is enabled.
-
+### Changed
-## [Unreleased]
+- Set upper limit on ReactPy version to `<2.0.0`.
+- ReactPy web modules are now streamed in chunks.
+- ReactPy web modules are now streamed using asynchronous file reading to improve performance.
+- Performed refactoring to utilize `ruff` as this repository's linter.
+
+## [5.1.0] - 2024-11-24
+
+### Added
+
+- `settings.py:REACTPY_ASYNC_RENDERING` to enable asynchronous rendering of components.
+
+### Changed
+
+- Bumped the minimum ReactPy version to `1.1.0`.
+
+## [5.0.0] - 2024-10-22
+
+### Changed
+
+- Now using ReactPy-Router v1 for URL routing, which comes with a slightly different API than before.
+- Removed dependency on `aiofile`.
+
+### Removed
-- Nothing (yet)!
+- Removed the following **deprecated** features:
+ - The `compatibility` argument on `reactpy_django.components.view_to_component`
+ - `reactpy_django.components.view_to_component` **usage as a decorator**
+ - `reactpy_django.decorators.auth_required`
+ - `reactpy_django.REACTPY_WEBSOCKET_PATH`
+ - `settings.py:REACTPY_WEBSOCKET_URL`
-## [4.0.0]
+## [4.0.0] - 2024-06-22
### Added
@@ -110,8 +165,8 @@ Using the following categories, list your changes in this order:
- New Django `User` related features!
- `reactpy_django.hooks.use_user` can be used to access the current user.
- `reactpy_django.hooks.use_user_data` provides a simplified interface for storing user key-value data.
- - `reactpy_django.decorators.user_passes_test` is inspired by the [equivalent Django decorator](http://docs.djangoproject.com/en/dev/topics/auth/default/#django.contrib.auth.decorators.user_passes_test), but ours works with ReactPy components.
- - `settings.py:REACTPY_AUTO_RELOGIN` will cause component WebSocket connections to automatically [re-login](https://channels.readthedocs.io/en/latest/topics/authentication.html#how-to-log-a-user-in-out) users that are already authenticated. This is useful to continuously update `last_login` timestamps and refresh the [Django login session](https://docs.djangoproject.com/en/dev/topics/http/sessions/).
+ - `reactpy_django.decorators.user_passes_test` is inspired by the [equivalent Django decorator](http://docs.djangoproject.com/en/stable/topics/auth/default/#django.contrib.auth.decorators.user_passes_test), but ours works with ReactPy components.
+ - `settings.py:REACTPY_AUTO_RELOGIN` will cause component WebSocket connections to automatically [re-login](https://channels.readthedocs.io/en/latest/topics/authentication.html#how-to-log-a-user-in-out) users that are already authenticated. This is useful to continuously update `last_login` timestamps and refresh the [Django login session](https://docs.djangoproject.com/en/stable/topics/http/sessions/).
### Changed
@@ -501,7 +556,12 @@ Using the following categories, list your changes in this order:
- Support for IDOM within the Django
-[Unreleased]: https://github.com/reactive-python/reactpy-django/compare/4.0.0...HEAD
+[Unreleased]: https://github.com/reactive-python/reactpy-django/compare/5.2.1...HEAD
+[5.2.1]: https://github.com/reactive-python/reactpy-django/compare/5.2.0...5.2.1
+[5.2.0]: https://github.com/reactive-python/reactpy-django/compare/5.1.1...5.2.0
+[5.1.1]: https://github.com/reactive-python/reactpy-django/compare/5.1.0...5.1.1
+[5.1.0]: https://github.com/reactive-python/reactpy-django/compare/5.0.0...5.1.0
+[5.0.0]: https://github.com/reactive-python/reactpy-django/compare/4.0.0...5.0.0
[4.0.0]: https://github.com/reactive-python/reactpy-django/compare/3.8.1...4.0.0
[3.8.1]: https://github.com/reactive-python/reactpy-django/compare/3.8.0...3.8.1
[3.8.0]: https://github.com/reactive-python/reactpy-django/compare/3.7.0...3.8.0
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index ddcb7f8d..00000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,3 +0,0 @@
-include src/reactpy_django/py.typed
-recursive-include src/reactpy_django/static *
-recursive-include src/reactpy_django/templates *.html
diff --git a/README.md b/README.md
index 817e684b..3ebd3faf 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# ReactPy-Django
-
-
+
+
@@ -28,8 +28,9 @@
- [Customizable reconnection behavior](https://reactive-python.github.io/reactpy-django/latest/reference/settings/#stability-settings)
- [Customizable disconnection behavior](https://reactive-python.github.io/reactpy-django/latest/reference/template-tag)
- [Multiple root components](https://reactive-python.github.io/reactpy-django/latest/reference/template-tag/)
-- [Cross-process communication/signaling (Channel Layers)](https://reactive-python.github.io/reactpy-django/latest/reference/hooks/#use-channel-layer)
+- [Cross-process communication/signaling](https://reactive-python.github.io/reactpy-django/latest/reference/hooks/#use-channel-layer)
- [Django view to ReactPy component conversion](https://reactive-python.github.io/reactpy-django/latest/reference/components/#view-to-component)
+- [Django form to ReactPy component conversion](https://reactive-python.github.io/reactpy-django/latest/reference/components/#django-form)
- [Django static file access](https://reactive-python.github.io/reactpy-django/latest/reference/components/#django-css)
- [Django database access](https://reactive-python.github.io/reactpy-django/latest/reference/hooks/#use-query)
@@ -83,7 +84,7 @@ def hello_world(recipient: str):
-## [`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/stable/topics/templates/)
diff --git a/docs/examples/html/django_form_bootstrap.html b/docs/examples/html/django_form_bootstrap.html
new file mode 100644
index 00000000..6aba84ca
--- /dev/null
+++ b/docs/examples/html/django_form_bootstrap.html
@@ -0,0 +1,11 @@
+{% load django_bootstrap5 %}
+
+
+{% bootstrap_css %}
+{% bootstrap_javascript %}
+
+
+{% bootstrap_form form %}
+{% bootstrap_button button_type="submit" content="OK" %}
+{% bootstrap_button button_type="reset" content="Reset" %}
diff --git a/docs/examples/html/pyscript-component.html b/docs/examples/html/pyscript_component.html
similarity index 100%
rename from docs/examples/html/pyscript-component.html
rename to docs/examples/html/pyscript_component.html
diff --git a/docs/examples/html/pyscript-initial-object.html b/docs/examples/html/pyscript_initial_object.html
similarity index 100%
rename from docs/examples/html/pyscript-initial-object.html
rename to docs/examples/html/pyscript_initial_object.html
diff --git a/docs/examples/html/pyscript-initial-string.html b/docs/examples/html/pyscript_initial_string.html
similarity index 100%
rename from docs/examples/html/pyscript-initial-string.html
rename to docs/examples/html/pyscript_initial_string.html
diff --git a/docs/examples/html/pyscript-js-module.html b/docs/examples/html/pyscript_local_import.html
similarity index 100%
rename from docs/examples/html/pyscript-js-module.html
rename to docs/examples/html/pyscript_local_import.html
diff --git a/docs/examples/html/pyscript-multiple-files.html b/docs/examples/html/pyscript_multiple_files.html
similarity index 100%
rename from docs/examples/html/pyscript-multiple-files.html
rename to docs/examples/html/pyscript_multiple_files.html
diff --git a/docs/examples/html/pyscript-root.html b/docs/examples/html/pyscript_root.html
similarity index 100%
rename from docs/examples/html/pyscript-root.html
rename to docs/examples/html/pyscript_root.html
diff --git a/docs/examples/html/pyscript-setup.html b/docs/examples/html/pyscript_setup.html
similarity index 100%
rename from docs/examples/html/pyscript-setup.html
rename to docs/examples/html/pyscript_setup.html
diff --git a/docs/examples/html/pyscript-setup-config-object.html b/docs/examples/html/pyscript_setup_config_object.html
similarity index 100%
rename from docs/examples/html/pyscript-setup-config-object.html
rename to docs/examples/html/pyscript_setup_config_object.html
diff --git a/docs/examples/html/pyscript-setup-config-string.html b/docs/examples/html/pyscript_setup_config_string.html
similarity index 100%
rename from docs/examples/html/pyscript-setup-config-string.html
rename to docs/examples/html/pyscript_setup_config_string.html
diff --git a/docs/examples/html/pyscript-setup-dependencies.html b/docs/examples/html/pyscript_setup_dependencies.html
similarity index 100%
rename from docs/examples/html/pyscript-setup-dependencies.html
rename to docs/examples/html/pyscript_setup_dependencies.html
diff --git a/docs/examples/html/pyscript-setup-extra-js-object.html b/docs/examples/html/pyscript_setup_extra_js_object.html
similarity index 100%
rename from docs/examples/html/pyscript-setup-extra-js-object.html
rename to docs/examples/html/pyscript_setup_extra_js_object.html
diff --git a/docs/examples/html/pyscript-setup-extra-js-string.html b/docs/examples/html/pyscript_setup_extra_js_string.html
similarity index 100%
rename from docs/examples/html/pyscript-setup-extra-js-string.html
rename to docs/examples/html/pyscript_setup_extra_js_string.html
diff --git a/docs/examples/html/pyscript_setup_local_interpreter.html b/docs/examples/html/pyscript_setup_local_interpreter.html
new file mode 100644
index 00000000..8371fa94
--- /dev/null
+++ b/docs/examples/html/pyscript_setup_local_interpreter.html
@@ -0,0 +1 @@
+{% pyscript_setup config='{"interpreter":"/static/pyodide/pyodide.mjs"}' %}
diff --git a/docs/examples/html/pyscript-ssr-parent.html b/docs/examples/html/pyscript_ssr_parent.html
similarity index 100%
rename from docs/examples/html/pyscript-ssr-parent.html
rename to docs/examples/html/pyscript_ssr_parent.html
diff --git a/docs/examples/html/pyscript-tag.html b/docs/examples/html/pyscript_tag.html
similarity index 100%
rename from docs/examples/html/pyscript-tag.html
rename to docs/examples/html/pyscript_tag.html
diff --git a/docs/examples/python/configure-asgi.py b/docs/examples/python/configure_asgi.py
similarity index 77%
rename from docs/examples/python/configure-asgi.py
rename to docs/examples/python/configure_asgi.py
index 8081d747..8feb0ec2 100644
--- a/docs/examples/python/configure-asgi.py
+++ b/docs/examples/python/configure_asgi.py
@@ -10,11 +10,10 @@
from channels.routing import ProtocolTypeRouter, URLRouter # noqa: E402
+
from reactpy_django import REACTPY_WEBSOCKET_ROUTE # noqa: E402
-application = ProtocolTypeRouter(
- {
- "http": django_asgi_app,
- "websocket": URLRouter([REACTPY_WEBSOCKET_ROUTE]),
- }
-)
+application = ProtocolTypeRouter({
+ "http": django_asgi_app,
+ "websocket": URLRouter([REACTPY_WEBSOCKET_ROUTE]),
+})
diff --git a/docs/examples/python/configure-asgi-middleware.py b/docs/examples/python/configure_asgi_middleware.py
similarity index 60%
rename from docs/examples/python/configure-asgi-middleware.py
rename to docs/examples/python/configure_asgi_middleware.py
index 6df35a39..0c5a7214 100644
--- a/docs/examples/python/configure-asgi-middleware.py
+++ b/docs/examples/python/configure_asgi_middleware.py
@@ -1,5 +1,6 @@
# Broken load order, only used for linting
from channels.routing import ProtocolTypeRouter, URLRouter
+
from reactpy_django import REACTPY_WEBSOCKET_ROUTE
django_asgi_app = ""
@@ -8,9 +9,7 @@
# start
from channels.auth import AuthMiddlewareStack # noqa: E402
-application = ProtocolTypeRouter(
- {
- "http": django_asgi_app,
- "websocket": AuthMiddlewareStack(URLRouter([REACTPY_WEBSOCKET_ROUTE])),
- }
-)
+application = ProtocolTypeRouter({
+ "http": django_asgi_app,
+ "websocket": AuthMiddlewareStack(URLRouter([REACTPY_WEBSOCKET_ROUTE])),
+})
diff --git a/docs/examples/python/configure-channels-asgi-app.py b/docs/examples/python/configure_channels_asgi_app.py
similarity index 100%
rename from docs/examples/python/configure-channels-asgi-app.py
rename to docs/examples/python/configure_channels_asgi_app.py
diff --git a/docs/examples/python/configure-channels-installed-app.py b/docs/examples/python/configure_channels_installed_app.py
similarity index 100%
rename from docs/examples/python/configure-channels-installed-app.py
rename to docs/examples/python/configure_channels_installed_app.py
diff --git a/docs/examples/python/configure-installed-apps.py b/docs/examples/python/configure_installed_apps.py
similarity index 100%
rename from docs/examples/python/configure-installed-apps.py
rename to docs/examples/python/configure_installed_apps.py
diff --git a/docs/examples/python/configure-urls.py b/docs/examples/python/configure_urls.py
similarity index 100%
rename from docs/examples/python/configure-urls.py
rename to docs/examples/python/configure_urls.py
diff --git a/docs/examples/python/django-css.py b/docs/examples/python/django_css.py
similarity index 99%
rename from docs/examples/python/django-css.py
rename to docs/examples/python/django_css.py
index aeb4addb..c7f60881 100644
--- a/docs/examples/python/django-css.py
+++ b/docs/examples/python/django_css.py
@@ -1,4 +1,5 @@
from reactpy import component, html
+
from reactpy_django.components import django_css
diff --git a/docs/examples/python/django-css-external-link.py b/docs/examples/python/django_css_external_link.py
similarity index 53%
rename from docs/examples/python/django-css-external-link.py
rename to docs/examples/python/django_css_external_link.py
index ac1d0fba..28eb3fca 100644
--- a/docs/examples/python/django-css-external-link.py
+++ b/docs/examples/python/django_css_external_link.py
@@ -4,8 +4,6 @@
@component
def my_component():
return html.div(
- html.link(
- {"rel": "stylesheet", "href": "https://example.com/external-styles.css"}
- ),
+ html.link({"rel": "stylesheet", "href": "https://example.com/external-styles.css"}),
html.button("My Button!"),
)
diff --git a/docs/examples/python/django-css-local-link.py b/docs/examples/python/django_css_local_link.py
similarity index 100%
rename from docs/examples/python/django-css-local-link.py
rename to docs/examples/python/django_css_local_link.py
diff --git a/docs/examples/python/django_form.py b/docs/examples/python/django_form.py
new file mode 100644
index 00000000..51960db1
--- /dev/null
+++ b/docs/examples/python/django_form.py
@@ -0,0 +1,10 @@
+from reactpy import component, html
+
+from example.forms import MyForm
+from reactpy_django.components import django_form
+
+
+@component
+def basic_form():
+ children = [html.input({"type": "submit"})]
+ return django_form(MyForm, bottom_children=children)
diff --git a/docs/examples/python/django_form_bootstrap.py b/docs/examples/python/django_form_bootstrap.py
new file mode 100644
index 00000000..449e1cc4
--- /dev/null
+++ b/docs/examples/python/django_form_bootstrap.py
@@ -0,0 +1,9 @@
+from reactpy import component
+
+from example.forms import MyForm
+from reactpy_django.components import django_form
+
+
+@component
+def basic_form():
+ return django_form(MyForm, form_template="bootstrap_form.html")
diff --git a/docs/examples/python/django_form_class.py b/docs/examples/python/django_form_class.py
new file mode 100644
index 00000000..e556295e
--- /dev/null
+++ b/docs/examples/python/django_form_class.py
@@ -0,0 +1,5 @@
+from django import forms
+
+
+class MyForm(forms.Form):
+ username = forms.CharField(label="Username")
diff --git a/docs/examples/python/django_form_on_success.py b/docs/examples/python/django_form_on_success.py
new file mode 100644
index 00000000..d8b6927c
--- /dev/null
+++ b/docs/examples/python/django_form_on_success.py
@@ -0,0 +1,21 @@
+from reactpy import component, hooks, html
+from reactpy_router import navigate
+
+from example.forms import MyForm
+from reactpy_django.components import django_form
+from reactpy_django.types import FormEventData
+
+
+@component
+def basic_form():
+ submitted, set_submitted = hooks.use_state(False)
+
+ def on_submit(event: FormEventData):
+ """This function will be called when the form is successfully submitted."""
+ set_submitted(True)
+
+ if submitted:
+ return navigate("/homepage")
+
+ children = [html.input({"type": "submit"})]
+ return django_form(MyForm, on_success=on_submit, bottom_children=children)
diff --git a/docs/examples/python/django-js.py b/docs/examples/python/django_js.py
similarity index 99%
rename from docs/examples/python/django-js.py
rename to docs/examples/python/django_js.py
index b4af014c..37868184 100644
--- a/docs/examples/python/django-js.py
+++ b/docs/examples/python/django_js.py
@@ -1,4 +1,5 @@
from reactpy import component, html
+
from reactpy_django.components import django_js
diff --git a/docs/examples/python/django-js-local-script.py b/docs/examples/python/django_js_local_script.py
similarity index 100%
rename from docs/examples/python/django-js-local-script.py
rename to docs/examples/python/django_js_local_script.py
diff --git a/docs/examples/python/django-js-remote-script.py b/docs/examples/python/django_js_remote_script.py
similarity index 100%
rename from docs/examples/python/django-js-remote-script.py
rename to docs/examples/python/django_js_remote_script.py
diff --git a/docs/examples/python/django-query-postprocessor.py b/docs/examples/python/django_query_postprocessor.py
similarity index 99%
rename from docs/examples/python/django-query-postprocessor.py
rename to docs/examples/python/django_query_postprocessor.py
index da33c362..7bdc870c 100644
--- a/docs/examples/python/django-query-postprocessor.py
+++ b/docs/examples/python/django_query_postprocessor.py
@@ -1,5 +1,6 @@
-from example.models import TodoItem
from reactpy import component
+
+from example.models import TodoItem
from reactpy_django.hooks import use_query
from reactpy_django.utils import django_query_postprocessor
diff --git a/docs/examples/python/django-router.py b/docs/examples/python/django_router.py
similarity index 92%
rename from docs/examples/python/django-router.py
rename to docs/examples/python/django_router.py
index 5c845967..721eb939 100644
--- a/docs/examples/python/django-router.py
+++ b/docs/examples/python/django_router.py
@@ -1,7 +1,8 @@
from reactpy import component, html
-from reactpy_django.router import django_router
from reactpy_router import route
+from reactpy_django.router import django_router
+
@component
def my_component():
@@ -14,5 +15,5 @@ def my_component():
route("/router/string/
Create user interfaces from components
Whether you work on your own or with thousands of other developers, using React feels the same. It is @@ -94,9 +94,9 @@
You don't have to build your whole page in ReactPy. Add React to your existing HTML page, and render diff --git a/docs/overrides/home-code-examples/add-interactivity.py b/docs/overrides/homepage_examples/add_interactivity.py similarity index 64% rename from docs/overrides/home-code-examples/add-interactivity.py rename to docs/overrides/homepage_examples/add_interactivity.py index 90976446..9a7bf76f 100644 --- a/docs/overrides/home-code-examples/add-interactivity.py +++ b/docs/overrides/homepage_examples/add_interactivity.py @@ -1,16 +1,15 @@ +# ruff: noqa: INP001 from reactpy import component, html, use_state -def filter_videos(videos, search_text): - return None +def filter_videos(*_, **__): + return [] -def search_input(dictionary, value): - return None +def search_input(*_, **__): ... -def video_list(videos, empty_heading): - return None +def video_list(*_, **__): ... @component @@ -20,7 +19,7 @@ def searchable_video_list(videos): return html._( search_input( - {"on_change": lambda new_text: set_search_text(new_text)}, + {"onChange": lambda event: set_search_text(event["target"]["value"])}, value=search_text, ), video_list( diff --git a/docs/overrides/home-code-examples/add-interactivity-demo.html b/docs/overrides/homepage_examples/add_interactivity_demo.html similarity index 100% rename from docs/overrides/home-code-examples/add-interactivity-demo.html rename to docs/overrides/homepage_examples/add_interactivity_demo.html diff --git a/docs/overrides/home-code-examples/code-block.html b/docs/overrides/homepage_examples/code_block.html similarity index 100% rename from docs/overrides/home-code-examples/code-block.html rename to docs/overrides/homepage_examples/code_block.html diff --git a/docs/overrides/homepage_examples/create_user_interfaces.py b/docs/overrides/homepage_examples/create_user_interfaces.py new file mode 100644 index 00000000..7878aa6b --- /dev/null +++ b/docs/overrides/homepage_examples/create_user_interfaces.py @@ -0,0 +1,21 @@ +# ruff: noqa: INP001 +from reactpy import component, html + + +def thumbnail(*_, **__): ... + + +def like_button(*_, **__): ... + + +@component +def video(data): + return html.div( + thumbnail(data), + html.a( + {"href": data.url}, + html.h3(data.title), + html.p(data.description), + ), + like_button(data), + ) diff --git a/docs/overrides/home-code-examples/create-user-interfaces-demo.html b/docs/overrides/homepage_examples/create_user_interfaces_demo.html similarity index 100% rename from docs/overrides/home-code-examples/create-user-interfaces-demo.html rename to docs/overrides/homepage_examples/create_user_interfaces_demo.html diff --git a/docs/overrides/home-code-examples/write-components-with-python.py b/docs/overrides/homepage_examples/write_components_with_python.py similarity index 76% rename from docs/overrides/home-code-examples/write-components-with-python.py rename to docs/overrides/homepage_examples/write_components_with_python.py index 6af43baa..5993046c 100644 --- a/docs/overrides/home-code-examples/write-components-with-python.py +++ b/docs/overrides/homepage_examples/write_components_with_python.py @@ -1,6 +1,10 @@ +# ruff: noqa: INP001 from reactpy import component, html +def video(*_, **__): ... + + @component def video_list(videos, empty_heading): count = len(videos) @@ -11,5 +15,5 @@ def video_list(videos, empty_heading): return html.section( html.h2(heading), - [video(video) for video in videos], + [video(x, key=x.id) for x in videos], ) diff --git a/docs/overrides/home-code-examples/write-components-with-python-demo.html b/docs/overrides/homepage_examples/write_components_with_python_demo.html similarity index 100% rename from docs/overrides/home-code-examples/write-components-with-python-demo.html rename to docs/overrides/homepage_examples/write_components_with_python_demo.html diff --git a/docs/src/about/code.md b/docs/src/about/code.md deleted file mode 100644 index 205c2c96..00000000 --- a/docs/src/about/code.md +++ /dev/null @@ -1,84 +0,0 @@ -## Overview - -
- - You will need to set up a Python environment to develop ReactPy-Django. - -
- -!!! abstract "Note" - - Looking to contribute features that are not Django specific? - - Everything within the `reactpy-django` repository must be specific to Django integration. Check out the [ReactPy Core documentation](https://reactpy.dev/docs/about/contributor-guide.html) to contribute general features such as components, hooks, and events. - ---- - -## Creating an environment - -If you plan to make code changes to this repository, you will need to install the following dependencies first: - -- [Python 3.9+](https://www.python.org/downloads/) -- [Git](https://git-scm.com/downloads) - -Once done, you should clone this repository: - -```bash linenums="0" -git clone https://github.com/reactive-python/reactpy-django.git -cd reactpy-django -``` - -Then, by running the command below you can install the dependencies needed to run the ReactPy-Django development environment. - -```bash linenums="0" -pip install -r requirements.txt --upgrade --verbose -``` - -!!! warning "Pitfall" - - Some of our development dependencies require a C++ compiler, which is not installed by default on Windows. If you receive errors related to this during installation, follow the instructions in your console errors. - - Additionally, be aware that ReactPy-Django's JavaScript bundle is built within the following scenarios: - - 1. When `pip install` is run on the `reactpy-django` package. - 2. Every time `python manage.py ...` or `nox ...` is run - -## Running the full test suite - -!!! abstract "Note" - - This repository uses [Nox](https://nox.thea.codes/en/stable/) to run tests. For a full test of available scripts run `nox -l`. - -By running the command below you can run the full test suite: - -```bash linenums="0" -nox -t test -``` - -Or, if you want to run the tests in the background: - -```bash linenums="0" -nox -t test -- --headless -``` - -## Running Django tests - -If you want to only run our Django tests in your current environment, you can use the following command: - -```bash linenums="0" -cd tests -python manage.py test -``` - -## Running Django test web server - -If you want to manually run the Django test application, you can use the following command: - -```bash linenums="0" -cd tests -python manage.py runserver -``` - -## Creating a pull request - -{% include-markdown "../../includes/pr.md" %} diff --git a/docs/src/about/contributing.md b/docs/src/about/contributing.md new file mode 100644 index 00000000..06499c3e --- /dev/null +++ b/docs/src/about/contributing.md @@ -0,0 +1,94 @@ +## Overview + ++ + You will need to set up a Python environment to develop ReactPy-Django. + +
+ +!!! abstract "Note" + + Looking to contribute features that are not Django specific? + + Everything within the `reactpy-django` repository must be specific to Django integration. Check out the [ReactPy Core documentation](https://reactpy.dev/docs/about/contributor-guide.html) to contribute general features such as components, hooks, and events. + +--- + +## Creating a development environment + +If you plan to make code changes to this repository, you will need to install the following dependencies first: + +- [Git](https://git-scm.com/downloads) +- [Python 3.9+](https://www.python.org/downloads/) +- [Hatch](https://hatch.pypa.io/latest/) +- [Bun](https://bun.sh/) + +Once you finish installing these dependencies, you can clone this repository: + +```bash linenums="0" +git clone https://github.com/reactive-python/reactpy-django.git +cd reactpy-django +``` + +## Executing test environment commands + +By utilizing `hatch`, the following commands are available to manage the development environment. + +### Tests + +| Command | Description | +| --- | --- | +| `hatch test` | Run Python tests using the current environment's Python version | +| `hatch test --all` | Run tests using all compatible Python versions | +| `hatch test --python 3.9` | Run tests using a specific Python version | +| `hatch test --include "django=5.1"` | Run tests using a specific Django version | +| `hatch test -k test_object_in_templatetag` | Run only a specific test | +| `hatch test --ds test_app.settings_multi_db` | Run tests with a specific Django settings file | +| `hatch run django:runserver` | Manually run the Django development server without running tests | + +??? question "What other arguments are available to me?" + + The `hatch test` command is a wrapper for `pytest`. Hatch "intercepts" a handful of arguments, which can be previewed by typing `hatch test --help`. + + Any additional arguments in the `test` command are provided directly to pytest. See the [pytest documentation](https://docs.pytest.org/en/stable/reference/reference.html#command-line-flags) for what additional arguments are available. + +### Linting and Formatting + +| Command | Description | +| --- | --- | +| `hatch fmt` | Run all linters and formatters | +| `hatch fmt --check` | Run all linters and formatters, but do not save fixes to the disk | +| `hatch fmt --linter` | Run only linters | +| `hatch fmt --formatter` | Run only formatters | +| `hatch run javascript:check` | Run the JavaScript linter/formatter | +| `hatch run javascript:fix` | Run the JavaScript linter/formatter and write fixes to disk | +| `hatch run python:type_check` | Run the Python type checker | + +??? tip "Configure your IDE for linting" + + This repository uses `hatch fmt` for linting and formatting, which is a [modestly customized](https://hatch.pypa.io/latest/config/internal/static-analysis/#default-settings) version of [`ruff`](https://github.com/astral-sh/ruff). + + You can install `ruff` as a plugin to your preferred code editor to create a similar environment. + +### Documentation + +| Command | Description | +| --- | --- | +| `hatch run docs:serve` | Start the [`mkdocs`](https://www.mkdocs.org/) server to view documentation locally | +| `hatch run docs:build` | Build the documentation | +| `hatch run docs:linkcheck` | Check for broken links in the documentation | +| `hatch fmt docs --check` | Run linter on code examples in the documentation | + +### Environment Management + +| Command | Description | +| --- | --- | +| `hatch build --clean` | Build the package from source | +| `hatch env prune` | Delete all virtual environments created by `hatch` | +| `hatch python install 3.12` | Install a specific Python version to your system | + +??? tip "Check out Hatch for all available commands!" + + This documentation only covers commonly used commands. + + You can type `hatch --help` to see all available commands. diff --git a/docs/src/about/docs.md b/docs/src/about/docs.md deleted file mode 100644 index 712570ec..00000000 --- a/docs/src/about/docs.md +++ /dev/null @@ -1,45 +0,0 @@ -## Overview - -- -You will need to set up a Python environment to create, test, and preview docs changes. - -
- ---- - -## Modifying Docs - -If you plan to make changes to this documentation, you will need to install the following dependencies first: - -- [Python 3.9+](https://www.python.org/downloads/) -- [Git](https://git-scm.com/downloads) - -Once done, you should clone this repository: - -```bash linenums="0" -git clone https://github.com/reactive-python/reactpy-django.git -cd reactpy-django -``` - -Then, by running the command below you can: - -- Install an editable version of the documentation -- Self-host a test server for the documentation - -```bash linenums="0" -pip install -r requirements.txt --upgrade -``` - -Finally, to verify that everything is working properly, you can manually run the docs preview web server. - -```bash linenums="0" -cd docs -mkdocs serve -``` - -Navigate to [`http://127.0.0.1:8000`](http://127.0.0.1:8000) to view a preview of the documentation. - -## GitHub Pull Request - -{% include-markdown "../../includes/pr.md" %} diff --git a/docs/src/assets/css/home.css b/docs/src/assets/css/home.css index c72e7093..70f05cf2 100644 --- a/docs/src/assets/css/home.css +++ b/docs/src/assets/css/home.css @@ -1,335 +1,337 @@ img.home-logo { - height: 120px; + height: 120px; } .home .row { - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - padding: 6rem 0.8rem; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + padding: 6rem 0.8rem; } .home .row:not(.first, .stripe) { - background: var(--row-bg-color); + background: var(--row-bg-color); } .home .row.stripe { - background: var(--row-stripe-bg-color); - border: 0 solid var(--stripe-border-color); - border-top-width: 1px; - border-bottom-width: 1px; + background: var(--row-stripe-bg-color); + border: 0 solid var(--stripe-border-color); + border-top-width: 1px; + border-bottom-width: 1px; } .home .row.first { - text-align: center; + text-align: center; } .home .row h1 { - max-width: 28rem; - line-height: 1.15; - font-weight: 500; - margin-bottom: 0.55rem; - margin-top: -1rem; + max-width: 28rem; + line-height: 1.15; + font-weight: 500; + margin-bottom: 0.55rem; + margin-top: -1rem; } .home .row.first h1 { - margin-top: 0.55rem; - margin-bottom: -0.75rem; + margin-top: 0.55rem; + margin-bottom: -0.75rem; } .home .row > p { - max-width: 35rem; - line-height: 1.5; - font-weight: 400; + max-width: 35rem; + line-height: 1.5; + font-weight: 400; } .home .row.first > p { - font-size: 32px; - font-weight: 500; + font-size: 32px; + font-weight: 500; } /* Code blocks */ .home .row .tabbed-set { - background: var(--home-tabbed-set-bg-color); - margin: 0; + background: var(--home-tabbed-set-bg-color); + margin: 0; } .home .row .tabbed-content { - padding: 20px 18px; - overflow-x: auto; + padding: 20px 18px; + overflow-x: auto; } .home .row .tabbed-content img { - user-select: none; - -moz-user-select: none; - -webkit-user-drag: none; - -webkit-user-select: none; - -ms-user-select: none; - max-width: 580px; + user-select: none; + -moz-user-select: none; + -webkit-user-drag: none; + -webkit-user-select: none; + -ms-user-select: none; + max-width: 580px; } .home .row .tabbed-content { - -webkit-filter: var(--code-block-filter); - filter: var(--code-block-filter); + -webkit-filter: var(--code-block-filter); + filter: var(--code-block-filter); } /* Code examples */ .home .example-container { - background: radial-gradient( - circle at 0% 100%, - rgb(41 84 147 / 11%) 0%, - rgb(22 89 189 / 4%) 70%, - rgb(48 99 175 / 0%) 80% - ), - radial-gradient( - circle at 100% 100%, - rgb(24 87 45 / 55%) 0%, - rgb(29 61 12 / 4%) 70%, - rgb(94 116 93 / 0%) 80% - ), - radial-gradient( - circle at 100% 0%, - rgba(54, 66, 84, 0.55) 0%, - rgb(102 111 125 / 4%) 70%, - rgba(54, 66, 84, 0) 80% - ), - radial-gradient( - circle at 0% 0%, - rgba(91, 114, 135, 0.55) 0%, - rgb(45 111 171 / 4%) 70%, - rgb(5 82 153 / 0%) 80% - ), - rgb(0, 0, 0) center center/cover no-repeat fixed; - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - align-items: center; - border-radius: 16px; - margin: 30px 0; - max-width: 100%; - grid-column-gap: 20px; - padding-left: 20px; - padding-right: 20px; + background: radial-gradient( + circle at 0% 100%, + rgb(41 84 147 / 11%) 0%, + rgb(22 89 189 / 4%) 70%, + rgb(48 99 175 / 0%) 80% + ), + radial-gradient( + circle at 100% 100%, + rgb(24 87 45 / 55%) 0%, + rgb(29 61 12 / 4%) 70%, + rgb(94 116 93 / 0%) 80% + ), + radial-gradient( + circle at 100% 0%, + rgba(54, 66, 84, 0.55) 0%, + rgb(102 111 125 / 4%) 70%, + rgba(54, 66, 84, 0) 80% + ), + radial-gradient( + circle at 0% 0%, + rgba(91, 114, 135, 0.55) 0%, + rgb(45 111 171 / 4%) 70%, + rgb(5 82 153 / 0%) 80% + ), + rgb(0, 0, 0) center center/cover no-repeat fixed; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + align-items: center; + border-radius: 16px; + margin: 30px 0; + max-width: 100%; + grid-column-gap: 20px; + padding-left: 20px; + padding-right: 20px; } .home .demo .white-bg { - background: #fff; - border-radius: 16px; - display: flex; - flex-direction: column; - max-width: 590px; - min-width: -webkit-min-content; - min-width: -moz-min-content; - min-width: min-content; - row-gap: 1rem; - padding: 1rem; + background: #fff; + border-radius: 16px; + display: flex; + flex-direction: column; + max-width: 590px; + min-width: -webkit-min-content; + min-width: -moz-min-content; + min-width: min-content; + row-gap: 1rem; + padding: 1rem; + border: 1px rgb(0 0 0 / 20%) solid; + overflow: hidden; } .home .demo .vid-row { - display: flex; - flex-direction: row; - -moz-column-gap: 12px; - column-gap: 12px; + display: flex; + flex-direction: row; + -moz-column-gap: 12px; + column-gap: 12px; } .home .demo { - color: #000; + color: #000; } .home .demo .vid-thumbnail { - background: radial-gradient( - circle at 0% 100%, - rgb(41 84 147 / 55%) 0%, - rgb(22 89 189 / 4%) 70%, - rgb(48 99 175 / 0%) 80% - ), - radial-gradient( - circle at 100% 100%, - rgb(24 63 87 / 55%) 0%, - rgb(29 61 12 / 4%) 70%, - rgb(94 116 93 / 0%) 80% - ), - radial-gradient( - circle at 100% 0%, - rgba(54, 66, 84, 0.55) 0%, - rgb(102 111 125 / 4%) 70%, - rgba(54, 66, 84, 0) 80% - ), - radial-gradient( - circle at 0% 0%, - rgba(91, 114, 135, 0.55) 0%, - rgb(45 111 171 / 4%) 70%, - rgb(5 82 153 / 0%) 80% - ), - rgb(0, 0, 0) center center/cover no-repeat fixed; - width: 9rem; - aspect-ratio: 16 / 9; - border-radius: 8px; - display: flex; - justify-content: center; - align-items: center; + background: radial-gradient( + circle at 0% 100%, + rgb(41 84 147 / 55%) 0%, + rgb(22 89 189 / 4%) 70%, + rgb(48 99 175 / 0%) 80% + ), + radial-gradient( + circle at 100% 100%, + rgb(24 63 87 / 55%) 0%, + rgb(29 61 12 / 4%) 70%, + rgb(94 116 93 / 0%) 80% + ), + radial-gradient( + circle at 100% 0%, + rgba(54, 66, 84, 0.55) 0%, + rgb(102 111 125 / 4%) 70%, + rgba(54, 66, 84, 0) 80% + ), + radial-gradient( + circle at 0% 0%, + rgba(91, 114, 135, 0.55) 0%, + rgb(45 111 171 / 4%) 70%, + rgb(5 82 153 / 0%) 80% + ), + rgb(0, 0, 0) center center/cover no-repeat fixed; + width: 9rem; + aspect-ratio: 16 / 9; + border-radius: 8px; + display: flex; + justify-content: center; + align-items: center; } .home .demo .vid-text { - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + width: 100%; } .home .demo h2 { - font-size: 18px; - line-height: 1.375; - margin: 0; - text-align: left; - font-weight: 700; + font-size: 18px; + line-height: 1.375; + margin: 0; + text-align: left; + font-weight: 700; } .home .demo h3 { - font-size: 16px; - line-height: 1.25; - margin: 0; + font-size: 16px; + line-height: 1.25; + margin: 0; } .home .demo p { - font-size: 14px; - line-height: 1.375; - margin: 0; + font-size: 14px; + line-height: 1.375; + margin: 0; } .home .demo .browser-nav-url { - background: rgba(153, 161, 179, 0.2); - border-radius: 9999px; - font-size: 14px; - color: grey; - display: flex; - align-items: center; - justify-content: center; - -moz-column-gap: 5px; - column-gap: 5px; + background: rgba(153, 161, 179, 0.2); + border-radius: 9999px; + font-size: 14px; + color: grey; + display: flex; + align-items: center; + justify-content: center; + -moz-column-gap: 5px; + column-gap: 5px; } .home .demo .browser-navbar { - margin: -1rem; - margin-bottom: 0; - padding: 0.75rem 1rem; - border-bottom: 1px solid darkgrey; + margin: -1rem; + margin-bottom: 0; + padding: 0.75rem 1rem; + border-bottom: 1px solid darkgrey; } .home .demo .browser-viewport { - background: #fff; - border-radius: 16px; - display: flex; - flex-direction: column; - row-gap: 1rem; - height: 400px; - overflow-y: scroll; - margin: -1rem; - padding: 1rem; + background: #fff; + border-radius: 16px; + display: flex; + flex-direction: column; + row-gap: 1rem; + height: 400px; + overflow-y: scroll; + margin: -1rem; + padding: 1rem; } .home .demo .browser-viewport .search-header > h1 { - color: #000; - text-align: left; - font-size: 24px; - margin: 0; + color: #000; + text-align: left; + font-size: 24px; + margin: 0; } .home .demo .browser-viewport .search-header > p { - text-align: left; - font-size: 16px; - margin: 10px 0; + text-align: left; + font-size: 16px; + margin: 10px 0; } .home .demo .search-bar input { - width: 100%; - background: rgba(153, 161, 179, 0.2); - border-radius: 9999px; - padding-left: 40px; - padding-right: 40px; - height: 40px; - color: #000; + width: 100%; + background: rgba(153, 161, 179, 0.2); + border-radius: 9999px; + padding-left: 40px; + padding-right: 40px; + height: 40px; + color: #000; } .home .demo .search-bar svg { - height: 40px; - position: absolute; - transform: translateX(75%); + height: 40px; + position: absolute; + transform: translateX(75%); } .home .demo .search-bar { - position: relative; + position: relative; } /* Desktop Styling */ @media screen and (min-width: 60em) { - .home .row { - text-align: center; - } - .home .row > p { - font-size: 21px; - } - .home .row > h1 { - font-size: 52px; - } - .home .row .pop-left { - margin-left: -20px; - margin-right: 0; - margin-top: -20px; - margin-bottom: -20px; - } - .home .row .pop-right { - margin-left: 0px; - margin-right: 0px; - margin-top: -20px; - margin-bottom: -20px; - } + .home .row { + text-align: center; + } + .home .row > p { + font-size: 21px; + } + .home .row > h1 { + font-size: 52px; + } + .home .row .pop-left { + margin-left: -20px; + margin-right: 0; + margin-top: -20px; + margin-bottom: -20px; + } + .home .row .pop-right { + margin-left: 0px; + margin-right: 0px; + margin-top: -20px; + margin-bottom: -20px; + } } /* Mobile Styling */ @media screen and (max-width: 60em) { - .home .row { - padding: 4rem 0.8rem; - } - .home .row > h1, - .home .row > p { - padding-left: 1rem; - padding-right: 1rem; - } - .home .row.first { - padding-top: 2rem; - } - .home-btns { - width: 100%; - display: grid; - grid-gap: 0.5rem; - gap: 0.5rem; - } - .home .example-container { - display: flex; - flex-direction: column; - row-gap: 20px; - width: 100%; - justify-content: center; - border-radius: 0; - padding: 1rem 0; - } - .home .row { - padding-left: 0; - padding-right: 0; - } - .home .tabbed-set { - width: 100%; - border-radius: 0; - } - .home .demo { - width: 100%; - display: flex; - justify-content: center; - } - .home .demo > .white-bg { - width: 80%; - max-width: 80%; - } + .home .row { + padding: 4rem 0.8rem; + } + .home .row > h1, + .home .row > p { + padding-left: 1rem; + padding-right: 1rem; + } + .home .row.first { + padding-top: 2rem; + } + .home-btns { + width: 100%; + display: grid; + grid-gap: 0.5rem; + gap: 0.5rem; + } + .home .example-container { + display: flex; + flex-direction: column; + row-gap: 20px; + width: 100%; + justify-content: center; + border-radius: 0; + padding: 1rem 0; + } + .home .row { + padding-left: 0; + padding-right: 0; + } + .home .tabbed-set { + width: 100%; + border-radius: 0; + } + .home .demo { + width: 100%; + display: flex; + justify-content: center; + } + .home .demo > .white-bg { + width: 80%; + max-width: 80%; + } } diff --git a/docs/src/assets/img/add-interactivity.png b/docs/src/assets/img/add-interactivity.png index e5e24d29..c3243190 100644 Binary files a/docs/src/assets/img/add-interactivity.png and b/docs/src/assets/img/add-interactivity.png differ diff --git a/docs/src/assets/img/create-user-interfaces.png b/docs/src/assets/img/create-user-interfaces.png index 13abd064..06f6ea0c 100644 Binary files a/docs/src/assets/img/create-user-interfaces.png and b/docs/src/assets/img/create-user-interfaces.png differ diff --git a/docs/src/assets/img/write-components-with-python.png b/docs/src/assets/img/write-components-with-python.png index ba34cdf9..380d2c3a 100644 Binary files a/docs/src/assets/img/write-components-with-python.png and b/docs/src/assets/img/write-components-with-python.png differ diff --git a/docs/src/dictionary.txt b/docs/src/dictionary.txt index 66265e78..d2ff722d 100644 --- a/docs/src/dictionary.txt +++ b/docs/src/dictionary.txt @@ -1,43 +1,51 @@ +asgi +async +backend +backends +backhaul +broadcasted +changelog django -sanic -plotly +frontend +frontends +hello_world +html +iframe +jupyter +keyworded +middleware +misconfiguration +misconfigurations +my_template nox -WebSocket -WebSockets -changelog -async +plotly +postfixed +postprocessing +postprocessor pre prefetch prefetching preloader -whitespace +preprocessor +py +pyodide +pyscript +reactpy refetch refetched refetching -html -jupyter -iframe -keyworded +sanic +serializable stylesheet stylesheets -unstyled -py -reactpy -asgi -postfixed -postprocessing -serializable -postprocessor -preprocessor -middleware -backends -backend -frontend -frontends -misconfiguration -misconfigurations -backhaul sublicense -broadcasted -hello_world -my_template +unstyled +WebSocket +WebSockets +whitespace +pytest +linter +linters +linting +formatters +bootstrap_form diff --git a/docs/src/learn/add-reactpy-to-a-django-project.md b/docs/src/learn/add-reactpy-to-a-django-project.md index dd258737..371893e1 100644 --- a/docs/src/learn/add-reactpy-to-a-django-project.md +++ b/docs/src/learn/add-reactpy-to-a-django-project.md @@ -8,7 +8,7 @@ If you want to add some interactivity to your existing **Django project**, you d !!! abstract "Note" - These docs assumes you have already created [a **Django project**](https://docs.djangoproject.com/en/dev/intro/tutorial01/), which involves creating and installing at least one **Django app**. + These docs assumes you have already created [a **Django project**](https://docs.djangoproject.com/en/stable/intro/tutorial01/), which involves creating and installing at least one **Django app**. If do not have a **Django project**, check out this [9 minute YouTube tutorial](https://www.youtube.com/watch?v=ZsJRXS_vrw0) created by _IDG TECHtalk_. @@ -24,31 +24,31 @@ pip install reactpy-django ## Step 2: Configure `settings.py` -Add `#!python "reactpy_django"` to [`INSTALLED_APPS`](https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-INSTALLED_APPS) in your [`settings.py`](https://docs.djangoproject.com/en/dev/topics/settings/) file. +Add `#!python "reactpy_django"` to [`INSTALLED_APPS`](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-INSTALLED_APPS) in your [`settings.py`](https://docs.djangoproject.com/en/stable/topics/settings/) file. === "settings.py" ```python - {% include "../../examples/python/configure-installed-apps.py" %} + {% include "../../examples/python/configure_installed_apps.py" %} ``` ??? warning "Enable ASGI and Django Channels (Required)" ReactPy-Django requires Django ASGI and [Django Channels](https://github.com/django/channels) WebSockets. - If you have not enabled ASGI on your **Django project** yet, here is a summary of the [`django`](https://docs.djangoproject.com/en/dev/howto/deployment/asgi/) and [`channels`](https://channels.readthedocs.io/en/stable/installation.html) installation docs: + If you have not enabled ASGI on your **Django project** yet, here is a summary of the [`django`](https://docs.djangoproject.com/en/stable/howto/deployment/asgi/) and [`channels`](https://channels.readthedocs.io/en/stable/installation.html) installation docs: 1. Install `channels[daphne]` 2. Add `#!python "daphne"` to `#!python INSTALLED_APPS`. ```python linenums="0" - {% include "../../examples/python/configure-channels-installed-app.py" %} + {% include "../../examples/python/configure_channels_installed_app.py" %} ``` 3. Set your `#!python ASGI_APPLICATION` variable. ```python linenums="0" - {% include "../../examples/python/configure-channels-asgi-app.py" %} + {% include "../../examples/python/configure_channels_asgi_app.py" %} ``` ??? info "Configure ReactPy settings (Optional)" @@ -59,22 +59,22 @@ Add `#!python "reactpy_django"` to [`INSTALLED_APPS`](https://docs.djangoproject ## Step 3: Configure `urls.py` -Add ReactPy HTTP paths to your `#!python urlpatterns` in your [`urls.py`](https://docs.djangoproject.com/en/dev/topics/http/urls/) file. +Add ReactPy HTTP paths to your `#!python urlpatterns` in your [`urls.py`](https://docs.djangoproject.com/en/stable/topics/http/urls/) file. === "urls.py" ```python - {% include "../../examples/python/configure-urls.py" %} + {% include "../../examples/python/configure_urls.py" %} ``` ## Step 4: Configure `asgi.py` -Register ReactPy's WebSocket using `#!python REACTPY_WEBSOCKET_ROUTE` in your [`asgi.py`](https://docs.djangoproject.com/en/dev/howto/deployment/asgi/) file. +Register ReactPy's WebSocket using `#!python REACTPY_WEBSOCKET_ROUTE` in your [`asgi.py`](https://docs.djangoproject.com/en/stable/howto/deployment/asgi/) file. === "asgi.py" ```python - {% include "../../examples/python/configure-asgi.py" %} + {% include "../../examples/python/configure_asgi.py" %} ``` ??? info "Add `#!python AuthMiddlewareStack` (Optional)" @@ -87,9 +87,7 @@ Register ReactPy's WebSocket using `#!python REACTPY_WEBSOCKET_ROUTE` in your [` In these situations will need to ensure you are using `#!python AuthMiddlewareStack`. - ```python linenums="0" - {% include "../../examples/python/configure-asgi-middleware.py" start="# start" %} - ``` + {% include "../../includes/auth-middleware-stack.md" %} ??? question "Where is my `asgi.py`?" @@ -97,7 +95,7 @@ Register ReactPy's WebSocket using `#!python REACTPY_WEBSOCKET_ROUTE` in your [` ## Step 5: Run database migrations -Run Django's [`migrate` command](https://docs.djangoproject.com/en/dev/topics/migrations/) to initialize ReactPy-Django's database table. +Run Django's [`migrate` command](https://docs.djangoproject.com/en/stable/topics/migrations/) to initialize ReactPy-Django's database table. ```bash linenums="0" python manage.py migrate @@ -105,7 +103,7 @@ python manage.py migrate ## Step 6: Check your configuration -Run Django's [`check` command](https://docs.djangoproject.com/en/dev/ref/django-admin/#check) to verify if ReactPy was set up correctly. +Run Django's [`check` command](https://docs.djangoproject.com/en/stable/ref/django-admin/#check) to verify if ReactPy was set up correctly. ```bash linenums="0" python manage.py check @@ -113,7 +111,7 @@ python manage.py check ## Step 7: Create your first component -The [next step](./your-first-component.md) will show you how to create your first ReactPy component. +The [next page](./your-first-component.md) will show you how to create your first ReactPy component. Prefer a quick summary? Read the **At a Glance** section below. diff --git a/docs/src/learn/your-first-component.md b/docs/src/learn/your-first-component.md index 08df6a57..e3be5da4 100644 --- a/docs/src/learn/your-first-component.md +++ b/docs/src/learn/your-first-component.md @@ -8,7 +8,7 @@ Components are one of the core concepts of ReactPy. They are the foundation upon !!! abstract "Note" - If you have reached this point, you should have already [installed ReactPy-Django](../learn/add-reactpy-to-a-django-project.md) through the previous steps. + If you have reached this point, you should have already [installed ReactPy-Django](./add-reactpy-to-a-django-project.md) through the previous steps. --- @@ -18,7 +18,7 @@ You will now need to pick at least one **Django app** to start using ReactPy-Dja For the following examples, we will assume the following: -1. You have a **Django app** named `my_app`, which was created by Django's [`startapp` command](https://docs.djangoproject.com/en/dev/intro/tutorial01/#creating-the-polls-app). +1. You have a **Django app** named `my_app`, which was created by Django's [`startapp` command](https://docs.djangoproject.com/en/stable/intro/tutorial01/#creating-the-polls-app). 2. You have placed `my_app` directly into your **Django project** folder (`./example_project/my_app`). This is common for small projects. ??? question "How do I organize my Django project for ReactPy?" @@ -31,7 +31,7 @@ You will need a file to start creating ReactPy components. We recommend creating a `components.py` file within your chosen **Django app** to start out. For this example, the file path will look like this: `./example_project/my_app/components.py`. -Within this file, you can define your component functions using ReactPy's `#!python @component` decorator. +Within this file, you will define your component function(s) using the `#!python @component` decorator. === "components.py" @@ -43,7 +43,7 @@ Within this file, you can define your component functions using ReactPy's `#!pyt We recommend creating a `components.py` for small **Django apps**. If your app has a lot of components, you should consider breaking them apart into individual modules such as `components/navbar.py`. - Ultimately, components are referenced by Python dotted path in `my_template.html` ([_see next step_](#embedding-in-a-template)). This path must be valid to Python's `#!python importlib`. + Ultimately, components are referenced by Python dotted path in `my_template.html` ([_see next step_](#embedding-in-a-template)). This dotted path must be valid to Python's `#!python importlib`. ??? question "What does the decorator actually do?" @@ -66,30 +66,36 @@ Additionally, you can pass in `#!python args` and `#!python kwargs` into your co {% include-markdown "../../../README.md" start="" end="" %} +???+ tip "Components are automatically registered!" + + ReactPy-Django will automatically register any component that is referenced in a Django HTML template. This means you typically do not need to [manually register](../reference/utils.md#register-component) components in your **Django app**. + + Please note that this HTML template must be properly stored within a registered Django app. ReactPy-Django will output a console log message containing all detected components when the server starts up. + {% include-markdown "../reference/template-tag.md" start="" end="" %} {% include-markdown "../reference/template-tag.md" start="" end="" %} ??? question "Where is my templates folder?" - If you do not have a `./templates/` folder in your **Django app**, you can simply create one! Keep in mind, templates within this folder will not be detected by Django unless you [add the corresponding **Django app** to `settings.py:INSTALLED_APPS`](https://docs.djangoproject.com/en/dev/ref/applications/#configuring-applications). + If you do not have a `./templates/` folder in your **Django app**, you can simply create one! Keep in mind, templates within this folder will not be detected by Django unless you [add the corresponding **Django app** to `settings.py:INSTALLED_APPS`](https://docs.djangoproject.com/en/stable/ref/applications/#configuring-applications). ## Setting up a Django view -Within your **Django app**'s `views.py` file, you will need to [create a view function](https://docs.djangoproject.com/en/dev/intro/tutorial01/#write-your-first-view) to render the HTML template `my_template.html` ([_from the previous step_](#embedding-in-a-template)). +Within your **Django app**'s `views.py` file, you will need to [create a view function](https://docs.djangoproject.com/en/stable/intro/tutorial01/#write-your-first-view) to render the HTML template `my_template.html` ([_from the previous step_](#embedding-in-a-template)). === "views.py" ```python - {% include "../../examples/python/example/views.py" %} + {% include "../../examples/python/first_view.py" %} ``` -We will add this new view into your [`urls.py`](https://docs.djangoproject.com/en/dev/intro/tutorial01/#write-your-first-view) and define what URL it should be accessible at. +We will add this new view into your [`urls.py`](https://docs.djangoproject.com/en/stable/intro/tutorial01/#write-your-first-view) and define what URL it should be accessible at. === "urls.py" ```python - {% include "../../examples/python/example/urls.py" %} + {% include "../../examples/python/first_urls.py" %} ``` ??? question "Which urls.py do I add my views to?" @@ -98,7 +104,7 @@ We will add this new view into your [`urls.py`](https://docs.djangoproject.com/e Once you reach that point, we recommend creating an individual `urls.py` within each of your **Django apps**. - Then, within your **Django project's** `urls.py` you will use Django's [`include` function](https://docs.djangoproject.com/en/dev/ref/urls/#include) to link it all together. + Then, within your **Django project's** `urls.py` you will use Django's [`include` function](https://docs.djangoproject.com/en/stable/ref/urls/#include) to link it all together. ## Viewing your component @@ -114,7 +120,7 @@ If you copy-pasted our example component, you will now see your component displa ??? warning "Do not use `manage.py runserver` for production" - This command is only intended for development purposes. For production deployments make sure to read [Django's documentation](https://docs.djangoproject.com/en/dev/howto/deployment/). + This command is only intended for development purposes. For production deployments make sure to read [Django's documentation](https://docs.djangoproject.com/en/stable/howto/deployment/). ## Learn more diff --git a/docs/src/reference/components.md b/docs/src/reference/components.md index aaeabba7..448af463 100644 --- a/docs/src/reference/components.md +++ b/docs/src/reference/components.md @@ -12,24 +12,26 @@ We supply some pre-designed that components can be used to help simplify develop This allows you to embedded any number of client-side PyScript components within traditional ReactPy components. -{% include-markdown "../reference/template-tag.md" start="" end="" %} +{% include-markdown "./template-tag.md" start="" end="" %} + +{% include-markdown "./template-tag.md" start="" end="" %} === "components.py" ```python - {% include "../../examples/python/pyscript-ssr-parent.py" %} + {% include "../../examples/python/pyscript_ssr_parent.py" %} ``` === "root.py" ```python - {% include "../../examples/python/pyscript-ssr-child.py" %} + {% include "../../examples/python/pyscript_ssr_child.py" %} ``` === "my_template.html" ```jinja - {% include "../../examples/html/pyscript-ssr-parent.html" %} + {% include "../../examples/html/pyscript_ssr_parent.html" %} ``` ??? example "See Interface" @@ -51,31 +53,33 @@ This allows you to embedded any number of client-side PyScript components within === "my_template.html" ```jinja - {% include "../../examples/html/pyscript-setup.html" %} + {% include "../../examples/html/pyscript_setup.html" %} ``` -{% include-markdown "../reference/template-tag.md" start="" end="" %} +{% include-markdown "./template-tag.md" start="" end="" %} + +{% include-markdown "./template-tag.md" start="" end="" %} -{% include-markdown "../reference/template-tag.md" start="" end="" trailing-newlines=false preserve-includer-indent=false %} +{% include-markdown "./template-tag.md" start="" end="" trailing-newlines=false preserve-includer-indent=false %} === "components.py" ```python - {% include "../../examples/python/pyscript-component-multiple-files-root.py" %} + {% include "../../examples/python/pyscript_component_multiple_files_root.py" %} ``` === "root.py" ```python - {% include "../../examples/python/pyscript-multiple-files-root.py" %} + {% include "../../examples/python/pyscript_multiple_files_root.py" %} ``` === "child.py" ```python - {% include "../../examples/python/pyscript-multiple-files-child.py" %} + {% include "../../examples/python/pyscript_multiple_files_child.py" %} ``` ??? question "How do I display something while the component is loading?" @@ -87,7 +91,7 @@ This allows you to embedded any number of client-side PyScript components within === "components.py" ```python - {% include "../../examples/python/pyscript-component-initial-object.py" %} + {% include "../../examples/python/pyscript_component_initial_object.py" %} ``` However, you can also use a string containing raw HTML. @@ -95,7 +99,7 @@ This allows you to embedded any number of client-side PyScript components within === "components.py" ```python - {% include "../../examples/python/pyscript-component-initial-string.py" %} + {% include "../../examples/python/pyscript_component_initial_string.py" %} ``` ??? question "Can I use a different name for my root component?" @@ -105,13 +109,13 @@ This allows you to embedded any number of client-side PyScript components within === "components.py" ```python - {% include "../../examples/python/pyscript-component-root.py" %} + {% include "../../examples/python/pyscript_component_root.py" %} ``` === "main.py" ```python - {% include "../../examples/python/pyscript-root.py" %} + {% include "../../examples/python/pyscript_root.py" %} ``` --- @@ -122,7 +126,7 @@ Automatically convert a Django view into a component. At this time, this works best with static views with no interactivity. -Compatible with sync or async [Function Based Views](https://docs.djangoproject.com/en/dev/topics/http/views/) and [Class Based Views](https://docs.djangoproject.com/en/dev/topics/class-based-views/). +Compatible with sync or async [Function Based Views](https://docs.djangoproject.com/en/stable/topics/http/views/) and [Class Based Views](https://docs.djangoproject.com/en/stable/topics/class-based-views/). === "components.py" @@ -154,11 +158,11 @@ Compatible with sync or async [Function Based Views](https://docs.djangoproject. ??? info "Existing limitations" - There are currently several limitations of using `#!python view_to_component` that may be resolved in a future version. + There are currently several limitations of using `#!python view_to_component` that will be [resolved in a future version](https://github.com/reactive-python/reactpy-django/issues/269). - Requires manual intervention to change HTTP methods to anything other than `GET`. - ReactPy events cannot conveniently be attached to converted view HTML. - - Has no option to automatically intercept local anchor link (such as `#!html `) click events. + - Has no option to automatically intercept click events from hyperlinks (such as `#!html `). ??? question "How do I use this for Class Based Views?" @@ -169,7 +173,7 @@ Compatible with sync or async [Function Based Views](https://docs.djangoproject. === "components.py" ```python - {% include "../../examples/python/vtc-cbv.py" %} + {% include "../../examples/python/vtc_cbv.py" %} ``` === "views.py" @@ -185,7 +189,7 @@ Compatible with sync or async [Function Based Views](https://docs.djangoproject. === "components.py" ```python - {% include "../../examples/python/vtc-args.py" %} + {% include "../../examples/python/vtc_args.py" %} ``` === "views.py" @@ -213,7 +217,7 @@ Compatible with sync or async [Function Based Views](https://docs.djangoproject. === "components.py" ```python - {% include "../../examples/python/vtc-strict-parsing.py" %} + {% include "../../examples/python/vtc_strict_parsing.py" %} ``` === "views.py" @@ -235,7 +239,7 @@ Compatible with sync or async [Function Based Views](https://docs.djangoproject. === "components.py" ```python - {% include "../../examples/python/vtc-transforms.py" %} + {% include "../../examples/python/vtc_transforms.py" %} ``` === "views.py" @@ -252,7 +256,7 @@ Automatically convert a Django view into an [`iframe` element](https://www.techt The contents of this `#!python iframe` is handled entirely by traditional Django view rendering. While this solution is compatible with more views than `#!python view_to_component`, it comes with different limitations. -Compatible with sync or async [Function Based Views](https://docs.djangoproject.com/en/dev/topics/http/views/) and [Class Based Views](https://docs.djangoproject.com/en/dev/topics/class-based-views/). +Compatible with sync or async [Function Based Views](https://docs.djangoproject.com/en/stable/topics/http/views/) and [Class Based Views](https://docs.djangoproject.com/en/stable/topics/class-based-views/). === "components.py" @@ -290,12 +294,12 @@ Compatible with sync or async [Function Based Views](https://docs.djangoproject. ??? info "Existing limitations" - There are currently several limitations of using `#!python view_to_iframe` that may be resolved in a future version. + There are currently several limitations of using `#!python view_to_iframe` which may be [resolved in a future version](https://github.com/reactive-python/reactpy-django/issues/268). - No built-in method of signalling events back to the parent component. - - All provided `#!python *args` and `#!python *kwargs` must be serializable values, since they are encoded into the URL. + - All provided `#!python args` and `#!python kwargs` must be serializable values, since they are encoded into the URL. - The `#!python iframe` will always load **after** the parent component. - - CSS styling for `#!python iframe` elements tends to be awkward/difficult. + - CSS styling for `#!python iframe` elements tends to be awkward. ??? question "How do I use this for Class Based Views?" @@ -306,7 +310,7 @@ Compatible with sync or async [Function Based Views](https://docs.djangoproject. === "components.py" ```python - {% include "../../examples/python/vti-cbv.py" %} + {% include "../../examples/python/vti_cbv.py" %} ``` === "views.py" @@ -330,7 +334,7 @@ Compatible with sync or async [Function Based Views](https://docs.djangoproject. === "components.py" ```python - {% include "../../examples/python/vti-args.py" %} + {% include "../../examples/python/vti_args.py" %} ``` === "views.py" @@ -362,7 +366,7 @@ Compatible with sync or async [Function Based Views](https://docs.djangoproject. === "components.py" ```python - {% include "../../examples/python/vti-extra-props.py" %} + {% include "../../examples/python/vti_extra_props.py" %} ``` === "views.py" @@ -379,14 +383,112 @@ Compatible with sync or async [Function Based Views](https://docs.djangoproject. --- +## Django Form + +Automatically convert a Django form into a ReactPy component. + +Compatible with both [standard Django forms](https://docs.djangoproject.com/en/stable/topics/forms/#building-a-form) and [ModelForms](https://docs.djangoproject.com/en/stable/topics/forms/modelforms/). + +=== "components.py" + + ```python + {% include "../../examples/python/django_form.py" %} + ``` + +=== "forms.py" + + ```python + {% include "../../examples/python/django_form_class.py" %} + ``` + +??? example "See Interface" + + **Parameters** + + | Name | Type | Description | Default | + | --- | --- | --- | --- | + | `#!python form` | `#!python type[Form | ModelForm]` | The form to convert. | N/A | + | `#!python on_success` | `#!python AsyncFormEvent | SyncFormEvent | None` | A callback function that is called when the form is successfully submitted. | `#!python None` | + | `#!python on_error` | `#!python AsyncFormEvent | SyncFormEvent | None` | A callback function that is called when the form submission fails. | `#!python None` | + | `#!python on_receive_data` | `#!python AsyncFormEvent | SyncFormEvent | None` | A callback function that is called before newly submitted form data is rendered. | `#!python None` | + | `#!python on_change` | `#!python AsyncFormEvent | SyncFormEvent | None` | A callback function that is called when a form field is modified by the user. | `#!python None` | + | `#!python auto_save` | `#!python bool` | If `#!python True`, the form will automatically call `#!python save` on successful submission of a `#!python ModelForm`. This has no effect on regular `#!python Form` instances. | `#!python True` | + | `#!python extra_props` | `#!python dict[str, Any] | None` | Additional properties to add to the `#!html